1 | //===--- StaticAccessedThroughInstanceCheck.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 "StaticAccessedThroughInstanceCheck.h" |
10 | #include "clang/AST/ASTContext.h" |
11 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
12 | #include "llvm/ADT/StringRef.h" |
13 | |
14 | using namespace clang::ast_matchers; |
15 | |
16 | namespace clang::tidy::readability { |
17 | |
18 | namespace { |
19 | AST_MATCHER(CXXMethodDecl, isStatic) { return Node.isStatic(); } |
20 | } // namespace |
21 | |
22 | static unsigned getNameSpecifierNestingLevel(const QualType &QType) { |
23 | if (const auto *ElType = QType->getAs<ElaboratedType>()) { |
24 | if (const NestedNameSpecifier *NestedSpecifiers = ElType->getQualifier()) { |
25 | unsigned NameSpecifierNestingLevel = 1; |
26 | do { |
27 | NameSpecifierNestingLevel++; |
28 | NestedSpecifiers = NestedSpecifiers->getPrefix(); |
29 | } while (NestedSpecifiers); |
30 | |
31 | return NameSpecifierNestingLevel; |
32 | } |
33 | } |
34 | return 0; |
35 | } |
36 | |
37 | void StaticAccessedThroughInstanceCheck::storeOptions( |
38 | ClangTidyOptions::OptionMap &Opts) { |
39 | Options.store(Options&: Opts, LocalName: "NameSpecifierNestingThreshold" , |
40 | Value: NameSpecifierNestingThreshold); |
41 | } |
42 | |
43 | void StaticAccessedThroughInstanceCheck::registerMatchers(MatchFinder *Finder) { |
44 | Finder->addMatcher( |
45 | NodeMatch: memberExpr(hasDeclaration(InnerMatcher: anyOf(cxxMethodDecl(isStatic()), |
46 | varDecl(hasStaticStorageDuration()), |
47 | enumConstantDecl()))) |
48 | .bind(ID: "memberExpression" ), |
49 | Action: this); |
50 | } |
51 | |
52 | void StaticAccessedThroughInstanceCheck::check( |
53 | const MatchFinder::MatchResult &Result) { |
54 | const auto *MemberExpression = |
55 | Result.Nodes.getNodeAs<MemberExpr>(ID: "memberExpression" ); |
56 | |
57 | if (MemberExpression->getBeginLoc().isMacroID()) |
58 | return; |
59 | |
60 | const Expr *BaseExpr = MemberExpression->getBase(); |
61 | |
62 | // Do not warn for overloaded -> operators. |
63 | if (isa<CXXOperatorCallExpr>(Val: BaseExpr)) |
64 | return; |
65 | |
66 | const QualType BaseType = |
67 | BaseExpr->getType()->isPointerType() |
68 | ? BaseExpr->getType()->getPointeeType().getUnqualifiedType() |
69 | : BaseExpr->getType().getUnqualifiedType(); |
70 | |
71 | const ASTContext *AstContext = Result.Context; |
72 | PrintingPolicy PrintingPolicyWithSuppressedTag(AstContext->getLangOpts()); |
73 | PrintingPolicyWithSuppressedTag.SuppressTagKeyword = true; |
74 | PrintingPolicyWithSuppressedTag.SuppressUnwrittenScope = true; |
75 | |
76 | PrintingPolicyWithSuppressedTag.PrintCanonicalTypes = |
77 | !BaseExpr->getType()->isTypedefNameType(); |
78 | |
79 | std::string BaseTypeName = |
80 | BaseType.getAsString(Policy: PrintingPolicyWithSuppressedTag); |
81 | |
82 | // Ignore anonymous structs/classes which will not have an identifier |
83 | const RecordDecl *RecDecl = BaseType->getAsCXXRecordDecl(); |
84 | if (!RecDecl || RecDecl->getIdentifier() == nullptr) |
85 | return; |
86 | |
87 | // Do not warn for CUDA built-in variables. |
88 | if (StringRef(BaseTypeName).starts_with(Prefix: "__cuda_builtin_" )) |
89 | return; |
90 | |
91 | SourceLocation MemberExprStartLoc = MemberExpression->getBeginLoc(); |
92 | auto Diag = |
93 | diag(Loc: MemberExprStartLoc, Description: "static member accessed through instance" ); |
94 | |
95 | if (BaseExpr->HasSideEffects(Ctx: *AstContext) || |
96 | getNameSpecifierNestingLevel(QType: BaseType) > NameSpecifierNestingThreshold) |
97 | return; |
98 | |
99 | Diag << FixItHint::CreateReplacement( |
100 | RemoveRange: CharSourceRange::getCharRange(B: MemberExprStartLoc, |
101 | E: MemberExpression->getMemberLoc()), |
102 | Code: BaseTypeName + "::" ); |
103 | } |
104 | |
105 | } // namespace clang::tidy::readability |
106 | |