blob: 6f51fcd5ad6b642b99a7a0d97ad03f490118caaa [file] [log] [blame]
Ilya Biryukov38d79772017-05-16 09:38:59 +00001//===--- ClangdUnit.cpp -----------------------------------------*- C++-*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===---------------------------------------------------------------------===//
9
10#include "ClangdUnit.h"
11#include "clang/Frontend/ASTUnit.h"
12#include "clang/Frontend/CompilerInstance.h"
13#include "clang/Frontend/CompilerInvocation.h"
Ilya Biryukov0f62ed22017-05-26 12:26:51 +000014#include "clang/Frontend/Utils.h"
Ilya Biryukov38d79772017-05-16 09:38:59 +000015#include "clang/Tooling/CompilationDatabase.h"
16
17using namespace clang::clangd;
18using namespace clang;
19
20ClangdUnit::ClangdUnit(PathRef FileName, StringRef Contents,
21 std::shared_ptr<PCHContainerOperations> PCHs,
Ilya Biryukov0f62ed22017-05-26 12:26:51 +000022 std::vector<tooling::CompileCommand> Commands,
23 IntrusiveRefCntPtr<vfs::FileSystem> VFS)
Ilya Biryukov38d79772017-05-16 09:38:59 +000024 : FileName(FileName), PCHs(PCHs) {
25 assert(!Commands.empty() && "No compile commands provided");
26
27 // Inject the resource dir.
28 // FIXME: Don't overwrite it if it's already there.
29 static int Dummy; // Just an address in this process.
30 std::string ResourceDir =
31 CompilerInvocation::GetResourcesPath("clangd", (void *)&Dummy);
32 Commands.front().CommandLine.push_back("-resource-dir=" + ResourceDir);
33
34 IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
35 CompilerInstance::createDiagnostics(new DiagnosticOptions);
36
37 std::vector<const char *> ArgStrs;
38 for (const auto &S : Commands.front().CommandLine)
39 ArgStrs.push_back(S.c_str());
40
41 ASTUnit::RemappedFile RemappedSource(
42 FileName,
43 llvm::MemoryBuffer::getMemBufferCopy(Contents, FileName).release());
44
45 auto ArgP = &*ArgStrs.begin();
46 Unit = std::unique_ptr<ASTUnit>(ASTUnit::LoadFromCommandLine(
47 ArgP, ArgP + ArgStrs.size(), PCHs, Diags, ResourceDir,
48 /*OnlyLocalDecls=*/false, /*CaptureDiagnostics=*/true, RemappedSource,
49 /*RemappedFilesKeepOriginalName=*/true,
Krasimir Georgievd8145332017-05-22 12:49:08 +000050 /*PrecompilePreambleAfterNParses=*/1, /*TUKind=*/TU_Prefix,
Ilya Biryukov38d79772017-05-16 09:38:59 +000051 /*CacheCodeCompletionResults=*/true,
52 /*IncludeBriefCommentsInCodeCompletion=*/true,
Ilya Biryukov0f62ed22017-05-26 12:26:51 +000053 /*AllowPCHWithCompilerErrors=*/true,
54 /*SkipFunctionBodies=*/false,
55 /*UserFilesAreVolatile=*/false, /*ForSerialization=*/false,
56 /*ModuleFormat=*/llvm::None,
57 /*ErrAST=*/nullptr, VFS));
58 assert(Unit && "Unit wasn't created");
Ilya Biryukov38d79772017-05-16 09:38:59 +000059}
60
Ilya Biryukov0f62ed22017-05-26 12:26:51 +000061void ClangdUnit::reparse(StringRef Contents,
62 IntrusiveRefCntPtr<vfs::FileSystem> VFS) {
Ilya Biryukov38d79772017-05-16 09:38:59 +000063 // Do a reparse if this wasn't the first parse.
64 // FIXME: This might have the wrong working directory if it changed in the
65 // meantime.
66 ASTUnit::RemappedFile RemappedSource(
67 FileName,
68 llvm::MemoryBuffer::getMemBufferCopy(Contents, FileName).release());
69
Ilya Biryukov0f62ed22017-05-26 12:26:51 +000070 Unit->Reparse(PCHs, RemappedSource, VFS);
Ilya Biryukov38d79772017-05-16 09:38:59 +000071}
72
73namespace {
74
75CompletionItemKind getKind(CXCursorKind K) {
76 switch (K) {
77 case CXCursor_MacroInstantiation:
78 case CXCursor_MacroDefinition:
79 return CompletionItemKind::Text;
80 case CXCursor_CXXMethod:
81 return CompletionItemKind::Method;
82 case CXCursor_FunctionDecl:
83 case CXCursor_FunctionTemplate:
84 return CompletionItemKind::Function;
85 case CXCursor_Constructor:
86 case CXCursor_Destructor:
87 return CompletionItemKind::Constructor;
88 case CXCursor_FieldDecl:
89 return CompletionItemKind::Field;
90 case CXCursor_VarDecl:
91 case CXCursor_ParmDecl:
92 return CompletionItemKind::Variable;
93 case CXCursor_ClassDecl:
94 case CXCursor_StructDecl:
95 case CXCursor_UnionDecl:
96 case CXCursor_ClassTemplate:
97 case CXCursor_ClassTemplatePartialSpecialization:
98 return CompletionItemKind::Class;
99 case CXCursor_Namespace:
100 case CXCursor_NamespaceAlias:
101 case CXCursor_NamespaceRef:
102 return CompletionItemKind::Module;
103 case CXCursor_EnumConstantDecl:
104 return CompletionItemKind::Value;
105 case CXCursor_EnumDecl:
106 return CompletionItemKind::Enum;
107 case CXCursor_TypeAliasDecl:
108 case CXCursor_TypeAliasTemplateDecl:
109 case CXCursor_TypedefDecl:
110 case CXCursor_MemberRef:
111 case CXCursor_TypeRef:
112 return CompletionItemKind::Reference;
113 default:
114 return CompletionItemKind::Missing;
115 }
116}
117
118class CompletionItemsCollector : public CodeCompleteConsumer {
119 std::vector<CompletionItem> *Items;
120 std::shared_ptr<clang::GlobalCodeCompletionAllocator> Allocator;
121 CodeCompletionTUInfo CCTUInfo;
122
123public:
124 CompletionItemsCollector(std::vector<CompletionItem> *Items,
125 const CodeCompleteOptions &CodeCompleteOpts)
126 : CodeCompleteConsumer(CodeCompleteOpts, /*OutputIsBinary=*/false),
127 Items(Items),
128 Allocator(std::make_shared<clang::GlobalCodeCompletionAllocator>()),
129 CCTUInfo(Allocator) {}
130
131 void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context,
132 CodeCompletionResult *Results,
133 unsigned NumResults) override {
134 for (unsigned I = 0; I != NumResults; ++I) {
135 CodeCompletionResult &Result = Results[I];
136 CodeCompletionString *CCS = Result.CreateCodeCompletionString(
137 S, Context, *Allocator, CCTUInfo,
138 CodeCompleteOpts.IncludeBriefComments);
139 if (CCS) {
140 CompletionItem Item;
141 assert(CCS->getTypedText());
142 Item.label = CCS->getTypedText();
143 Item.kind = getKind(Result.CursorKind);
144 if (CCS->getBriefComment())
145 Item.documentation = CCS->getBriefComment();
146 Items->push_back(std::move(Item));
147 }
148 }
149 }
150
151 GlobalCodeCompletionAllocator &getAllocator() override { return *Allocator; }
152
153 CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
154};
155} // namespace
156
Ilya Biryukov0f62ed22017-05-26 12:26:51 +0000157std::vector<CompletionItem>
158ClangdUnit::codeComplete(StringRef Contents, Position Pos,
159 IntrusiveRefCntPtr<vfs::FileSystem> VFS) {
Ilya Biryukov38d79772017-05-16 09:38:59 +0000160 CodeCompleteOptions CCO;
161 CCO.IncludeBriefComments = 1;
162 // This is where code completion stores dirty buffers. Need to free after
163 // completion.
164 SmallVector<const llvm::MemoryBuffer *, 4> OwnedBuffers;
165 SmallVector<StoredDiagnostic, 4> StoredDiagnostics;
166 IntrusiveRefCntPtr<DiagnosticsEngine> DiagEngine(
167 new DiagnosticsEngine(new DiagnosticIDs, new DiagnosticOptions));
168 std::vector<CompletionItem> Items;
169 CompletionItemsCollector Collector(&Items, CCO);
170
171 ASTUnit::RemappedFile RemappedSource(
172 FileName,
173 llvm::MemoryBuffer::getMemBufferCopy(Contents, FileName).release());
174
Ilya Biryukov0f62ed22017-05-26 12:26:51 +0000175 IntrusiveRefCntPtr<FileManager> FileMgr(
176 new FileManager(Unit->getFileSystemOpts(), VFS));
Ilya Biryukov38d79772017-05-16 09:38:59 +0000177 IntrusiveRefCntPtr<SourceManager> SourceMgr(
Ilya Biryukov0f62ed22017-05-26 12:26:51 +0000178 new SourceManager(*DiagEngine, *FileMgr));
Ilya Biryukov38d79772017-05-16 09:38:59 +0000179 // CodeComplete seems to require fresh LangOptions.
180 LangOptions LangOpts = Unit->getLangOpts();
181 // The language server protocol uses zero-based line and column numbers.
182 // The clang code completion uses one-based numbers.
183 Unit->CodeComplete(FileName, Pos.line + 1, Pos.character + 1, RemappedSource,
184 CCO.IncludeMacros, CCO.IncludeCodePatterns,
185 CCO.IncludeBriefComments, Collector, PCHs, *DiagEngine,
Ilya Biryukov0f62ed22017-05-26 12:26:51 +0000186 LangOpts, *SourceMgr, *FileMgr, StoredDiagnostics,
187 OwnedBuffers);
Ilya Biryukov38d79772017-05-16 09:38:59 +0000188 for (const llvm::MemoryBuffer *Buffer : OwnedBuffers)
189 delete Buffer;
190 return Items;
191}
192
193namespace {
194/// Convert from clang diagnostic level to LSP severity.
195static int getSeverity(DiagnosticsEngine::Level L) {
196 switch (L) {
197 case DiagnosticsEngine::Remark:
198 return 4;
199 case DiagnosticsEngine::Note:
200 return 3;
201 case DiagnosticsEngine::Warning:
202 return 2;
203 case DiagnosticsEngine::Fatal:
204 case DiagnosticsEngine::Error:
205 return 1;
206 case DiagnosticsEngine::Ignored:
207 return 0;
208 }
209 llvm_unreachable("Unknown diagnostic level!");
210}
211} // namespace
212
213std::vector<DiagWithFixIts> ClangdUnit::getLocalDiagnostics() const {
214 std::vector<DiagWithFixIts> Result;
215 for (ASTUnit::stored_diag_iterator D = Unit->stored_diag_begin(),
216 DEnd = Unit->stored_diag_end();
217 D != DEnd; ++D) {
218 if (!D->getLocation().isValid() ||
219 !D->getLocation().getManager().isInMainFile(D->getLocation()))
220 continue;
221 Position P;
222 P.line = D->getLocation().getSpellingLineNumber() - 1;
223 P.character = D->getLocation().getSpellingColumnNumber();
224 Range R = {P, P};
225 clangd::Diagnostic Diag = {R, getSeverity(D->getLevel()), D->getMessage()};
226
227 llvm::SmallVector<tooling::Replacement, 1> FixItsForDiagnostic;
228 for (const FixItHint &Fix : D->getFixIts()) {
229 FixItsForDiagnostic.push_back(clang::tooling::Replacement(
230 Unit->getSourceManager(), Fix.RemoveRange, Fix.CodeToInsert));
231 }
232 Result.push_back({Diag, std::move(FixItsForDiagnostic)});
233 }
234 return Result;
235}
Ilya Biryukovf01af682017-05-23 13:42:59 +0000236
237void ClangdUnit::dumpAST(llvm::raw_ostream &OS) const {
238 Unit->getASTContext().getTranslationUnitDecl()->dump(OS, true);
239}