bump product version to 7.2.5.1
[LibreOffice.git] / compilerplugins / clang / plugin.cxx
blobc68eceb67c32eac6001bd718c390503198e7df83
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 "config_clang.h"
23 #include "compat.hxx"
24 #include "pluginhandler.hxx"
25 #include "check.hxx"
27 #if CLANG_VERSION >= 110000
28 #include "clang/AST/ParentMapContext.h"
29 #endif
32 Base classes for plugin actions.
34 namespace loplugin
37 namespace {
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();
46 return expr;
49 bool structurallyIdentical(Stmt const * stmt1, Stmt const * stmt2) {
50 if (stmt1->getStmtClass() != stmt2->getStmtClass()) {
51 return false;
53 switch (stmt1->getStmtClass()) {
54 case Stmt::CXXConstructExprClass:
55 if (cast<CXXConstructExpr>(stmt1)->getConstructor()->getCanonicalDecl()
56 != cast<CXXConstructExpr>(stmt2)->getConstructor()->getCanonicalDecl())
58 return false;
60 break;
61 case Stmt::DeclRefExprClass:
62 if (cast<DeclRefExpr>(stmt1)->getDecl()->getCanonicalDecl()
63 != cast<DeclRefExpr>(stmt2)->getDecl()->getCanonicalDecl())
65 return false;
67 break;
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())
75 return false;
77 break;
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())
86 return false;
88 break;
90 case Stmt::CXXMemberCallExprClass:
91 case Stmt::CXXOperatorCallExprClass:
92 if (cast<Expr>(stmt1)->getType().getCanonicalType()
93 != cast<Expr>(stmt2)->getType().getCanonicalType())
95 return false;
97 break;
98 case Stmt::MaterializeTemporaryExprClass:
99 case Stmt::CXXBindTemporaryExprClass:
100 case Stmt::ParenExprClass:
101 break;
102 case Stmt::CXXNullPtrLiteralExprClass:
103 return true;
104 default:
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):
108 return false;
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)) {
116 return false;
119 return i2 == e2;
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;;)
138 i = s.find("/.", i);
139 if (i == std::string::npos) {
140 break;
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]
148 break;
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
157 // absolute):
158 break;
160 s.erase(j, i + 3 - j); // AAA/BBB/..[/CCC] -> AAA[/CCC]
161 i = j;
162 } else {
163 i += 2;
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()))
178 return true;
180 if (isa<CXXNullPtrLiteralExpr>(expr)) {
181 x = 0;
182 return true;
184 return false;
187 const Stmt* Plugin::getParentStmt( const Stmt* stmt )
189 auto parentsRange = compiler.getASTContext().getParents(*stmt);
190 if ( parentsRange.begin() == parentsRange.end())
191 return nullptr;
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())
199 return nullptr;
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())
209 return nullptr;
211 const Decl *decl = it->get<Decl>();
212 if (decl)
214 if (isa<VarDecl>(decl))
215 return dyn_cast<FunctionDecl>(decl->getDeclContext());
216 return decl;
219 stmt = it->get<Stmt>();
220 if (stmt)
221 return getFunctionDeclContext(context, stmt);
223 return nullptr;
226 const FunctionDecl* Plugin::getParentFunctionDecl( const Stmt* stmt )
228 const Decl *decl = getFunctionDeclContext(compiler.getASTContext(), stmt);
229 if (decl)
230 return static_cast<const FunctionDecl*>(decl->getNonClosureContext());
232 return nullptr;
235 StringRef Plugin::getFilenameOfLocation(SourceLocation spellingLocation) const
237 // prevent crashes when running the global-analysis plugins
238 if (!spellingLocation.isValid())
239 return "";
241 static enum { NOINIT, STDIN, GOOD } s_Mode(NOINIT);
242 if (s_Mode == GOOD)
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();
250 return bufferName;
252 else
254 char const*const pCXX = getenv("CXX");
255 if (pCXX && strstr(pCXX, "sccache"))
256 { // heuristic; sccache passes file with -frewrite-directives by name
257 s_Mode = STDIN;
258 return getFilenameOfLocation(spellingLocation);
260 auto const fn(compiler.getSourceManager().getFilename(spellingLocation));
261 if (!fn.data()) // wtf? happens in sot/source/sdstor/stg.cxx
263 return fn;
265 #if !defined _WIN32
266 assert(fn.startswith("/") || fn == "<stdin>");
267 #endif
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(
312 range.getBegin());
313 auto const end = compiler.getSourceManager().getExpansionLoc(
314 range.getEnd());
315 assert(begin.isFileID() && end.isFileID());
316 if (!(begin == end
317 || compiler.getSourceManager().isBeforeInTranslationUnit(
318 begin, end)))
320 if (isDebugMode()) {
321 report(
322 DiagnosticsEngine::Fatal,
323 ("unexpected broken range for Plugin::containsPreprocessingConditionalInclusion,"
324 " case 1"),
325 range.getBegin())
326 << range;
328 // Conservatively assume "yes" if lexing fails:
329 return true;
331 auto hash = false;
332 for (auto loc = begin;;) {
333 Token tok;
334 if (Lexer::getRawToken(
335 loc, tok, compiler.getSourceManager(),
336 compiler.getLangOpts(), true))
338 if (isDebugMode()) {
339 report(
340 DiagnosticsEngine::Fatal,
341 ("unexpected broken range for"
342 " Plugin::containsPreprocessingConditionalInclusion, case 2"),
343 loc)
344 << range;
346 // Conservatively assume "yes" if lexing fails:
347 return true;
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")
354 return true;
357 if (loc == end) {
358 break;
360 hash = tok.is(tok::hash) && tok.isAtStartOfLine();
361 loc = loc.getLocWithOffset(
362 std::max<unsigned>(
363 Lexer::MeasureTokenLength(
364 loc, compiler.getSourceManager(),
365 compiler.getLangOpts()),
366 1));
368 return false;
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;
385 APSInt x1, x2;
386 if (evaluate(argument1, x1) && evaluate(argument2, x2))
388 return x1 == 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) {
414 break;
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();
426 continue;
428 if (ce1->getNumArgs() == 0 && ce2->getNumArgs() == 0) {
429 return IdenticalDefaultArgumentsResult::Yes;
431 break;
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;
439 if (isDebugMode()) {
440 report(
441 DiagnosticsEngine::Fatal, "TODO: Unexpected 'IdenticalDefaultArgumentsResult::Maybe'",
442 argument1->getExprLoc())
443 << argument1->getSourceRange();
444 report(
445 DiagnosticsEngine::Note, "TODO: second argument is here", argument2->getExprLoc())
446 << argument2->getSourceRange();
447 argument1->dump();
448 argument2->dump();
450 return IdenticalDefaultArgumentsResult::Maybe;
453 RewritePlugin::RewritePlugin( const InstantiationData& data )
454 : Plugin( data )
455 , rewriter( data.rewriter )
459 bool RewritePlugin::insertText( SourceLocation Loc, StringRef Str, bool InsertAfter, bool indentNewLines )
461 assert( rewriter );
462 if (wouldRewriteWorkdir(Loc))
463 return false;
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());
468 return false;
470 if( rewriter->InsertText( Loc, Str, InsertAfter, indentNewLines ))
471 return reportEditFailure( Loc );
472 handler.addSourceModification(Range);
473 return true;
476 bool RewritePlugin::insertTextAfter( SourceLocation Loc, StringRef Str )
478 assert( rewriter );
479 if (wouldRewriteWorkdir(Loc))
480 return false;
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());
485 return false;
487 if( rewriter->InsertTextAfter( Loc, Str ))
488 return reportEditFailure( Loc );
489 handler.addSourceModification(Range);
490 return true;
493 bool RewritePlugin::insertTextAfterToken( SourceLocation Loc, StringRef Str )
495 assert( rewriter );
496 if (wouldRewriteWorkdir(Loc))
497 return false;
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());
502 return false;
504 if( rewriter->InsertTextAfterToken( Loc, Str ))
505 return reportEditFailure( Loc );
506 handler.addSourceModification(Range);
507 return true;
510 bool RewritePlugin::insertTextBefore( SourceLocation Loc, StringRef Str )
512 assert( rewriter );
513 if (wouldRewriteWorkdir(Loc))
514 return false;
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());
519 return false;
521 if( rewriter->InsertTextBefore( Loc, Str ))
522 return reportEditFailure( Loc );
523 handler.addSourceModification(Range);
524 return true;
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 )
540 assert( rewriter );
541 if (wouldRewriteWorkdir(range.getBegin()))
542 return false;
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());
548 return false;
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());
558 return true;
561 bool RewritePlugin::adjustRangeForOptions( CharSourceRange* range, RewriteOptions opts )
563 assert( rewriter );
564 SourceManager& SM = rewriter->getSourceMgr();
565 SourceLocation fileStartLoc = SM.getLocForStartOfFile( SM.getFileID( range->getBegin()));
566 if( fileStartLoc.isInvalid())
567 return false;
568 bool isInvalid = false;
569 const char* fileBuf = SM.getCharacterData( fileStartLoc, &isInvalid );
570 if( isInvalid )
571 return false;
572 const char* startBuf = SM.getCharacterData( range->getBegin(), &isInvalid );
573 if( isInvalid )
574 return false;
575 SourceLocation locationEnd = range->getEnd();
576 if( range->isTokenRange())
577 locationEnd = locationAfterToken( locationEnd );
578 const char* endBuf = SM.getCharacterData( locationEnd, &isInvalid );
579 if( isInvalid )
580 return false;
581 const char* startPos = startBuf;
582 --startPos;
583 while( startPos >= fileBuf && ( *startPos == ' ' || *startPos == '\t' ))
584 --startPos;
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' )
589 ++endPos;
590 if( opts.flags & RemoveWholeStatement )
592 if( *endPos == ';' )
593 ++endPos;
594 else
595 return false;
597 *range = CharSourceRange( SourceRange( range->getBegin().getLocWithOffset( startPos - startBuf + 1 ),
598 locationEnd.getLocWithOffset( endPos - endBuf )), false );
599 return true;
602 bool RewritePlugin::replaceText( SourceLocation Start, unsigned OrigLength, StringRef NewStr )
604 assert( rewriter );
605 if (wouldRewriteWorkdir(Start))
606 return false;
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 );
611 return false;
613 if( rewriter->ReplaceText( Start, OrigLength, NewStr ))
614 return reportEditFailure( Start );
615 handler.addSourceModification(Range);
616 return true;
619 bool RewritePlugin::replaceText( SourceRange range, StringRef NewStr )
621 assert( rewriter );
622 if (wouldRewriteWorkdir(range.getBegin()))
623 return false;
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());
629 return false;
631 if( rewriter->ReplaceText( range, NewStr ))
632 return reportEditFailure( range.getBegin());
633 handler.addSourceModification(range);
634 return true;
637 bool RewritePlugin::replaceText( SourceRange range, SourceRange replacementRange )
639 assert( rewriter );
640 if (wouldRewriteWorkdir(range.getBegin()))
641 return false;
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());
647 return false;
649 if( rewriter->ReplaceText( range, replacementRange ))
650 return reportEditFailure( range.getBegin());
651 handler.addSourceModification(range);
652 return true;
655 bool RewritePlugin::wouldRewriteWorkdir(SourceLocation loc)
657 if (loc.isInvalid() || loc.isMacroID()) {
658 return false;
660 return
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 );
668 return false;
671 namespace {
673 template<typename Fn> bool checkPathname(
674 StringRef pathname, StringRef against, Fn check)
676 if (check(pathname, against)) {
677 return true;
679 #if defined _WIN32
680 for (std::size_t n = 0;;)
682 std::size_t n1 = pathname.find('\\', n);
683 std::size_t n2 = against.find('\\', n);
684 if (n1 <= n2) {
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))
691 break;
693 n = n1 + 1;
694 } else {
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))
701 break;
703 n = n2 + 1;
706 #endif
707 return false;
712 bool hasPathnamePrefix(StringRef pathname, StringRef prefix)
714 return checkPathname(
715 pathname, prefix,
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()) {
728 return true;
730 if (decl->isInExternCContext()) {
731 return true;
733 return false;
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);
742 if (!recordType)
743 return nullptr;
744 return dyn_cast_or_null<CXXRecordDecl>(recordType->getDecl());
747 int derivedFromCount(const CXXRecordDecl* subclassRecordDecl, const CXXRecordDecl* baseclassRecordDecl)
749 if (!subclassRecordDecl || !baseclassRecordDecl)
750 return 0;
751 int derivedCount = 0;
752 if (subclassRecordDecl == baseclassRecordDecl)
753 derivedCount++;
754 if (!subclassRecordDecl->hasDefinition())
755 return derivedCount;
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)
761 return derivedCount;
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)
768 return derivedCount;
770 return derivedCount;
773 int derivedFromCount(QualType subclassQt, QualType baseclassQt)
775 auto baseclassRecordDecl = stripTypeSugar(baseclassQt);
776 if (!baseclassRecordDecl)
777 return 0;
778 auto subclassRecordDecl = stripTypeSugar(subclassQt);
779 if (!subclassRecordDecl)
780 return 0;
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) {
791 return false;
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()) {
799 return true;
801 if (auto prev = decl->getPreviousDecl()) {
802 return hasExternalLinkage(prev);
804 return !decl->isInAnonymousNamespace();
807 return true;
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
814 // type:
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();
822 return true;
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())
841 return true;
843 return false;
847 } // namespace
849 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */