1 //===--- IncorrectEnableSharedFromThisCheck.cpp - clang-tidy --------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "IncorrectEnableSharedFromThisCheck.h" 10 #include "clang/AST/ASTContext.h" 11 #include "clang/AST/DeclCXX.h" 12 #include "clang/ASTMatchers/ASTMatchFinder.h" 13 14 using namespace clang::ast_matchers; 15 16 namespace clang::tidy::bugprone { 17 18 void IncorrectEnableSharedFromThisCheck::registerMatchers(MatchFinder *Finder) { 19 const auto EnableSharedFromThis = 20 cxxRecordDecl(hasName("enable_shared_from_this"), isInStdNamespace()); 21 const auto QType = hasCanonicalType(hasDeclaration( 22 cxxRecordDecl( 23 anyOf(EnableSharedFromThis.bind("enable_rec"), 24 cxxRecordDecl(hasAnyBase(cxxBaseSpecifier( 25 isPublic(), hasType(hasCanonicalType( 26 hasDeclaration(EnableSharedFromThis)))))))) 27 .bind("base_rec"))); 28 Finder->addMatcher( 29 cxxRecordDecl( 30 unless(isExpansionInSystemHeader()), 31 hasDirectBase(cxxBaseSpecifier(unless(isPublic()), hasType(QType)) 32 .bind("base"))) 33 .bind("derived"), 34 this); 35 } 36 37 void IncorrectEnableSharedFromThisCheck::check( 38 const MatchFinder::MatchResult &Result) { 39 const auto *BaseSpec = Result.Nodes.getNodeAs<CXXBaseSpecifier>("base"); 40 const auto *Base = Result.Nodes.getNodeAs<CXXRecordDecl>("base_rec"); 41 const auto *Derived = Result.Nodes.getNodeAs<CXXRecordDecl>("derived"); 42 const bool IsEnableSharedFromThisDirectBase = 43 Result.Nodes.getNodeAs<CXXRecordDecl>("enable_rec") == Base; 44 const bool HasWrittenAccessSpecifier = 45 BaseSpec->getAccessSpecifierAsWritten() != AS_none; 46 const auto ReplacementRange = CharSourceRange( 47 SourceRange(BaseSpec->getBeginLoc()), HasWrittenAccessSpecifier); 48 const llvm::StringRef Replacement = 49 HasWrittenAccessSpecifier ? "public" : "public "; 50 const FixItHint Hint = 51 IsEnableSharedFromThisDirectBase 52 ? FixItHint::CreateReplacement(ReplacementRange, Replacement) 53 : FixItHint(); 54 diag(Derived->getLocation(), 55 "%2 is not publicly inheriting from " 56 "%select{%1 which inherits from |}0'std::enable_shared_" 57 "from_this', " 58 "which will cause unintended behaviour " 59 "when using 'shared_from_this'; make the inheritance " 60 "public", 61 DiagnosticIDs::Warning) 62 << IsEnableSharedFromThisDirectBase << Base << Derived << Hint; 63 } 64 65 } // namespace clang::tidy::bugprone 66