blob: 218b2c75be2d5d5d9ae71710624e0559b6dfa9df [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"
Krasimir Georgieva1de3c92017-06-15 09:11:57 +000016#include "llvm/Support/Format.h"
Ilya Biryukov38d79772017-05-16 09:38:59 +000017
18using namespace clang::clangd;
19using namespace clang;
20
21ClangdUnit::ClangdUnit(PathRef FileName, StringRef Contents,
22 std::shared_ptr<PCHContainerOperations> PCHs,
Ilya Biryukov0f62ed22017-05-26 12:26:51 +000023 std::vector<tooling::CompileCommand> Commands,
24 IntrusiveRefCntPtr<vfs::FileSystem> VFS)
Ilya Biryukov38d79772017-05-16 09:38:59 +000025 : FileName(FileName), PCHs(PCHs) {
26 assert(!Commands.empty() && "No compile commands provided");
27
28 // Inject the resource dir.
29 // FIXME: Don't overwrite it if it's already there.
30 static int Dummy; // Just an address in this process.
31 std::string ResourceDir =
32 CompilerInvocation::GetResourcesPath("clangd", (void *)&Dummy);
33 Commands.front().CommandLine.push_back("-resource-dir=" + ResourceDir);
34
35 IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
36 CompilerInstance::createDiagnostics(new DiagnosticOptions);
37
38 std::vector<const char *> ArgStrs;
39 for (const auto &S : Commands.front().CommandLine)
40 ArgStrs.push_back(S.c_str());
41
42 ASTUnit::RemappedFile RemappedSource(
43 FileName,
44 llvm::MemoryBuffer::getMemBufferCopy(Contents, FileName).release());
45
46 auto ArgP = &*ArgStrs.begin();
47 Unit = std::unique_ptr<ASTUnit>(ASTUnit::LoadFromCommandLine(
48 ArgP, ArgP + ArgStrs.size(), PCHs, Diags, ResourceDir,
49 /*OnlyLocalDecls=*/false, /*CaptureDiagnostics=*/true, RemappedSource,
50 /*RemappedFilesKeepOriginalName=*/true,
Krasimir Georgievd8145332017-05-22 12:49:08 +000051 /*PrecompilePreambleAfterNParses=*/1, /*TUKind=*/TU_Prefix,
Ilya Biryukov38d79772017-05-16 09:38:59 +000052 /*CacheCodeCompletionResults=*/true,
53 /*IncludeBriefCommentsInCodeCompletion=*/true,
Ilya Biryukov0f62ed22017-05-26 12:26:51 +000054 /*AllowPCHWithCompilerErrors=*/true,
55 /*SkipFunctionBodies=*/false,
Argyrios Kyrtzidis783e4c22017-06-09 02:04:19 +000056 /*SingleFileParse=*/false,
Ilya Biryukov0f62ed22017-05-26 12:26:51 +000057 /*UserFilesAreVolatile=*/false, /*ForSerialization=*/false,
58 /*ModuleFormat=*/llvm::None,
59 /*ErrAST=*/nullptr, VFS));
60 assert(Unit && "Unit wasn't created");
Ilya Biryukov38d79772017-05-16 09:38:59 +000061}
62
Ilya Biryukov0f62ed22017-05-26 12:26:51 +000063void ClangdUnit::reparse(StringRef Contents,
64 IntrusiveRefCntPtr<vfs::FileSystem> VFS) {
Ilya Biryukov38d79772017-05-16 09:38:59 +000065 // Do a reparse if this wasn't the first parse.
66 // FIXME: This might have the wrong working directory if it changed in the
67 // meantime.
68 ASTUnit::RemappedFile RemappedSource(
69 FileName,
70 llvm::MemoryBuffer::getMemBufferCopy(Contents, FileName).release());
71
Ilya Biryukov0f62ed22017-05-26 12:26:51 +000072 Unit->Reparse(PCHs, RemappedSource, VFS);
Ilya Biryukov38d79772017-05-16 09:38:59 +000073}
74
75namespace {
76
77CompletionItemKind getKind(CXCursorKind K) {
78 switch (K) {
79 case CXCursor_MacroInstantiation:
80 case CXCursor_MacroDefinition:
81 return CompletionItemKind::Text;
82 case CXCursor_CXXMethod:
83 return CompletionItemKind::Method;
84 case CXCursor_FunctionDecl:
85 case CXCursor_FunctionTemplate:
86 return CompletionItemKind::Function;
87 case CXCursor_Constructor:
88 case CXCursor_Destructor:
89 return CompletionItemKind::Constructor;
90 case CXCursor_FieldDecl:
91 return CompletionItemKind::Field;
92 case CXCursor_VarDecl:
93 case CXCursor_ParmDecl:
94 return CompletionItemKind::Variable;
95 case CXCursor_ClassDecl:
96 case CXCursor_StructDecl:
97 case CXCursor_UnionDecl:
98 case CXCursor_ClassTemplate:
99 case CXCursor_ClassTemplatePartialSpecialization:
100 return CompletionItemKind::Class;
101 case CXCursor_Namespace:
102 case CXCursor_NamespaceAlias:
103 case CXCursor_NamespaceRef:
104 return CompletionItemKind::Module;
105 case CXCursor_EnumConstantDecl:
106 return CompletionItemKind::Value;
107 case CXCursor_EnumDecl:
108 return CompletionItemKind::Enum;
109 case CXCursor_TypeAliasDecl:
110 case CXCursor_TypeAliasTemplateDecl:
111 case CXCursor_TypedefDecl:
112 case CXCursor_MemberRef:
113 case CXCursor_TypeRef:
114 return CompletionItemKind::Reference;
115 default:
116 return CompletionItemKind::Missing;
117 }
118}
119
120class CompletionItemsCollector : public CodeCompleteConsumer {
121 std::vector<CompletionItem> *Items;
122 std::shared_ptr<clang::GlobalCodeCompletionAllocator> Allocator;
123 CodeCompletionTUInfo CCTUInfo;
124
125public:
126 CompletionItemsCollector(std::vector<CompletionItem> *Items,
127 const CodeCompleteOptions &CodeCompleteOpts)
128 : CodeCompleteConsumer(CodeCompleteOpts, /*OutputIsBinary=*/false),
129 Items(Items),
130 Allocator(std::make_shared<clang::GlobalCodeCompletionAllocator>()),
131 CCTUInfo(Allocator) {}
132
133 void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context,
134 CodeCompletionResult *Results,
135 unsigned NumResults) override {
136 for (unsigned I = 0; I != NumResults; ++I) {
137 CodeCompletionResult &Result = Results[I];
138 CodeCompletionString *CCS = Result.CreateCodeCompletionString(
139 S, Context, *Allocator, CCTUInfo,
140 CodeCompleteOpts.IncludeBriefComments);
141 if (CCS) {
142 CompletionItem Item;
Krasimir Georgieve6035a52017-06-08 15:11:51 +0000143 for (CodeCompletionString::Chunk C : *CCS) {
144 switch (C.Kind) {
145 case CodeCompletionString::CK_ResultType:
146 Item.detail = C.Text;
147 break;
148 case CodeCompletionString::CK_Optional:
149 break;
150 default:
151 Item.label += C.Text;
152 break;
153 }
154 }
Ilya Biryukov38d79772017-05-16 09:38:59 +0000155 assert(CCS->getTypedText());
Ilya Biryukov38d79772017-05-16 09:38:59 +0000156 Item.kind = getKind(Result.CursorKind);
Krasimir Georgieva1de3c92017-06-15 09:11:57 +0000157 // Priority is a 16-bit integer, hence at most 5 digits.
158 // Since identifiers with higher priority need to come first,
159 // we subtract the priority from 99999.
160 // For example, the sort text of the identifier 'a' with priority 35
161 // is 99964a.
162 assert(CCS->getPriority() < 99999 && "Expecting code completion result "
163 "priority to have at most "
164 "5-digits");
165 llvm::raw_string_ostream(Item.sortText) << llvm::format(
166 "%05d%s", 99999 - CCS->getPriority(), CCS->getTypedText());
167 Item.insertText = Item.filterText = CCS->getTypedText();
Ilya Biryukov38d79772017-05-16 09:38:59 +0000168 if (CCS->getBriefComment())
169 Item.documentation = CCS->getBriefComment();
170 Items->push_back(std::move(Item));
171 }
172 }
173 }
174
175 GlobalCodeCompletionAllocator &getAllocator() override { return *Allocator; }
176
177 CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
178};
179} // namespace
180
Ilya Biryukov0f62ed22017-05-26 12:26:51 +0000181std::vector<CompletionItem>
182ClangdUnit::codeComplete(StringRef Contents, Position Pos,
183 IntrusiveRefCntPtr<vfs::FileSystem> VFS) {
Ilya Biryukov38d79772017-05-16 09:38:59 +0000184 CodeCompleteOptions CCO;
185 CCO.IncludeBriefComments = 1;
186 // This is where code completion stores dirty buffers. Need to free after
187 // completion.
188 SmallVector<const llvm::MemoryBuffer *, 4> OwnedBuffers;
189 SmallVector<StoredDiagnostic, 4> StoredDiagnostics;
190 IntrusiveRefCntPtr<DiagnosticsEngine> DiagEngine(
191 new DiagnosticsEngine(new DiagnosticIDs, new DiagnosticOptions));
192 std::vector<CompletionItem> Items;
193 CompletionItemsCollector Collector(&Items, CCO);
194
195 ASTUnit::RemappedFile RemappedSource(
196 FileName,
197 llvm::MemoryBuffer::getMemBufferCopy(Contents, FileName).release());
198
Ilya Biryukov0f62ed22017-05-26 12:26:51 +0000199 IntrusiveRefCntPtr<FileManager> FileMgr(
200 new FileManager(Unit->getFileSystemOpts(), VFS));
Ilya Biryukov38d79772017-05-16 09:38:59 +0000201 IntrusiveRefCntPtr<SourceManager> SourceMgr(
Ilya Biryukov0f62ed22017-05-26 12:26:51 +0000202 new SourceManager(*DiagEngine, *FileMgr));
Ilya Biryukov38d79772017-05-16 09:38:59 +0000203 // CodeComplete seems to require fresh LangOptions.
204 LangOptions LangOpts = Unit->getLangOpts();
205 // The language server protocol uses zero-based line and column numbers.
206 // The clang code completion uses one-based numbers.
207 Unit->CodeComplete(FileName, Pos.line + 1, Pos.character + 1, RemappedSource,
208 CCO.IncludeMacros, CCO.IncludeCodePatterns,
209 CCO.IncludeBriefComments, Collector, PCHs, *DiagEngine,
Ilya Biryukov0f62ed22017-05-26 12:26:51 +0000210 LangOpts, *SourceMgr, *FileMgr, StoredDiagnostics,
211 OwnedBuffers);
Ilya Biryukov38d79772017-05-16 09:38:59 +0000212 for (const llvm::MemoryBuffer *Buffer : OwnedBuffers)
213 delete Buffer;
214 return Items;
215}
216
217namespace {
218/// Convert from clang diagnostic level to LSP severity.
219static int getSeverity(DiagnosticsEngine::Level L) {
220 switch (L) {
221 case DiagnosticsEngine::Remark:
222 return 4;
223 case DiagnosticsEngine::Note:
224 return 3;
225 case DiagnosticsEngine::Warning:
226 return 2;
227 case DiagnosticsEngine::Fatal:
228 case DiagnosticsEngine::Error:
229 return 1;
230 case DiagnosticsEngine::Ignored:
231 return 0;
232 }
233 llvm_unreachable("Unknown diagnostic level!");
234}
235} // namespace
236
237std::vector<DiagWithFixIts> ClangdUnit::getLocalDiagnostics() const {
238 std::vector<DiagWithFixIts> Result;
239 for (ASTUnit::stored_diag_iterator D = Unit->stored_diag_begin(),
240 DEnd = Unit->stored_diag_end();
241 D != DEnd; ++D) {
242 if (!D->getLocation().isValid() ||
243 !D->getLocation().getManager().isInMainFile(D->getLocation()))
244 continue;
245 Position P;
246 P.line = D->getLocation().getSpellingLineNumber() - 1;
247 P.character = D->getLocation().getSpellingColumnNumber();
248 Range R = {P, P};
249 clangd::Diagnostic Diag = {R, getSeverity(D->getLevel()), D->getMessage()};
250
251 llvm::SmallVector<tooling::Replacement, 1> FixItsForDiagnostic;
252 for (const FixItHint &Fix : D->getFixIts()) {
253 FixItsForDiagnostic.push_back(clang::tooling::Replacement(
254 Unit->getSourceManager(), Fix.RemoveRange, Fix.CodeToInsert));
255 }
256 Result.push_back({Diag, std::move(FixItsForDiagnostic)});
257 }
258 return Result;
259}
Ilya Biryukovf01af682017-05-23 13:42:59 +0000260
261void ClangdUnit::dumpAST(llvm::raw_ostream &OS) const {
262 Unit->getASTContext().getTranslationUnitDecl()->dump(OS, true);
263}