Coverage Report

Created: 2020-02-15 09:57

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/tools/libclang/CIndexHigh.cpp
Line
Count
Source (jump to first uncovered line)
1
//===- CIndexHigh.cpp - Higher level API functions ------------------------===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
9
#include "CursorVisitor.h"
10
#include "CLog.h"
11
#include "CXCursor.h"
12
#include "CXSourceLocation.h"
13
#include "CXTranslationUnit.h"
14
#include "clang/AST/DeclObjC.h"
15
#include "clang/Frontend/ASTUnit.h"
16
#include "llvm/Support/Compiler.h"
17
18
using namespace clang;
19
using namespace cxcursor;
20
using namespace cxindex;
21
22
static void getTopOverriddenMethods(CXTranslationUnit TU,
23
                                    const Decl *D,
24
146
                                    SmallVectorImpl<const Decl *> &Methods) {
25
146
  if (!D)
26
0
    return;
27
146
  if (!isa<ObjCMethodDecl>(D) && 
!isa<CXXMethodDecl>(D)87
)
28
27
    return;
29
119
30
119
  SmallVector<CXCursor, 8> Overridden;
31
119
  cxcursor::getOverriddenCursors(cxcursor::MakeCXCursor(D, TU), Overridden);
32
119
  
33
119
  if (Overridden.empty()) {
34
103
    Methods.push_back(D->getCanonicalDecl());
35
103
    return;
36
103
  }
37
16
38
16
  for (SmallVectorImpl<CXCursor>::iterator
39
32
         I = Overridden.begin(), E = Overridden.end(); I != E; 
++I16
)
40
16
    getTopOverriddenMethods(TU, cxcursor::getCursorDecl(*I), Methods);
41
16
}
42
43
namespace {
44
45
struct FindFileIdRefVisitData {
46
  CXTranslationUnit TU;
47
  FileID FID;
48
  const Decl *Dcl;
49
  int SelectorIdIdx;
50
  CXCursorAndRangeVisitor visitor;
51
52
  typedef SmallVector<const Decl *, 8> TopMethodsTy;
53
  TopMethodsTy TopMethods;
54
55
  FindFileIdRefVisitData(CXTranslationUnit TU, FileID FID,
56
                         const Decl *D, int selectorIdIdx,
57
                         CXCursorAndRangeVisitor visitor)
58
31
    : TU(TU), FID(FID), SelectorIdIdx(selectorIdIdx), visitor(visitor) {
59
31
    Dcl = getCanonical(D);
60
31
    getTopOverriddenMethods(TU, Dcl, TopMethods);
61
31
  }
62
63
96
  ASTContext &getASTContext() const {
64
96
    return cxtu::getASTUnit(TU)->getASTContext();
65
96
  }
66
67
  /// We are looking to find all semantically relevant identifiers,
68
  /// so the definition of "canonical" here is different than in the AST, e.g.
69
  ///
70
  /// \code
71
  ///   class C {
72
  ///     C() {}
73
  ///   };
74
  /// \endcode
75
  ///
76
  /// we consider the canonical decl of the constructor decl to be the class
77
  /// itself, so both 'C' can be highlighted.
78
1.06k
  const Decl *getCanonical(const Decl *D) const {
79
1.06k
    if (!D)
80
0
      return nullptr;
81
1.06k
82
1.06k
    D = D->getCanonicalDecl();
83
1.06k
84
1.06k
    if (const ObjCImplDecl *ImplD = dyn_cast<ObjCImplDecl>(D)) {
85
13
      if (ImplD->getClassInterface())
86
13
        return getCanonical(ImplD->getClassInterface());
87
1.05k
88
1.05k
    } else if (const CXXConstructorDecl *CXXCtorD =
89
48
                   dyn_cast<CXXConstructorDecl>(D)) {
90
48
      return getCanonical(CXXCtorD->getParent());
91
48
    }
92
1.00k
    
93
1.00k
    return D;
94
1.00k
  }
95
96
977
  bool isHit(const Decl *D) const {
97
977
    if (!D)
98
0
      return false;
99
977
100
977
    D = getCanonical(D);
101
977
    if (D == Dcl)
102
115
      return true;
103
862
104
862
    if (isa<ObjCMethodDecl>(D) || 
isa<CXXMethodDecl>(D)821
)
105
101
      return isOverriddingMethod(D);
106
761
107
761
    return false;
108
761
  }
109
110
private:
111
101
  bool isOverriddingMethod(const Decl *D) const {
112
101
    if (llvm::find(TopMethods, D) != TopMethods.end())
113
2
      return true;
114
99
115
99
    TopMethodsTy methods;
116
99
    getTopOverriddenMethods(TU, D, methods);
117
99
    for (TopMethodsTy::iterator
118
195
           I = methods.begin(), E = methods.end(); I != E; 
++I96
) {
119
99
      if (llvm::find(TopMethods, *I) != TopMethods.end())
120
3
        return true;
121
99
    }
122
99
123
99
    
return false96
;
124
99
  }
125
};
126
127
} // end anonymous namespace.
128
129
/// For a macro \arg Loc, returns the file spelling location and sets
130
/// to \arg isMacroArg whether the spelling resides inside a macro definition or
131
/// a macro argument.
132
static SourceLocation getFileSpellingLoc(SourceManager &SM,
133
                                         SourceLocation Loc,
134
0
                                         bool &isMacroArg) {
135
0
  assert(Loc.isMacroID());
136
0
  SourceLocation SpellLoc = SM.getImmediateSpellingLoc(Loc);
137
0
  if (SpellLoc.isMacroID())
138
0
    return getFileSpellingLoc(SM, SpellLoc, isMacroArg);
139
0
  
140
0
  isMacroArg = SM.isMacroArgExpansion(Loc);
141
0
  return SpellLoc;
142
0
}
143
144
static enum CXChildVisitResult findFileIdRefVisit(CXCursor cursor,
145
                                                  CXCursor parent,
146
1.28k
                                                  CXClientData client_data) {
147
1.28k
  CXCursor declCursor = clang_getCursorReferenced(cursor);
148
1.28k
  if (!clang_isDeclaration(declCursor.kind))
149
310
    return CXChildVisit_Recurse;
150
977
151
977
  const Decl *D = cxcursor::getCursorDecl(declCursor);
152
977
  if (!D)
153
0
    return CXChildVisit_Continue;
154
977
155
977
  FindFileIdRefVisitData *data = (FindFileIdRefVisitData *)client_data;
156
977
  if (data->isHit(D)) {
157
120
    cursor = cxcursor::getSelectorIdentifierCursor(data->SelectorIdIdx, cursor);
158
120
159
120
    // We are looking for identifiers to highlight so for objc methods (and
160
120
    // not a parameter) we can only highlight the selector identifiers.
161
120
    if ((cursor.kind == CXCursor_ObjCClassMethodDecl ||
162
120
         cursor.kind == CXCursor_ObjCInstanceMethodDecl) &&
163
120
         
cxcursor::getSelectorIdentifierIndex(cursor) == -110
)
164
0
      return CXChildVisit_Recurse;
165
120
166
120
    if (clang_isExpression(cursor.kind)) {
167
49
      if (cursor.kind == CXCursor_DeclRefExpr ||
168
49
          
cursor.kind == CXCursor_MemberRefExpr35
) {
169
17
        // continue..
170
17
171
32
      } else if (cursor.kind == CXCursor_ObjCMessageExpr &&
172
32
                 
cxcursor::getSelectorIdentifierIndex(cursor) != -18
) {
173
8
        // continue..
174
8
                
175
8
      } else
176
24
        return CXChildVisit_Recurse;
177
96
    }
178
96
179
96
    SourceLocation
180
96
      Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
181
96
    SourceLocation SelIdLoc = cxcursor::getSelectorIdentifierLoc(cursor);
182
96
    if (SelIdLoc.isValid())
183
18
      Loc = SelIdLoc;
184
96
185
96
    ASTContext &Ctx = data->getASTContext();
186
96
    SourceManager &SM = Ctx.getSourceManager();
187
96
    bool isInMacroDef = false;
188
96
    if (Loc.isMacroID()) {
189
0
      bool isMacroArg;
190
0
      Loc = getFileSpellingLoc(SM, Loc, isMacroArg);
191
0
      isInMacroDef = !isMacroArg;
192
0
    }
193
96
194
96
    // We are looking for identifiers in a specific file.
195
96
    std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
196
96
    if (LocInfo.first != data->FID)
197
0
      return CXChildVisit_Recurse;
198
96
199
96
    if (isInMacroDef) {
200
0
      // FIXME: For a macro definition make sure that all expansions
201
0
      // of it expand to the same reference before allowing to point to it.
202
0
      return CXChildVisit_Recurse;
203
0
    }
204
96
205
96
    if (data->visitor.visit(data->visitor.context, cursor,
206
96
                        cxloc::translateSourceRange(Ctx, Loc)) == CXVisit_Break)
207
0
      return CXChildVisit_Break;
208
953
  }
209
953
  return CXChildVisit_Recurse;
210
953
}
211
212
static bool findIdRefsInFile(CXTranslationUnit TU, CXCursor declCursor,
213
                             const FileEntry *File,
214
31
                             CXCursorAndRangeVisitor Visitor) {
215
31
  assert(clang_isDeclaration(declCursor.kind));
216
31
  SourceManager &SM = cxtu::getASTUnit(TU)->getSourceManager();
217
31
218
31
  FileID FID = SM.translateFile(File);
219
31
  const Decl *Dcl = cxcursor::getCursorDecl(declCursor);
220
31
  if (!Dcl)
221
0
    return false;
222
31
223
31
  FindFileIdRefVisitData data(TU, FID, Dcl,
224
31
                              cxcursor::getSelectorIdentifierIndex(declCursor),
225
31
                              Visitor);
226
31
227
31
  if (const DeclContext *DC = Dcl->getParentFunctionOrMethod()) {
228
7
    return clang_visitChildren(cxcursor::MakeCXCursor(cast<Decl>(DC), TU),
229
7
                               findFileIdRefVisit, &data);
230
7
  }
231
24
232
24
  SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
233
24
  CursorVisitor FindIdRefsVisitor(TU,
234
24
                                  findFileIdRefVisit, &data,
235
24
                                  /*VisitPreprocessorLast=*/true,
236
24
                                  /*VisitIncludedEntities=*/false,
237
24
                                  Range,
238
24
                                  /*VisitDeclsOnly=*/true);
239
24
  return FindIdRefsVisitor.visitFileRegion();
240
24
}
241
242
namespace {
243
244
struct FindFileMacroRefVisitData {
245
  ASTUnit &Unit;
246
  const FileEntry *File;
247
  const IdentifierInfo *Macro;
248
  CXCursorAndRangeVisitor visitor;
249
250
  FindFileMacroRefVisitData(ASTUnit &Unit, const FileEntry *File,
251
                            const IdentifierInfo *Macro,
252
                            CXCursorAndRangeVisitor visitor)
253
2
    : Unit(Unit), File(File), Macro(Macro), visitor(visitor) { }
254
255
6
  ASTContext &getASTContext() const {
256
6
    return Unit.getASTContext();
257
6
  }
258
};
259
260
} // anonymous namespace
261
262
static enum CXChildVisitResult findFileMacroRefVisit(CXCursor cursor,
263
                                                     CXCursor parent,
264
6
                                                     CXClientData client_data) {
265
6
  const IdentifierInfo *Macro = nullptr;
266
6
  if (cursor.kind == CXCursor_MacroDefinition)
267
2
    Macro = getCursorMacroDefinition(cursor)->getName();
268
4
  else if (cursor.kind == CXCursor_MacroExpansion)
269
4
    Macro = getCursorMacroExpansion(cursor).getName();
270
6
  if (!Macro)
271
0
    return CXChildVisit_Continue;
272
6
273
6
  FindFileMacroRefVisitData *data = (FindFileMacroRefVisitData *)client_data;
274
6
  if (data->Macro != Macro)
275
0
    return CXChildVisit_Continue;
276
6
277
6
  SourceLocation
278
6
    Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
279
6
280
6
  ASTContext &Ctx = data->getASTContext();
281
6
  SourceManager &SM = Ctx.getSourceManager();
282
6
  bool isInMacroDef = false;
283
6
  if (Loc.isMacroID()) {
284
0
    bool isMacroArg;
285
0
    Loc = getFileSpellingLoc(SM, Loc, isMacroArg);
286
0
    isInMacroDef = !isMacroArg;
287
0
  }
288
6
289
6
  // We are looking for identifiers in a specific file.
290
6
  std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
291
6
  if (SM.getFileEntryForID(LocInfo.first) != data->File)
292
0
    return CXChildVisit_Continue;
293
6
294
6
  if (isInMacroDef) {
295
0
    // FIXME: For a macro definition make sure that all expansions
296
0
    // of it expand to the same reference before allowing to point to it.
297
0
    return CXChildVisit_Continue;
298
0
  }
299
6
300
6
  if (data->visitor.visit(data->visitor.context, cursor,
301
6
                        cxloc::translateSourceRange(Ctx, Loc)) == CXVisit_Break)
302
0
    return CXChildVisit_Break;
303
6
  return CXChildVisit_Continue;
304
6
}
305
306
static bool findMacroRefsInFile(CXTranslationUnit TU, CXCursor Cursor,
307
                                const FileEntry *File,
308
2
                                CXCursorAndRangeVisitor Visitor) {
309
2
  if (Cursor.kind != CXCursor_MacroDefinition &&
310
2
      Cursor.kind != CXCursor_MacroExpansion)
311
0
    return false;
312
2
313
2
  ASTUnit *Unit = cxtu::getASTUnit(TU);
314
2
  SourceManager &SM = Unit->getSourceManager();
315
2
316
2
  FileID FID = SM.translateFile(File);
317
2
  const IdentifierInfo *Macro = nullptr;
318
2
  if (Cursor.kind == CXCursor_MacroDefinition)
319
0
    Macro = getCursorMacroDefinition(Cursor)->getName();
320
2
  else
321
2
    Macro = getCursorMacroExpansion(Cursor).getName();
322
2
  if (!Macro)
323
0
    return false;
324
2
325
2
  FindFileMacroRefVisitData data(*Unit, File, Macro, Visitor);
326
2
327
2
  SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
328
2
  CursorVisitor FindMacroRefsVisitor(TU,
329
2
                                  findFileMacroRefVisit, &data,
330
2
                                  /*VisitPreprocessorLast=*/false,
331
2
                                  /*VisitIncludedEntities=*/false,
332
2
                                  Range);
333
2
  return FindMacroRefsVisitor.visitPreprocessedEntitiesInRegion();
334
2
}
335
336
namespace {
337
338
struct FindFileIncludesVisitor {
339
  ASTUnit &Unit;
340
  const FileEntry *File;
341
  CXCursorAndRangeVisitor visitor;
342
343
  FindFileIncludesVisitor(ASTUnit &Unit, const FileEntry *File,
344
                          CXCursorAndRangeVisitor visitor)
345
9
    : Unit(Unit), File(File), visitor(visitor) { }
346
347
16
  ASTContext &getASTContext() const {
348
16
    return Unit.getASTContext();
349
16
  }
350
351
20
  enum CXChildVisitResult visit(CXCursor cursor, CXCursor parent) {
352
20
    if (cursor.kind != CXCursor_InclusionDirective)
353
4
      return CXChildVisit_Continue;
354
16
355
16
    SourceLocation
356
16
      Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
357
16
358
16
    ASTContext &Ctx = getASTContext();
359
16
    SourceManager &SM = Ctx.getSourceManager();
360
16
361
16
    // We are looking for includes in a specific file.
362
16
    std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
363
16
    if (SM.getFileEntryForID(LocInfo.first) != File)
364
0
      return CXChildVisit_Continue;
365
16
366
16
    if (visitor.visit(visitor.context, cursor,
367
16
                      cxloc::translateSourceRange(Ctx, Loc)) == CXVisit_Break)
368
0
      return CXChildVisit_Break;
369
16
    return CXChildVisit_Continue;
370
16
  }
371
372
  static enum CXChildVisitResult visit(CXCursor cursor, CXCursor parent,
373
20
                                       CXClientData client_data) {
374
20
    return static_cast<FindFileIncludesVisitor*>(client_data)->
375
20
                                                          visit(cursor, parent);
376
20
  }
377
};
378
379
} // anonymous namespace
380
381
static bool findIncludesInFile(CXTranslationUnit TU, const FileEntry *File,
382
9
                               CXCursorAndRangeVisitor Visitor) {
383
9
  assert(TU && File && Visitor.visit);
384
9
385
9
  ASTUnit *Unit = cxtu::getASTUnit(TU);
386
9
  SourceManager &SM = Unit->getSourceManager();
387
9
388
9
  FileID FID = SM.translateFile(File);
389
9
390
9
  FindFileIncludesVisitor IncludesVisitor(*Unit, File, Visitor);
391
9
392
9
  SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
393
9
  CursorVisitor InclusionCursorsVisitor(TU,
394
9
                                        FindFileIncludesVisitor::visit,
395
9
                                        &IncludesVisitor,
396
9
                                        /*VisitPreprocessorLast=*/false,
397
9
                                        /*VisitIncludedEntities=*/false,
398
9
                                        Range);
399
9
  return InclusionCursorsVisitor.visitPreprocessedEntitiesInRegion();
400
9
}
401
402
403
//===----------------------------------------------------------------------===//
404
// libclang public APIs.
405
//===----------------------------------------------------------------------===//
406
407
extern "C" {
408
409
CXResult clang_findReferencesInFile(CXCursor cursor, CXFile file,
410
33
                                    CXCursorAndRangeVisitor visitor) {
411
33
  LogRef Log = Logger::make(__func__);
412
33
413
33
  if (clang_Cursor_isNull(cursor)) {
414
0
    if (Log)
415
0
      *Log << "Null cursor";
416
0
    return CXResult_Invalid;
417
0
  }
418
33
  if (cursor.kind == CXCursor_NoDeclFound) {
419
0
    if (Log)
420
0
      *Log << "Got CXCursor_NoDeclFound";
421
0
    return CXResult_Invalid;
422
0
  }
423
33
  if (!file) {
424
0
    if (Log)
425
0
      *Log << "Null file";
426
0
    return CXResult_Invalid;
427
0
  }
428
33
  if (!visitor.visit) {
429
0
    if (Log)
430
0
      *Log << "Null visitor";
431
0
    return CXResult_Invalid;
432
0
  }
433
33
434
33
  if (Log)
435
0
    *Log << cursor << " @" << static_cast<const FileEntry *>(file);
436
33
437
33
  ASTUnit *CXXUnit = cxcursor::getCursorASTUnit(cursor);
438
33
  if (!CXXUnit)
439
0
    return CXResult_Invalid;
440
33
441
33
  ASTUnit::ConcurrencyCheck Check(*CXXUnit);
442
33
443
33
  if (cursor.kind == CXCursor_MacroDefinition ||
444
33
      cursor.kind == CXCursor_MacroExpansion) {
445
2
    if (findMacroRefsInFile(cxcursor::getCursorTU(cursor),
446
2
                            cursor,
447
2
                            static_cast<const FileEntry *>(file),
448
2
                            visitor))
449
0
      return CXResult_VisitBreak;
450
2
    return CXResult_Success;
451
2
  }
452
31
453
31
  // We are interested in semantics of identifiers so for C++ constructor exprs
454
31
  // prefer type references, e.g.:
455
31
  //
456
31
  //  return MyStruct();
457
31
  //
458
31
  // for 'MyStruct' we'll have a cursor pointing at the constructor decl but
459
31
  // we are actually interested in the type declaration.
460
31
  cursor = cxcursor::getTypeRefCursor(cursor);
461
31
462
31
  CXCursor refCursor = clang_getCursorReferenced(cursor);
463
31
464
31
  if (!clang_isDeclaration(refCursor.kind)) {
465
0
    if (Log)
466
0
      *Log << "cursor is not referencing a declaration";
467
0
    return CXResult_Invalid;
468
0
  }
469
31
470
31
  if (findIdRefsInFile(cxcursor::getCursorTU(cursor),
471
31
                       refCursor,
472
31
                       static_cast<const FileEntry *>(file),
473
31
                       visitor))
474
0
    return CXResult_VisitBreak;
475
31
  return CXResult_Success;
476
31
}
477
478
CXResult clang_findIncludesInFile(CXTranslationUnit TU, CXFile file,
479
9
                             CXCursorAndRangeVisitor visitor) {
480
9
  if (cxtu::isNotUsableTU(TU)) {
481
0
    LOG_BAD_TU(TU);
482
0
    return CXResult_Invalid;
483
0
  }
484
9
485
9
  LogRef Log = Logger::make(__func__);
486
9
  if (!file) {
487
0
    if (Log)
488
0
      *Log << "Null file";
489
0
    return CXResult_Invalid;
490
0
  }
491
9
  if (!visitor.visit) {
492
0
    if (Log)
493
0
      *Log << "Null visitor";
494
0
    return CXResult_Invalid;
495
0
  }
496
9
497
9
  if (Log)
498
0
    *Log << TU << " @" << static_cast<const FileEntry *>(file);
499
9
500
9
  ASTUnit *CXXUnit = cxtu::getASTUnit(TU);
501
9
  if (!CXXUnit)
502
0
    return CXResult_Invalid;
503
9
504
9
  ASTUnit::ConcurrencyCheck Check(*CXXUnit);
505
9
506
9
  if (findIncludesInFile(TU, static_cast<const FileEntry *>(file), visitor))
507
0
    return CXResult_VisitBreak;
508
9
  return CXResult_Success;
509
9
}
510
511
static enum CXVisitorResult _visitCursorAndRange(void *context,
512
                                                 CXCursor cursor,
513
0
                                                 CXSourceRange range) {
514
0
  CXCursorAndRangeVisitorBlock block = (CXCursorAndRangeVisitorBlock)context;
515
0
  return INVOKE_BLOCK2(block, cursor, range);
516
0
}
517
518
CXResult clang_findReferencesInFileWithBlock(CXCursor cursor,
519
                                             CXFile file,
520
0
                                           CXCursorAndRangeVisitorBlock block) {
521
0
  CXCursorAndRangeVisitor visitor = { block,
522
0
                                      block ? _visitCursorAndRange : nullptr };
523
0
  return clang_findReferencesInFile(cursor, file, visitor);
524
0
}
525
526
CXResult clang_findIncludesInFileWithBlock(CXTranslationUnit TU,
527
                                           CXFile file,
528
0
                                           CXCursorAndRangeVisitorBlock block) {
529
0
  CXCursorAndRangeVisitor visitor = { block,
530
0
                                      block ? _visitCursorAndRange : nullptr };
531
0
  return clang_findIncludesInFile(TU, file, visitor);
532
0
}
533
534
} // end: extern "C"