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