blob: 9fc9a7ccda053c6060e86aa5c83e858de19e0ce2 [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,
Argyrios Kyrtzidis783e4c22017-06-09 02:04:19 +000055 /*SingleFileParse=*/false,
Ilya Biryukov0f62ed22017-05-26 12:26:51 +000056 /*UserFilesAreVolatile=*/false, /*ForSerialization=*/false,
57 /*ModuleFormat=*/llvm::None,
58 /*ErrAST=*/nullptr, VFS));
59 assert(Unit && "Unit wasn't created");
Ilya Biryukov38d79772017-05-16 09:38:59 +000060}
61
Ilya Biryukov0f62ed22017-05-26 12:26:51 +000062void ClangdUnit::reparse(StringRef Contents,
63 IntrusiveRefCntPtr<vfs::FileSystem> VFS) {
Ilya Biryukov38d79772017-05-16 09:38:59 +000064 // Do a reparse if this wasn't the first parse.
65 // FIXME: This might have the wrong working directory if it changed in the
66 // meantime.
67 ASTUnit::RemappedFile RemappedSource(
68 FileName,
69 llvm::MemoryBuffer::getMemBufferCopy(Contents, FileName).release());
70
Ilya Biryukov0f62ed22017-05-26 12:26:51 +000071 Unit->Reparse(PCHs, RemappedSource, VFS);
Ilya Biryukov38d79772017-05-16 09:38:59 +000072}
73
74namespace {
75
76CompletionItemKind getKind(CXCursorKind K) {
77 switch (K) {
78 case CXCursor_MacroInstantiation:
79 case CXCursor_MacroDefinition:
80 return CompletionItemKind::Text;
81 case CXCursor_CXXMethod:
82 return CompletionItemKind::Method;
83 case CXCursor_FunctionDecl:
84 case CXCursor_FunctionTemplate:
85 return CompletionItemKind::Function;
86 case CXCursor_Constructor:
87 case CXCursor_Destructor:
88 return CompletionItemKind::Constructor;
89 case CXCursor_FieldDecl:
90 return CompletionItemKind::Field;
91 case CXCursor_VarDecl:
92 case CXCursor_ParmDecl:
93 return CompletionItemKind::Variable;
94 case CXCursor_ClassDecl:
95 case CXCursor_StructDecl:
96 case CXCursor_UnionDecl:
97 case CXCursor_ClassTemplate:
98 case CXCursor_ClassTemplatePartialSpecialization:
99 return CompletionItemKind::Class;
100 case CXCursor_Namespace:
101 case CXCursor_NamespaceAlias:
102 case CXCursor_NamespaceRef:
103 return CompletionItemKind::Module;
104 case CXCursor_EnumConstantDecl:
105 return CompletionItemKind::Value;
106 case CXCursor_EnumDecl:
107 return CompletionItemKind::Enum;
108 case CXCursor_TypeAliasDecl:
109 case CXCursor_TypeAliasTemplateDecl:
110 case CXCursor_TypedefDecl:
111 case CXCursor_MemberRef:
112 case CXCursor_TypeRef:
113 return CompletionItemKind::Reference;
114 default:
115 return CompletionItemKind::Missing;
116 }
117}
118
119class CompletionItemsCollector : public CodeCompleteConsumer {
120 std::vector<CompletionItem> *Items;
121 std::shared_ptr<clang::GlobalCodeCompletionAllocator> Allocator;
122 CodeCompletionTUInfo CCTUInfo;
123
124public:
125 CompletionItemsCollector(std::vector<CompletionItem> *Items,
126 const CodeCompleteOptions &CodeCompleteOpts)
127 : CodeCompleteConsumer(CodeCompleteOpts, /*OutputIsBinary=*/false),
128 Items(Items),
129 Allocator(std::make_shared<clang::GlobalCodeCompletionAllocator>()),
130 CCTUInfo(Allocator) {}
131
132 void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context,
133 CodeCompletionResult *Results,
134 unsigned NumResults) override {
135 for (unsigned I = 0; I != NumResults; ++I) {
136 CodeCompletionResult &Result = Results[I];
137 CodeCompletionString *CCS = Result.CreateCodeCompletionString(
138 S, Context, *Allocator, CCTUInfo,
139 CodeCompleteOpts.IncludeBriefComments);
140 if (CCS) {
141 CompletionItem Item;
Krasimir Georgieve6035a52017-06-08 15:11:51 +0000142 for (CodeCompletionString::Chunk C : *CCS) {
143 switch (C.Kind) {
144 case CodeCompletionString::CK_ResultType:
145 Item.detail = C.Text;
146 break;
147 case CodeCompletionString::CK_Optional:
148 break;
149 default:
150 Item.label += C.Text;
151 break;
152 }
153 }
Ilya Biryukov38d79772017-05-16 09:38:59 +0000154 assert(CCS->getTypedText());
Ilya Biryukov38d79772017-05-16 09:38:59 +0000155 Item.kind = getKind(Result.CursorKind);
Krasimir Georgieve6035a52017-06-08 15:11:51 +0000156 Item.insertText = Item.sortText = Item.filterText = CCS->getTypedText();
Ilya Biryukov38d79772017-05-16 09:38:59 +0000157 if (CCS->getBriefComment())
158 Item.documentation = CCS->getBriefComment();
159 Items->push_back(std::move(Item));
160 }
161 }
162 }
163
164 GlobalCodeCompletionAllocator &getAllocator() override { return *Allocator; }
165
166 CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
167};
168} // namespace
169
Ilya Biryukov0f62ed22017-05-26 12:26:51 +0000170std::vector<CompletionItem>
171ClangdUnit::codeComplete(StringRef Contents, Position Pos,
172 IntrusiveRefCntPtr<vfs::FileSystem> VFS) {
Ilya Biryukov38d79772017-05-16 09:38:59 +0000173 CodeCompleteOptions CCO;
174 CCO.IncludeBriefComments = 1;
175 // This is where code completion stores dirty buffers. Need to free after
176 // completion.
177 SmallVector<const llvm::MemoryBuffer *, 4> OwnedBuffers;
178 SmallVector<StoredDiagnostic, 4> StoredDiagnostics;
179 IntrusiveRefCntPtr<DiagnosticsEngine> DiagEngine(
180 new DiagnosticsEngine(new DiagnosticIDs, new DiagnosticOptions));
181 std::vector<CompletionItem> Items;
182 CompletionItemsCollector Collector(&Items, CCO);
183
184 ASTUnit::RemappedFile RemappedSource(
185 FileName,
186 llvm::MemoryBuffer::getMemBufferCopy(Contents, FileName).release());
187
Ilya Biryukov0f62ed22017-05-26 12:26:51 +0000188 IntrusiveRefCntPtr<FileManager> FileMgr(
189 new FileManager(Unit->getFileSystemOpts(), VFS));
Ilya Biryukov38d79772017-05-16 09:38:59 +0000190 IntrusiveRefCntPtr<SourceManager> SourceMgr(
Ilya Biryukov0f62ed22017-05-26 12:26:51 +0000191 new SourceManager(*DiagEngine, *FileMgr));
Ilya Biryukov38d79772017-05-16 09:38:59 +0000192 // CodeComplete seems to require fresh LangOptions.
193 LangOptions LangOpts = Unit->getLangOpts();
194 // The language server protocol uses zero-based line and column numbers.
195 // The clang code completion uses one-based numbers.
196 Unit->CodeComplete(FileName, Pos.line + 1, Pos.character + 1, RemappedSource,
197 CCO.IncludeMacros, CCO.IncludeCodePatterns,
198 CCO.IncludeBriefComments, Collector, PCHs, *DiagEngine,
Ilya Biryukov0f62ed22017-05-26 12:26:51 +0000199 LangOpts, *SourceMgr, *FileMgr, StoredDiagnostics,
200 OwnedBuffers);
Ilya Biryukov38d79772017-05-16 09:38:59 +0000201 for (const llvm::MemoryBuffer *Buffer : OwnedBuffers)
202 delete Buffer;
203 return Items;
204}
205
206namespace {
207/// Convert from clang diagnostic level to LSP severity.
208static int getSeverity(DiagnosticsEngine::Level L) {
209 switch (L) {
210 case DiagnosticsEngine::Remark:
211 return 4;
212 case DiagnosticsEngine::Note:
213 return 3;
214 case DiagnosticsEngine::Warning:
215 return 2;
216 case DiagnosticsEngine::Fatal:
217 case DiagnosticsEngine::Error:
218 return 1;
219 case DiagnosticsEngine::Ignored:
220 return 0;
221 }
222 llvm_unreachable("Unknown diagnostic level!");
223}
224} // namespace
225
226std::vector<DiagWithFixIts> ClangdUnit::getLocalDiagnostics() const {
227 std::vector<DiagWithFixIts> Result;
228 for (ASTUnit::stored_diag_iterator D = Unit->stored_diag_begin(),
229 DEnd = Unit->stored_diag_end();
230 D != DEnd; ++D) {
231 if (!D->getLocation().isValid() ||
232 !D->getLocation().getManager().isInMainFile(D->getLocation()))
233 continue;
234 Position P;
235 P.line = D->getLocation().getSpellingLineNumber() - 1;
236 P.character = D->getLocation().getSpellingColumnNumber();
237 Range R = {P, P};
238 clangd::Diagnostic Diag = {R, getSeverity(D->getLevel()), D->getMessage()};
239
240 llvm::SmallVector<tooling::Replacement, 1> FixItsForDiagnostic;
241 for (const FixItHint &Fix : D->getFixIts()) {
242 FixItsForDiagnostic.push_back(clang::tooling::Replacement(
243 Unit->getSourceManager(), Fix.RemoveRange, Fix.CodeToInsert));
244 }
245 Result.push_back({Diag, std::move(FixItsForDiagnostic)});
246 }
247 return Result;
248}
Ilya Biryukovf01af682017-05-23 13:42:59 +0000249
250void ClangdUnit::dumpAST(llvm::raw_ostream &OS) const {
251 Unit->getASTContext().getTranslationUnitDecl()->dump(OS, true);
252}