1/****************************************************************************
2 * Copyright (C) 2012-2016 Woboq GmbH
3 * Olivier Goffart <contact at woboq.com>
4 * https://woboq.com/codebrowser.html
5 *
6 * This file is part of the Woboq Code Browser.
7 *
8 * Commercial License Usage:
9 * Licensees holding valid commercial licenses provided by Woboq may use
10 * this file in accordance with the terms contained in a written agreement
11 * between the licensee and Woboq.
12 * For further information see https://woboq.com/codebrowser.html
13 *
14 * Alternatively, this work may be used under a Creative Commons
15 * Attribution-NonCommercial-ShareAlike 3.0 (CC-BY-NC-SA 3.0) License.
16 * http://creativecommons.org/licenses/by-nc-sa/3.0/deed.en_US
17 * This license does not allow you to use the code browser to assist the
18 * development of your commercial software. If you intent to do so, consider
19 * purchasing a commercial licence.
20 ****************************************************************************/
21
22#pragma once
23
24#include "annotator.h"
25#include "qtsupport.h"
26#include <clang/AST/ASTConsumer.h>
27#include <clang/AST/Attr.h>
28#include <clang/AST/Decl.h>
29#include <clang/AST/DeclGroup.h>
30#include <clang/AST/Mangle.h>
31#include <clang/AST/RecursiveASTVisitor.h>
32#include <clang/AST/Stmt.h>
33#include <clang/Basic/Version.h>
34#include <clang/Lex/Lexer.h>
35#include <clang/Lex/Preprocessor.h>
36#include <llvm/Support/SaveAndRestore.h>
37
38#include <deque>
39#include <iostream>
40
41struct BrowserASTVisitor : clang::RecursiveASTVisitor<BrowserASTVisitor>
42{
43 typedef clang::RecursiveASTVisitor<BrowserASTVisitor> Base;
44 Annotator &annotator;
45 clang::NamedDecl *currentContext = nullptr;
46 int recursionCount = 0; // Used to avoid a stack overflow
47 bool isNestedNameSpecifier = false;
48
49 struct : std::deque<clang::Expr *>
50 {
51 clang::Expr *topExpr = 0;
52 Annotator::DeclType topType = Annotator::Use;
53 } expr_stack;
54
55 BrowserASTVisitor(Annotator &R)
56 : annotator(R)
57 {
58 }
59
60 bool VisitTypedefNameDecl(clang::TypedefNameDecl *d)
61 {
62 annotator.registerReference(decl: d, range: d->getLocation(), type: Annotator::Typedef, declType: Annotator::Declaration,
63 typeRef: annotator.getTypeRef(type: d->getUnderlyingType()));
64 return true;
65 }
66
67 bool VisitTagDecl(clang::TagDecl *d)
68 {
69 if (!shouldProcess(d))
70 return false;
71 if (d->isThisDeclarationADefinition()) {
72 if (clang::CXXRecordDecl *cxx = llvm::dyn_cast<clang::CXXRecordDecl>(Val: d)) {
73 for (auto it = cxx->bases_begin(); it != cxx->bases_end(); ++it) {
74 if (!it->getType()->getAsCXXRecordDecl()) {
75 // std::cerr << " INHERITING but not from a CXXRecrod
76 // " << std::endl; it->getType().dump();
77 // probably template type... FIXME
78 continue;
79 }
80 annotator.registerOverride(decl: d, overrided: it->getType()->getAsCXXRecordDecl(),
81 loc: d->getLocation());
82 }
83 }
84 }
85 annotator.registerReference(decl: d, range: d->getLocation(), type: Annotator::Type,
86 declType: d->isThisDeclarationADefinition() ? Annotator::Definition
87 : Annotator::Declaration);
88 return true;
89 }
90
91 bool VisitNamespaceDecl(clang::NamespaceDecl *d)
92 {
93 annotator.registerReference(decl: d, range: d->getLocation(), type: Annotator::Namespace,
94 declType: Annotator::Declaration);
95 return true;
96 }
97 bool VisitNamespaceAliasDecl(clang::NamespaceAliasDecl *d)
98 {
99 annotator.registerReference(decl: d, range: d->getLocation(), type: Annotator::Namespace,
100 declType: Annotator::Declaration);
101 annotator.registerReference(decl: d, range: d->getTargetNameLoc(), type: Annotator::Namespace);
102 return true;
103 }
104 bool VisitFunctionDecl(clang::FunctionDecl *d)
105 {
106 if (!shouldProcess(d))
107 return false;
108 std::string typeText;
109 {
110 llvm::raw_string_ostream typeTextStream(typeText);
111
112 bool isConst = false;
113 if (clang::CXXMethodDecl *cxx = llvm::dyn_cast<clang::CXXMethodDecl>(Val: d)) {
114 if (cxx->isStatic())
115 typeTextStream << "static ";
116 isConst = cxx->isConst();
117 if (cxx->isThisDeclarationADefinition()) {
118 for (auto it = cxx->begin_overridden_methods();
119 it != cxx->end_overridden_methods(); ++it) {
120 const clang::CXXMethodDecl *ovr = (*it)->getCanonicalDecl();
121 annotator.registerOverride(decl: d, overrided: const_cast<clang::CXXMethodDecl *>(ovr),
122 loc: d->getNameInfo().getBeginLoc());
123 }
124 }
125 }
126 typeTextStream << annotator.getTypeRef(type: getResultType(decl: d)) << " "
127 << d->getQualifiedNameAsString() << "(";
128 for (unsigned int i = 0; i < d->getNumParams(); i++) {
129 if (i != 0)
130 typeTextStream << ", ";
131 clang::ParmVarDecl *PVD = d->getParamDecl(i);
132 typeTextStream << annotator.getTypeRef(type: PVD->getType()) << " " << PVD->getName();
133 if (PVD->hasDefaultArg() && !PVD->hasUninstantiatedDefaultArg()) {
134 typeTextStream << " = ";
135 PVD->getDefaultArg()->printPretty(OS&: typeTextStream, Helper: 0, Policy: annotator.getLangOpts());
136 }
137 }
138 typeTextStream << ")";
139 if (isConst) {
140 typeTextStream << " const";
141 }
142 }
143
144 bool isDefinition = d->isThisDeclarationADefinition() || d->hasAttr<clang::AliasAttr>();
145
146 annotator.registerReference(decl: d, range: d->getNameInfo().getSourceRange(), type: Annotator::Decl,
147 declType: isDefinition ? Annotator::Definition : Annotator::Declaration,
148 typeRef: typeText);
149 return true;
150 }
151 bool VisitEnumConstantDecl(clang::EnumConstantDecl *d)
152 {
153#if CLANG_VERSION_MAJOR >= 13
154 annotator.registerReference(decl: d, range: d->getLocation(), type: Annotator::EnumDecl,
155 declType: Annotator::Declaration, typeRef: llvm::toString(I: d->getInitVal(), Radix: 10));
156#else
157 annotator.registerReference(d, d->getLocation(), Annotator::EnumDecl,
158 Annotator::Declaration, d->getInitVal().toString(10));
159#endif
160 return true;
161 }
162 bool VisitVarDecl(clang::VarDecl *d)
163 {
164 if (!shouldProcess(d))
165 return false;
166 annotator.registerReference(decl: d, range: d->getLocation(), type: Annotator::Decl,
167 declType: d->isThisDeclarationADefinition() ? Annotator::Definition
168 : Annotator::Declaration,
169 typeRef: annotator.getTypeRef(type: d->getType()));
170 return true;
171 }
172 bool VisitFieldDecl(clang::FieldDecl *d)
173 {
174 annotator.registerReference(decl: d, range: d->getLocation(), type: Annotator::Decl, declType: Annotator::Declaration,
175 typeRef: annotator.getTypeRef(type: d->getType()));
176 return true;
177 }
178
179 bool VisitMemberExpr(clang::MemberExpr *e)
180 {
181 auto range = e->getMemberNameInfo().getSourceRange();
182 if (range.getBegin().isInvalid()) {
183 // implicit conversion operator;
184 range = { e->getSourceRange().getBegin(), clang::SourceLocation {} };
185 }
186 annotator.registerUse(decl: e->getMemberDecl(), range,
187 tt: isMember(d: e->getMemberDecl()) ? Annotator::Member : Annotator::Ref,
188 currentContext, useType: classify());
189 return true;
190 }
191 bool VisitDeclRefExpr(clang::DeclRefExpr *e)
192 {
193 clang::ValueDecl *decl = e->getDecl();
194 annotator.registerUse(decl, range: e->getNameInfo().getSourceRange(),
195 tt: llvm::isa<clang::EnumConstantDecl>(Val: decl) ? Annotator::EnumDecl
196 : isMember(d: decl) ? Annotator::Member
197 : Annotator::Ref,
198 currentContext, useType: classify());
199 return true;
200 }
201
202 bool VisitDesignatedInitExpr(clang::DesignatedInitExpr *e)
203 {
204#if CLANG_VERSION_MAJOR == 3 && CLANG_VERSION_MINOR < 5
205 llvm::ArrayRef<clang::DesignatedInitExpr::Designator> designators { e->designators_begin(),
206 e->designators_end() };
207#else
208 auto designators = e->designators();
209#endif
210 for (auto it : designators) {
211 if (it.isFieldDesignator()) {
212#if CLANG_VERSION_MAJOR >= 17
213 if (auto decl = it.getFieldDecl()) {
214#else
215 if (auto decl = it.getField()) {
216#endif
217 annotator.registerUse(decl, range: it.getFieldLoc(), tt: Annotator::Ref, currentContext,
218 useType: Annotator::Use_Write);
219 }
220 }
221 }
222 return true;
223 }
224
225 bool VisitTypedefTypeLoc(clang::TypedefTypeLoc TL)
226 {
227 clang::SourceRange range = TL.getSourceRange();
228 annotator.registerReference(
229 decl: TL.getTypedefNameDecl(), range, type: Annotator::Typedef, declType: Annotator::Use,
230 typeRef: annotator.getTypeRef(type: TL.getTypedefNameDecl()->getUnderlyingType()), usedContext: currentContext);
231 return true;
232 }
233
234 bool VisitTagTypeLoc(clang::TagTypeLoc TL)
235 {
236 clang::SourceRange range = TL.getSourceRange();
237 annotator.registerUse(decl: TL.getDecl(), range: range.getBegin(), tt: Annotator::Type, currentContext,
238 useType: isNestedNameSpecifier ? Annotator::Use_NestedName : Annotator::Use);
239 return true;
240 }
241
242 bool VisitTemplateSpecializationTypeLoc(clang::TemplateSpecializationTypeLoc TL)
243 {
244
245 clang::TemplateDecl *decl = TL.getTypePtr()->getTemplateName().getAsTemplateDecl();
246 if (decl) {
247 auto loc = TL.getTemplateNameLoc();
248 annotator.registerUse(decl, range: loc, tt: Annotator::Type, currentContext);
249 } else {
250 std::cerr << "VisitTemplateSpecializationTypeLoc "
251 << " " << TL.getType().getAsString();
252 }
253 return true;
254 }
255
256 bool TraverseNestedNameSpecifierLoc(clang::NestedNameSpecifierLoc NNS)
257 {
258 if (!NNS)
259 return true;
260
261 switch (NNS.getNestedNameSpecifier()->getKind()) {
262 case clang::NestedNameSpecifier::Namespace:
263 if (NNS.getNestedNameSpecifier()->getAsNamespace()->isAnonymousNamespace())
264 break;
265 annotator.registerReference(decl: NNS.getNestedNameSpecifier()->getAsNamespace(),
266 range: NNS.getSourceRange(), type: Annotator::Namespace);
267 return true; // skip prefixes
268 case clang::NestedNameSpecifier::NamespaceAlias:
269 annotator.registerReference(
270 decl: NNS.getNestedNameSpecifier()->getAsNamespaceAlias()->getAliasedNamespace(),
271 range: NNS.getSourceRange(), type: Annotator::Namespace);
272 return true; // skip prefixes
273 default:
274 break;
275 }
276 llvm::SaveAndRestore<bool> nns(isNestedNameSpecifier, true);
277 return Base::TraverseNestedNameSpecifierLoc(NNS);
278 }
279 bool TraverseUsingDirectiveDecl(clang::UsingDirectiveDecl *d)
280 {
281 auto qualBeginLoc = d->getQualifierLoc().getBeginLoc();
282 auto identLoc = d->getIdentLocation();
283 annotator.registerReference(decl: d->getNominatedNamespace(),
284 range: { qualBeginLoc.isValid() ? qualBeginLoc : identLoc, identLoc },
285 type: Annotator::Namespace);
286 // don't call Base::TraverseUsingDirectiveDecl in order to skip prefix
287 return true;
288 }
289
290 // the initializers would not be highlighted otherwise
291 bool TraverseConstructorInitializer(clang::CXXCtorInitializer *Init)
292 {
293 if (Init->isAnyMemberInitializer() && Init->isWritten()) {
294 annotator.registerUse(
295 decl: Init->getAnyMember(), range: Init->getMemberLocation(),
296 tt: Init->isMemberInitializer() ? Annotator::Member : Annotator::Ref, currentContext,
297 useType: Init->isMemberInitializer() ? Annotator::Use_Write : Annotator::Use);
298 }
299 decltype(expr_stack) old_stack;
300 std::swap(a&: expr_stack, b&: old_stack);
301 expr_stack.topExpr = Init->getInit();
302 expr_stack.topType = Annotator::Use_Read;
303 Base::TraverseConstructorInitializer(Init);
304 std::swap(a&: expr_stack, b&: old_stack);
305 return true;
306 }
307
308 // try to put a link to the right constructor
309 bool VisitCXXConstructExpr(clang::CXXConstructExpr *ctr)
310 {
311 if (clang::CXXConstructorDecl *decl = ctr->getConstructor()) {
312 clang::SourceLocation parenLoc = ctr->getParenOrBraceRange().getBegin();
313 if (parenLoc.isValid()) {
314 // Highlight the opening parenthese
315 annotator.registerUse(decl, range: parenLoc, tt: Annotator::Ref, currentContext,
316 useType: Annotator::Use_Call);
317 } else if (!ctr->isElidable()) {
318 annotator.registerUse(decl, range: { ctr->getLocation(), clang::SourceLocation {} },
319 tt: Annotator::Ref, currentContext, useType: Annotator::Use_Call);
320 }
321 }
322 QtSupport qt { .annotator: annotator, .currentContext: currentContext };
323 qt.visitCXXConstructExpr(e: ctr);
324 return true;
325 }
326
327
328 bool VisitCallExpr(clang::CallExpr *e)
329 {
330 // Find out arguments passed by references
331 auto decl = e->getDirectCallee();
332 if (decl && !llvm::isa<clang::CXXOperatorCallExpr>(Val: e)) {
333 // Don't handle CXXOperatorCallExpr because it is obvious for operator= += and so on.
334 // And also because of the wierd rules regarding the member operators and their number
335 // of arguments
336 for (unsigned int i = 0; decl && i < e->getNumArgs() && i < decl->getNumParams(); ++i) {
337 auto paramDecl = decl->getParamDecl(i);
338 auto t = paramDecl->getType();
339 auto arg = e->getArg(Arg: i);
340 std::string prefix = annotator.getParamNameForArg(callExpr: e, paramDecl, arg);
341 if (t->isLValueReferenceType() && !t.getNonReferenceType().isConstQualified()) {
342 annotator.annotateSourceRange(range: e->getArg(Arg: i)->getSourceRange(), tag: "span",
343 attributes: "class='refarg'");
344 }
345 annotator.addInlayHint(range: e->getArg(Arg: i)->getBeginLoc(), inlayHint: prefix);
346 }
347 }
348
349 // support QObject::connect SIGNAL and SLOT
350 QtSupport qt { .annotator: annotator, .currentContext: currentContext };
351 qt.visitCallExpr(e);
352 return true;
353 }
354
355 bool VisitInitListExpr(clang::InitListExpr *Syn)
356 {
357 if (!Syn->isSyntacticForm())
358 return false;
359 if (Syn->isIdiomaticZeroInitializer(LangOpts: annotator.getLangOpts()))
360 return false;
361 llvm::DenseMap<clang::SourceLocation, std::string> designators =
362 annotator.getDesignatorInlayHints(Syn);
363 for (const auto &d : designators) {
364 annotator.addInlayHint(range: d.getFirst(), inlayHint: d.getSecond());
365 }
366 return true;
367 }
368
369 bool VisitGotoStmt(clang::GotoStmt *stm)
370 {
371 if (auto label = stm->getLabel()) {
372 annotator.registerReference(decl: label, range: stm->getLabelLoc(), type: Annotator::Label, declType: Annotator::Use,
373 typeRef: {}, usedContext: currentContext);
374 }
375 return true;
376 }
377 bool VisitLabelStmt(clang::LabelStmt *stm)
378 {
379 if (auto label = stm->getDecl()) {
380 annotator.registerReference(decl: label, range: stm->getIdentLoc(), type: Annotator::Label,
381 declType: Annotator::Declaration, typeRef: {}, usedContext: currentContext);
382 }
383 return true;
384 }
385
386 bool TraverseDecl(clang::Decl *d)
387 {
388 if (!d)
389 return true;
390 auto saved = currentContext;
391 if (clang::FunctionDecl::classof(D: d) || clang::RecordDecl::classof(D: d)
392 || clang::NamespaceDecl::classof(D: d) || clang::TemplateDecl::classof(D: d)) {
393 currentContext = llvm::dyn_cast<clang::NamedDecl>(Val: d);
394 }
395 if (auto v = llvm::dyn_cast<clang::VarDecl>(Val: d)) {
396 if (v->getInit() && !expr_stack.topExpr) {
397 expr_stack.topExpr = v->getInit();
398 auto t = v->getType();
399 expr_stack.topType =
400 (t->isReferenceType() && !t.getNonReferenceType().isConstQualified())
401 ? Annotator::Use_Address
402 : Annotator::Use_Read;
403 }
404 }
405 Base::TraverseDecl(D: d);
406 currentContext = saved;
407 return true;
408 }
409
410 // Since we cannot find up the parent of a node, we keep a stack of parents
411 bool TraverseStmt(clang::Stmt *s)
412 {
413 if (++recursionCount > 10000) {
414 // Give up if the stack is too big to avoid stack overflow
415 std::cerr << "TraverseStmt: Stack overflow, giving up traversal";
416 return true;
417 }
418 auto e = llvm::dyn_cast_or_null<clang::Expr>(Val: s);
419 decltype(expr_stack) old_stack;
420 if (e) {
421 expr_stack.push_front(x: e);
422 } else {
423 std::swap(a&: old_stack, b&: expr_stack);
424 if (auto i = llvm::dyn_cast_or_null<clang::IfStmt>(Val: s)) {
425 expr_stack.topExpr = i->getCond();
426 expr_stack.topType = Annotator::Use_Read;
427 } else if (auto r = llvm::dyn_cast_or_null<clang::ReturnStmt>(Val: s)) {
428 expr_stack.topExpr = r->getRetValue();
429 if (auto f = llvm::dyn_cast_or_null<clang::FunctionDecl>(Val: currentContext)) {
430 auto t = getResultType(decl: f);
431 if (t->isReferenceType() /*&& !t.getNonReferenceType().isConstQualified()*/)
432 expr_stack.topType = Annotator::Use_Address; // non const reference
433 else
434 expr_stack.topType =
435 Annotator::Use_Read; // anything else is considered as read;
436 }
437 } else if (auto sw = llvm::dyn_cast_or_null<clang::SwitchStmt>(Val: s)) {
438 expr_stack.topExpr = sw->getCond();
439 expr_stack.topType = Annotator::Use_Read;
440 } else if (auto d = llvm::dyn_cast_or_null<clang::DoStmt>(Val: s)) {
441 expr_stack.topExpr = d->getCond();
442 expr_stack.topType = Annotator::Use_Read;
443 } else if (auto w = llvm::dyn_cast_or_null<clang::WhileStmt>(Val: s)) {
444 expr_stack.topExpr = w->getCond();
445 expr_stack.topType = Annotator::Use_Read;
446 }
447 }
448 auto r = Base::TraverseStmt(S: s);
449 if (e) {
450 expr_stack.pop_front();
451 } else {
452 std::swap(a&: old_stack, b&: expr_stack);
453 }
454 recursionCount--;
455 return r;
456 }
457
458#if CLANG_VERSION_MAJOR == 3 && CLANG_VERSION_MINOR < 8
459 bool shouldUseDataRecursionFor(clang::Stmt *S)
460 {
461 // We need to disable this data recursion feature otherwise this break the detection
462 // of parents (expr_stack). Especially for the CaseStmt
463 return false;
464 }
465#endif
466
467 bool TraverseDeclarationNameInfo(clang::DeclarationNameInfo NameInfo)
468 {
469 // Do not visit the TypeLoc of constructor or destructors
470 return true;
471 }
472
473private:
474 Annotator::DeclType classify()
475 {
476 bool first = true;
477 clang::Expr *previous = nullptr;
478 for (auto expr : expr_stack) {
479 if (first) {
480 previous = expr;
481 first = false;
482 continue; // skip the first element (ourself)
483 }
484 if (llvm::isa<clang::MemberExpr>(Val: expr)) {
485 return Annotator::Use_MemberAccess;
486 }
487 if (auto op = llvm::dyn_cast<clang::ImplicitCastExpr>(Val: expr)) {
488 if (op->getCastKind() == clang::CK_LValueToRValue)
489 return Annotator::Use_Read;
490 }
491 if (auto op = llvm::dyn_cast<clang::BinaryOperator>(Val: expr)) {
492 if (op->isAssignmentOp() && op->getLHS() == previous)
493 return Annotator::Use_Write;
494 return Annotator::Use_Read;
495 }
496 if (auto op = llvm::dyn_cast<clang::UnaryOperator>(Val: expr)) {
497 if (op->isIncrementDecrementOp())
498 return Annotator::Use_Write;
499 if (op->isArithmeticOp() || op->getOpcode() == clang::UO_Deref)
500 return Annotator::Use_Read;
501 if (op->getOpcode() == clang::UO_AddrOf)
502 return Annotator::Use_Address;
503 return Annotator::Use;
504 }
505 if (auto op = llvm::dyn_cast<clang::CXXOperatorCallExpr>(Val: expr)) {
506 // Special case for some of the CXXOperatorCallExpr to check if it is Use_Write
507 // Anything else goes through normal CallExpr
508 auto o = op->getOperator();
509 if (o == clang::OO_Equal || (o >= clang::OO_PlusEqual && o <= clang::OO_PipeEqual)
510 || (o >= clang::OO_LessLessEqual && o <= clang::OO_GreaterGreaterEqual)
511 || (o >= clang::OO_PlusPlus && o <= clang::OO_MinusMinus)) {
512 if (op->getNumArgs() >= 1 && op->getArg(Arg: 0) == previous) {
513 return Annotator::Use_Write;
514 }
515 }
516 }
517 if (auto call = llvm::dyn_cast<clang::CallExpr>(Val: expr)) {
518 if (previous == call->getCallee())
519 return Annotator::Use_Call;
520 auto decl = call->getDirectCallee();
521 if (!decl)
522 return Annotator::Use;
523 for (unsigned int i = 0; i < call->getNumArgs(); ++i) {
524 if (call->getArg(Arg: i) != previous)
525 continue;
526 if (llvm::isa<clang::CXXOperatorCallExpr>(Val: call)
527 && decl->getNumParams() < call->getNumArgs()) {
528 // For example, member operators: first argument is the 'this'
529 if (i == 0)
530 return Annotator::Use_MemberAccess;
531 i--;
532 }
533 if (i >= decl->getNumParams())
534 break;
535 auto t = decl->getParamDecl(i)->getType();
536 if (t->isReferenceType() && !t.getNonReferenceType().isConstQualified())
537 return Annotator::Use_Address; // non const reference
538 return Annotator::Use_Read; // anything else is considered as read;
539 }
540 return Annotator::Use;
541 }
542 if (auto call = llvm::dyn_cast<clang::CXXConstructExpr>(Val: expr)) {
543 auto decl = call->getConstructor();
544 for (unsigned int i = 0; i < call->getNumArgs(); ++i) {
545 if (!decl || decl->getNumParams() <= i)
546 break;
547 if (call->getArg(Arg: i) != previous)
548 continue;
549 auto t = decl->getParamDecl(i)->getType();
550 if (t->isReferenceType() && !t.getNonReferenceType().isConstQualified())
551 return Annotator::Use_Address; // non const reference
552 return Annotator::Use_Read; // anything else is considered as read;
553 }
554 return Annotator::Use;
555 }
556 previous = expr;
557 }
558 if (previous == expr_stack.topExpr)
559 return expr_stack.topType;
560 return Annotator::Use;
561 }
562
563 bool isMember(clang::NamedDecl *d) const
564 {
565 if (!currentContext)
566 return false;
567 clang::CXXRecordDecl *ctx =
568 llvm::dyn_cast<clang::CXXRecordDecl>(Val: currentContext->getDeclContext());
569 if (!ctx)
570 return false;
571 if (d->getDeclContext() == ctx)
572 return true;
573
574 // try to see if it is in a inhertited class
575 clang::CXXRecordDecl *rec = llvm::dyn_cast<clang::CXXRecordDecl>(Val: d->getDeclContext());
576 return rec && ctx->isDerivedFrom(Base: rec);
577 }
578
579 bool shouldProcess(clang::NamedDecl *d)
580 {
581 return annotator.shouldProcess(
582 clang::FullSourceLoc(d->getLocation(), annotator.getSourceMgr())
583 .getExpansionLoc()
584 .getFileID());
585 }
586};
587

source code of codebrowser/generator/browserastvisitor.h