blob: b1e0d4955d73f620eff960f59cfb2e55c6c912cd [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,
Ilya Biryukova46f7a92017-06-28 10:34:50 +000022 StringRef ResourceDir,
Ilya Biryukov38d79772017-05-16 09:38:59 +000023 std::shared_ptr<PCHContainerOperations> PCHs,
Ilya Biryukov0f62ed22017-05-26 12:26:51 +000024 std::vector<tooling::CompileCommand> Commands,
25 IntrusiveRefCntPtr<vfs::FileSystem> VFS)
Ilya Biryukov38d79772017-05-16 09:38:59 +000026 : FileName(FileName), PCHs(PCHs) {
27 assert(!Commands.empty() && "No compile commands provided");
28
29 // Inject the resource dir.
30 // FIXME: Don't overwrite it if it's already there.
Ilya Biryukova46f7a92017-06-28 10:34:50 +000031 Commands.front().CommandLine.push_back("-resource-dir=" + std::string(ResourceDir));
Ilya Biryukov38d79772017-05-16 09:38:59 +000032
33 IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
34 CompilerInstance::createDiagnostics(new DiagnosticOptions);
35
36 std::vector<const char *> ArgStrs;
37 for (const auto &S : Commands.front().CommandLine)
38 ArgStrs.push_back(S.c_str());
39
40 ASTUnit::RemappedFile RemappedSource(
41 FileName,
42 llvm::MemoryBuffer::getMemBufferCopy(Contents, FileName).release());
43
44 auto ArgP = &*ArgStrs.begin();
45 Unit = std::unique_ptr<ASTUnit>(ASTUnit::LoadFromCommandLine(
46 ArgP, ArgP + ArgStrs.size(), PCHs, Diags, ResourceDir,
47 /*OnlyLocalDecls=*/false, /*CaptureDiagnostics=*/true, RemappedSource,
48 /*RemappedFilesKeepOriginalName=*/true,
Krasimir Georgievd8145332017-05-22 12:49:08 +000049 /*PrecompilePreambleAfterNParses=*/1, /*TUKind=*/TU_Prefix,
Ilya Biryukov38d79772017-05-16 09:38:59 +000050 /*CacheCodeCompletionResults=*/true,
51 /*IncludeBriefCommentsInCodeCompletion=*/true,
Ilya Biryukov0f62ed22017-05-26 12:26:51 +000052 /*AllowPCHWithCompilerErrors=*/true,
53 /*SkipFunctionBodies=*/false,
Argyrios Kyrtzidis783e4c22017-06-09 02:04:19 +000054 /*SingleFileParse=*/false,
Ilya Biryukov0f62ed22017-05-26 12:26:51 +000055 /*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;
Krasimir Georgieve6035a52017-06-08 15:11:51 +0000141 for (CodeCompletionString::Chunk C : *CCS) {
142 switch (C.Kind) {
143 case CodeCompletionString::CK_ResultType:
144 Item.detail = C.Text;
145 break;
146 case CodeCompletionString::CK_Optional:
147 break;
148 default:
149 Item.label += C.Text;
150 break;
151 }
152 }
Ilya Biryukov38d79772017-05-16 09:38:59 +0000153 assert(CCS->getTypedText());
Ilya Biryukov38d79772017-05-16 09:38:59 +0000154 Item.kind = getKind(Result.CursorKind);
Krasimir Georgieva1de3c92017-06-15 09:11:57 +0000155 // Priority is a 16-bit integer, hence at most 5 digits.
156 // Since identifiers with higher priority need to come first,
157 // we subtract the priority from 99999.
158 // For example, the sort text of the identifier 'a' with priority 35
159 // is 99964a.
160 assert(CCS->getPriority() < 99999 && "Expecting code completion result "
161 "priority to have at most "
162 "5-digits");
163 llvm::raw_string_ostream(Item.sortText) << llvm::format(
164 "%05d%s", 99999 - CCS->getPriority(), CCS->getTypedText());
165 Item.insertText = Item.filterText = CCS->getTypedText();
Ilya Biryukov38d79772017-05-16 09:38:59 +0000166 if (CCS->getBriefComment())
167 Item.documentation = CCS->getBriefComment();
168 Items->push_back(std::move(Item));
169 }
170 }
171 }
172
173 GlobalCodeCompletionAllocator &getAllocator() override { return *Allocator; }
174
175 CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
176};
177} // namespace
178
Ilya Biryukov0f62ed22017-05-26 12:26:51 +0000179std::vector<CompletionItem>
180ClangdUnit::codeComplete(StringRef Contents, Position Pos,
181 IntrusiveRefCntPtr<vfs::FileSystem> VFS) {
Ilya Biryukov38d79772017-05-16 09:38:59 +0000182 CodeCompleteOptions CCO;
183 CCO.IncludeBriefComments = 1;
184 // This is where code completion stores dirty buffers. Need to free after
185 // completion.
186 SmallVector<const llvm::MemoryBuffer *, 4> OwnedBuffers;
187 SmallVector<StoredDiagnostic, 4> StoredDiagnostics;
188 IntrusiveRefCntPtr<DiagnosticsEngine> DiagEngine(
189 new DiagnosticsEngine(new DiagnosticIDs, new DiagnosticOptions));
190 std::vector<CompletionItem> Items;
191 CompletionItemsCollector Collector(&Items, CCO);
192
193 ASTUnit::RemappedFile RemappedSource(
194 FileName,
195 llvm::MemoryBuffer::getMemBufferCopy(Contents, FileName).release());
196
Ilya Biryukov0f62ed22017-05-26 12:26:51 +0000197 IntrusiveRefCntPtr<FileManager> FileMgr(
198 new FileManager(Unit->getFileSystemOpts(), VFS));
Ilya Biryukov38d79772017-05-16 09:38:59 +0000199 IntrusiveRefCntPtr<SourceManager> SourceMgr(
Ilya Biryukov0f62ed22017-05-26 12:26:51 +0000200 new SourceManager(*DiagEngine, *FileMgr));
Ilya Biryukov38d79772017-05-16 09:38:59 +0000201 // CodeComplete seems to require fresh LangOptions.
202 LangOptions LangOpts = Unit->getLangOpts();
203 // The language server protocol uses zero-based line and column numbers.
204 // The clang code completion uses one-based numbers.
205 Unit->CodeComplete(FileName, Pos.line + 1, Pos.character + 1, RemappedSource,
206 CCO.IncludeMacros, CCO.IncludeCodePatterns,
207 CCO.IncludeBriefComments, Collector, PCHs, *DiagEngine,
Ilya Biryukov0f62ed22017-05-26 12:26:51 +0000208 LangOpts, *SourceMgr, *FileMgr, StoredDiagnostics,
209 OwnedBuffers);
Ilya Biryukov38d79772017-05-16 09:38:59 +0000210 for (const llvm::MemoryBuffer *Buffer : OwnedBuffers)
211 delete Buffer;
212 return Items;
213}
214
215namespace {
216/// Convert from clang diagnostic level to LSP severity.
217static int getSeverity(DiagnosticsEngine::Level L) {
218 switch (L) {
219 case DiagnosticsEngine::Remark:
220 return 4;
221 case DiagnosticsEngine::Note:
222 return 3;
223 case DiagnosticsEngine::Warning:
224 return 2;
225 case DiagnosticsEngine::Fatal:
226 case DiagnosticsEngine::Error:
227 return 1;
228 case DiagnosticsEngine::Ignored:
229 return 0;
230 }
231 llvm_unreachable("Unknown diagnostic level!");
232}
233} // namespace
234
235std::vector<DiagWithFixIts> ClangdUnit::getLocalDiagnostics() const {
236 std::vector<DiagWithFixIts> Result;
237 for (ASTUnit::stored_diag_iterator D = Unit->stored_diag_begin(),
238 DEnd = Unit->stored_diag_end();
239 D != DEnd; ++D) {
240 if (!D->getLocation().isValid() ||
241 !D->getLocation().getManager().isInMainFile(D->getLocation()))
242 continue;
243 Position P;
244 P.line = D->getLocation().getSpellingLineNumber() - 1;
245 P.character = D->getLocation().getSpellingColumnNumber();
246 Range R = {P, P};
247 clangd::Diagnostic Diag = {R, getSeverity(D->getLevel()), D->getMessage()};
248
249 llvm::SmallVector<tooling::Replacement, 1> FixItsForDiagnostic;
250 for (const FixItHint &Fix : D->getFixIts()) {
251 FixItsForDiagnostic.push_back(clang::tooling::Replacement(
252 Unit->getSourceManager(), Fix.RemoveRange, Fix.CodeToInsert));
253 }
254 Result.push_back({Diag, std::move(FixItsForDiagnostic)});
255 }
256 return Result;
257}
Ilya Biryukovf01af682017-05-23 13:42:59 +0000258
259void ClangdUnit::dumpAST(llvm::raw_ostream &OS) const {
260 Unit->getASTContext().getTranslationUnitDecl()->dump(OS, true);
261}