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 
13 using namespace clang::ast_matchers;
14 
15 namespace clang {
16 namespace tidy {
17 namespace readability {
18 
getNameSpecifierNestingLevel(const QualType & QType)19 static unsigned getNameSpecifierNestingLevel(const QualType &QType) {
20   if (const ElaboratedType *ElType = QType->getAs<ElaboratedType>()) {
21     const NestedNameSpecifier *NestedSpecifiers = ElType->getQualifier();
22     unsigned NameSpecifierNestingLevel = 1;
23     do {
24       NameSpecifierNestingLevel++;
25       NestedSpecifiers = NestedSpecifiers->getPrefix();
26     } while (NestedSpecifiers);
27 
28     return NameSpecifierNestingLevel;
29   }
30   return 0;
31 }
32 
storeOptions(ClangTidyOptions::OptionMap & Opts)33 void StaticAccessedThroughInstanceCheck::storeOptions(
34     ClangTidyOptions::OptionMap &Opts) {
35   Options.store(Opts, "NameSpecifierNestingThreshold",
36                 NameSpecifierNestingThreshold);
37 }
38 
registerMatchers(MatchFinder * Finder)39 void StaticAccessedThroughInstanceCheck::registerMatchers(MatchFinder *Finder) {
40   Finder->addMatcher(
41       memberExpr(hasDeclaration(anyOf(cxxMethodDecl(isStaticStorageClass()),
42                                       varDecl(hasStaticStorageDuration()))),
43                  unless(isInTemplateInstantiation()))
44           .bind("memberExpression"),
45       this);
46 }
47 
check(const MatchFinder::MatchResult & Result)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 overlaoded -> 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   SourceLocation MemberExprStartLoc = MemberExpression->getBeginLoc();
75   auto Diag =
76       diag(MemberExprStartLoc, "static member accessed through instance");
77 
78   if (BaseExpr->HasSideEffects(*AstContext) ||
79       getNameSpecifierNestingLevel(BaseType) > NameSpecifierNestingThreshold)
80     return;
81 
82   Diag << FixItHint::CreateReplacement(
83       CharSourceRange::getCharRange(MemberExprStartLoc,
84                                     MemberExpression->getMemberLoc()),
85       BaseTypeName + "::");
86 }
87 
88 } // namespace readability
89 } // namespace tidy
90 } // namespace clang
91