1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
7 * This file is distributed under the University of Illinois Open Source
8 * License. See LICENSE.TXT for details.
18 #include <clang/Basic/FileManager.h>
19 #include <clang/Lex/Lexer.h>
21 #include "config_clang.h"
24 #include "pluginhandler.hxx"
27 #if CLANG_VERSION >= 110000
28 #include "clang/AST/ParentMapContext.h"
32 Base classes for plugin actions.
39 Expr
const * skipImplicit(Expr
const * expr
) {
40 if (auto const e
= dyn_cast
<MaterializeTemporaryExpr
>(expr
)) {
41 expr
= compat::getSubExpr(e
)->IgnoreImpCasts();
43 if (auto const e
= dyn_cast
<CXXBindTemporaryExpr
>(expr
)) {
44 expr
= e
->getSubExpr();
49 bool structurallyIdentical(Stmt
const * stmt1
, Stmt
const * stmt2
) {
50 if (stmt1
->getStmtClass() != stmt2
->getStmtClass()) {
53 switch (stmt1
->getStmtClass()) {
54 case Stmt::CXXConstructExprClass
:
55 if (cast
<CXXConstructExpr
>(stmt1
)->getConstructor()->getCanonicalDecl()
56 != cast
<CXXConstructExpr
>(stmt2
)->getConstructor()->getCanonicalDecl())
61 case Stmt::DeclRefExprClass
:
62 if (cast
<DeclRefExpr
>(stmt1
)->getDecl()->getCanonicalDecl()
63 != cast
<DeclRefExpr
>(stmt2
)->getDecl()->getCanonicalDecl())
68 case Stmt::ImplicitCastExprClass
:
70 auto const e1
= cast
<ImplicitCastExpr
>(stmt1
);
71 auto const e2
= cast
<ImplicitCastExpr
>(stmt2
);
72 if (e1
->getCastKind() != e2
->getCastKind()
73 || e1
->getType().getCanonicalType() != e2
->getType().getCanonicalType())
79 case Stmt::MemberExprClass
:
81 auto const e1
= cast
<MemberExpr
>(stmt1
);
82 auto const e2
= cast
<MemberExpr
>(stmt2
);
83 if (e1
->isArrow() != e2
->isArrow()
84 || e1
->getType().getCanonicalType() != e2
->getType().getCanonicalType())
90 case Stmt::CXXMemberCallExprClass
:
91 case Stmt::CXXOperatorCallExprClass
:
92 if (cast
<Expr
>(stmt1
)->getType().getCanonicalType()
93 != cast
<Expr
>(stmt2
)->getType().getCanonicalType())
98 case Stmt::MaterializeTemporaryExprClass
:
99 case Stmt::CXXBindTemporaryExprClass
:
100 case Stmt::ParenExprClass
:
102 case Stmt::CXXNullPtrLiteralExprClass
:
105 // Conservatively assume non-identical for expressions that don't happen for us in practice
106 // when compiling the LO code base (and for which the above set of supported classes would
107 // need to be extended):
110 auto i1
= stmt1
->child_begin();
111 auto e1
= stmt1
->child_end();
112 auto i2
= stmt2
->child_begin();
113 auto e2
= stmt2
->child_end();
114 for (; i1
!= e1
; ++i1
, ++i2
) {
115 if (i2
== e2
|| !structurallyIdentical(*i1
, *i2
)) {
124 Plugin::Plugin( const InstantiationData
& data
)
125 : compiler( data
.compiler
), handler( data
.handler
), name( data
.name
)
129 DiagnosticBuilder
Plugin::report( DiagnosticsEngine::Level level
, StringRef message
, SourceLocation loc
) const
131 return handler
.report( level
, name
, message
, compiler
, loc
);
134 void normalizeDotDotInFilePath( std::string
& s
)
136 for (std::string::size_type i
= 0;;)
139 if (i
== std::string::npos
) {
142 if (i
+ 2 == s
.length() || s
[i
+ 2] == '/') {
143 s
.erase(i
, 2); // [AAA]/.[/CCC] -> [AAA][/CCC]
144 } else if (s
[i
+ 2] == '.'
145 && (i
+ 3 == s
.length() || s
[i
+ 3] == '/'))
147 if (i
== 0) { // /..[/CCC] -> /..[/CCC]
150 auto j
= s
.rfind('/', i
- 1);
151 if (j
== std::string::npos
)
153 // BBB/..[/CCC] -> BBB/..[/CCC] (instead of BBB/../CCC ->
154 // CCC, to avoid wrong ../../CCC -> CCC; relative paths
155 // shouldn't happen anyway, and even if they did, wouldn't
156 // match against WORKDIR anyway, as WORKDIR should be
160 s
.erase(j
, i
+ 3 - j
); // AAA/BBB/..[/CCC] -> AAA[/CCC]
168 void Plugin::registerPlugin( Plugin
* (*create
)( const InstantiationData
& ), const char* optionName
,
169 bool isPPCallback
, bool isSharedPlugin
, bool byDefault
)
171 PluginHandler::registerPlugin( create
, optionName
, isPPCallback
, isSharedPlugin
, byDefault
);
174 bool Plugin::evaluate(const Expr
* expr
, APSInt
& x
)
176 if (compat::EvaluateAsInt(expr
, x
, compiler
.getASTContext()))
180 if (isa
<CXXNullPtrLiteralExpr
>(expr
)) {
187 const Stmt
* Plugin::getParentStmt( const Stmt
* stmt
)
189 auto parentsRange
= compiler
.getASTContext().getParents(*stmt
);
190 if ( parentsRange
.begin() == parentsRange
.end())
192 return parentsRange
.begin()->get
<Stmt
>();
195 Stmt
* Plugin::getParentStmt( Stmt
* stmt
)
197 auto parentsRange
= compiler
.getASTContext().getParents(*stmt
);
198 if ( parentsRange
.begin() == parentsRange
.end())
200 return const_cast<Stmt
*>(parentsRange
.begin()->get
<Stmt
>());
203 const Decl
* getFunctionDeclContext(ASTContext
& context
, const Stmt
* stmt
)
205 auto const parents
= context
.getParents(*stmt
);
206 auto it
= parents
.begin();
208 if (it
== parents
.end())
211 const Decl
*decl
= it
->get
<Decl
>();
214 if (isa
<VarDecl
>(decl
))
215 return dyn_cast
<FunctionDecl
>(decl
->getDeclContext());
219 stmt
= it
->get
<Stmt
>();
221 return getFunctionDeclContext(context
, stmt
);
226 const FunctionDecl
* Plugin::getParentFunctionDecl( const Stmt
* stmt
)
228 const Decl
*decl
= getFunctionDeclContext(compiler
.getASTContext(), stmt
);
230 return static_cast<const FunctionDecl
*>(decl
->getNonClosureContext());
235 StringRef
Plugin::getFilenameOfLocation(SourceLocation spellingLocation
) const
237 // prevent crashes when running the global-analysis plugins
238 if (!spellingLocation
.isValid())
241 static enum { NOINIT
, STDIN
, GOOD
} s_Mode(NOINIT
);
244 return compiler
.getSourceManager().getFilename(spellingLocation
);
246 else if (s_Mode
== STDIN
247 || !compiler
.getSourceManager().isInMainFile(spellingLocation
))
249 const char* bufferName
= compiler
.getSourceManager().getPresumedLoc(spellingLocation
).getFilename();
254 char const*const pCXX
= getenv("CXX");
255 if (pCXX
&& strstr(pCXX
, "sccache"))
256 { // heuristic; sccache passes file with -frewrite-directives by name
258 return getFilenameOfLocation(spellingLocation
);
260 auto const fn(compiler
.getSourceManager().getFilename(spellingLocation
));
261 if (!fn
.data()) // wtf? happens in sot/source/sdstor/stg.cxx
266 assert(fn
.startswith("/") || fn
== "<stdin>");
268 s_Mode
= fn
== "<stdin>" ? STDIN
: GOOD
;
269 return getFilenameOfLocation(spellingLocation
);
273 bool Plugin::isInUnoIncludeFile(SourceLocation spellingLocation
) const
275 StringRef name
{ getFilenameOfLocation(spellingLocation
) };
276 return compiler
.getSourceManager().isInMainFile(spellingLocation
)
277 ? (isSamePathname(name
, SRCDIR
"/cppu/source/cppu/compat.cxx")
278 || isSamePathname(name
, SRCDIR
"/cppuhelper/source/compat.cxx")
279 || isSamePathname(name
, SRCDIR
"/sal/osl/all/compat.cxx"))
280 : (hasPathnamePrefix(name
, SRCDIR
"/include/com/")
281 || hasPathnamePrefix(name
, SRCDIR
"/include/cppu/")
282 || hasPathnamePrefix(name
, SRCDIR
"/include/cppuhelper/")
283 || hasPathnamePrefix(name
, SRCDIR
"/include/osl/")
284 || hasPathnamePrefix(name
, SRCDIR
"/include/rtl/")
285 || hasPathnamePrefix(name
, SRCDIR
"/include/sal/")
286 || hasPathnamePrefix(name
, SRCDIR
"/include/salhelper/")
287 || hasPathnamePrefix(name
, SRCDIR
"/include/typelib/")
288 || hasPathnamePrefix(name
, SRCDIR
"/include/uno/"));
291 bool Plugin::isInUnoIncludeFile(const FunctionDecl
* functionDecl
) const
293 return isInUnoIncludeFile(compiler
.getSourceManager().getSpellingLoc(
294 functionDecl
->getCanonicalDecl()->getNameInfo().getLoc()));
297 SourceLocation
Plugin::locationAfterToken( SourceLocation location
)
299 return Lexer::getLocForEndOfToken( location
, 0, compiler
.getSourceManager(), compiler
.getLangOpts());
302 bool Plugin::isUnitTestMode()
304 return PluginHandler::isUnitTestMode();
307 bool Plugin::containsPreprocessingConditionalInclusion(SourceRange range
)
309 // Preprocessing directives (other than _Pragma, which is not relevant here) cannot appear in
310 // macro expansions, so it is safe to just consider the range of expansion locations:
311 auto const begin
= compiler
.getSourceManager().getExpansionLoc(
313 auto const end
= compiler
.getSourceManager().getExpansionLoc(
315 assert(begin
.isFileID() && end
.isFileID());
317 || compiler
.getSourceManager().isBeforeInTranslationUnit(
322 DiagnosticsEngine::Fatal
,
323 ("unexpected broken range for Plugin::containsPreprocessingConditionalInclusion,"
328 // Conservatively assume "yes" if lexing fails:
332 for (auto loc
= begin
;;) {
334 if (Lexer::getRawToken(
335 loc
, tok
, compiler
.getSourceManager(),
336 compiler
.getLangOpts(), true))
340 DiagnosticsEngine::Fatal
,
341 ("unexpected broken range for"
342 " Plugin::containsPreprocessingConditionalInclusion, case 2"),
346 // Conservatively assume "yes" if lexing fails:
349 if (hash
&& tok
.is(tok::raw_identifier
)) {
350 auto const id
= tok
.getRawIdentifier();
351 if (id
== "if" || id
== "ifdef" || id
== "ifndef"
352 || id
== "elif" || id
== "else" || id
== "endif")
360 hash
= tok
.is(tok::hash
) && tok
.isAtStartOfLine();
361 loc
= loc
.getLocWithOffset(
363 Lexer::MeasureTokenLength(
364 loc
, compiler
.getSourceManager(),
365 compiler
.getLangOpts()),
371 Plugin::IdenticalDefaultArgumentsResult
Plugin::checkIdenticalDefaultArguments(
372 Expr
const * argument1
, Expr
const * argument2
)
374 if ((argument1
== nullptr) != (argument2
== nullptr)) {
375 return IdenticalDefaultArgumentsResult::No
;
377 if (argument1
== nullptr) {
378 return IdenticalDefaultArgumentsResult::Yes
;
380 if (argument1
->isNullPointerConstant(compiler
.getASTContext(), Expr::NPC_NeverValueDependent
)
381 && argument2
->isNullPointerConstant(compiler
.getASTContext(), Expr::NPC_NeverValueDependent
))
383 return IdenticalDefaultArgumentsResult::Yes
;
386 if (evaluate(argument1
, x1
) && evaluate(argument2
, x2
))
389 ? IdenticalDefaultArgumentsResult::Yes
390 : IdenticalDefaultArgumentsResult::No
;
392 APFloat
f1(0.0f
), f2(0.0f
);
393 if (argument1
->EvaluateAsFloat(f1
, compiler
.getASTContext())
394 && argument2
->EvaluateAsFloat(f2
, compiler
.getASTContext()))
396 return f1
.bitwiseIsEqual(f2
)
397 ? IdenticalDefaultArgumentsResult::Yes
398 : IdenticalDefaultArgumentsResult::No
;
400 auto const desugared1
= argument1
->IgnoreParenImpCasts();
401 auto const desugared2
= argument2
->IgnoreParenImpCasts();
402 if (auto const lit1
= dyn_cast
<clang::StringLiteral
>(desugared1
)) {
403 if (auto const lit2
= dyn_cast
<clang::StringLiteral
>(desugared2
)) {
404 return lit1
->getBytes() == lit2
->getBytes()
405 ? IdenticalDefaultArgumentsResult::Yes
406 : IdenticalDefaultArgumentsResult::No
;
409 // catch params with defaults like "= OUString()"
410 for (Expr
const * e1
= desugared1
, * e2
= desugared2
;;) {
411 auto const ce1
= dyn_cast
<CXXConstructExpr
>(skipImplicit(e1
));
412 auto const ce2
= dyn_cast
<CXXConstructExpr
>(skipImplicit(e2
));
413 if (ce1
== nullptr || ce2
== nullptr) {
416 if (ce1
->getConstructor()->getCanonicalDecl() != ce2
->getConstructor()->getCanonicalDecl())
418 return IdenticalDefaultArgumentsResult::No
;
420 if (ce1
->isElidable() && ce2
->isElidable() && ce1
->getNumArgs() == 1
421 && ce2
->getNumArgs() == 1)
423 assert(ce1
->getConstructor()->isCopyOrMoveConstructor());
424 e1
= ce1
->getArg(0)->IgnoreImpCasts();
425 e2
= ce2
->getArg(0)->IgnoreImpCasts();
428 if (ce1
->getNumArgs() == 0 && ce2
->getNumArgs() == 0) {
429 return IdenticalDefaultArgumentsResult::Yes
;
433 // If the EvaluateAsRValue derivatives above failed because the arguments use e.g. (constexpr)
434 // function template specializations that happen to not have been instantiated in this TU, try a
435 // structural comparison of the arguments:
436 if (structurallyIdentical(argument1
, argument2
)) {
437 return IdenticalDefaultArgumentsResult::Yes
;
441 DiagnosticsEngine::Fatal
, "TODO: Unexpected 'IdenticalDefaultArgumentsResult::Maybe'",
442 argument1
->getExprLoc())
443 << argument1
->getSourceRange();
445 DiagnosticsEngine::Note
, "TODO: second argument is here", argument2
->getExprLoc())
446 << argument2
->getSourceRange();
450 return IdenticalDefaultArgumentsResult::Maybe
;
453 RewritePlugin::RewritePlugin( const InstantiationData
& data
)
455 , rewriter( data
.rewriter
)
459 bool RewritePlugin::insertText( SourceLocation Loc
, StringRef Str
, bool InsertAfter
, bool indentNewLines
)
462 if (wouldRewriteWorkdir(Loc
))
464 SourceRange
Range(SourceRange(Loc
, Loc
.getLocWithOffset(Str
.size())));
465 if( !handler
.checkOverlap( Range
) )
467 report( DiagnosticsEngine::Warning
, "double code removal, possible plugin error", Range
.getBegin());
470 if( rewriter
->InsertText( Loc
, Str
, InsertAfter
, indentNewLines
))
471 return reportEditFailure( Loc
);
472 handler
.addSourceModification(Range
);
476 bool RewritePlugin::insertTextAfter( SourceLocation Loc
, StringRef Str
)
479 if (wouldRewriteWorkdir(Loc
))
481 SourceRange
Range(SourceRange(Loc
, Loc
.getLocWithOffset(Str
.size())));
482 if( !handler
.checkOverlap( Range
) )
484 report( DiagnosticsEngine::Warning
, "double code removal, possible plugin error", Range
.getBegin());
487 if( rewriter
->InsertTextAfter( Loc
, Str
))
488 return reportEditFailure( Loc
);
489 handler
.addSourceModification(Range
);
493 bool RewritePlugin::insertTextAfterToken( SourceLocation Loc
, StringRef Str
)
496 if (wouldRewriteWorkdir(Loc
))
498 SourceRange
Range(SourceRange(Loc
, Loc
.getLocWithOffset(Str
.size())));
499 if( !handler
.checkOverlap( Range
) )
501 report( DiagnosticsEngine::Warning
, "double code removal, possible plugin error", Range
.getBegin());
504 if( rewriter
->InsertTextAfterToken( Loc
, Str
))
505 return reportEditFailure( Loc
);
506 handler
.addSourceModification(Range
);
510 bool RewritePlugin::insertTextBefore( SourceLocation Loc
, StringRef Str
)
513 if (wouldRewriteWorkdir(Loc
))
515 SourceRange
Range(SourceRange(Loc
, Loc
.getLocWithOffset(Str
.size())));
516 if( !handler
.checkOverlap( Range
) )
518 report( DiagnosticsEngine::Warning
, "double code removal, possible plugin error", Range
.getBegin());
521 if( rewriter
->InsertTextBefore( Loc
, Str
))
522 return reportEditFailure( Loc
);
523 handler
.addSourceModification(Range
);
527 bool RewritePlugin::removeText( SourceLocation Start
, unsigned Length
, RewriteOptions opts
)
529 CharSourceRange
range( SourceRange( Start
, Start
.getLocWithOffset( Length
)), false );
530 return removeText( range
, opts
);
533 bool RewritePlugin::removeText( SourceRange range
, RewriteOptions opts
)
535 return removeText( CharSourceRange( range
, true ), opts
);
538 bool RewritePlugin::removeText( CharSourceRange range
, RewriteOptions opts
)
541 if (wouldRewriteWorkdir(range
.getBegin()))
543 if( rewriter
->getRangeSize( range
, opts
) == -1 )
544 return reportEditFailure( range
.getBegin());
545 if( !handler
.checkOverlap( range
.getAsRange() ) )
547 report( DiagnosticsEngine::Warning
, "double code removal, possible plugin error", range
.getBegin());
550 if( opts
.flags
& RemoveWholeStatement
|| opts
.flags
& RemoveAllWhitespace
)
552 if( !adjustRangeForOptions( &range
, opts
))
553 return reportEditFailure( range
.getBegin());
555 if( rewriter
->RemoveText( range
, opts
))
556 return reportEditFailure( range
.getBegin());
557 handler
.addSourceModification(range
.getAsRange());
561 bool RewritePlugin::adjustRangeForOptions( CharSourceRange
* range
, RewriteOptions opts
)
564 SourceManager
& SM
= rewriter
->getSourceMgr();
565 SourceLocation fileStartLoc
= SM
.getLocForStartOfFile( SM
.getFileID( range
->getBegin()));
566 if( fileStartLoc
.isInvalid())
568 bool isInvalid
= false;
569 const char* fileBuf
= SM
.getCharacterData( fileStartLoc
, &isInvalid
);
572 const char* startBuf
= SM
.getCharacterData( range
->getBegin(), &isInvalid
);
575 SourceLocation locationEnd
= range
->getEnd();
576 if( range
->isTokenRange())
577 locationEnd
= locationAfterToken( locationEnd
);
578 const char* endBuf
= SM
.getCharacterData( locationEnd
, &isInvalid
);
581 const char* startPos
= startBuf
;
583 while( startPos
>= fileBuf
&& ( *startPos
== ' ' || *startPos
== '\t' ))
585 if( startPos
>= fileBuf
&& *startPos
== '\n' )
586 startPos
= startBuf
- 1; // do not remove indentation whitespace (RemoveLineIfEmpty can do that)
587 const char* endPos
= endBuf
;
588 while( *endPos
== ' ' || *endPos
== '\t' )
590 if( opts
.flags
& RemoveWholeStatement
)
597 *range
= CharSourceRange( SourceRange( range
->getBegin().getLocWithOffset( startPos
- startBuf
+ 1 ),
598 locationEnd
.getLocWithOffset( endPos
- endBuf
)), false );
602 bool RewritePlugin::replaceText( SourceLocation Start
, unsigned OrigLength
, StringRef NewStr
)
605 if (wouldRewriteWorkdir(Start
))
607 SourceRange
Range(Start
, Start
.getLocWithOffset(std::max
<size_t>(OrigLength
, NewStr
.size())));
608 if( OrigLength
!= 0 && !handler
.checkOverlap( Range
) )
610 report( DiagnosticsEngine::Warning
, "overlapping code replacement, possible plugin error", Start
);
613 if( rewriter
->ReplaceText( Start
, OrigLength
, NewStr
))
614 return reportEditFailure( Start
);
615 handler
.addSourceModification(Range
);
619 bool RewritePlugin::replaceText( SourceRange range
, StringRef NewStr
)
622 if (wouldRewriteWorkdir(range
.getBegin()))
624 if( rewriter
->getRangeSize( range
) == -1 )
625 return reportEditFailure( range
.getBegin());
626 if( !handler
.checkOverlap( range
) )
628 report( DiagnosticsEngine::Warning
, "overlapping code replacement, possible plugin error", range
.getBegin());
631 if( rewriter
->ReplaceText( range
, NewStr
))
632 return reportEditFailure( range
.getBegin());
633 handler
.addSourceModification(range
);
637 bool RewritePlugin::replaceText( SourceRange range
, SourceRange replacementRange
)
640 if (wouldRewriteWorkdir(range
.getBegin()))
642 if( rewriter
->getRangeSize( range
) == -1 )
643 return reportEditFailure( range
.getBegin());
644 if( !handler
.checkOverlap( range
) )
646 report( DiagnosticsEngine::Warning
, "overlapping code replacement, possible plugin error", range
.getBegin());
649 if( rewriter
->ReplaceText( range
, replacementRange
))
650 return reportEditFailure( range
.getBegin());
651 handler
.addSourceModification(range
);
655 bool RewritePlugin::wouldRewriteWorkdir(SourceLocation loc
)
657 if (loc
.isInvalid() || loc
.isMacroID()) {
661 getFilenameOfLocation(compiler
.getSourceManager().getSpellingLoc(loc
))
662 .startswith(WORKDIR
"/");
665 bool RewritePlugin::reportEditFailure( SourceLocation loc
)
667 report( DiagnosticsEngine::Warning
, "cannot perform source modification (macro expansion involved?)", loc
);
673 template<typename Fn
> bool checkPathname(
674 StringRef pathname
, StringRef against
, Fn check
)
676 if (check(pathname
, against
)) {
680 for (std::size_t n
= 0;;)
682 std::size_t n1
= pathname
.find('\\', n
);
683 std::size_t n2
= against
.find('\\', n
);
685 if (n1
>= against
.size()) {
686 return check(pathname
.substr(n
), against
.substr(n
));
688 if ((against
[n1
] != '/' && against
[n1
] != '\\')
689 || pathname
.substr(n
, n1
- n
) != against
.substr(n
, n1
- n
))
695 if (n2
>= pathname
.size()) {
696 return check(pathname
.substr(n
), against
.substr(n
));
698 if (pathname
[n2
] != '/'
699 || pathname
.substr(n
, n2
- n
) != against
.substr(n
, n2
- n
))
712 bool hasPathnamePrefix(StringRef pathname
, StringRef prefix
)
714 return checkPathname(
716 [](StringRef p
, StringRef a
) { return p
.startswith(a
); });
719 bool isSamePathname(StringRef pathname
, StringRef other
)
721 return checkPathname(
722 pathname
, other
, [](StringRef p
, StringRef a
) { return p
== a
; });
725 bool hasCLanguageLinkageType(FunctionDecl
const * decl
) {
726 assert(decl
!= nullptr);
727 if (decl
->isExternC()) {
730 if (decl
->isInExternCContext()) {
736 static const CXXRecordDecl
* stripTypeSugar(QualType qt
)
738 const clang::Type
* t
= qt
.getTypePtr();
739 while (auto elaboratedType
= dyn_cast
<ElaboratedType
>(t
))
740 t
= elaboratedType
->desugar().getTypePtr();
741 auto recordType
= dyn_cast
<RecordType
>(t
);
744 return dyn_cast_or_null
<CXXRecordDecl
>(recordType
->getDecl());
747 int derivedFromCount(const CXXRecordDecl
* subclassRecordDecl
, const CXXRecordDecl
* baseclassRecordDecl
)
749 if (!subclassRecordDecl
|| !baseclassRecordDecl
)
751 int derivedCount
= 0;
752 if (subclassRecordDecl
== baseclassRecordDecl
)
754 if (!subclassRecordDecl
->hasDefinition())
756 for (auto it
= subclassRecordDecl
->bases_begin(); it
!= subclassRecordDecl
->bases_end(); ++it
)
758 derivedCount
+= derivedFromCount(stripTypeSugar(it
->getType()), baseclassRecordDecl
);
759 // short-circuit, we only care about 0,1,2
760 if (derivedCount
> 1)
763 for (auto it
= subclassRecordDecl
->vbases_begin(); it
!= subclassRecordDecl
->vbases_end(); ++it
)
765 derivedCount
+= derivedFromCount(stripTypeSugar(it
->getType()), baseclassRecordDecl
);
766 // short-circuit, we only care about 0,1,2
767 if (derivedCount
> 1)
773 int derivedFromCount(QualType subclassQt
, QualType baseclassQt
)
775 auto baseclassRecordDecl
= stripTypeSugar(baseclassQt
);
776 if (!baseclassRecordDecl
)
778 auto subclassRecordDecl
= stripTypeSugar(subclassQt
);
779 if (!subclassRecordDecl
)
782 return derivedFromCount(subclassRecordDecl
, baseclassRecordDecl
);
785 // It looks like Clang wrongly implements DR 4
786 // (<http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#4>) and treats
787 // a variable declared in an 'extern "..." {...}'-style linkage-specification as
788 // if it contained the 'extern' specifier:
789 bool hasExternalLinkage(VarDecl
const * decl
) {
790 if (decl
->getLinkageAndVisibility().getLinkage() != ExternalLinkage
) {
793 for (auto ctx
= decl
->getLexicalDeclContext();
794 ctx
->getDeclKind() != Decl::TranslationUnit
;
795 ctx
= ctx
->getLexicalParent())
797 if (auto ls
= dyn_cast
<LinkageSpecDecl
>(ctx
)) {
798 if (!ls
->hasBraces()) {
801 if (auto prev
= decl
->getPreviousDecl()) {
802 return hasExternalLinkage(prev
);
804 return !decl
->isInAnonymousNamespace();
810 bool isSmartPointerType(const Expr
* e
)
812 // First check whether the object type as written is, or is derived from, std::unique_ptr or
813 // std::shared_ptr, in case the get member function is declared at a base class of that std
815 if (loplugin::isDerivedFrom(
816 e
->IgnoreImpCasts()->getType()->getAsCXXRecordDecl(),
817 [](Decl
const * decl
) {
818 auto const dc
= loplugin::DeclCheck(decl
);
819 return dc
.ClassOrStruct("unique_ptr").StdNamespace()
820 || dc
.ClassOrStruct("shared_ptr").StdNamespace();
824 // Then check the object type coerced to the type of the get member function, in
825 // case the type-as-written is derived from one of these types (tools::SvRef is
826 // final, but the rest are not):
827 auto const tc2
= loplugin::TypeCheck(e
->getType());
828 if (tc2
.ClassOrStruct("unique_ptr").StdNamespace()
829 || tc2
.ClassOrStruct("shared_ptr").StdNamespace()
830 || tc2
.Class("Reference").Namespace("uno").Namespace("star")
831 .Namespace("sun").Namespace("com").GlobalNamespace()
832 || tc2
.Class("Reference").Namespace("rtl").GlobalNamespace()
833 || tc2
.Class("SvRef").Namespace("tools").GlobalNamespace()
834 || tc2
.Class("WeakReference").Namespace("tools").GlobalNamespace()
835 || tc2
.Class("ScopedReadAccess").Namespace("Bitmap").GlobalNamespace()
836 || tc2
.Class("ScopedVclPtrInstance").GlobalNamespace()
837 || tc2
.Class("VclPtr").GlobalNamespace()
838 || tc2
.Class("ScopedVclPtr").GlobalNamespace()
839 || tc2
.Class("intrusive_ptr").Namespace("boost").GlobalNamespace())
849 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */