clang-tools 24.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 "../utils/Matchers.h"
11#include "clang/AST/ASTContext.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "llvm/ADT/StringRef.h"
14
15using namespace clang::ast_matchers;
16using namespace clang::tidy::matchers;
17
19
20static unsigned getNameSpecifierNestingLevel(QualType QType) {
21 unsigned NameSpecifierNestingLevel = 1;
22 for (NestedNameSpecifier Qualifier = QType->getPrefix(); /**/;
23 ++NameSpecifierNestingLevel) {
24 switch (Qualifier.getKind()) {
25 case NestedNameSpecifier::Kind::Null:
26 return NameSpecifierNestingLevel;
27 case NestedNameSpecifier::Kind::Global:
28 case NestedNameSpecifier::Kind::MicrosoftSuper:
29 return NameSpecifierNestingLevel + 1;
30 case NestedNameSpecifier::Kind::Namespace:
31 Qualifier = Qualifier.getAsNamespaceAndPrefix().Prefix;
32 continue;
33 case NestedNameSpecifier::Kind::Type:
34 Qualifier = Qualifier.getAsType()->getPrefix();
35 continue;
36 }
37 llvm_unreachable("unhandled nested name specifier kind");
38 }
39}
40
43 Options.store(Opts, "NameSpecifierNestingThreshold",
44 NameSpecifierNestingThreshold);
45}
46
48 Finder->addMatcher(
49 memberExpr(hasDeclaration(anyOf(cxxMethodDecl(isStatic()),
50 varDecl(hasStaticStorageDuration()),
51 enumConstantDecl())))
52 .bind("memberExpression"),
53 this);
54}
55
57 const MatchFinder::MatchResult &Result) {
58 const auto *MemberExpression =
59 Result.Nodes.getNodeAs<MemberExpr>("memberExpression");
60
61 if (MemberExpression->getBeginLoc().isMacroID())
62 return;
63
64 const Expr *BaseExpr = MemberExpression->getBase();
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.PrintAsCanonical =
77 !BaseExpr->getType()->isTypedefNameType();
78
79 std::string BaseTypeName =
80 BaseType.getAsString(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("__cuda_builtin_"))
89 return;
90
91 SourceLocation MemberExprStartLoc = MemberExpression->getBeginLoc();
92 const auto CreateFix = [&] {
93 return FixItHint::CreateReplacement(
94 CharSourceRange::getCharRange(MemberExprStartLoc,
95 MemberExpression->getMemberLoc()),
96 BaseTypeName + "::");
97 };
98
99 {
100 const auto Diag =
101 diag(MemberExprStartLoc, "static member accessed through instance");
102
103 if (getNameSpecifierNestingLevel(BaseType) > NameSpecifierNestingThreshold)
104 return;
105
106 if (!BaseExpr->HasSideEffects(*AstContext,
107 /* IncludePossibleEffects =*/true)) {
108 Diag << CreateFix();
109 return;
110 }
111 }
112
113 diag(MemberExprStartLoc, "member base expression may carry some side effects",
114 DiagnosticIDs::Level::Note)
115 << BaseExpr->getSourceRange() << CreateFix();
116}
117
118} // namespace clang::tidy::readability
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
static unsigned getNameSpecifierNestingLevel(QualType QType)
llvm::StringMap< ClangTidyValue > OptionMap