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