clang-tools 22.0.0git
StaticAccessedThroughInstanceCheck.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
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
10#include "clang/AST/ASTContext.h"
11#include "clang/ASTMatchers/ASTMatchFinder.h"
12#include "llvm/ADT/StringRef.h"
13
14using namespace clang::ast_matchers;
15
17
18namespace {
19AST_MATCHER(CXXMethodDecl, isStatic) { return Node.isStatic(); }
20} // namespace
21
22static unsigned getNameSpecifierNestingLevel(QualType QType) {
23 unsigned NameSpecifierNestingLevel = 1;
24 for (NestedNameSpecifier Qualifier = QType->getPrefix(); /**/;
25 ++NameSpecifierNestingLevel) {
26 switch (Qualifier.getKind()) {
27 case NestedNameSpecifier::Kind::Null:
28 return NameSpecifierNestingLevel;
29 case NestedNameSpecifier::Kind::Global:
30 case NestedNameSpecifier::Kind::MicrosoftSuper:
31 return NameSpecifierNestingLevel + 1;
32 case NestedNameSpecifier::Kind::Namespace:
33 Qualifier = Qualifier.getAsNamespaceAndPrefix().Prefix;
34 continue;
35 case NestedNameSpecifier::Kind::Type:
36 Qualifier = Qualifier.getAsType()->getPrefix();
37 continue;
38 }
39 llvm_unreachable("unhandled nested name specifier kind");
40 }
41}
42
45 Options.store(Opts, "NameSpecifierNestingThreshold",
46 NameSpecifierNestingThreshold);
47}
48
50 Finder->addMatcher(
51 memberExpr(hasDeclaration(anyOf(cxxMethodDecl(isStatic()),
52 varDecl(hasStaticStorageDuration()),
53 enumConstantDecl())))
54 .bind("memberExpression"),
55 this);
56}
57
59 const MatchFinder::MatchResult &Result) {
60 const auto *MemberExpression =
61 Result.Nodes.getNodeAs<MemberExpr>("memberExpression");
62
63 if (MemberExpression->getBeginLoc().isMacroID())
64 return;
65
66 const Expr *BaseExpr = MemberExpression->getBase();
67
68 const QualType BaseType =
69 BaseExpr->getType()->isPointerType()
70 ? BaseExpr->getType()->getPointeeType().getUnqualifiedType()
71 : BaseExpr->getType().getUnqualifiedType();
72
73 const ASTContext *AstContext = Result.Context;
74 PrintingPolicy PrintingPolicyWithSuppressedTag(AstContext->getLangOpts());
75 PrintingPolicyWithSuppressedTag.SuppressTagKeyword = true;
76 PrintingPolicyWithSuppressedTag.SuppressUnwrittenScope = true;
77
78 PrintingPolicyWithSuppressedTag.PrintAsCanonical =
79 !BaseExpr->getType()->isTypedefNameType();
80
81 std::string BaseTypeName =
82 BaseType.getAsString(PrintingPolicyWithSuppressedTag);
83
84 // Ignore anonymous structs/classes which will not have an identifier
85 const RecordDecl *RecDecl = BaseType->getAsCXXRecordDecl();
86 if (!RecDecl || RecDecl->getIdentifier() == nullptr)
87 return;
88
89 // Do not warn for CUDA built-in variables.
90 if (StringRef(BaseTypeName).starts_with("__cuda_builtin_"))
91 return;
92
93 SourceLocation MemberExprStartLoc = MemberExpression->getBeginLoc();
94 auto CreateFix = [&] {
95 return FixItHint::CreateReplacement(
96 CharSourceRange::getCharRange(MemberExprStartLoc,
97 MemberExpression->getMemberLoc()),
98 BaseTypeName + "::");
99 };
100
101 {
102 auto Diag =
103 diag(MemberExprStartLoc, "static member accessed through instance");
104
105 if (getNameSpecifierNestingLevel(BaseType) > NameSpecifierNestingThreshold)
106 return;
107
108 if (!BaseExpr->HasSideEffects(*AstContext,
109 /* IncludePossibleEffects =*/true)) {
110 Diag << CreateFix();
111 return;
112 }
113 }
114
115 diag(MemberExprStartLoc, "member base expression may carry some side effects",
116 DiagnosticIDs::Level::Note)
117 << BaseExpr->getSourceRange() << CreateFix();
118}
119
120} // namespace clang::tidy::readability
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
AST_MATCHER(BinaryOperator, isRelationalOperator)
static unsigned getNameSpecifierNestingLevel(QualType QType)
llvm::StringMap< ClangTidyValue > OptionMap