bump product version to 6.4.0.3
[LibreOffice.git] / compilerplugins / clang / plugin.cxx
blob01484bddc432442945b1b1ebea3afe0b2ce8df3f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * Based on LLVM/Clang.
7 * This file is distributed under the University of Illinois Open Source
8 * License. See LICENSE.TXT for details.
12 #include "plugin.hxx"
14 #include <cassert>
15 #include <cstddef>
16 #include <string>
18 #include <clang/Basic/FileManager.h>
19 #include <clang/Lex/Lexer.h>
21 #include "pluginhandler.hxx"
24 Base classes for plugin actions.
26 namespace loplugin
29 namespace {
31 Expr const * skipImplicit(Expr const * expr) {
32 if (auto const e = dyn_cast<MaterializeTemporaryExpr>(expr)) {
33 expr = e->GetTemporaryExpr()->IgnoreImpCasts();
35 if (auto const e = dyn_cast<CXXBindTemporaryExpr>(expr)) {
36 expr = e->getSubExpr();
38 return expr;
41 bool structurallyIdentical(Stmt const * stmt1, Stmt const * stmt2) {
42 if (stmt1->getStmtClass() != stmt2->getStmtClass()) {
43 return false;
45 switch (stmt1->getStmtClass()) {
46 case Stmt::CXXConstructExprClass:
47 if (cast<CXXConstructExpr>(stmt1)->getConstructor()->getCanonicalDecl()
48 != cast<CXXConstructExpr>(stmt2)->getConstructor()->getCanonicalDecl())
50 return false;
52 break;
53 case Stmt::DeclRefExprClass:
54 if (cast<DeclRefExpr>(stmt1)->getDecl()->getCanonicalDecl()
55 != cast<DeclRefExpr>(stmt2)->getDecl()->getCanonicalDecl())
57 return false;
59 break;
60 case Stmt::ImplicitCastExprClass:
62 auto const e1 = cast<ImplicitCastExpr>(stmt1);
63 auto const e2 = cast<ImplicitCastExpr>(stmt2);
64 if (e1->getCastKind() != e2->getCastKind()
65 || e1->getType().getCanonicalType() != e2->getType().getCanonicalType())
67 return false;
69 break;
71 case Stmt::MemberExprClass:
73 auto const e1 = cast<MemberExpr>(stmt1);
74 auto const e2 = cast<MemberExpr>(stmt2);
75 if (e1->isArrow() != e2->isArrow()
76 || e1->getType().getCanonicalType() != e2->getType().getCanonicalType())
78 return false;
80 break;
82 case Stmt::CXXMemberCallExprClass:
83 case Stmt::CXXOperatorCallExprClass:
84 if (cast<Expr>(stmt1)->getType().getCanonicalType()
85 != cast<Expr>(stmt2)->getType().getCanonicalType())
87 return false;
89 break;
90 case Stmt::MaterializeTemporaryExprClass:
91 case Stmt::ParenExprClass:
92 break;
93 default:
94 // Conservatively assume non-identical for expressions that don't happen for us in practice
95 // when compiling the LO code base (and for which the above set of supported classes would
96 // need to be extended):
97 return false;
99 auto i1 = stmt1->child_begin();
100 auto e1 = stmt1->child_end();
101 auto i2 = stmt2->child_begin();
102 auto e2 = stmt2->child_end();
103 for (; i1 != e1; ++i1, ++i2) {
104 if (i2 == e2 || !structurallyIdentical(*i1, *i2)) {
105 return false;
108 return i2 == e2;
113 Plugin::Plugin( const InstantiationData& data )
114 : compiler( data.compiler ), handler( data.handler ), name( data.name )
118 DiagnosticBuilder Plugin::report( DiagnosticsEngine::Level level, StringRef message, SourceLocation loc ) const
120 return handler.report( level, name, message, compiler, loc );
123 void normalizeDotDotInFilePath( std::string & s )
125 for (std::string::size_type i = 0;;)
127 i = s.find("/.", i);
128 if (i == std::string::npos) {
129 break;
131 if (i + 2 == s.length() || s[i + 2] == '/') {
132 s.erase(i, 2); // [AAA]/.[/CCC] -> [AAA][/CCC]
133 } else if (s[i + 2] == '.'
134 && (i + 3 == s.length() || s[i + 3] == '/'))
136 if (i == 0) { // /..[/CCC] -> /..[/CCC]
137 break;
139 auto j = s.rfind('/', i - 1);
140 if (j == std::string::npos)
142 // BBB/..[/CCC] -> BBB/..[/CCC] (instead of BBB/../CCC ->
143 // CCC, to avoid wrong ../../CCC -> CCC; relative paths
144 // shouldn't happen anyway, and even if they did, wouldn't
145 // match against WORKDIR anyway, as WORKDIR should be
146 // absolute):
147 break;
149 s.erase(j, i + 3 - j); // AAA/BBB/..[/CCC] -> AAA[/CCC]
150 i = j;
151 } else {
152 i += 2;
157 void Plugin::registerPlugin( Plugin* (*create)( const InstantiationData& ), const char* optionName,
158 bool isPPCallback, bool isSharedPlugin, bool byDefault )
160 PluginHandler::registerPlugin( create, optionName, isPPCallback, isSharedPlugin, byDefault );
163 bool Plugin::evaluate(const Expr* expr, APSInt& x)
165 if (compat::EvaluateAsInt(expr, x, compiler.getASTContext()))
167 return true;
169 if (isa<CXXNullPtrLiteralExpr>(expr)) {
170 x = 0;
171 return true;
173 return false;
176 const Stmt* Plugin::getParentStmt( const Stmt* stmt )
178 auto parentsRange = compiler.getASTContext().getParents(*stmt);
179 if ( parentsRange.begin() == parentsRange.end())
180 return nullptr;
181 return parentsRange.begin()->get<Stmt>();
184 Stmt* Plugin::getParentStmt( Stmt* stmt )
186 auto parentsRange = compiler.getASTContext().getParents(*stmt);
187 if ( parentsRange.begin() == parentsRange.end())
188 return nullptr;
189 return const_cast<Stmt*>(parentsRange.begin()->get<Stmt>());
192 static const Decl* getDeclContext(ASTContext& context, const Stmt* stmt)
194 auto it = context.getParents(*stmt).begin();
196 if (it == context.getParents(*stmt).end())
197 return nullptr;
199 const Decl *aDecl = it->get<Decl>();
200 if (aDecl)
201 return aDecl;
203 const Stmt *aStmt = it->get<Stmt>();
204 if (aStmt)
205 return getDeclContext(context, aStmt);
207 return nullptr;
210 const FunctionDecl* Plugin::getParentFunctionDecl( const Stmt* stmt )
212 const Decl *decl = getDeclContext(compiler.getASTContext(), stmt);
213 if (decl)
214 return static_cast<const FunctionDecl*>(decl->getNonClosureContext());
216 return nullptr;
219 StringRef Plugin::getFilenameOfLocation(SourceLocation spellingLocation) const
221 // prevent crashes when running the global-analysis plugins
222 if (!spellingLocation.isValid())
223 return "";
225 static enum { NOINIT, STDIN, GOOD } s_Mode(NOINIT);
226 if (s_Mode == GOOD)
228 return compiler.getSourceManager().getFilename(spellingLocation);
230 else if (s_Mode == STDIN
231 || !compiler.getSourceManager().isInMainFile(spellingLocation))
233 const char* bufferName = compiler.getSourceManager().getPresumedLoc(spellingLocation).getFilename();
234 return bufferName;
236 else
238 auto const fn(compiler.getSourceManager().getFilename(spellingLocation));
239 if (!fn.data()) // wtf? happens in sot/source/sdstor/stg.cxx
241 return fn;
243 #if !defined _WIN32
244 assert(fn.startswith("/") || fn == "<stdin>");
245 #endif
246 s_Mode = fn == "<stdin>" ? STDIN : GOOD;
247 return getFilenameOfLocation(spellingLocation);
251 bool Plugin::isInUnoIncludeFile(SourceLocation spellingLocation) const
253 StringRef name{ getFilenameOfLocation(spellingLocation) };
254 return compiler.getSourceManager().isInMainFile(spellingLocation)
255 ? (isSamePathname(name, SRCDIR "/cppu/source/cppu/compat.cxx")
256 || isSamePathname(name, SRCDIR "/cppuhelper/source/compat.cxx")
257 || isSamePathname(name, SRCDIR "/sal/osl/all/compat.cxx"))
258 : (hasPathnamePrefix(name, SRCDIR "/include/com/")
259 || hasPathnamePrefix(name, SRCDIR "/include/cppu/")
260 || hasPathnamePrefix(name, SRCDIR "/include/cppuhelper/")
261 || hasPathnamePrefix(name, SRCDIR "/include/osl/")
262 || hasPathnamePrefix(name, SRCDIR "/include/rtl/")
263 || hasPathnamePrefix(name, SRCDIR "/include/sal/")
264 || hasPathnamePrefix(name, SRCDIR "/include/salhelper/")
265 || hasPathnamePrefix(name, SRCDIR "/include/systools/")
266 || hasPathnamePrefix(name, SRCDIR "/include/typelib/")
267 || hasPathnamePrefix(name, SRCDIR "/include/uno/"));
270 bool Plugin::isInUnoIncludeFile(const FunctionDecl* functionDecl) const
272 return isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(
273 functionDecl->getCanonicalDecl()->getNameInfo().getLoc()));
276 SourceLocation Plugin::locationAfterToken( SourceLocation location )
278 return Lexer::getLocForEndOfToken( location, 0, compiler.getSourceManager(), compiler.getLangOpts());
281 bool Plugin::isUnitTestMode()
283 return PluginHandler::isUnitTestMode();
286 bool Plugin::containsPreprocessingConditionalInclusion(SourceRange range)
288 // Preprocessing directives (other than _Pragma, which is not relevant here) cannot appear in
289 // macro expansions, so it is safe to just consider the range of expansion locations:
290 auto const begin = compiler.getSourceManager().getExpansionLoc(
291 range.getBegin());
292 auto const end = compiler.getSourceManager().getExpansionLoc(
293 range.getEnd());
294 assert(begin.isFileID() && end.isFileID());
295 if (!(begin == end
296 || compiler.getSourceManager().isBeforeInTranslationUnit(
297 begin, end)))
299 if (isDebugMode()) {
300 report(
301 DiagnosticsEngine::Fatal,
302 ("unexpected broken range for Plugin::containsPreprocessingConditionalInclusion,"
303 " case 1"),
304 range.getBegin())
305 << range;
307 // Conservatively assume "yes" if lexing fails:
308 return true;
310 auto hash = false;
311 for (auto loc = begin;;) {
312 Token tok;
313 if (Lexer::getRawToken(
314 loc, tok, compiler.getSourceManager(),
315 compiler.getLangOpts(), true))
317 if (isDebugMode()) {
318 report(
319 DiagnosticsEngine::Fatal,
320 ("unexpected broken range for"
321 " Plugin::containsPreprocessingConditionalInclusion, case 2"),
322 loc)
323 << range;
325 // Conservatively assume "yes" if lexing fails:
326 return true;
328 if (hash && tok.is(tok::raw_identifier)) {
329 auto const id = tok.getRawIdentifier();
330 if (id == "if" || id == "ifdef" || id == "ifndef"
331 || id == "elif" || id == "else" || id == "endif")
333 return true;
336 if (loc == end) {
337 break;
339 hash = tok.is(tok::hash) && tok.isAtStartOfLine();
340 loc = loc.getLocWithOffset(
341 std::max<unsigned>(
342 Lexer::MeasureTokenLength(
343 loc, compiler.getSourceManager(),
344 compiler.getLangOpts()),
345 1));
347 return false;
350 Plugin::IdenticalDefaultArgumentsResult Plugin::checkIdenticalDefaultArguments(
351 Expr const * argument1, Expr const * argument2)
353 if ((argument1 == nullptr) != (argument2 == nullptr)) {
354 return IdenticalDefaultArgumentsResult::No;
356 if (argument1 == nullptr) {
357 return IdenticalDefaultArgumentsResult::Yes;
359 if (argument1->isNullPointerConstant(compiler.getASTContext(), Expr::NPC_NeverValueDependent)
360 && argument2->isNullPointerConstant(compiler.getASTContext(), Expr::NPC_NeverValueDependent))
362 return IdenticalDefaultArgumentsResult::Yes;
364 APSInt x1, x2;
365 if (evaluate(argument1, x1) && evaluate(argument2, x2))
367 return x1 == x2
368 ? IdenticalDefaultArgumentsResult::Yes
369 : IdenticalDefaultArgumentsResult::No;
371 APFloat f1(0.0f), f2(0.0f);
372 if (argument1->EvaluateAsFloat(f1, compiler.getASTContext())
373 && argument2->EvaluateAsFloat(f2, compiler.getASTContext()))
375 return f1.bitwiseIsEqual(f2)
376 ? IdenticalDefaultArgumentsResult::Yes
377 : IdenticalDefaultArgumentsResult::No;
379 auto const desugared1 = argument1->IgnoreParenImpCasts();
380 auto const desugared2 = argument2->IgnoreParenImpCasts();
381 if (auto const lit1 = dyn_cast<clang::StringLiteral>(desugared1)) {
382 if (auto const lit2 = dyn_cast<clang::StringLiteral>(desugared2)) {
383 return lit1->getBytes() == lit2->getBytes()
384 ? IdenticalDefaultArgumentsResult::Yes
385 : IdenticalDefaultArgumentsResult::No;
388 // catch params with defaults like "= OUString()"
389 for (Expr const * e1 = desugared1, * e2 = desugared2;;) {
390 auto const ce1 = dyn_cast<CXXConstructExpr>(skipImplicit(e1));
391 auto const ce2 = dyn_cast<CXXConstructExpr>(skipImplicit(e2));
392 if (ce1 == nullptr || ce2 == nullptr) {
393 break;
395 if (ce1->getConstructor()->getCanonicalDecl() != ce2->getConstructor()->getCanonicalDecl())
397 return IdenticalDefaultArgumentsResult::No;
399 if (ce1->isElidable() && ce2->isElidable() && ce1->getNumArgs() == 1
400 && ce2->getNumArgs() == 1)
402 assert(ce1->getConstructor()->isCopyOrMoveConstructor());
403 e1 = ce1->getArg(0)->IgnoreImpCasts();
404 e2 = ce2->getArg(0)->IgnoreImpCasts();
405 continue;
407 if (ce1->getNumArgs() == 0 && ce2->getNumArgs() == 0) {
408 return IdenticalDefaultArgumentsResult::Yes;
410 break;
412 // If the EvaluateAsRValue derivatives above failed because the arguments use e.g. (constexpr)
413 // function template specializations that happen to not have been instantiated in this TU, try a
414 // structural comparison of the arguments:
415 if (structurallyIdentical(argument1, argument2)) {
416 return IdenticalDefaultArgumentsResult::Yes;
418 if (isDebugMode()) {
419 report(
420 DiagnosticsEngine::Fatal, "TODO: Unexpected 'IdenticalDefaultArgumentsResult::Maybe'",
421 argument1->getExprLoc())
422 << argument1->getSourceRange();
423 report(
424 DiagnosticsEngine::Note, "TODO: second argument is here", argument2->getExprLoc())
425 << argument2->getSourceRange();
426 argument1->dump();
427 argument2->dump();
429 return IdenticalDefaultArgumentsResult::Maybe;
432 RewritePlugin::RewritePlugin( const InstantiationData& data )
433 : Plugin( data )
434 , rewriter( data.rewriter )
438 bool RewritePlugin::insertText( SourceLocation Loc, StringRef Str, bool InsertAfter, bool indentNewLines )
440 assert( rewriter );
441 if (wouldRewriteWorkdir(Loc))
442 return false;
443 SourceRange Range(SourceRange(Loc, Loc.getLocWithOffset(Str.size())));
444 if( !handler.checkOverlap( Range ) )
446 report( DiagnosticsEngine::Warning, "double code removal, possible plugin error", Range.getBegin());
447 return false;
449 if( rewriter->InsertText( Loc, Str, InsertAfter, indentNewLines ))
450 return reportEditFailure( Loc );
451 handler.addSourceModification(Range);
452 return true;
455 bool RewritePlugin::insertTextAfter( SourceLocation Loc, StringRef Str )
457 assert( rewriter );
458 if (wouldRewriteWorkdir(Loc))
459 return false;
460 SourceRange Range(SourceRange(Loc, Loc.getLocWithOffset(Str.size())));
461 if( !handler.checkOverlap( Range ) )
463 report( DiagnosticsEngine::Warning, "double code removal, possible plugin error", Range.getBegin());
464 return false;
466 if( rewriter->InsertTextAfter( Loc, Str ))
467 return reportEditFailure( Loc );
468 handler.addSourceModification(Range);
469 return true;
472 bool RewritePlugin::insertTextAfterToken( SourceLocation Loc, StringRef Str )
474 assert( rewriter );
475 if (wouldRewriteWorkdir(Loc))
476 return false;
477 SourceRange Range(SourceRange(Loc, Loc.getLocWithOffset(Str.size())));
478 if( !handler.checkOverlap( Range ) )
480 report( DiagnosticsEngine::Warning, "double code removal, possible plugin error", Range.getBegin());
481 return false;
483 if( rewriter->InsertTextAfterToken( Loc, Str ))
484 return reportEditFailure( Loc );
485 handler.addSourceModification(Range);
486 return true;
489 bool RewritePlugin::insertTextBefore( SourceLocation Loc, StringRef Str )
491 assert( rewriter );
492 if (wouldRewriteWorkdir(Loc))
493 return false;
494 SourceRange Range(SourceRange(Loc, Loc.getLocWithOffset(Str.size())));
495 if( !handler.checkOverlap( Range ) )
497 report( DiagnosticsEngine::Warning, "double code removal, possible plugin error", Range.getBegin());
498 return false;
500 if( rewriter->InsertTextBefore( Loc, Str ))
501 return reportEditFailure( Loc );
502 handler.addSourceModification(Range);
503 return true;
506 bool RewritePlugin::removeText( SourceLocation Start, unsigned Length, RewriteOptions opts )
508 CharSourceRange range( SourceRange( Start, Start.getLocWithOffset( Length )), false );
509 return removeText( range, opts );
512 bool RewritePlugin::removeText( SourceRange range, RewriteOptions opts )
514 return removeText( CharSourceRange( range, true ), opts );
517 bool RewritePlugin::removeText( CharSourceRange range, RewriteOptions opts )
519 assert( rewriter );
520 if (wouldRewriteWorkdir(range.getBegin()))
521 return false;
522 if( rewriter->getRangeSize( range, opts ) == -1 )
523 return reportEditFailure( range.getBegin());
524 if( !handler.checkOverlap( range.getAsRange() ) )
526 report( DiagnosticsEngine::Warning, "double code removal, possible plugin error", range.getBegin());
527 return false;
529 if( opts.flags & RemoveWholeStatement || opts.flags & RemoveAllWhitespace )
531 if( !adjustRangeForOptions( &range, opts ))
532 return reportEditFailure( range.getBegin());
534 if( rewriter->RemoveText( range, opts ))
535 return reportEditFailure( range.getBegin());
536 handler.addSourceModification(range.getAsRange());
537 return true;
540 bool RewritePlugin::adjustRangeForOptions( CharSourceRange* range, RewriteOptions opts )
542 assert( rewriter );
543 SourceManager& SM = rewriter->getSourceMgr();
544 SourceLocation fileStartLoc = SM.getLocForStartOfFile( SM.getFileID( range->getBegin()));
545 if( fileStartLoc.isInvalid())
546 return false;
547 bool isInvalid = false;
548 const char* fileBuf = SM.getCharacterData( fileStartLoc, &isInvalid );
549 if( isInvalid )
550 return false;
551 const char* startBuf = SM.getCharacterData( range->getBegin(), &isInvalid );
552 if( isInvalid )
553 return false;
554 SourceLocation locationEnd = range->getEnd();
555 if( range->isTokenRange())
556 locationEnd = locationAfterToken( locationEnd );
557 const char* endBuf = SM.getCharacterData( locationEnd, &isInvalid );
558 if( isInvalid )
559 return false;
560 const char* startPos = startBuf;
561 --startPos;
562 while( startPos >= fileBuf && ( *startPos == ' ' || *startPos == '\t' ))
563 --startPos;
564 if( startPos >= fileBuf && *startPos == '\n' )
565 startPos = startBuf - 1; // do not remove indentation whitespace (RemoveLineIfEmpty can do that)
566 const char* endPos = endBuf;
567 while( *endPos == ' ' || *endPos == '\t' )
568 ++endPos;
569 if( opts.flags & RemoveWholeStatement )
571 if( *endPos == ';' )
572 ++endPos;
573 else
574 return false;
576 *range = CharSourceRange( SourceRange( range->getBegin().getLocWithOffset( startPos - startBuf + 1 ),
577 locationEnd.getLocWithOffset( endPos - endBuf )), false );
578 return true;
581 bool RewritePlugin::replaceText( SourceLocation Start, unsigned OrigLength, StringRef NewStr )
583 assert( rewriter );
584 if (wouldRewriteWorkdir(Start))
585 return false;
586 SourceRange Range(Start, Start.getLocWithOffset(std::max<size_t>(OrigLength, NewStr.size())));
587 if( OrigLength != 0 && !handler.checkOverlap( Range ) )
589 report( DiagnosticsEngine::Warning, "overlapping code replacement, possible plugin error", Start );
590 return false;
592 if( rewriter->ReplaceText( Start, OrigLength, NewStr ))
593 return reportEditFailure( Start );
594 handler.addSourceModification(Range);
595 return true;
598 bool RewritePlugin::replaceText( SourceRange range, StringRef NewStr )
600 assert( rewriter );
601 if (wouldRewriteWorkdir(range.getBegin()))
602 return false;
603 if( rewriter->getRangeSize( range ) == -1 )
604 return reportEditFailure( range.getBegin());
605 if( !handler.checkOverlap( range ) )
607 report( DiagnosticsEngine::Warning, "overlapping code replacement, possible plugin error", range.getBegin());
608 return false;
610 if( rewriter->ReplaceText( range, NewStr ))
611 return reportEditFailure( range.getBegin());
612 handler.addSourceModification(range);
613 return true;
616 bool RewritePlugin::replaceText( SourceRange range, SourceRange replacementRange )
618 assert( rewriter );
619 if (wouldRewriteWorkdir(range.getBegin()))
620 return false;
621 if( rewriter->getRangeSize( range ) == -1 )
622 return reportEditFailure( range.getBegin());
623 if( !handler.checkOverlap( range ) )
625 report( DiagnosticsEngine::Warning, "overlapping code replacement, possible plugin error", range.getBegin());
626 return false;
628 if( rewriter->ReplaceText( range, replacementRange ))
629 return reportEditFailure( range.getBegin());
630 handler.addSourceModification(range);
631 return true;
634 bool RewritePlugin::wouldRewriteWorkdir(SourceLocation loc)
636 if (loc.isInvalid() || loc.isMacroID()) {
637 return false;
639 return
640 getFilenameOfLocation(compiler.getSourceManager().getSpellingLoc(loc))
641 .startswith(WORKDIR "/");
644 bool RewritePlugin::reportEditFailure( SourceLocation loc )
646 report( DiagnosticsEngine::Warning, "cannot perform source modification (macro expansion involved?)", loc );
647 return false;
650 namespace {
652 template<typename Fn> bool checkPathname(
653 StringRef pathname, StringRef against, Fn check)
655 if (check(pathname, against)) {
656 return true;
658 #if defined _WIN32
659 for (std::size_t n = 0;;)
661 std::size_t n1 = pathname.find('\\', n);
662 std::size_t n2 = against.find('\\', n);
663 if (n1 <= n2) {
664 if (n1 >= against.size()) {
665 return check(pathname.substr(n), against.substr(n));
667 if ((against[n1] != '/' && against[n1] != '\\')
668 || pathname.substr(n, n1 - n) != against.substr(n, n1 - n))
670 break;
672 n = n1 + 1;
673 } else {
674 if (n2 >= pathname.size()) {
675 return check(pathname.substr(n), against.substr(n));
677 if (pathname[n2] != '/'
678 || pathname.substr(n, n2 - n) != against.substr(n, n2 - n))
680 break;
682 n = n2 + 1;
685 #endif
686 return false;
691 bool hasPathnamePrefix(StringRef pathname, StringRef prefix)
693 return checkPathname(
694 pathname, prefix,
695 [](StringRef p, StringRef a) { return p.startswith(a); });
698 bool isSamePathname(StringRef pathname, StringRef other)
700 return checkPathname(
701 pathname, other, [](StringRef p, StringRef a) { return p == a; });
704 bool hasCLanguageLinkageType(FunctionDecl const * decl) {
705 assert(decl != nullptr);
706 if (decl->isExternC()) {
707 return true;
709 if (decl->isInExternCContext()) {
710 return true;
712 return false;
715 static const CXXRecordDecl* stripTypeSugar(QualType qt)
717 const clang::Type* t = qt.getTypePtr();
718 while (auto elaboratedType = dyn_cast<ElaboratedType>(t))
719 t = elaboratedType->desugar().getTypePtr();
720 auto recordType = dyn_cast<RecordType>(t);
721 if (!recordType)
722 return nullptr;
723 return dyn_cast_or_null<CXXRecordDecl>(recordType->getDecl());
726 int derivedFromCount(const CXXRecordDecl* subclassRecordDecl, const CXXRecordDecl* baseclassRecordDecl)
728 if (!subclassRecordDecl || !baseclassRecordDecl)
729 return 0;
730 int derivedCount = 0;
731 if (subclassRecordDecl == baseclassRecordDecl)
732 derivedCount++;
733 if (!subclassRecordDecl->hasDefinition())
734 return derivedCount;
735 for (auto it = subclassRecordDecl->bases_begin(); it != subclassRecordDecl->bases_end(); ++it)
737 derivedCount += derivedFromCount(stripTypeSugar(it->getType()), baseclassRecordDecl);
738 // short-circuit, we only care about 0,1,2
739 if (derivedCount > 1)
740 return derivedCount;
742 for (auto it = subclassRecordDecl->vbases_begin(); it != subclassRecordDecl->vbases_end(); ++it)
744 derivedCount += derivedFromCount(stripTypeSugar(it->getType()), baseclassRecordDecl);
745 // short-circuit, we only care about 0,1,2
746 if (derivedCount > 1)
747 return derivedCount;
749 return derivedCount;
752 int derivedFromCount(QualType subclassQt, QualType baseclassQt)
754 auto baseclassRecordDecl = stripTypeSugar(baseclassQt);
755 if (!baseclassRecordDecl)
756 return 0;
757 auto subclassRecordDecl = stripTypeSugar(subclassQt);
758 if (!subclassRecordDecl)
759 return 0;
761 return derivedFromCount(subclassRecordDecl, baseclassRecordDecl);
765 } // namespace
767 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */