xref: /llvm-project/clang-tools-extra/clang-tidy/bugprone/IncorrectEnableSharedFromThisCheck.cpp (revision 8ebc35f8d041f097a2b973b455dc3533420af6bf)
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