1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "clang/AST/AST.h"
7 #include "clang/AST/ASTConsumer.h"
8 #include "clang/AST/ASTContext.h"
9 #include "clang/AST/Expr.h"
10 #include "clang/AST/ExprCXX.h"
11 #include "clang/AST/Mangle.h"
12 #include "clang/AST/RecursiveASTVisitor.h"
13 #include "clang/Basic/SourceManager.h"
14 #include "clang/Basic/Version.h"
15 #include "clang/Frontend/CompilerInstance.h"
16 #include "clang/Frontend/FrontendPluginRegistry.h"
17 #include "clang/Lex/Lexer.h"
18 #include "clang/Lex/PPCallbacks.h"
19 #include "clang/Lex/Preprocessor.h"
20 #include "llvm/ADT/SmallString.h"
21 #include "llvm/Support/raw_ostream.h"
22
23 #include <iostream>
24 #include <map>
25 #include <memory>
26 #include <sstream>
27 #include <tuple>
28 #include <unordered_set>
29
30 #include <stdio.h>
31 #include <stdlib.h>
32
33 #include "FileOperations.h"
34 #include "JSONFormatter.h"
35 #include "StringOperations.h"
36
37 using namespace clang;
38
39 const std::string GENERATED("__GENERATED__" PATHSEP_STRING);
40
41 // Absolute path to directory containing source code.
42 std::string Srcdir;
43
44 // Absolute path to objdir (including generated code).
45 std::string Objdir;
46
47 // Absolute path where analysis JSON output will be stored.
48 std::string Outdir;
49
50 #if !defined(_WIN32) && !defined(_WIN64)
51 #include <sys/time.h>
52
time()53 static double time() {
54 struct timeval Tv;
55 gettimeofday(&Tv, nullptr);
56 return double(Tv.tv_sec) + double(Tv.tv_usec) / 1000000.;
57 }
58 #endif
59
60 // Return true if |input| is a valid C++ identifier. We don't want to generate
61 // analysis information for operators, string literals, etc. by accident since
62 // it trips up consumers of the data.
isValidIdentifier(std::string Input)63 static bool isValidIdentifier(std::string Input) {
64 for (char C : Input) {
65 if (!(isalpha(C) || isdigit(C) || C == '_')) {
66 return false;
67 }
68 }
69 return true;
70 }
71
72 struct RAIITracer {
RAIITracerRAIITracer73 RAIITracer(const char *log) : mLog(log) {
74 printf("<%s>\n", mLog);
75 }
76
~RAIITracerRAIITracer77 ~RAIITracer() {
78 printf("</%s>\n", mLog);
79 }
80
81 const char* mLog;
82 };
83
84 #define TRACEFUNC RAIITracer tracer(__FUNCTION__);
85
86 class IndexConsumer;
87
88 // For each C++ file seen by the analysis (.cpp or .h), we track a
89 // FileInfo. This object tracks whether the file is "interesting" (i.e., whether
90 // it's in the source dir or the objdir). We also store the analysis output
91 // here.
92 struct FileInfo {
FileInfoFileInfo93 FileInfo(std::string &Rname) : Realname(Rname) {
94 if (Rname.compare(0, Objdir.length(), Objdir) == 0) {
95 // We're in the objdir, so we are probably a generated header
96 // We use the escape character to indicate the objdir nature.
97 // Note that output also has the `/' already placed
98 Interesting = true;
99 Realname.replace(0, Objdir.length(), GENERATED);
100 return;
101 }
102
103 // Empty filenames can get turned into Srcdir when they are resolved as
104 // absolute paths, so we should exclude files that are exactly equal to
105 // Srcdir or anything outside Srcdir.
106 Interesting = (Rname.length() > Srcdir.length()) &&
107 (Rname.compare(0, Srcdir.length(), Srcdir) == 0);
108 if (Interesting) {
109 // Remove the trailing `/' as well.
110 Realname.erase(0, Srcdir.length() + 1);
111 }
112 }
113 std::string Realname;
114 std::vector<std::string> Output;
115 bool Interesting;
116 };
117
118 class IndexConsumer;
119
120 class PreprocessorHook : public PPCallbacks {
121 IndexConsumer *Indexer;
122
123 public:
PreprocessorHook(IndexConsumer * C)124 PreprocessorHook(IndexConsumer *C) : Indexer(C) {}
125
126 virtual void MacroDefined(const Token &Tok,
127 const MacroDirective *Md) override;
128
129 virtual void MacroExpands(const Token &Tok, const MacroDefinition &Md,
130 SourceRange Range, const MacroArgs *Ma) override;
131 #if CLANG_VERSION_MAJOR >= 5
132 virtual void MacroUndefined(const Token &Tok, const MacroDefinition &Md,
133 const MacroDirective *Undef) override;
134 #else
135 virtual void MacroUndefined(const Token &Tok,
136 const MacroDefinition &Md) override;
137 #endif
138 virtual void Defined(const Token &Tok, const MacroDefinition &Md,
139 SourceRange Range) override;
140 virtual void Ifdef(SourceLocation Loc, const Token &Tok,
141 const MacroDefinition &Md) override;
142 virtual void Ifndef(SourceLocation Loc, const Token &Tok,
143 const MacroDefinition &Md) override;
144 };
145
146 class IndexConsumer : public ASTConsumer,
147 public RecursiveASTVisitor<IndexConsumer>,
148 public DiagnosticConsumer {
149 private:
150 CompilerInstance &CI;
151 SourceManager &SM;
152 std::map<FileID, std::unique_ptr<FileInfo>> FileMap;
153 MangleContext *CurMangleContext;
154 ASTContext *AstContext;
155
156 typedef RecursiveASTVisitor<IndexConsumer> Super;
157
158 // Tracks the set of declarations that the current expression/statement is
159 // nested inside of.
160 struct AutoSetContext {
AutoSetContextIndexConsumer::AutoSetContext161 AutoSetContext(IndexConsumer *Self, NamedDecl *Context)
162 : Self(Self), Prev(Self->CurDeclContext), Decl(Context) {
163 Self->CurDeclContext = this;
164 }
165
~AutoSetContextIndexConsumer::AutoSetContext166 ~AutoSetContext() { Self->CurDeclContext = Prev; }
167
168 IndexConsumer *Self;
169 AutoSetContext *Prev;
170 NamedDecl *Decl;
171 };
172 AutoSetContext *CurDeclContext;
173
getFileInfo(SourceLocation Loc)174 FileInfo *getFileInfo(SourceLocation Loc) {
175 FileID Id = SM.getFileID(Loc);
176
177 std::map<FileID, std::unique_ptr<FileInfo>>::iterator It;
178 It = FileMap.find(Id);
179 if (It == FileMap.end()) {
180 // We haven't seen this file before. We need to make the FileInfo
181 // structure information ourselves
182 std::string Filename = SM.getFilename(Loc);
183 std::string Absolute;
184 // If Loc is a macro id rather than a file id, it Filename might be
185 // empty. Also for some types of file locations that are clang-internal
186 // like "<scratch>" it can return an empty Filename. In these cases we
187 // want to leave Absolute as empty.
188 if (!Filename.empty()) {
189 Absolute = getAbsolutePath(Filename);
190 if (Absolute.empty()) {
191 Absolute = Filename;
192 }
193 }
194 std::unique_ptr<FileInfo> Info = llvm::make_unique<FileInfo>(Absolute);
195 It = FileMap.insert(std::make_pair(Id, std::move(Info))).first;
196 }
197 return It->second.get();
198 }
199
200 // Helpers for processing declarations
201 // Should we ignore this location?
isInterestingLocation(SourceLocation Loc)202 bool isInterestingLocation(SourceLocation Loc) {
203 if (Loc.isInvalid()) {
204 return false;
205 }
206
207 return getFileInfo(Loc)->Interesting;
208 }
209
locationToString(SourceLocation Loc,size_t Length=0)210 std::string locationToString(SourceLocation Loc, size_t Length = 0) {
211 std::pair<FileID, unsigned> Pair = SM.getDecomposedLoc(Loc);
212
213 bool IsInvalid;
214 unsigned Line = SM.getLineNumber(Pair.first, Pair.second, &IsInvalid);
215 if (IsInvalid) {
216 return "";
217 }
218 unsigned Column = SM.getColumnNumber(Pair.first, Pair.second, &IsInvalid);
219 if (IsInvalid) {
220 return "";
221 }
222
223 if (Length) {
224 return stringFormat("%05d:%d-%d", Line, Column - 1, Column - 1 + Length);
225 } else {
226 return stringFormat("%05d:%d", Line, Column - 1);
227 }
228 }
229
lineRangeToString(SourceRange Range)230 std::string lineRangeToString(SourceRange Range) {
231 std::pair<FileID, unsigned> Begin = SM.getDecomposedLoc(Range.getBegin());
232 std::pair<FileID, unsigned> End = SM.getDecomposedLoc(Range.getEnd());
233
234 bool IsInvalid;
235 unsigned Line1 = SM.getLineNumber(Begin.first, Begin.second, &IsInvalid);
236 if (IsInvalid) {
237 return "";
238 }
239 unsigned Line2 = SM.getLineNumber(End.first, End.second, &IsInvalid);
240 if (IsInvalid) {
241 return "";
242 }
243
244 return stringFormat("%d-%d", Line1, Line2);
245 }
246
247 // Returns the qualified name of `d` without considering template parameters.
getQualifiedName(const NamedDecl * D)248 std::string getQualifiedName(const NamedDecl *D) {
249 const DeclContext *Ctx = D->getDeclContext();
250 if (Ctx->isFunctionOrMethod()) {
251 return D->getQualifiedNameAsString();
252 }
253
254 std::vector<const DeclContext *> Contexts;
255
256 // Collect contexts.
257 while (Ctx && isa<NamedDecl>(Ctx)) {
258 Contexts.push_back(Ctx);
259 Ctx = Ctx->getParent();
260 }
261
262 std::string Result;
263
264 std::reverse(Contexts.begin(), Contexts.end());
265
266 for (const DeclContext *DC : Contexts) {
267 if (const auto *Spec = dyn_cast<ClassTemplateSpecializationDecl>(DC)) {
268 Result += Spec->getNameAsString();
269
270 if (Spec->getSpecializationKind() == TSK_ExplicitSpecialization) {
271 std::string Backing;
272 llvm::raw_string_ostream Stream(Backing);
273 const TemplateArgumentList &TemplateArgs = Spec->getTemplateArgs();
274 #if CLANG_VERSION_MAJOR > 3 || \
275 (CLANG_VERSION_MAJOR == 3 && CLANG_VERSION_MINOR >= 9)
276 TemplateSpecializationType::PrintTemplateArgumentList(
277 Stream, TemplateArgs.asArray(), PrintingPolicy(CI.getLangOpts()));
278 #else
279 TemplateSpecializationType::PrintTemplateArgumentList(
280 stream, templateArgs.data(), templateArgs.size(),
281 PrintingPolicy(CI.getLangOpts()));
282 #endif
283 Result += Stream.str();
284 }
285 } else if (const auto *Nd = dyn_cast<NamespaceDecl>(DC)) {
286 if (Nd->isAnonymousNamespace() || Nd->isInline()) {
287 continue;
288 }
289 Result += Nd->getNameAsString();
290 } else if (const auto *Rd = dyn_cast<RecordDecl>(DC)) {
291 if (!Rd->getIdentifier()) {
292 Result += "(anonymous)";
293 } else {
294 Result += Rd->getNameAsString();
295 }
296 } else if (const auto *Fd = dyn_cast<FunctionDecl>(DC)) {
297 Result += Fd->getNameAsString();
298 } else if (const auto *Ed = dyn_cast<EnumDecl>(DC)) {
299 // C++ [dcl.enum]p10: Each enum-name and each unscoped
300 // enumerator is declared in the scope that immediately contains
301 // the enum-specifier. Each scoped enumerator is declared in the
302 // scope of the enumeration.
303 if (Ed->isScoped() || Ed->getIdentifier())
304 Result += Ed->getNameAsString();
305 else
306 continue;
307 } else {
308 Result += cast<NamedDecl>(DC)->getNameAsString();
309 }
310 Result += "::";
311 }
312
313 if (D->getDeclName())
314 Result += D->getNameAsString();
315 else
316 Result += "(anonymous)";
317
318 return Result;
319 }
320
mangleLocation(SourceLocation Loc,std::string Backup=std::string ())321 std::string mangleLocation(SourceLocation Loc,
322 std::string Backup = std::string()) {
323 FileInfo *F = getFileInfo(Loc);
324 std::string Filename = F->Realname;
325 if (Filename.length() == 0 && Backup.length() != 0) {
326 return Backup;
327 }
328 return hash(Filename + std::string("@") + locationToString(Loc));
329 }
330
mangleQualifiedName(std::string Name)331 std::string mangleQualifiedName(std::string Name) {
332 std::replace(Name.begin(), Name.end(), ' ', '_');
333 return Name;
334 }
335
getMangledName(clang::MangleContext * Ctx,const clang::NamedDecl * Decl)336 std::string getMangledName(clang::MangleContext *Ctx,
337 const clang::NamedDecl *Decl) {
338 if (isa<FunctionDecl>(Decl) && cast<FunctionDecl>(Decl)->isExternC()) {
339 return cast<FunctionDecl>(Decl)->getNameAsString();
340 }
341
342 if (isa<FunctionDecl>(Decl) || isa<VarDecl>(Decl)) {
343 const DeclContext *DC = Decl->getDeclContext();
344 if (isa<TranslationUnitDecl>(DC) || isa<NamespaceDecl>(DC) ||
345 isa<LinkageSpecDecl>(DC) ||
346 // isa<ExternCContextDecl>(DC) ||
347 isa<TagDecl>(DC)) {
348 llvm::SmallVector<char, 512> Output;
349 llvm::raw_svector_ostream Out(Output);
350 if (const CXXConstructorDecl *D = dyn_cast<CXXConstructorDecl>(Decl)) {
351 Ctx->mangleCXXCtor(D, CXXCtorType::Ctor_Complete, Out);
352 } else if (const CXXDestructorDecl *D =
353 dyn_cast<CXXDestructorDecl>(Decl)) {
354 Ctx->mangleCXXDtor(D, CXXDtorType::Dtor_Complete, Out);
355 } else {
356 Ctx->mangleName(Decl, Out);
357 }
358 return Out.str().str();
359 } else {
360 return std::string("V_") + mangleLocation(Decl->getLocation()) +
361 std::string("_") + hash(Decl->getName());
362 }
363 } else if (isa<TagDecl>(Decl) || isa<TypedefNameDecl>(Decl)) {
364 if (!Decl->getIdentifier()) {
365 // Anonymous.
366 return std::string("T_") + mangleLocation(Decl->getLocation());
367 }
368
369 return std::string("T_") + mangleQualifiedName(getQualifiedName(Decl));
370 } else if (isa<NamespaceDecl>(Decl) || isa<NamespaceAliasDecl>(Decl)) {
371 if (!Decl->getIdentifier()) {
372 // Anonymous.
373 return std::string("NS_") + mangleLocation(Decl->getLocation());
374 }
375
376 return std::string("NS_") + mangleQualifiedName(getQualifiedName(Decl));
377 } else if (const FieldDecl *D2 = dyn_cast<FieldDecl>(Decl)) {
378 const RecordDecl *Record = D2->getParent();
379 return std::string("F_<") + getMangledName(Ctx, Record) + ">_" +
380 toString(D2->getFieldIndex());
381 } else if (const EnumConstantDecl *D2 = dyn_cast<EnumConstantDecl>(Decl)) {
382 const DeclContext *DC = Decl->getDeclContext();
383 if (const NamedDecl *Named = dyn_cast<NamedDecl>(DC)) {
384 return std::string("E_<") + getMangledName(Ctx, Named) + ">_" +
385 D2->getNameAsString();
386 }
387 }
388
389 assert(false);
390 return std::string("");
391 }
392
debugLocation(SourceLocation Loc)393 void debugLocation(SourceLocation Loc) {
394 std::string S = locationToString(Loc);
395 StringRef Filename = SM.getFilename(Loc);
396 printf("--> %s %s\n", std::string(Filename).c_str(), S.c_str());
397 }
398
debugRange(SourceRange Range)399 void debugRange(SourceRange Range) {
400 printf("Range\n");
401 debugLocation(Range.getBegin());
402 debugLocation(Range.getEnd());
403 }
404
405 public:
IndexConsumer(CompilerInstance & CI)406 IndexConsumer(CompilerInstance &CI)
407 : CI(CI), SM(CI.getSourceManager()), CurMangleContext(nullptr),
408 AstContext(nullptr), CurDeclContext(nullptr), TemplateStack(nullptr) {
409 CI.getPreprocessor().addPPCallbacks(
410 llvm::make_unique<PreprocessorHook>(this));
411 }
412
clone(DiagnosticsEngine & Diags) const413 virtual DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const {
414 return new IndexConsumer(CI);
415 }
416
417 #if !defined(_WIN32) && !defined(_WIN64)
418 struct AutoTime {
AutoTimeIndexConsumer::AutoTime419 AutoTime(double *Counter) : Counter(Counter), Start(time()) {}
~AutoTimeIndexConsumer::AutoTime420 ~AutoTime() {
421 if (Start) {
422 *Counter += time() - Start;
423 }
424 }
stopIndexConsumer::AutoTime425 void stop() {
426 *Counter += time() - Start;
427 Start = 0;
428 }
429 double *Counter;
430 double Start;
431 };
432 #endif
433
434 // All we need is to follow the final declaration.
HandleTranslationUnit(ASTContext & Ctx)435 virtual void HandleTranslationUnit(ASTContext &Ctx) {
436 CurMangleContext =
437 clang::ItaniumMangleContext::create(Ctx, CI.getDiagnostics());
438
439 AstContext = &Ctx;
440 TraverseDecl(Ctx.getTranslationUnitDecl());
441
442 // Emit the JSON data for all files now.
443 std::map<FileID, std::unique_ptr<FileInfo>>::iterator It;
444 for (It = FileMap.begin(); It != FileMap.end(); It++) {
445 if (!It->second->Interesting) {
446 continue;
447 }
448
449 FileInfo &Info = *It->second;
450
451 std::string Filename = Outdir;
452 Filename += It->second->Realname;
453
454 ensurePath(Filename);
455
456 // We lock the output file in case some other clang process is trying to
457 // write to it at the same time.
458 AutoLockFile Lock(Filename);
459
460 if (!Lock.success()) {
461 continue;
462 }
463
464 std::vector<std::string> Lines;
465
466 // Read all the existing lines in from the output file. Rather than
467 // overwrite them, we want to merge our results with what was already
468 // there. This ensures that header files that are included multiple times
469 // in different ways are analyzed completely.
470 char Buffer[65536];
471 FILE *Fp = Lock.openFile("r");
472 if (!Fp) {
473 fprintf(stderr, "Unable to open input file %s\n", Filename.c_str());
474 exit(1);
475 }
476 while (fgets(Buffer, sizeof(Buffer), Fp)) {
477 Lines.push_back(std::string(Buffer));
478 }
479 fclose(Fp);
480
481 // Insert the newly generated analysis data into what was read. Sort the
482 // results and then remove duplicates.
483 Lines.insert(Lines.end(), Info.Output.begin(), Info.Output.end());
484 std::sort(Lines.begin(), Lines.end());
485
486 std::vector<std::string> Nodupes;
487 std::unique_copy(Lines.begin(), Lines.end(), std::back_inserter(Nodupes));
488
489 // Overwrite the output file with the merged data. Since we have the lock,
490 // this will happen atomically.
491 Fp = Lock.openFile("w");
492 if (!Fp) {
493 fprintf(stderr, "Unable to open output file %s\n", Filename.c_str());
494 exit(1);
495 }
496 size_t Length = 0;
497 for (std::string &Line : Nodupes) {
498 Length += Line.length();
499 if (fwrite(Line.c_str(), Line.length(), 1, Fp) != 1) {
500 fprintf(stderr, "Unable to write to output file %s\n", Filename.c_str());
501 }
502 }
503 fclose(Fp);
504
505 if (!Lock.truncateFile(Length)) {
506 return;
507 }
508 }
509 }
510
511 // Return a list of mangled names of all the methods that the given method
512 // overrides.
findOverriddenMethods(const CXXMethodDecl * Method,std::vector<std::string> & Symbols)513 void findOverriddenMethods(const CXXMethodDecl *Method,
514 std::vector<std::string> &Symbols) {
515 std::string Mangled = getMangledName(CurMangleContext, Method);
516 Symbols.push_back(Mangled);
517
518 CXXMethodDecl::method_iterator Iter = Method->begin_overridden_methods();
519 CXXMethodDecl::method_iterator End = Method->end_overridden_methods();
520 for (; Iter != End; Iter++) {
521 const CXXMethodDecl *Decl = *Iter;
522 if (Decl->isTemplateInstantiation()) {
523 Decl = dyn_cast<CXXMethodDecl>(Decl->getTemplateInstantiationPattern());
524 }
525 return findOverriddenMethods(Decl, Symbols);
526 }
527 }
528
529 // Unfortunately, we have to override all these methods in order to track the
530 // context we're inside.
531
TraverseEnumDecl(EnumDecl * D)532 bool TraverseEnumDecl(EnumDecl *D) {
533 AutoSetContext Asc(this, D);
534 return Super::TraverseEnumDecl(D);
535 }
TraverseRecordDecl(RecordDecl * D)536 bool TraverseRecordDecl(RecordDecl *D) {
537 AutoSetContext Asc(this, D);
538 return Super::TraverseRecordDecl(D);
539 }
TraverseCXXRecordDecl(CXXRecordDecl * D)540 bool TraverseCXXRecordDecl(CXXRecordDecl *D) {
541 AutoSetContext Asc(this, D);
542 return Super::TraverseCXXRecordDecl(D);
543 }
TraverseFunctionDecl(FunctionDecl * D)544 bool TraverseFunctionDecl(FunctionDecl *D) {
545 AutoSetContext Asc(this, D);
546 const FunctionDecl *Def;
547 // (See the larger AutoTemplateContext comment for more information.) If a
548 // method on a templated class is declared out-of-line, we need to analyze
549 // the definition inside the scope of the template or else we won't properly
550 // handle member access on the templated type.
551 if (TemplateStack && D->isDefined(Def) && Def && D != Def) {
552 TraverseFunctionDecl(const_cast<FunctionDecl *>(Def));
553 }
554 return Super::TraverseFunctionDecl(D);
555 }
TraverseCXXMethodDecl(CXXMethodDecl * D)556 bool TraverseCXXMethodDecl(CXXMethodDecl *D) {
557 AutoSetContext Asc(this, D);
558 const FunctionDecl *Def;
559 // See TraverseFunctionDecl.
560 if (TemplateStack && D->isDefined(Def) && Def && D != Def) {
561 TraverseFunctionDecl(const_cast<FunctionDecl *>(Def));
562 }
563 return Super::TraverseCXXMethodDecl(D);
564 }
TraverseCXXConstructorDecl(CXXConstructorDecl * D)565 bool TraverseCXXConstructorDecl(CXXConstructorDecl *D) {
566 AutoSetContext Asc(this, D);
567 const FunctionDecl *Def;
568 // See TraverseFunctionDecl.
569 if (TemplateStack && D->isDefined(Def) && Def && D != Def) {
570 TraverseFunctionDecl(const_cast<FunctionDecl *>(Def));
571 }
572 return Super::TraverseCXXConstructorDecl(D);
573 }
TraverseCXXConversionDecl(CXXConversionDecl * D)574 bool TraverseCXXConversionDecl(CXXConversionDecl *D) {
575 AutoSetContext Asc(this, D);
576 const FunctionDecl *Def;
577 // See TraverseFunctionDecl.
578 if (TemplateStack && D->isDefined(Def) && Def && D != Def) {
579 TraverseFunctionDecl(const_cast<FunctionDecl *>(Def));
580 }
581 return Super::TraverseCXXConversionDecl(D);
582 }
TraverseCXXDestructorDecl(CXXDestructorDecl * D)583 bool TraverseCXXDestructorDecl(CXXDestructorDecl *D) {
584 AutoSetContext Asc(this, D);
585 const FunctionDecl *Def;
586 // See TraverseFunctionDecl.
587 if (TemplateStack && D->isDefined(Def) && Def && D != Def) {
588 TraverseFunctionDecl(const_cast<FunctionDecl *>(Def));
589 }
590 return Super::TraverseCXXDestructorDecl(D);
591 }
592
593 // Used to keep track of the context in which a token appears.
594 struct Context {
595 // Ultimately this becomes the "context" JSON property.
596 std::string Name;
597
598 // Ultimately this becomes the "contextsym" JSON property.
599 std::vector<std::string> Symbols;
600
ContextIndexConsumer::Context601 Context() {}
ContextIndexConsumer::Context602 Context(std::string Name, std::vector<std::string> Symbols)
603 : Name(Name), Symbols(Symbols) {}
604 };
605
translateContext(NamedDecl * D)606 Context translateContext(NamedDecl *D) {
607 const FunctionDecl *F = dyn_cast<FunctionDecl>(D);
608 if (F && F->isTemplateInstantiation()) {
609 D = F->getTemplateInstantiationPattern();
610 }
611
612 std::vector<std::string> Symbols = {getMangledName(CurMangleContext, D)};
613 if (CXXMethodDecl::classof(D)) {
614 Symbols.clear();
615 findOverriddenMethods(dyn_cast<CXXMethodDecl>(D), Symbols);
616 }
617 return Context(D->getQualifiedNameAsString(), Symbols);
618 }
619
getContext(SourceLocation Loc)620 Context getContext(SourceLocation Loc) {
621 if (SM.isMacroBodyExpansion(Loc)) {
622 // If we're inside a macro definition, we don't return any context. It
623 // will probably not be what the user expects if we do.
624 return Context();
625 }
626
627 if (CurDeclContext) {
628 return translateContext(CurDeclContext->Decl);
629 }
630 return Context();
631 }
632
633 // Similar to GetContext(SourceLocation), but it skips the declaration passed
634 // in. This is useful if we want the context of a declaration that's already
635 // on the stack.
getContext(Decl * D)636 Context getContext(Decl *D) {
637 if (SM.isMacroBodyExpansion(D->getLocation())) {
638 // If we're inside a macro definition, we don't return any context. It
639 // will probably not be what the user expects if we do.
640 return Context();
641 }
642
643 AutoSetContext *Ctxt = CurDeclContext;
644 while (Ctxt) {
645 if (Ctxt->Decl != D) {
646 return translateContext(Ctxt->Decl);
647 }
648 Ctxt = Ctxt->Prev;
649 }
650 return Context();
651 }
652
concatSymbols(const std::vector<std::string> Symbols)653 static std::string concatSymbols(const std::vector<std::string> Symbols) {
654 if (Symbols.empty()) {
655 return "";
656 }
657
658 size_t Total = 0;
659 for (auto It = Symbols.begin(); It != Symbols.end(); It++) {
660 Total += It->length();
661 }
662 Total += Symbols.size() - 1;
663
664 std::string SymbolList;
665 SymbolList.reserve(Total);
666
667 for (auto It = Symbols.begin(); It != Symbols.end(); It++) {
668 std::string Symbol = *It;
669
670 if (It != Symbols.begin()) {
671 SymbolList.push_back(',');
672 }
673 SymbolList.append(Symbol);
674 }
675
676 return SymbolList;
677 }
678
679 // Analyzing template code is tricky. Suppose we have this code:
680 //
681 // template<class T>
682 // bool Foo(T* ptr) { return T::StaticMethod(ptr); }
683 //
684 // If we analyze the body of Foo without knowing the type T, then we will not
685 // be able to generate any information for StaticMethod. However, analyzing
686 // Foo for every possible instantiation is inefficient and it also generates
687 // too much data in some cases. For example, the following code would generate
688 // one definition of Baz for every instantiation, which is undesirable:
689 //
690 // template<class T>
691 // class Bar { struct Baz { ... }; };
692 //
693 // To solve this problem, we analyze templates only once. We do so in a
694 // GatherDependent mode where we look for "dependent scoped member
695 // expressions" (i.e., things like StaticMethod). We keep track of the
696 // locations of these expressions. If we find one or more of them, we analyze
697 // the template for each instantiation, in an AnalyzeDependent mode. This mode
698 // ignores all source locations except for the ones where we found dependent
699 // scoped member expressions before. For these locations, we generate a
700 // separate JSON result for each instantiation.
701 struct AutoTemplateContext {
AutoTemplateContextIndexConsumer::AutoTemplateContext702 AutoTemplateContext(IndexConsumer *Self)
703 : Self(Self), CurMode(Mode::GatherDependent),
704 Parent(Self->TemplateStack) {
705 Self->TemplateStack = this;
706 }
707
~AutoTemplateContextIndexConsumer::AutoTemplateContext708 ~AutoTemplateContext() { Self->TemplateStack = Parent; }
709
710 // We traverse templates in two modes:
711 enum class Mode {
712 // Gather mode does not traverse into specializations. It looks for
713 // locations where it would help to have more info from template
714 // specializations.
715 GatherDependent,
716
717 // Analyze mode traverses into template specializations and records
718 // information about token locations saved in gather mode.
719 AnalyzeDependent,
720 };
721
722 // We found a dependent scoped member expression! Keep track of it for
723 // later.
visitDependentIndexConsumer::AutoTemplateContext724 void visitDependent(SourceLocation Loc) {
725 if (CurMode == Mode::AnalyzeDependent) {
726 return;
727 }
728
729 DependentLocations.insert(Loc.getRawEncoding());
730 if (Parent) {
731 Parent->visitDependent(Loc);
732 }
733 }
734
735 // Do we need to perform the extra AnalyzeDependent passes (one per
736 // instantiation)?
needsAnalysisIndexConsumer::AutoTemplateContext737 bool needsAnalysis() const {
738 if (!DependentLocations.empty()) {
739 return true;
740 }
741 if (Parent) {
742 return Parent->needsAnalysis();
743 }
744 return false;
745 }
746
switchModeIndexConsumer::AutoTemplateContext747 void switchMode() { CurMode = Mode::AnalyzeDependent; }
748
749 // Do we want to analyze each template instantiation separately?
shouldVisitTemplateInstantiationsIndexConsumer::AutoTemplateContext750 bool shouldVisitTemplateInstantiations() const {
751 if (CurMode == Mode::AnalyzeDependent) {
752 return true;
753 }
754 if (Parent) {
755 return Parent->shouldVisitTemplateInstantiations();
756 }
757 return false;
758 }
759
760 // For a given expression/statement, should we emit JSON data for it?
shouldVisitIndexConsumer::AutoTemplateContext761 bool shouldVisit(SourceLocation Loc) {
762 if (CurMode == Mode::GatherDependent) {
763 return true;
764 }
765 if (DependentLocations.find(Loc.getRawEncoding()) !=
766 DependentLocations.end()) {
767 return true;
768 }
769 if (Parent) {
770 return Parent->shouldVisit(Loc);
771 }
772 return false;
773 }
774
775 private:
776 IndexConsumer *Self;
777 Mode CurMode;
778 std::unordered_set<unsigned> DependentLocations;
779 AutoTemplateContext *Parent;
780 };
781
782 AutoTemplateContext *TemplateStack;
783
shouldVisitTemplateInstantiations() const784 bool shouldVisitTemplateInstantiations() const {
785 if (TemplateStack) {
786 return TemplateStack->shouldVisitTemplateInstantiations();
787 }
788 return false;
789 }
790
TraverseClassTemplateDecl(ClassTemplateDecl * D)791 bool TraverseClassTemplateDecl(ClassTemplateDecl *D) {
792 AutoTemplateContext Atc(this);
793 Super::TraverseClassTemplateDecl(D);
794
795 if (!Atc.needsAnalysis()) {
796 return true;
797 }
798
799 Atc.switchMode();
800
801 if (D != D->getCanonicalDecl()) {
802 return true;
803 }
804
805 for (auto *Spec : D->specializations()) {
806 for (auto *Rd : Spec->redecls()) {
807 // We don't want to visit injected-class-names in this traversal.
808 if (cast<CXXRecordDecl>(Rd)->isInjectedClassName())
809 continue;
810
811 TraverseDecl(Rd);
812 }
813 }
814
815 return true;
816 }
817
TraverseFunctionTemplateDecl(FunctionTemplateDecl * D)818 bool TraverseFunctionTemplateDecl(FunctionTemplateDecl *D) {
819 AutoTemplateContext Atc(this);
820 Super::TraverseFunctionTemplateDecl(D);
821
822 if (!Atc.needsAnalysis()) {
823 return true;
824 }
825
826 Atc.switchMode();
827
828 if (D != D->getCanonicalDecl()) {
829 return true;
830 }
831
832 for (auto *Spec : D->specializations()) {
833 for (auto *Rd : Spec->redecls()) {
834 TraverseDecl(Rd);
835 }
836 }
837
838 return true;
839 }
840
shouldVisit(SourceLocation Loc)841 bool shouldVisit(SourceLocation Loc) {
842 if (TemplateStack) {
843 return TemplateStack->shouldVisit(Loc);
844 }
845 return true;
846 }
847
848 enum {
849 NoCrossref = 1 << 0,
850 OperatorToken = 1 << 1,
851 };
852
853 // This is the only function that emits analysis JSON data. It should be
854 // called for each identifier that corresponds to a symbol.
visitIdentifier(const char * Kind,const char * SyntaxKind,std::string QualName,SourceLocation Loc,const std::vector<std::string> & Symbols,Context TokenContext=Context (),int Flags=0,SourceRange PeekRange=SourceRange ())855 void visitIdentifier(const char *Kind, const char *SyntaxKind,
856 std::string QualName, SourceLocation Loc,
857 const std::vector<std::string> &Symbols,
858 Context TokenContext = Context(), int Flags = 0,
859 SourceRange PeekRange = SourceRange()) {
860 if (!shouldVisit(Loc)) {
861 return;
862 }
863
864 // Find the file positions corresponding to the token.
865 unsigned StartOffset = SM.getFileOffset(Loc);
866 unsigned EndOffset =
867 StartOffset + Lexer::MeasureTokenLength(Loc, SM, CI.getLangOpts());
868
869 std::string LocStr = locationToString(Loc, EndOffset - StartOffset);
870 std::string RangeStr = locationToString(Loc, EndOffset - StartOffset);
871 std::string PeekRangeStr;
872
873 if (!(Flags & OperatorToken)) {
874 // Get the token's characters so we can make sure it's a valid token.
875 const char *StartChars = SM.getCharacterData(Loc);
876 std::string Text(StartChars, EndOffset - StartOffset);
877 if (!isValidIdentifier(Text)) {
878 return;
879 }
880 }
881
882 FileInfo *F = getFileInfo(Loc);
883
884 std::string SymbolList;
885
886 // Reserve space in symbolList for everything in `symbols`. `symbols` can
887 // contain some very long strings.
888 size_t Total = 0;
889 for (auto It = Symbols.begin(); It != Symbols.end(); It++) {
890 Total += It->length();
891 }
892
893 // Space for commas.
894 Total += Symbols.size() - 1;
895 SymbolList.reserve(Total);
896
897 // For each symbol, generate one "target":1 item. We want to find this line
898 // if someone searches for any one of these symbols.
899 for (auto It = Symbols.begin(); It != Symbols.end(); It++) {
900 std::string Symbol = *It;
901
902 if (!(Flags & NoCrossref)) {
903 JSONFormatter Fmt;
904
905 Fmt.add("loc", LocStr);
906 Fmt.add("target", 1);
907 Fmt.add("kind", Kind);
908 Fmt.add("pretty", QualName);
909 Fmt.add("sym", Symbol);
910 if (!TokenContext.Name.empty()) {
911 Fmt.add("context", TokenContext.Name);
912 }
913 std::string ContextSymbol = concatSymbols(TokenContext.Symbols);
914 if (!ContextSymbol.empty()) {
915 Fmt.add("contextsym", ContextSymbol);
916 }
917 if (PeekRange.isValid()) {
918 PeekRangeStr = lineRangeToString(PeekRange);
919 if (!PeekRangeStr.empty()) {
920 Fmt.add("peekRange", PeekRangeStr);
921 }
922 }
923
924 std::string S;
925 Fmt.format(S);
926 F->Output.push_back(std::move(S));
927 }
928
929 if (It != Symbols.begin()) {
930 SymbolList.push_back(',');
931 }
932 SymbolList.append(Symbol);
933 }
934
935 // Generate a single "source":1 for all the symbols. If we search from here,
936 // we want to union the results for every symbol in `symbols`.
937 JSONFormatter Fmt;
938
939 Fmt.add("loc", RangeStr);
940 Fmt.add("source", 1);
941
942 std::string Syntax;
943 if (Flags & NoCrossref) {
944 Fmt.add("syntax", "");
945 } else {
946 Syntax = Kind;
947 Syntax.push_back(',');
948 Syntax.append(SyntaxKind);
949 Fmt.add("syntax", Syntax);
950 }
951
952 std::string Pretty(SyntaxKind);
953 Pretty.push_back(' ');
954 Pretty.append(QualName);
955 Fmt.add("pretty", Pretty);
956
957 Fmt.add("sym", SymbolList);
958
959 if (Flags & NoCrossref) {
960 Fmt.add("no_crossref", 1);
961 }
962
963 std::string Buf;
964 Fmt.format(Buf);
965 F->Output.push_back(std::move(Buf));
966 }
967
visitIdentifier(const char * Kind,const char * SyntaxKind,std::string QualName,SourceLocation Loc,std::string Symbol,Context TokenContext=Context (),int Flags=0,SourceRange PeekRange=SourceRange ())968 void visitIdentifier(const char *Kind, const char *SyntaxKind,
969 std::string QualName, SourceLocation Loc, std::string Symbol,
970 Context TokenContext = Context(), int Flags = 0,
971 SourceRange PeekRange = SourceRange()) {
972 std::vector<std::string> V = {Symbol};
973 visitIdentifier(Kind, SyntaxKind, QualName, Loc, V, TokenContext, Flags, PeekRange);
974 }
975
normalizeLocation(SourceLocation * Loc)976 void normalizeLocation(SourceLocation *Loc) {
977 *Loc = SM.getSpellingLoc(*Loc);
978 }
979
getFunctionPeekRange(FunctionDecl * D)980 SourceRange getFunctionPeekRange(FunctionDecl* D) {
981 // We always start at the start of the function decl, which may include the
982 // return type on a separate line.
983 SourceLocation Start = D->getLocStart();
984
985 // By default, we end at the line containing the function's name.
986 SourceLocation End = D->getLocation();
987
988 std::pair<FileID, unsigned> FuncLoc = SM.getDecomposedLoc(End);
989
990 // But if there are parameters, we want to include those as well.
991 for (ParmVarDecl* Param : D->parameters()) {
992 std::pair<FileID, unsigned> ParamLoc = SM.getDecomposedLoc(Param->getLocation());
993
994 // It's possible there are macros involved or something. We don't include
995 // the parameters in that case.
996 if (ParamLoc.first == FuncLoc.first) {
997 // Assume parameters are in order, so we always take the last one.
998 End = Param->getLocEnd();
999 }
1000 }
1001
1002 return SourceRange(Start, End);
1003 }
1004
getTagPeekRange(TagDecl * D)1005 SourceRange getTagPeekRange(TagDecl* D) {
1006 SourceLocation Start = D->getLocStart();
1007
1008 // By default, we end at the line containing the name.
1009 SourceLocation End = D->getLocation();
1010
1011 std::pair<FileID, unsigned> FuncLoc = SM.getDecomposedLoc(End);
1012
1013 if (CXXRecordDecl* D2 = dyn_cast<CXXRecordDecl>(D)) {
1014 // But if there are parameters, we want to include those as well.
1015 for (CXXBaseSpecifier& Base : D2->bases()) {
1016 std::pair<FileID, unsigned> Loc = SM.getDecomposedLoc(Base.getLocEnd());
1017
1018 // It's possible there are macros involved or something. We don't include
1019 // the parameters in that case.
1020 if (Loc.first == FuncLoc.first) {
1021 // Assume parameters are in order, so we always take the last one.
1022 End = Base.getLocEnd();
1023 }
1024 }
1025 }
1026
1027 return SourceRange(Start, End);
1028 }
1029
getCommentRange(NamedDecl * D)1030 SourceRange getCommentRange(NamedDecl* D) {
1031 const RawComment* RC =
1032 AstContext->getRawCommentForDeclNoCache(D);
1033 if (!RC) {
1034 return SourceRange();
1035 }
1036
1037 return RC->getSourceRange();
1038 }
1039
combineRanges(SourceRange Range1,SourceRange Range2)1040 SourceRange combineRanges(SourceRange Range1, SourceRange Range2) {
1041 if (Range1.isInvalid()) {
1042 return Range2;
1043 }
1044 if (Range2.isInvalid()) {
1045 return Range1;
1046 }
1047
1048 std::pair<FileID, unsigned> Begin1 = SM.getDecomposedLoc(Range1.getBegin());
1049 std::pair<FileID, unsigned> End1 = SM.getDecomposedLoc(Range1.getEnd());
1050 std::pair<FileID, unsigned> Begin2 = SM.getDecomposedLoc(Range2.getBegin());
1051 std::pair<FileID, unsigned> End2 = SM.getDecomposedLoc(Range2.getEnd());
1052
1053 if (End1.first != Begin2.first) {
1054 // Something weird is probably happening with the preprocessor. Just
1055 // return the first range.
1056 return Range1;
1057 }
1058
1059 // See which range comes first.
1060 if (Begin1.second <= End2.second) {
1061 return SourceRange(Range1.getBegin(), Range2.getEnd());
1062 } else {
1063 return SourceRange(Range2.getBegin(), Range1.getEnd());
1064 }
1065 }
1066
validateRange(SourceLocation Loc,SourceRange Range)1067 SourceRange validateRange(SourceLocation Loc, SourceRange Range) {
1068 std::pair<FileID, unsigned> Decomposed = SM.getDecomposedLoc(Loc);
1069 std::pair<FileID, unsigned> Begin = SM.getDecomposedLoc(Range.getBegin());
1070 std::pair<FileID, unsigned> End = SM.getDecomposedLoc(Range.getEnd());
1071
1072 if (Begin.first != Decomposed.first || End.first != Decomposed.first) {
1073 return SourceRange();
1074 }
1075
1076 if (Begin.second >= End.second) {
1077 return SourceRange();
1078 }
1079
1080 return Range;
1081 }
1082
VisitNamedDecl(NamedDecl * D)1083 bool VisitNamedDecl(NamedDecl *D) {
1084 SourceLocation Loc = D->getLocation();
1085
1086 if (isa<EnumConstantDecl>(D) && SM.isMacroBodyExpansion(Loc)) {
1087 // for enum constants generated by macro expansion, update location
1088 // to point to the expansion location as that is more useful. We might
1089 // want to do this for more token types but until we have good regression
1090 // testing for the Indexer it's best to be as conservative and explicit
1091 // as possible with the changes.
1092 Loc = SM.getFileLoc(Loc);
1093 }
1094
1095 normalizeLocation(&Loc);
1096 if (!isInterestingLocation(Loc)) {
1097 return true;
1098 }
1099
1100 if (isa<ParmVarDecl>(D) && !D->getDeclName().getAsIdentifierInfo()) {
1101 // Unnamed parameter in function proto.
1102 return true;
1103 }
1104
1105 int Flags = 0;
1106 const char *Kind = "def";
1107 const char *PrettyKind = "?";
1108 SourceRange PeekRange(D->getLocStart(), D->getLocEnd());
1109 if (FunctionDecl *D2 = dyn_cast<FunctionDecl>(D)) {
1110 if (D2->isTemplateInstantiation()) {
1111 D = D2->getTemplateInstantiationPattern();
1112 }
1113 Kind = D2->isThisDeclarationADefinition() ? "def" : "decl";
1114 PrettyKind = "function";
1115 PeekRange = getFunctionPeekRange(D2);
1116 } else if (TagDecl *D2 = dyn_cast<TagDecl>(D)) {
1117 Kind = D2->isThisDeclarationADefinition() ? "def" : "decl";
1118 PrettyKind = "type";
1119
1120 if (D2->isThisDeclarationADefinition() && D2->getDefinition() == D2) {
1121 PeekRange = getTagPeekRange(D2);
1122 } else {
1123 PeekRange = SourceRange();
1124 }
1125 } else if (isa<TypedefNameDecl>(D)) {
1126 Kind = "def";
1127 PrettyKind = "type";
1128 PeekRange = SourceRange(Loc, Loc);
1129 } else if (VarDecl *D2 = dyn_cast<VarDecl>(D)) {
1130 if (D2->isLocalVarDeclOrParm()) {
1131 Flags = NoCrossref;
1132 }
1133
1134 Kind = D2->isThisDeclarationADefinition() == VarDecl::DeclarationOnly
1135 ? "decl"
1136 : "def";
1137 PrettyKind = "variable";
1138 } else if (isa<NamespaceDecl>(D) || isa<NamespaceAliasDecl>(D)) {
1139 Kind = "def";
1140 PrettyKind = "namespace";
1141 PeekRange = SourceRange(Loc, Loc);
1142 } else if (isa<FieldDecl>(D)) {
1143 Kind = "def";
1144 PrettyKind = "field";
1145 } else if (isa<EnumConstantDecl>(D)) {
1146 Kind = "def";
1147 PrettyKind = "enum constant";
1148 } else {
1149 return true;
1150 }
1151
1152 SourceRange CommentRange = getCommentRange(D);
1153 PeekRange = combineRanges(PeekRange, CommentRange);
1154 PeekRange = validateRange(Loc, PeekRange);
1155
1156 std::vector<std::string> Symbols = {getMangledName(CurMangleContext, D)};
1157 if (CXXMethodDecl::classof(D)) {
1158 Symbols.clear();
1159 findOverriddenMethods(dyn_cast<CXXMethodDecl>(D), Symbols);
1160 }
1161
1162 // For destructors, loc points to the ~ character. We want to skip to the
1163 // class name.
1164 if (isa<CXXDestructorDecl>(D)) {
1165 const char *P = SM.getCharacterData(Loc);
1166 assert(*p == '~');
1167 P++;
1168
1169 unsigned Skipped = 1;
1170 while (*P == ' ' || *P == '\t' || *P == '\r' || *P == '\n') {
1171 P++;
1172 Skipped++;
1173 }
1174
1175 Loc = Loc.getLocWithOffset(Skipped);
1176
1177 PrettyKind = "destructor";
1178 }
1179
1180 visitIdentifier(Kind, PrettyKind, getQualifiedName(D), Loc, Symbols,
1181 getContext(D), Flags, PeekRange);
1182
1183 return true;
1184 }
1185
VisitCXXConstructExpr(CXXConstructExpr * E)1186 bool VisitCXXConstructExpr(CXXConstructExpr *E) {
1187 SourceLocation Loc = E->getLocStart();
1188 normalizeLocation(&Loc);
1189 if (!isInterestingLocation(Loc)) {
1190 return true;
1191 }
1192
1193 FunctionDecl *Ctor = E->getConstructor();
1194 if (Ctor->isTemplateInstantiation()) {
1195 Ctor = Ctor->getTemplateInstantiationPattern();
1196 }
1197 std::string Mangled = getMangledName(CurMangleContext, Ctor);
1198
1199 // FIXME: Need to do something different for list initialization.
1200
1201 visitIdentifier("use", "constructor", getQualifiedName(Ctor), Loc, Mangled,
1202 getContext(Loc));
1203
1204 return true;
1205 }
1206
VisitCallExpr(CallExpr * E)1207 bool VisitCallExpr(CallExpr *E) {
1208 Decl *Callee = E->getCalleeDecl();
1209 if (!Callee || !FunctionDecl::classof(Callee)) {
1210 return true;
1211 }
1212
1213 const NamedDecl *NamedCallee = dyn_cast<NamedDecl>(Callee);
1214
1215 SourceLocation Loc;
1216
1217 const FunctionDecl *F = dyn_cast<FunctionDecl>(NamedCallee);
1218 if (F->isTemplateInstantiation()) {
1219 NamedCallee = F->getTemplateInstantiationPattern();
1220 }
1221
1222 std::string Mangled = getMangledName(CurMangleContext, NamedCallee);
1223 int Flags = 0;
1224
1225 Expr *CalleeExpr = E->getCallee()->IgnoreParenImpCasts();
1226
1227 if (CXXOperatorCallExpr::classof(E)) {
1228 // Just take the first token.
1229 CXXOperatorCallExpr *Op = dyn_cast<CXXOperatorCallExpr>(E);
1230 Loc = Op->getOperatorLoc();
1231 Flags |= OperatorToken;
1232 } else if (MemberExpr::classof(CalleeExpr)) {
1233 MemberExpr *Member = dyn_cast<MemberExpr>(CalleeExpr);
1234 Loc = Member->getMemberLoc();
1235 } else if (DeclRefExpr::classof(CalleeExpr)) {
1236 // We handle this in VisitDeclRefExpr.
1237 return true;
1238 } else {
1239 return true;
1240 }
1241
1242 normalizeLocation(&Loc);
1243
1244 if (!isInterestingLocation(Loc)) {
1245 return true;
1246 }
1247
1248 visitIdentifier("use", "function", getQualifiedName(NamedCallee), Loc, Mangled,
1249 getContext(Loc), Flags);
1250
1251 return true;
1252 }
1253
VisitTagTypeLoc(TagTypeLoc L)1254 bool VisitTagTypeLoc(TagTypeLoc L) {
1255 SourceLocation Loc = L.getBeginLoc();
1256 normalizeLocation(&Loc);
1257 if (!isInterestingLocation(Loc)) {
1258 return true;
1259 }
1260
1261 TagDecl *Decl = L.getDecl();
1262 std::string Mangled = getMangledName(CurMangleContext, Decl);
1263 visitIdentifier("use", "type", getQualifiedName(Decl), Loc, Mangled,
1264 getContext(Loc));
1265 return true;
1266 }
1267
VisitTypedefTypeLoc(TypedefTypeLoc L)1268 bool VisitTypedefTypeLoc(TypedefTypeLoc L) {
1269 SourceLocation Loc = L.getBeginLoc();
1270 normalizeLocation(&Loc);
1271 if (!isInterestingLocation(Loc)) {
1272 return true;
1273 }
1274
1275 NamedDecl *Decl = L.getTypedefNameDecl();
1276 std::string Mangled = getMangledName(CurMangleContext, Decl);
1277 visitIdentifier("use", "type", getQualifiedName(Decl), Loc, Mangled,
1278 getContext(Loc));
1279 return true;
1280 }
1281
VisitInjectedClassNameTypeLoc(InjectedClassNameTypeLoc L)1282 bool VisitInjectedClassNameTypeLoc(InjectedClassNameTypeLoc L) {
1283 SourceLocation Loc = L.getBeginLoc();
1284 normalizeLocation(&Loc);
1285 if (!isInterestingLocation(Loc)) {
1286 return true;
1287 }
1288
1289 NamedDecl *Decl = L.getDecl();
1290 std::string Mangled = getMangledName(CurMangleContext, Decl);
1291 visitIdentifier("use", "type", getQualifiedName(Decl), Loc, Mangled,
1292 getContext(Loc));
1293 return true;
1294 }
1295
VisitTemplateSpecializationTypeLoc(TemplateSpecializationTypeLoc L)1296 bool VisitTemplateSpecializationTypeLoc(TemplateSpecializationTypeLoc L) {
1297 SourceLocation Loc = L.getBeginLoc();
1298 normalizeLocation(&Loc);
1299 if (!isInterestingLocation(Loc)) {
1300 return true;
1301 }
1302
1303 TemplateDecl *Td = L.getTypePtr()->getTemplateName().getAsTemplateDecl();
1304 if (ClassTemplateDecl *D = dyn_cast<ClassTemplateDecl>(Td)) {
1305 NamedDecl *Decl = D->getTemplatedDecl();
1306 std::string Mangled = getMangledName(CurMangleContext, Decl);
1307 visitIdentifier("use", "type", getQualifiedName(Decl), Loc, Mangled,
1308 getContext(Loc));
1309 } else if (TypeAliasTemplateDecl *D = dyn_cast<TypeAliasTemplateDecl>(Td)) {
1310 NamedDecl *Decl = D->getTemplatedDecl();
1311 std::string Mangled = getMangledName(CurMangleContext, Decl);
1312 visitIdentifier("use", "type", getQualifiedName(Decl), Loc, Mangled,
1313 getContext(Loc));
1314 }
1315
1316 return true;
1317 }
1318
VisitDeclRefExpr(DeclRefExpr * E)1319 bool VisitDeclRefExpr(DeclRefExpr *E) {
1320 SourceLocation Loc = E->getExprLoc();
1321 normalizeLocation(&Loc);
1322 if (!isInterestingLocation(Loc)) {
1323 return true;
1324 }
1325
1326 if (E->hasQualifier()) {
1327 Loc = E->getNameInfo().getLoc();
1328 normalizeLocation(&Loc);
1329 }
1330
1331 NamedDecl *Decl = E->getDecl();
1332 if (const VarDecl *D2 = dyn_cast<VarDecl>(Decl)) {
1333 int Flags = 0;
1334 if (D2->isLocalVarDeclOrParm()) {
1335 Flags = NoCrossref;
1336 }
1337 std::string Mangled = getMangledName(CurMangleContext, Decl);
1338 visitIdentifier("use", "variable", getQualifiedName(Decl), Loc, Mangled,
1339 getContext(Loc), Flags);
1340 } else if (isa<FunctionDecl>(Decl)) {
1341 const FunctionDecl *F = dyn_cast<FunctionDecl>(Decl);
1342 if (F->isTemplateInstantiation()) {
1343 Decl = F->getTemplateInstantiationPattern();
1344 }
1345
1346 std::string Mangled = getMangledName(CurMangleContext, Decl);
1347 visitIdentifier("use", "function", getQualifiedName(Decl), Loc, Mangled,
1348 getContext(Loc));
1349 } else if (isa<EnumConstantDecl>(Decl)) {
1350 std::string Mangled = getMangledName(CurMangleContext, Decl);
1351 visitIdentifier("use", "enum", getQualifiedName(Decl), Loc, Mangled,
1352 getContext(Loc));
1353 }
1354
1355 return true;
1356 }
1357
VisitCXXConstructorDecl(CXXConstructorDecl * D)1358 bool VisitCXXConstructorDecl(CXXConstructorDecl *D) {
1359 if (!isInterestingLocation(D->getLocation())) {
1360 return true;
1361 }
1362
1363 for (CXXConstructorDecl::init_const_iterator It = D->init_begin();
1364 It != D->init_end(); ++It) {
1365 const CXXCtorInitializer *Ci = *It;
1366 if (!Ci->getMember() || !Ci->isWritten()) {
1367 continue;
1368 }
1369
1370 SourceLocation Loc = Ci->getMemberLocation();
1371 normalizeLocation(&Loc);
1372 if (!isInterestingLocation(Loc)) {
1373 continue;
1374 }
1375
1376 FieldDecl *Member = Ci->getMember();
1377 std::string Mangled = getMangledName(CurMangleContext, Member);
1378 visitIdentifier("use", "field", getQualifiedName(Member), Loc, Mangled,
1379 getContext(D));
1380 }
1381
1382 return true;
1383 }
1384
VisitMemberExpr(MemberExpr * E)1385 bool VisitMemberExpr(MemberExpr *E) {
1386 SourceLocation Loc = E->getExprLoc();
1387 normalizeLocation(&Loc);
1388 if (!isInterestingLocation(Loc)) {
1389 return true;
1390 }
1391
1392 ValueDecl *Decl = E->getMemberDecl();
1393 if (FieldDecl *Field = dyn_cast<FieldDecl>(Decl)) {
1394 std::string Mangled = getMangledName(CurMangleContext, Field);
1395 visitIdentifier("use", "field", getQualifiedName(Field), Loc, Mangled,
1396 getContext(Loc));
1397 }
1398 return true;
1399 }
1400
VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr * E)1401 bool VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr *E) {
1402 SourceLocation Loc = E->getMemberLoc();
1403 normalizeLocation(&Loc);
1404 if (!isInterestingLocation(Loc)) {
1405 return true;
1406 }
1407
1408 if (TemplateStack) {
1409 TemplateStack->visitDependent(Loc);
1410 }
1411 return true;
1412 }
1413
macroDefined(const Token & Tok,const MacroDirective * Macro)1414 void macroDefined(const Token &Tok, const MacroDirective *Macro) {
1415 if (Macro->getMacroInfo()->isBuiltinMacro()) {
1416 return;
1417 }
1418 SourceLocation Loc = Tok.getLocation();
1419 normalizeLocation(&Loc);
1420 if (!isInterestingLocation(Loc)) {
1421 return;
1422 }
1423
1424 IdentifierInfo *Ident = Tok.getIdentifierInfo();
1425 if (Ident) {
1426 std::string Mangled =
1427 std::string("M_") + mangleLocation(Loc, Ident->getName());
1428 visitIdentifier("def", "macro", Ident->getName(), Loc, Mangled);
1429 }
1430 }
1431
macroUsed(const Token & Tok,const MacroInfo * Macro)1432 void macroUsed(const Token &Tok, const MacroInfo *Macro) {
1433 if (!Macro) {
1434 return;
1435 }
1436 if (Macro->isBuiltinMacro()) {
1437 return;
1438 }
1439 SourceLocation Loc = Tok.getLocation();
1440 normalizeLocation(&Loc);
1441 if (!isInterestingLocation(Loc)) {
1442 return;
1443 }
1444
1445 IdentifierInfo *Ident = Tok.getIdentifierInfo();
1446 if (Ident) {
1447 std::string Mangled =
1448 std::string("M_") +
1449 mangleLocation(Macro->getDefinitionLoc(), Ident->getName());
1450 visitIdentifier("use", "macro", Ident->getName(), Loc, Mangled);
1451 }
1452 }
1453 };
1454
MacroDefined(const Token & Tok,const MacroDirective * Md)1455 void PreprocessorHook::MacroDefined(const Token &Tok,
1456 const MacroDirective *Md) {
1457 Indexer->macroDefined(Tok, Md);
1458 }
1459
MacroExpands(const Token & Tok,const MacroDefinition & Md,SourceRange Range,const MacroArgs * Ma)1460 void PreprocessorHook::MacroExpands(const Token &Tok, const MacroDefinition &Md,
1461 SourceRange Range, const MacroArgs *Ma) {
1462 Indexer->macroUsed(Tok, Md.getMacroInfo());
1463 }
1464
1465 #if CLANG_VERSION_MAJOR >= 5
MacroUndefined(const Token & Tok,const MacroDefinition & Md,const MacroDirective * Undef)1466 void PreprocessorHook::MacroUndefined(const Token &Tok,
1467 const MacroDefinition &Md,
1468 const MacroDirective *Undef)
1469 #else
1470 void PreprocessorHook::MacroUndefined(const Token &Tok,
1471 const MacroDefinition &Md)
1472 #endif
1473 {
1474 Indexer->macroUsed(Tok, Md.getMacroInfo());
1475 }
1476
Defined(const Token & Tok,const MacroDefinition & Md,SourceRange Range)1477 void PreprocessorHook::Defined(const Token &Tok, const MacroDefinition &Md,
1478 SourceRange Range) {
1479 Indexer->macroUsed(Tok, Md.getMacroInfo());
1480 }
1481
Ifdef(SourceLocation Loc,const Token & Tok,const MacroDefinition & Md)1482 void PreprocessorHook::Ifdef(SourceLocation Loc, const Token &Tok,
1483 const MacroDefinition &Md) {
1484 Indexer->macroUsed(Tok, Md.getMacroInfo());
1485 }
1486
Ifndef(SourceLocation Loc,const Token & Tok,const MacroDefinition & Md)1487 void PreprocessorHook::Ifndef(SourceLocation Loc, const Token &Tok,
1488 const MacroDefinition &Md) {
1489 Indexer->macroUsed(Tok, Md.getMacroInfo());
1490 }
1491
1492 class IndexAction : public PluginASTAction {
1493 protected:
CreateASTConsumer(CompilerInstance & CI,llvm::StringRef F)1494 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
1495 llvm::StringRef F) {
1496 return llvm::make_unique<IndexConsumer>(CI);
1497 }
1498
ParseArgs(const CompilerInstance & CI,const std::vector<std::string> & Args)1499 bool ParseArgs(const CompilerInstance &CI,
1500 const std::vector<std::string> &Args) {
1501 if (Args.size() != 3) {
1502 DiagnosticsEngine &D = CI.getDiagnostics();
1503 unsigned DiagID = D.getCustomDiagID(
1504 DiagnosticsEngine::Error,
1505 "Need arguments for the source, output, and object directories");
1506 D.Report(DiagID);
1507 return false;
1508 }
1509
1510 // Load our directories
1511 Srcdir = getAbsolutePath(Args[0]);
1512 if (Srcdir.empty()) {
1513 DiagnosticsEngine &D = CI.getDiagnostics();
1514 unsigned DiagID = D.getCustomDiagID(
1515 DiagnosticsEngine::Error, "Source directory '%0' does not exist");
1516 D.Report(DiagID) << Args[0];
1517 return false;
1518 }
1519
1520 ensurePath(Args[1] + PATHSEP_STRING);
1521 Outdir = getAbsolutePath(Args[1]);
1522 Outdir += PATHSEP_STRING;
1523
1524 Objdir = getAbsolutePath(Args[2]);
1525 if (Objdir.empty()) {
1526 DiagnosticsEngine &D = CI.getDiagnostics();
1527 unsigned DiagID = D.getCustomDiagID(DiagnosticsEngine::Error,
1528 "Objdir '%0' does not exist");
1529 D.Report(DiagID) << Args[2];
1530 return false;
1531 }
1532 Objdir += PATHSEP_STRING;
1533
1534 printf("MOZSEARCH: %s %s %s\n", Srcdir.c_str(), Outdir.c_str(),
1535 Objdir.c_str());
1536
1537 return true;
1538 }
1539
printHelp(llvm::raw_ostream & Ros)1540 void printHelp(llvm::raw_ostream &Ros) {
1541 Ros << "Help for mozsearch plugin goes here\n";
1542 }
1543 };
1544
1545 static FrontendPluginRegistry::Add<IndexAction>
1546 Y("mozsearch-index", "create the mozsearch index database");
1547