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 {
17 namespace tidy {
18 namespace readability {
19 
20 static unsigned getNameSpecifierNestingLevel(const QualType &QType) {
21   if (const ElaboratedType *ElType = QType->getAs<ElaboratedType>()) {
22     const NestedNameSpecifier *NestedSpecifiers = ElType->getQualifier();
23     unsigned NameSpecifierNestingLevel = 1;
24     do {
25       NameSpecifierNestingLevel++;
26       NestedSpecifiers = NestedSpecifiers->getPrefix();
27     } while (NestedSpecifiers);
28 
29     return NameSpecifierNestingLevel;
30   }
31   return 0;
32 }
33 
34 void StaticAccessedThroughInstanceCheck::storeOptions(
35     ClangTidyOptions::OptionMap &Opts) {
36   Options.store(Opts, "NameSpecifierNestingThreshold",
37                 NameSpecifierNestingThreshold);
38 }
39 
40 void StaticAccessedThroughInstanceCheck::registerMatchers(MatchFinder *Finder) {
41   Finder->addMatcher(
42       memberExpr(hasDeclaration(anyOf(cxxMethodDecl(isStaticStorageClass()),
43                                       varDecl(hasStaticStorageDuration()))))
44           .bind("memberExpression"),
45       this);
46 }
47 
48 void StaticAccessedThroughInstanceCheck::check(
49     const MatchFinder::MatchResult &Result) {
50   const auto *MemberExpression =
51       Result.Nodes.getNodeAs<MemberExpr>("memberExpression");
52 
53   if (MemberExpression->getBeginLoc().isMacroID())
54     return;
55 
56   const Expr *BaseExpr = MemberExpression->getBase();
57 
58   // Do not warn for overloaded -> operators.
59   if (isa<CXXOperatorCallExpr>(BaseExpr))
60     return;
61 
62   QualType BaseType =
63       BaseExpr->getType()->isPointerType()
64           ? BaseExpr->getType()->getPointeeType().getUnqualifiedType()
65           : BaseExpr->getType().getUnqualifiedType();
66 
67   const ASTContext *AstContext = Result.Context;
68   PrintingPolicy PrintingPolicyWithSupressedTag(AstContext->getLangOpts());
69   PrintingPolicyWithSupressedTag.SuppressTagKeyword = true;
70   PrintingPolicyWithSupressedTag.SuppressUnwrittenScope = true;
71   std::string BaseTypeName =
72       BaseType.getAsString(PrintingPolicyWithSupressedTag);
73 
74   // Do not warn for CUDA built-in variables.
75   if (StringRef(BaseTypeName).startswith("__cuda_builtin_"))
76     return;
77 
78   SourceLocation MemberExprStartLoc = MemberExpression->getBeginLoc();
79   auto Diag =
80       diag(MemberExprStartLoc, "static member accessed through instance");
81 
82   if (BaseExpr->HasSideEffects(*AstContext) ||
83       getNameSpecifierNestingLevel(BaseType) > NameSpecifierNestingThreshold)
84     return;
85 
86   Diag << FixItHint::CreateReplacement(
87       CharSourceRange::getCharRange(MemberExprStartLoc,
88                                     MemberExpression->getMemberLoc()),
89       BaseTypeName + "::");
90 }
91 
92 } // namespace readability
93 } // namespace tidy
94 } // namespace clang
95