1 //===--- StaticAccessedThroughInstanceCheck.cpp - clang-tidy---------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "StaticAccessedThroughInstanceCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "llvm/ADT/StringRef.h"
14 using namespace clang::ast_matchers
;
16 namespace clang::tidy::readability
{
19 AST_MATCHER(CXXMethodDecl
, isStatic
) { return Node
.isStatic(); }
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;
27 NameSpecifierNestingLevel
++;
28 NestedSpecifiers
= NestedSpecifiers
->getPrefix();
29 } while (NestedSpecifiers
);
31 return NameSpecifierNestingLevel
;
37 void StaticAccessedThroughInstanceCheck::storeOptions(
38 ClangTidyOptions::OptionMap
&Opts
) {
39 Options
.store(Opts
, "NameSpecifierNestingThreshold",
40 NameSpecifierNestingThreshold
);
43 void StaticAccessedThroughInstanceCheck::registerMatchers(MatchFinder
*Finder
) {
45 memberExpr(hasDeclaration(anyOf(cxxMethodDecl(isStatic()),
46 varDecl(hasStaticStorageDuration()),
48 .bind("memberExpression"),
52 void StaticAccessedThroughInstanceCheck::check(
53 const MatchFinder::MatchResult
&Result
) {
54 const auto *MemberExpression
=
55 Result
.Nodes
.getNodeAs
<MemberExpr
>("memberExpression");
57 if (MemberExpression
->getBeginLoc().isMacroID())
60 const Expr
*BaseExpr
= MemberExpression
->getBase();
62 // Do not warn for overloaded -> operators.
63 if (isa
<CXXOperatorCallExpr
>(BaseExpr
))
66 const QualType BaseType
=
67 BaseExpr
->getType()->isPointerType()
68 ? BaseExpr
->getType()->getPointeeType().getUnqualifiedType()
69 : BaseExpr
->getType().getUnqualifiedType();
71 const ASTContext
*AstContext
= Result
.Context
;
72 PrintingPolicy
PrintingPolicyWithSuppressedTag(AstContext
->getLangOpts());
73 PrintingPolicyWithSuppressedTag
.SuppressTagKeyword
= true;
74 PrintingPolicyWithSuppressedTag
.SuppressUnwrittenScope
= true;
76 PrintingPolicyWithSuppressedTag
.PrintCanonicalTypes
=
77 !BaseExpr
->getType()->isTypedefNameType();
79 std::string BaseTypeName
=
80 BaseType
.getAsString(PrintingPolicyWithSuppressedTag
);
82 // Ignore anonymous structs/classes which will not have an identifier
83 const RecordDecl
*RecDecl
= BaseType
->getAsCXXRecordDecl();
84 if (!RecDecl
|| RecDecl
->getIdentifier() == nullptr)
87 // Do not warn for CUDA built-in variables.
88 if (StringRef(BaseTypeName
).startswith("__cuda_builtin_"))
91 SourceLocation MemberExprStartLoc
= MemberExpression
->getBeginLoc();
93 diag(MemberExprStartLoc
, "static member accessed through instance");
95 if (BaseExpr
->HasSideEffects(*AstContext
) ||
96 getNameSpecifierNestingLevel(BaseType
) > NameSpecifierNestingThreshold
)
99 Diag
<< FixItHint::CreateReplacement(
100 CharSourceRange::getCharRange(MemberExprStartLoc
,
101 MemberExpression
->getMemberLoc()),
102 BaseTypeName
+ "::");
105 } // namespace clang::tidy::readability