blob: 9f02187ad12ee71bcb8e3c3425b71938ba2e974b [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"
Marc-Andre Laperle2cbf0372017-06-28 16:12:10 +000011
Ilya Biryukov38d79772017-05-16 09:38:59 +000012#include "clang/Frontend/ASTUnit.h"
13#include "clang/Frontend/CompilerInstance.h"
14#include "clang/Frontend/CompilerInvocation.h"
Ilya Biryukov0f62ed22017-05-26 12:26:51 +000015#include "clang/Frontend/Utils.h"
Marc-Andre Laperle2cbf0372017-06-28 16:12:10 +000016#include "clang/Index/IndexingAction.h"
17#include "clang/Index/IndexDataConsumer.h"
18#include "clang/Lex/Lexer.h"
19#include "clang/Lex/MacroInfo.h"
20#include "clang/Lex/Preprocessor.h"
Ilya Biryukov38d79772017-05-16 09:38:59 +000021#include "clang/Tooling/CompilationDatabase.h"
Krasimir Georgieva1de3c92017-06-15 09:11:57 +000022#include "llvm/Support/Format.h"
Ilya Biryukov38d79772017-05-16 09:38:59 +000023
Marc-Andre Laperle2cbf0372017-06-28 16:12:10 +000024#include <algorithm>
25
Ilya Biryukov38d79772017-05-16 09:38:59 +000026using namespace clang::clangd;
27using namespace clang;
28
29ClangdUnit::ClangdUnit(PathRef FileName, StringRef Contents,
Ilya Biryukova46f7a92017-06-28 10:34:50 +000030 StringRef ResourceDir,
Ilya Biryukov38d79772017-05-16 09:38:59 +000031 std::shared_ptr<PCHContainerOperations> PCHs,
Ilya Biryukov0f62ed22017-05-26 12:26:51 +000032 std::vector<tooling::CompileCommand> Commands,
33 IntrusiveRefCntPtr<vfs::FileSystem> VFS)
Ilya Biryukov38d79772017-05-16 09:38:59 +000034 : FileName(FileName), PCHs(PCHs) {
35 assert(!Commands.empty() && "No compile commands provided");
36
37 // Inject the resource dir.
38 // FIXME: Don't overwrite it if it's already there.
Kirill Bobyrev46213872017-06-28 20:57:28 +000039 Commands.front().CommandLine.push_back("-resource-dir=" +
40 std::string(ResourceDir));
Ilya Biryukov38d79772017-05-16 09:38:59 +000041
42 IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
43 CompilerInstance::createDiagnostics(new DiagnosticOptions);
44
45 std::vector<const char *> ArgStrs;
46 for (const auto &S : Commands.front().CommandLine)
47 ArgStrs.push_back(S.c_str());
48
49 ASTUnit::RemappedFile RemappedSource(
50 FileName,
51 llvm::MemoryBuffer::getMemBufferCopy(Contents, FileName).release());
52
53 auto ArgP = &*ArgStrs.begin();
54 Unit = std::unique_ptr<ASTUnit>(ASTUnit::LoadFromCommandLine(
55 ArgP, ArgP + ArgStrs.size(), PCHs, Diags, ResourceDir,
56 /*OnlyLocalDecls=*/false, /*CaptureDiagnostics=*/true, RemappedSource,
57 /*RemappedFilesKeepOriginalName=*/true,
Krasimir Georgievd8145332017-05-22 12:49:08 +000058 /*PrecompilePreambleAfterNParses=*/1, /*TUKind=*/TU_Prefix,
Ilya Biryukov38d79772017-05-16 09:38:59 +000059 /*CacheCodeCompletionResults=*/true,
60 /*IncludeBriefCommentsInCodeCompletion=*/true,
Ilya Biryukov0f62ed22017-05-26 12:26:51 +000061 /*AllowPCHWithCompilerErrors=*/true,
62 /*SkipFunctionBodies=*/false,
Argyrios Kyrtzidis783e4c22017-06-09 02:04:19 +000063 /*SingleFileParse=*/false,
Ilya Biryukov0f62ed22017-05-26 12:26:51 +000064 /*UserFilesAreVolatile=*/false, /*ForSerialization=*/false,
65 /*ModuleFormat=*/llvm::None,
66 /*ErrAST=*/nullptr, VFS));
67 assert(Unit && "Unit wasn't created");
Ilya Biryukov38d79772017-05-16 09:38:59 +000068}
69
Ilya Biryukov0f62ed22017-05-26 12:26:51 +000070void ClangdUnit::reparse(StringRef Contents,
71 IntrusiveRefCntPtr<vfs::FileSystem> VFS) {
Ilya Biryukov38d79772017-05-16 09:38:59 +000072 // Do a reparse if this wasn't the first parse.
73 // FIXME: This might have the wrong working directory if it changed in the
74 // meantime.
75 ASTUnit::RemappedFile RemappedSource(
76 FileName,
77 llvm::MemoryBuffer::getMemBufferCopy(Contents, FileName).release());
78
Ilya Biryukov0f62ed22017-05-26 12:26:51 +000079 Unit->Reparse(PCHs, RemappedSource, VFS);
Ilya Biryukov38d79772017-05-16 09:38:59 +000080}
81
82namespace {
83
84CompletionItemKind getKind(CXCursorKind K) {
85 switch (K) {
86 case CXCursor_MacroInstantiation:
87 case CXCursor_MacroDefinition:
88 return CompletionItemKind::Text;
89 case CXCursor_CXXMethod:
90 return CompletionItemKind::Method;
91 case CXCursor_FunctionDecl:
92 case CXCursor_FunctionTemplate:
93 return CompletionItemKind::Function;
94 case CXCursor_Constructor:
95 case CXCursor_Destructor:
96 return CompletionItemKind::Constructor;
97 case CXCursor_FieldDecl:
98 return CompletionItemKind::Field;
99 case CXCursor_VarDecl:
100 case CXCursor_ParmDecl:
101 return CompletionItemKind::Variable;
102 case CXCursor_ClassDecl:
103 case CXCursor_StructDecl:
104 case CXCursor_UnionDecl:
105 case CXCursor_ClassTemplate:
106 case CXCursor_ClassTemplatePartialSpecialization:
107 return CompletionItemKind::Class;
108 case CXCursor_Namespace:
109 case CXCursor_NamespaceAlias:
110 case CXCursor_NamespaceRef:
111 return CompletionItemKind::Module;
112 case CXCursor_EnumConstantDecl:
113 return CompletionItemKind::Value;
114 case CXCursor_EnumDecl:
115 return CompletionItemKind::Enum;
116 case CXCursor_TypeAliasDecl:
117 case CXCursor_TypeAliasTemplateDecl:
118 case CXCursor_TypedefDecl:
119 case CXCursor_MemberRef:
120 case CXCursor_TypeRef:
121 return CompletionItemKind::Reference;
122 default:
123 return CompletionItemKind::Missing;
124 }
125}
126
127class CompletionItemsCollector : public CodeCompleteConsumer {
128 std::vector<CompletionItem> *Items;
129 std::shared_ptr<clang::GlobalCodeCompletionAllocator> Allocator;
130 CodeCompletionTUInfo CCTUInfo;
131
132public:
133 CompletionItemsCollector(std::vector<CompletionItem> *Items,
134 const CodeCompleteOptions &CodeCompleteOpts)
135 : CodeCompleteConsumer(CodeCompleteOpts, /*OutputIsBinary=*/false),
136 Items(Items),
137 Allocator(std::make_shared<clang::GlobalCodeCompletionAllocator>()),
138 CCTUInfo(Allocator) {}
139
140 void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context,
141 CodeCompletionResult *Results,
142 unsigned NumResults) override {
143 for (unsigned I = 0; I != NumResults; ++I) {
144 CodeCompletionResult &Result = Results[I];
145 CodeCompletionString *CCS = Result.CreateCodeCompletionString(
146 S, Context, *Allocator, CCTUInfo,
147 CodeCompleteOpts.IncludeBriefComments);
148 if (CCS) {
149 CompletionItem Item;
Krasimir Georgieve6035a52017-06-08 15:11:51 +0000150 for (CodeCompletionString::Chunk C : *CCS) {
151 switch (C.Kind) {
152 case CodeCompletionString::CK_ResultType:
153 Item.detail = C.Text;
154 break;
155 case CodeCompletionString::CK_Optional:
156 break;
157 default:
158 Item.label += C.Text;
159 break;
160 }
161 }
Ilya Biryukov38d79772017-05-16 09:38:59 +0000162 assert(CCS->getTypedText());
Ilya Biryukov38d79772017-05-16 09:38:59 +0000163 Item.kind = getKind(Result.CursorKind);
Krasimir Georgieva1de3c92017-06-15 09:11:57 +0000164 // Priority is a 16-bit integer, hence at most 5 digits.
165 // Since identifiers with higher priority need to come first,
166 // we subtract the priority from 99999.
167 // For example, the sort text of the identifier 'a' with priority 35
168 // is 99964a.
169 assert(CCS->getPriority() < 99999 && "Expecting code completion result "
170 "priority to have at most "
171 "5-digits");
172 llvm::raw_string_ostream(Item.sortText) << llvm::format(
173 "%05d%s", 99999 - CCS->getPriority(), CCS->getTypedText());
174 Item.insertText = Item.filterText = CCS->getTypedText();
Ilya Biryukov38d79772017-05-16 09:38:59 +0000175 if (CCS->getBriefComment())
176 Item.documentation = CCS->getBriefComment();
177 Items->push_back(std::move(Item));
178 }
179 }
180 }
181
182 GlobalCodeCompletionAllocator &getAllocator() override { return *Allocator; }
183
184 CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
185};
186} // namespace
187
Ilya Biryukov0f62ed22017-05-26 12:26:51 +0000188std::vector<CompletionItem>
189ClangdUnit::codeComplete(StringRef Contents, Position Pos,
190 IntrusiveRefCntPtr<vfs::FileSystem> VFS) {
Ilya Biryukov38d79772017-05-16 09:38:59 +0000191 CodeCompleteOptions CCO;
192 CCO.IncludeBriefComments = 1;
193 // This is where code completion stores dirty buffers. Need to free after
194 // completion.
195 SmallVector<const llvm::MemoryBuffer *, 4> OwnedBuffers;
196 SmallVector<StoredDiagnostic, 4> StoredDiagnostics;
197 IntrusiveRefCntPtr<DiagnosticsEngine> DiagEngine(
198 new DiagnosticsEngine(new DiagnosticIDs, new DiagnosticOptions));
199 std::vector<CompletionItem> Items;
200 CompletionItemsCollector Collector(&Items, CCO);
201
202 ASTUnit::RemappedFile RemappedSource(
203 FileName,
204 llvm::MemoryBuffer::getMemBufferCopy(Contents, FileName).release());
205
Ilya Biryukov0f62ed22017-05-26 12:26:51 +0000206 IntrusiveRefCntPtr<FileManager> FileMgr(
207 new FileManager(Unit->getFileSystemOpts(), VFS));
Ilya Biryukov38d79772017-05-16 09:38:59 +0000208 IntrusiveRefCntPtr<SourceManager> SourceMgr(
Ilya Biryukov0f62ed22017-05-26 12:26:51 +0000209 new SourceManager(*DiagEngine, *FileMgr));
Ilya Biryukov38d79772017-05-16 09:38:59 +0000210 // CodeComplete seems to require fresh LangOptions.
211 LangOptions LangOpts = Unit->getLangOpts();
212 // The language server protocol uses zero-based line and column numbers.
213 // The clang code completion uses one-based numbers.
214 Unit->CodeComplete(FileName, Pos.line + 1, Pos.character + 1, RemappedSource,
215 CCO.IncludeMacros, CCO.IncludeCodePatterns,
216 CCO.IncludeBriefComments, Collector, PCHs, *DiagEngine,
Ilya Biryukov0f62ed22017-05-26 12:26:51 +0000217 LangOpts, *SourceMgr, *FileMgr, StoredDiagnostics,
218 OwnedBuffers);
Ilya Biryukov38d79772017-05-16 09:38:59 +0000219 for (const llvm::MemoryBuffer *Buffer : OwnedBuffers)
220 delete Buffer;
221 return Items;
222}
223
224namespace {
225/// Convert from clang diagnostic level to LSP severity.
226static int getSeverity(DiagnosticsEngine::Level L) {
227 switch (L) {
228 case DiagnosticsEngine::Remark:
229 return 4;
230 case DiagnosticsEngine::Note:
231 return 3;
232 case DiagnosticsEngine::Warning:
233 return 2;
234 case DiagnosticsEngine::Fatal:
235 case DiagnosticsEngine::Error:
236 return 1;
237 case DiagnosticsEngine::Ignored:
238 return 0;
239 }
240 llvm_unreachable("Unknown diagnostic level!");
241}
242} // namespace
243
244std::vector<DiagWithFixIts> ClangdUnit::getLocalDiagnostics() const {
245 std::vector<DiagWithFixIts> Result;
246 for (ASTUnit::stored_diag_iterator D = Unit->stored_diag_begin(),
247 DEnd = Unit->stored_diag_end();
248 D != DEnd; ++D) {
249 if (!D->getLocation().isValid() ||
250 !D->getLocation().getManager().isInMainFile(D->getLocation()))
251 continue;
252 Position P;
253 P.line = D->getLocation().getSpellingLineNumber() - 1;
254 P.character = D->getLocation().getSpellingColumnNumber();
255 Range R = {P, P};
256 clangd::Diagnostic Diag = {R, getSeverity(D->getLevel()), D->getMessage()};
257
258 llvm::SmallVector<tooling::Replacement, 1> FixItsForDiagnostic;
259 for (const FixItHint &Fix : D->getFixIts()) {
260 FixItsForDiagnostic.push_back(clang::tooling::Replacement(
261 Unit->getSourceManager(), Fix.RemoveRange, Fix.CodeToInsert));
262 }
263 Result.push_back({Diag, std::move(FixItsForDiagnostic)});
264 }
265 return Result;
266}
Ilya Biryukovf01af682017-05-23 13:42:59 +0000267
268void ClangdUnit::dumpAST(llvm::raw_ostream &OS) const {
269 Unit->getASTContext().getTranslationUnitDecl()->dump(OS, true);
270}
Marc-Andre Laperle2cbf0372017-06-28 16:12:10 +0000271
272namespace {
273/// Finds declarations locations that a given source location refers to.
274class DeclarationLocationsFinder : public index::IndexDataConsumer {
275 std::vector<Location> DeclarationLocations;
276 const SourceLocation &SearchedLocation;
277 ASTUnit &Unit;
278public:
279 DeclarationLocationsFinder(raw_ostream &OS,
280 const SourceLocation &SearchedLocation, ASTUnit &Unit) :
Kirill Bobyrev46213872017-06-28 20:57:28 +0000281 SearchedLocation(SearchedLocation), Unit(Unit) {}
Marc-Andre Laperle2cbf0372017-06-28 16:12:10 +0000282
283 std::vector<Location> takeLocations() {
284 // Don't keep the same location multiple times.
285 // This can happen when nodes in the AST are visited twice.
286 std::sort(DeclarationLocations.begin(), DeclarationLocations.end());
Kirill Bobyrev46213872017-06-28 20:57:28 +0000287 auto last =
288 std::unique(DeclarationLocations.begin(), DeclarationLocations.end());
Marc-Andre Laperle2cbf0372017-06-28 16:12:10 +0000289 DeclarationLocations.erase(last, DeclarationLocations.end());
290 return std::move(DeclarationLocations);
291 }
292
293 bool handleDeclOccurence(const Decl* D, index::SymbolRoleSet Roles,
294 ArrayRef<index::SymbolRelation> Relations, FileID FID, unsigned Offset,
295 index::IndexDataConsumer::ASTNodeInfo ASTNode) override
296 {
297 if (isSearchedLocation(FID, Offset)) {
298 addDeclarationLocation(D->getSourceRange());
299 }
300 return true;
301 }
302
303private:
304 bool isSearchedLocation(FileID FID, unsigned Offset) const {
305 const SourceManager &SourceMgr = Unit.getSourceManager();
306 return SourceMgr.getFileOffset(SearchedLocation) == Offset
307 && SourceMgr.getFileID(SearchedLocation) == FID;
308 }
309
310 void addDeclarationLocation(const SourceRange& ValSourceRange) {
311 const SourceManager& SourceMgr = Unit.getSourceManager();
312 const LangOptions& LangOpts = Unit.getLangOpts();
313 SourceLocation LocStart = ValSourceRange.getBegin();
314 SourceLocation LocEnd = Lexer::getLocForEndOfToken(ValSourceRange.getEnd(),
315 0, SourceMgr, LangOpts);
Kirill Bobyrev46213872017-06-28 20:57:28 +0000316 Position Begin;
317 Begin.line = SourceMgr.getSpellingLineNumber(LocStart) - 1;
318 Begin.character = SourceMgr.getSpellingColumnNumber(LocStart) - 1;
319 Position End;
320 End.line = SourceMgr.getSpellingLineNumber(LocEnd) - 1;
321 End.character = SourceMgr.getSpellingColumnNumber(LocEnd) - 1;
322 Range R = {Begin, End};
Marc-Andre Laperle2cbf0372017-06-28 16:12:10 +0000323 Location L;
324 L.uri = URI::fromFile(
325 SourceMgr.getFilename(SourceMgr.getSpellingLoc(LocStart)));
326 L.range = R;
327 DeclarationLocations.push_back(L);
328 }
329
Kirill Bobyrev46213872017-06-28 20:57:28 +0000330 void finish() override {
Marc-Andre Laperle2cbf0372017-06-28 16:12:10 +0000331 // Also handle possible macro at the searched location.
332 Token Result;
333 if (!Lexer::getRawToken(SearchedLocation, Result, Unit.getSourceManager(),
334 Unit.getASTContext().getLangOpts(), false)) {
335 if (Result.is(tok::raw_identifier)) {
336 Unit.getPreprocessor().LookUpIdentifierInfo(Result);
337 }
338 IdentifierInfo* IdentifierInfo = Result.getIdentifierInfo();
339 if (IdentifierInfo && IdentifierInfo->hadMacroDefinition()) {
340 std::pair<FileID, unsigned int> DecLoc =
341 Unit.getSourceManager().getDecomposedExpansionLoc(SearchedLocation);
342 // Get the definition just before the searched location so that a macro
343 // referenced in a '#undef MACRO' can still be found.
344 SourceLocation BeforeSearchedLocation = Unit.getLocation(
345 Unit.getSourceManager().getFileEntryForID(DecLoc.first),
346 DecLoc.second - 1);
347 MacroDefinition MacroDef =
348 Unit.getPreprocessor().getMacroDefinitionAtLoc(IdentifierInfo,
349 BeforeSearchedLocation);
350 MacroInfo* MacroInf = MacroDef.getMacroInfo();
351 if (MacroInf) {
352 addDeclarationLocation(
353 SourceRange(MacroInf->getDefinitionLoc(),
354 MacroInf->getDefinitionEndLoc()));
355 }
356 }
357 }
358 }
359};
360} // namespace
361
362std::vector<Location> ClangdUnit::findDefinitions(Position Pos) {
363 const FileEntry *FE = Unit->getFileManager().getFile(Unit->getMainFileName());
364 if (!FE)
365 return {};
366
367 SourceLocation SourceLocationBeg = getBeginningOfIdentifier(Pos, FE);
368
Kirill Bobyrev46213872017-06-28 20:57:28 +0000369 auto DeclLocationsFinder = std::make_shared<DeclarationLocationsFinder>(
370 llvm::errs(), SourceLocationBeg, *Unit);
Marc-Andre Laperle2cbf0372017-06-28 16:12:10 +0000371 index::IndexingOptions IndexOpts;
372 IndexOpts.SystemSymbolFilter =
373 index::IndexingOptions::SystemSymbolFilterKind::All;
374 IndexOpts.IndexFunctionLocals = true;
375 index::indexASTUnit(*Unit, DeclLocationsFinder, IndexOpts);
376
377 return DeclLocationsFinder->takeLocations();
378}
379
380SourceLocation ClangdUnit::getBeginningOfIdentifier(const Position &Pos,
381 const FileEntry *FE) const {
382 // The language server protocol uses zero-based line and column numbers.
383 // Clang uses one-based numbers.
384 SourceLocation InputLocation = Unit->getLocation(FE, Pos.line + 1,
385 Pos.character + 1);
386
387 if (Pos.character == 0) {
388 return InputLocation;
389 }
390
391 // This handle cases where the position is in the middle of a token or right
392 // after the end of a token. In theory we could just use GetBeginningOfToken
393 // to find the start of the token at the input position, but this doesn't
394 // work when right after the end, i.e. foo|.
395 // So try to go back by one and see if we're still inside the an identifier
396 // token. If so, Take the beginning of this token.
397 // (It should be the same identifier because you can't have two adjacent
398 // identifiers without another token in between.)
399 SourceLocation PeekBeforeLocation = Unit->getLocation(FE, Pos.line + 1,
400 Pos.character);
401 const SourceManager &SourceMgr = Unit->getSourceManager();
402 Token Result;
Ilya Biryukov4203d2a2017-06-29 17:11:32 +0000403 if (Lexer::getRawToken(PeekBeforeLocation, Result, SourceMgr,
404 Unit->getASTContext().getLangOpts(), false)) {
405 // getRawToken failed, just use InputLocation.
406 return InputLocation;
407 }
408
Marc-Andre Laperle2cbf0372017-06-28 16:12:10 +0000409 if (Result.is(tok::raw_identifier)) {
410 return Lexer::GetBeginningOfToken(PeekBeforeLocation, SourceMgr,
411 Unit->getASTContext().getLangOpts());
412 }
413
414 return InputLocation;
415}