121 lines · cpp
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-exception6//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 14using namespace clang::ast_matchers;15 16namespace clang::tidy::readability {17 18namespace {19AST_MATCHER(CXXMethodDecl, isStatic) { return Node.isStatic(); }20} // namespace21 22static unsigned getNameSpecifierNestingLevel(QualType QType) {23 unsigned NameSpecifierNestingLevel = 1;24 for (NestedNameSpecifier Qualifier = QType->getPrefix(); /**/;25 ++NameSpecifierNestingLevel) {26 switch (Qualifier.getKind()) {27 case NestedNameSpecifier::Kind::Null:28 return NameSpecifierNestingLevel;29 case NestedNameSpecifier::Kind::Global:30 case NestedNameSpecifier::Kind::MicrosoftSuper:31 return NameSpecifierNestingLevel + 1;32 case NestedNameSpecifier::Kind::Namespace:33 Qualifier = Qualifier.getAsNamespaceAndPrefix().Prefix;34 continue;35 case NestedNameSpecifier::Kind::Type:36 Qualifier = Qualifier.getAsType()->getPrefix();37 continue;38 }39 llvm_unreachable("unhandled nested name specifier kind");40 }41}42 43void StaticAccessedThroughInstanceCheck::storeOptions(44 ClangTidyOptions::OptionMap &Opts) {45 Options.store(Opts, "NameSpecifierNestingThreshold",46 NameSpecifierNestingThreshold);47}48 49void StaticAccessedThroughInstanceCheck::registerMatchers(MatchFinder *Finder) {50 Finder->addMatcher(51 memberExpr(hasDeclaration(anyOf(cxxMethodDecl(isStatic()),52 varDecl(hasStaticStorageDuration()),53 enumConstantDecl())))54 .bind("memberExpression"),55 this);56}57 58void StaticAccessedThroughInstanceCheck::check(59 const MatchFinder::MatchResult &Result) {60 const auto *MemberExpression =61 Result.Nodes.getNodeAs<MemberExpr>("memberExpression");62 63 if (MemberExpression->getBeginLoc().isMacroID())64 return;65 66 const Expr *BaseExpr = MemberExpression->getBase();67 68 const QualType BaseType =69 BaseExpr->getType()->isPointerType()70 ? BaseExpr->getType()->getPointeeType().getUnqualifiedType()71 : BaseExpr->getType().getUnqualifiedType();72 73 const ASTContext *AstContext = Result.Context;74 PrintingPolicy PrintingPolicyWithSuppressedTag(AstContext->getLangOpts());75 PrintingPolicyWithSuppressedTag.SuppressTagKeyword = true;76 PrintingPolicyWithSuppressedTag.SuppressUnwrittenScope = true;77 78 PrintingPolicyWithSuppressedTag.PrintAsCanonical =79 !BaseExpr->getType()->isTypedefNameType();80 81 std::string BaseTypeName =82 BaseType.getAsString(PrintingPolicyWithSuppressedTag);83 84 // Ignore anonymous structs/classes which will not have an identifier85 const RecordDecl *RecDecl = BaseType->getAsCXXRecordDecl();86 if (!RecDecl || RecDecl->getIdentifier() == nullptr)87 return;88 89 // Do not warn for CUDA built-in variables.90 if (StringRef(BaseTypeName).starts_with("__cuda_builtin_"))91 return;92 93 SourceLocation MemberExprStartLoc = MemberExpression->getBeginLoc();94 auto CreateFix = [&] {95 return FixItHint::CreateReplacement(96 CharSourceRange::getCharRange(MemberExprStartLoc,97 MemberExpression->getMemberLoc()),98 BaseTypeName + "::");99 };100 101 {102 auto Diag =103 diag(MemberExprStartLoc, "static member accessed through instance");104 105 if (getNameSpecifierNestingLevel(BaseType) > NameSpecifierNestingThreshold)106 return;107 108 if (!BaseExpr->HasSideEffects(*AstContext,109 /* IncludePossibleEffects =*/true)) {110 Diag << CreateFix();111 return;112 }113 }114 115 diag(MemberExprStartLoc, "member base expression may carry some side effects",116 DiagnosticIDs::Level::Note)117 << BaseExpr->getSourceRange() << CreateFix();118}119 120} // namespace clang::tidy::readability121