//===--- IdentifierNamingCheck.cpp - clang-tidy ---------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "IdentifierNamingCheck.h" #include "../utils/ASTUtils.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Lex/PPCallbacks.h" #include "clang/Lex/Preprocessor.h" #include "llvm/ADT/DenseMapInfo.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Format.h" #define DEBUG_TYPE "clang-tidy" using namespace clang::ast_matchers; namespace llvm { /// Specialisation of DenseMapInfo to allow NamingCheckId objects in DenseMaps template <> struct DenseMapInfo< clang::tidy::readability::IdentifierNamingCheck::NamingCheckId> { using NamingCheckId = clang::tidy::readability::IdentifierNamingCheck::NamingCheckId; static inline NamingCheckId getEmptyKey() { return NamingCheckId( clang::SourceLocation::getFromRawEncoding(static_cast(-1)), "EMPTY"); } static inline NamingCheckId getTombstoneKey() { return NamingCheckId( clang::SourceLocation::getFromRawEncoding(static_cast(-2)), "TOMBSTONE"); } static unsigned getHashValue(NamingCheckId Val) { assert(Val != getEmptyKey() && "Cannot hash the empty key!"); assert(Val != getTombstoneKey() && "Cannot hash the tombstone key!"); std::hash SecondHash; return Val.first.getRawEncoding() + SecondHash(Val.second); } static bool isEqual(const NamingCheckId &LHS, const NamingCheckId &RHS) { if (RHS == getEmptyKey()) return LHS == getEmptyKey(); if (RHS == getTombstoneKey()) return LHS == getTombstoneKey(); return LHS == RHS; } }; } // namespace llvm namespace clang { namespace tidy { namespace readability { // clang-format off #define NAMING_KEYS(m) \ m(Namespace) \ m(InlineNamespace) \ m(EnumConstant) \ m(ConstexprVariable) \ m(ConstantMember) \ m(PrivateMember) \ m(ProtectedMember) \ m(PublicMember) \ m(Member) \ m(ClassConstant) \ m(ClassMember) \ m(GlobalConstant) \ m(GlobalConstantPointer) \ m(GlobalPointer) \ m(GlobalVariable) \ m(LocalConstant) \ m(LocalConstantPointer) \ m(LocalPointer) \ m(LocalVariable) \ m(StaticConstant) \ m(StaticVariable) \ m(Constant) \ m(Variable) \ m(ConstantParameter) \ m(ParameterPack) \ m(Parameter) \ m(PointerParameter) \ m(ConstantPointerParameter) \ m(AbstractClass) \ m(Struct) \ m(Class) \ m(Union) \ m(Enum) \ m(GlobalFunction) \ m(ConstexprFunction) \ m(Function) \ m(ConstexprMethod) \ m(VirtualMethod) \ m(ClassMethod) \ m(PrivateMethod) \ m(ProtectedMethod) \ m(PublicMethod) \ m(Method) \ m(Typedef) \ m(TypeTemplateParameter) \ m(ValueTemplateParameter) \ m(TemplateTemplateParameter) \ m(TemplateParameter) \ m(TypeAlias) \ m(MacroDefinition) \ m(ObjcIvar) \ enum StyleKind { #define ENUMERATE(v) SK_ ## v, NAMING_KEYS(ENUMERATE) #undef ENUMERATE SK_Count, SK_Invalid }; static StringRef const StyleNames[] = { #define STRINGIZE(v) #v, NAMING_KEYS(STRINGIZE) #undef STRINGIZE }; #undef NAMING_KEYS // clang-format on namespace { /// Callback supplies macros to IdentifierNamingCheck::checkMacro class IdentifierNamingCheckPPCallbacks : public PPCallbacks { public: IdentifierNamingCheckPPCallbacks(Preprocessor *PP, IdentifierNamingCheck *Check) : PP(PP), Check(Check) {} /// MacroDefined calls checkMacro for macros in the main file void MacroDefined(const Token &MacroNameTok, const MacroDirective *MD) override { Check->checkMacro(PP->getSourceManager(), MacroNameTok, MD->getMacroInfo()); } /// MacroExpands calls expandMacro for macros in the main file void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD, SourceRange /*Range*/, const MacroArgs * /*Args*/) override { Check->expandMacro(MacroNameTok, MD.getMacroInfo()); } private: Preprocessor *PP; IdentifierNamingCheck *Check; }; } // namespace IdentifierNamingCheck::IdentifierNamingCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context) { auto const fromString = [](StringRef Str) { return llvm::StringSwitch>(Str) .Case("aNy_CasE", CT_AnyCase) .Case("lower_case", CT_LowerCase) .Case("UPPER_CASE", CT_UpperCase) .Case("camelBack", CT_CamelBack) .Case("CamelCase", CT_CamelCase) .Case("Camel_Snake_Case", CT_CamelSnakeCase) .Case("camel_Snake_Back", CT_CamelSnakeBack) .Default(llvm::None); }; for (auto const &Name : StyleNames) { auto const caseOptional = fromString(Options.get((Name + "Case").str(), "")); auto prefix = Options.get((Name + "Prefix").str(), ""); auto postfix = Options.get((Name + "Suffix").str(), ""); if (caseOptional || !prefix.empty() || !postfix.empty()) { NamingStyles.push_back(NamingStyle(caseOptional, prefix, postfix)); } else { NamingStyles.push_back(llvm::None); } } IgnoreFailedSplit = Options.get("IgnoreFailedSplit", 0); } void IdentifierNamingCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { auto const toString = [](CaseType Type) { switch (Type) { case CT_AnyCase: return "aNy_CasE"; case CT_LowerCase: return "lower_case"; case CT_CamelBack: return "camelBack"; case CT_UpperCase: return "UPPER_CASE"; case CT_CamelCase: return "CamelCase"; case CT_CamelSnakeCase: return "Camel_Snake_Case"; case CT_CamelSnakeBack: return "camel_Snake_Back"; } llvm_unreachable("Unknown Case Type"); }; for (size_t i = 0; i < SK_Count; ++i) { if (NamingStyles[i]) { if (NamingStyles[i]->Case) { Options.store(Opts, (StyleNames[i] + "Case").str(), toString(*NamingStyles[i]->Case)); } Options.store(Opts, (StyleNames[i] + "Prefix").str(), NamingStyles[i]->Prefix); Options.store(Opts, (StyleNames[i] + "Suffix").str(), NamingStyles[i]->Suffix); } } Options.store(Opts, "IgnoreFailedSplit", IgnoreFailedSplit); } void IdentifierNamingCheck::registerMatchers(MatchFinder *Finder) { Finder->addMatcher(namedDecl().bind("decl"), this); Finder->addMatcher(usingDecl().bind("using"), this); Finder->addMatcher(declRefExpr().bind("declRef"), this); Finder->addMatcher(cxxConstructorDecl().bind("classRef"), this); Finder->addMatcher(cxxDestructorDecl().bind("classRef"), this); Finder->addMatcher(typeLoc().bind("typeLoc"), this); Finder->addMatcher(nestedNameSpecifierLoc().bind("nestedNameLoc"), this); } void IdentifierNamingCheck::registerPPCallbacks( const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) { ModuleExpanderPP->addPPCallbacks( llvm::make_unique(ModuleExpanderPP, this)); } static bool matchesStyle(StringRef Name, IdentifierNamingCheck::NamingStyle Style) { static llvm::Regex Matchers[] = { llvm::Regex("^.*$"), llvm::Regex("^[a-z][a-z0-9_]*$"), llvm::Regex("^[a-z][a-zA-Z0-9]*$"), llvm::Regex("^[A-Z][A-Z0-9_]*$"), llvm::Regex("^[A-Z][a-zA-Z0-9]*$"), llvm::Regex("^[A-Z]([a-z0-9]*(_[A-Z])?)*"), llvm::Regex("^[a-z]([a-z0-9]*(_[A-Z])?)*"), }; bool Matches = true; if (Name.startswith(Style.Prefix)) Name = Name.drop_front(Style.Prefix.size()); else Matches = false; if (Name.endswith(Style.Suffix)) Name = Name.drop_back(Style.Suffix.size()); else Matches = false; // Ensure the name doesn't have any extra underscores beyond those specified // in the prefix and suffix. if (Name.startswith("_") || Name.endswith("_")) Matches = false; if (Style.Case && !Matchers[static_cast(*Style.Case)].match(Name)) Matches = false; return Matches; } static std::string fixupWithCase(StringRef Name, IdentifierNamingCheck::CaseType Case) { static llvm::Regex Splitter( "([a-z0-9A-Z]*)(_+)|([A-Z]?[a-z0-9]+)([A-Z]|$)|([A-Z]+)([A-Z]|$)"); SmallVector Substrs; Name.split(Substrs, "_", -1, false); SmallVector Words; for (auto Substr : Substrs) { while (!Substr.empty()) { SmallVector Groups; if (!Splitter.match(Substr, &Groups)) break; if (Groups[2].size() > 0) { Words.push_back(Groups[1]); Substr = Substr.substr(Groups[0].size()); } else if (Groups[3].size() > 0) { Words.push_back(Groups[3]); Substr = Substr.substr(Groups[0].size() - Groups[4].size()); } else if (Groups[5].size() > 0) { Words.push_back(Groups[5]); Substr = Substr.substr(Groups[0].size() - Groups[6].size()); } } } if (Words.empty()) return Name; std::string Fixup; switch (Case) { case IdentifierNamingCheck::CT_AnyCase: Fixup += Name; break; case IdentifierNamingCheck::CT_LowerCase: for (auto const &Word : Words) { if (&Word != &Words.front()) Fixup += "_"; Fixup += Word.lower(); } break; case IdentifierNamingCheck::CT_UpperCase: for (auto const &Word : Words) { if (&Word != &Words.front()) Fixup += "_"; Fixup += Word.upper(); } break; case IdentifierNamingCheck::CT_CamelCase: for (auto const &Word : Words) { Fixup += Word.substr(0, 1).upper(); Fixup += Word.substr(1).lower(); } break; case IdentifierNamingCheck::CT_CamelBack: for (auto const &Word : Words) { if (&Word == &Words.front()) { Fixup += Word.lower(); } else { Fixup += Word.substr(0, 1).upper(); Fixup += Word.substr(1).lower(); } } break; case IdentifierNamingCheck::CT_CamelSnakeCase: for (auto const &Word : Words) { if (&Word != &Words.front()) Fixup += "_"; Fixup += Word.substr(0, 1).upper(); Fixup += Word.substr(1).lower(); } break; case IdentifierNamingCheck::CT_CamelSnakeBack: for (auto const &Word : Words) { if (&Word != &Words.front()) { Fixup += "_"; Fixup += Word.substr(0, 1).upper(); } else { Fixup += Word.substr(0, 1).lower(); } Fixup += Word.substr(1).lower(); } break; } return Fixup; } static std::string fixupWithStyle(StringRef Name, const IdentifierNamingCheck::NamingStyle &Style) { const std::string Fixed = fixupWithCase( Name, Style.Case.getValueOr(IdentifierNamingCheck::CaseType::CT_AnyCase)); StringRef Mid = StringRef(Fixed).trim("_"); if (Mid.empty()) Mid = "_"; return (Style.Prefix + Mid + Style.Suffix).str(); } static StyleKind findStyleKind( const NamedDecl *D, const std::vector> &NamingStyles) { assert(D && D->getIdentifier() && !D->getName().empty() && !D->isImplicit() && "Decl must be an explicit identifier with a name."); if (isa(D) && NamingStyles[SK_ObjcIvar]) return SK_ObjcIvar; if (isa(D) && NamingStyles[SK_Typedef]) return SK_Typedef; if (isa(D) && NamingStyles[SK_TypeAlias]) return SK_TypeAlias; if (const auto *Decl = dyn_cast(D)) { if (Decl->isAnonymousNamespace()) return SK_Invalid; if (Decl->isInline() && NamingStyles[SK_InlineNamespace]) return SK_InlineNamespace; if (NamingStyles[SK_Namespace]) return SK_Namespace; } if (isa(D) && NamingStyles[SK_Enum]) return SK_Enum; if (isa(D)) { if (NamingStyles[SK_EnumConstant]) return SK_EnumConstant; if (NamingStyles[SK_Constant]) return SK_Constant; return SK_Invalid; } if (const auto *Decl = dyn_cast(D)) { if (Decl->isAnonymousStructOrUnion()) return SK_Invalid; if (!Decl->getCanonicalDecl()->isThisDeclarationADefinition()) return SK_Invalid; if (Decl->hasDefinition() && Decl->isAbstract() && NamingStyles[SK_AbstractClass]) return SK_AbstractClass; if (Decl->isStruct() && NamingStyles[SK_Struct]) return SK_Struct; if (Decl->isStruct() && NamingStyles[SK_Class]) return SK_Class; if (Decl->isClass() && NamingStyles[SK_Class]) return SK_Class; if (Decl->isClass() && NamingStyles[SK_Struct]) return SK_Struct; if (Decl->isUnion() && NamingStyles[SK_Union]) return SK_Union; if (Decl->isEnum() && NamingStyles[SK_Enum]) return SK_Enum; return SK_Invalid; } if (const auto *Decl = dyn_cast(D)) { QualType Type = Decl->getType(); if (!Type.isNull() && Type.isConstQualified()) { if (NamingStyles[SK_ConstantMember]) return SK_ConstantMember; if (NamingStyles[SK_Constant]) return SK_Constant; } if (Decl->getAccess() == AS_private && NamingStyles[SK_PrivateMember]) return SK_PrivateMember; if (Decl->getAccess() == AS_protected && NamingStyles[SK_ProtectedMember]) return SK_ProtectedMember; if (Decl->getAccess() == AS_public && NamingStyles[SK_PublicMember]) return SK_PublicMember; if (NamingStyles[SK_Member]) return SK_Member; return SK_Invalid; } if (const auto *Decl = dyn_cast(D)) { QualType Type = Decl->getType(); if (Decl->isConstexpr() && NamingStyles[SK_ConstexprVariable]) return SK_ConstexprVariable; if (!Type.isNull() && Type.isConstQualified()) { if (Type.getTypePtr()->isAnyPointerType() && NamingStyles[SK_ConstantPointerParameter]) return SK_ConstantPointerParameter; if (NamingStyles[SK_ConstantParameter]) return SK_ConstantParameter; if (NamingStyles[SK_Constant]) return SK_Constant; } if (Decl->isParameterPack() && NamingStyles[SK_ParameterPack]) return SK_ParameterPack; if (!Type.isNull() && Type.getTypePtr()->isAnyPointerType() && NamingStyles[SK_PointerParameter]) return SK_PointerParameter; if (NamingStyles[SK_Parameter]) return SK_Parameter; return SK_Invalid; } if (const auto *Decl = dyn_cast(D)) { QualType Type = Decl->getType(); if (Decl->isConstexpr() && NamingStyles[SK_ConstexprVariable]) return SK_ConstexprVariable; if (!Type.isNull() && Type.isConstQualified()) { if (Decl->isStaticDataMember() && NamingStyles[SK_ClassConstant]) return SK_ClassConstant; if (Decl->isFileVarDecl() && Type.getTypePtr()->isAnyPointerType() && NamingStyles[SK_GlobalConstantPointer]) return SK_GlobalConstantPointer; if (Decl->isFileVarDecl() && NamingStyles[SK_GlobalConstant]) return SK_GlobalConstant; if (Decl->isStaticLocal() && NamingStyles[SK_StaticConstant]) return SK_StaticConstant; if (Decl->isLocalVarDecl() && Type.getTypePtr()->isAnyPointerType() && NamingStyles[SK_LocalConstantPointer]) return SK_LocalConstantPointer; if (Decl->isLocalVarDecl() && NamingStyles[SK_LocalConstant]) return SK_LocalConstant; if (Decl->isFunctionOrMethodVarDecl() && NamingStyles[SK_LocalConstant]) return SK_LocalConstant; if (NamingStyles[SK_Constant]) return SK_Constant; } if (Decl->isStaticDataMember() && NamingStyles[SK_ClassMember]) return SK_ClassMember; if (Decl->isFileVarDecl() && Type.getTypePtr()->isAnyPointerType() && NamingStyles[SK_GlobalPointer]) return SK_GlobalPointer; if (Decl->isFileVarDecl() && NamingStyles[SK_GlobalVariable]) return SK_GlobalVariable; if (Decl->isStaticLocal() && NamingStyles[SK_StaticVariable]) return SK_StaticVariable; if (Decl->isLocalVarDecl() && Type.getTypePtr()->isAnyPointerType() && NamingStyles[SK_LocalPointer]) return SK_LocalPointer; if (Decl->isLocalVarDecl() && NamingStyles[SK_LocalVariable]) return SK_LocalVariable; if (Decl->isFunctionOrMethodVarDecl() && NamingStyles[SK_LocalVariable]) return SK_LocalVariable; if (NamingStyles[SK_Variable]) return SK_Variable; return SK_Invalid; } if (const auto *Decl = dyn_cast(D)) { if (Decl->isMain() || !Decl->isUserProvided() || Decl->size_overridden_methods() > 0) return SK_Invalid; if (Decl->isConstexpr() && NamingStyles[SK_ConstexprMethod]) return SK_ConstexprMethod; if (Decl->isConstexpr() && NamingStyles[SK_ConstexprFunction]) return SK_ConstexprFunction; if (Decl->isStatic() && NamingStyles[SK_ClassMethod]) return SK_ClassMethod; if (Decl->isVirtual() && NamingStyles[SK_VirtualMethod]) return SK_VirtualMethod; if (Decl->getAccess() == AS_private && NamingStyles[SK_PrivateMethod]) return SK_PrivateMethod; if (Decl->getAccess() == AS_protected && NamingStyles[SK_ProtectedMethod]) return SK_ProtectedMethod; if (Decl->getAccess() == AS_public && NamingStyles[SK_PublicMethod]) return SK_PublicMethod; if (NamingStyles[SK_Method]) return SK_Method; if (NamingStyles[SK_Function]) return SK_Function; return SK_Invalid; } if (const auto *Decl = dyn_cast(D)) { if (Decl->isMain()) return SK_Invalid; if (Decl->isConstexpr() && NamingStyles[SK_ConstexprFunction]) return SK_ConstexprFunction; if (Decl->isGlobal() && NamingStyles[SK_GlobalFunction]) return SK_GlobalFunction; if (NamingStyles[SK_Function]) return SK_Function; } if (isa(D)) { if (NamingStyles[SK_TypeTemplateParameter]) return SK_TypeTemplateParameter; if (NamingStyles[SK_TemplateParameter]) return SK_TemplateParameter; return SK_Invalid; } if (isa(D)) { if (NamingStyles[SK_ValueTemplateParameter]) return SK_ValueTemplateParameter; if (NamingStyles[SK_TemplateParameter]) return SK_TemplateParameter; return SK_Invalid; } if (isa(D)) { if (NamingStyles[SK_TemplateTemplateParameter]) return SK_TemplateTemplateParameter; if (NamingStyles[SK_TemplateParameter]) return SK_TemplateParameter; return SK_Invalid; } return SK_Invalid; } static void addUsage(IdentifierNamingCheck::NamingCheckFailureMap &Failures, const IdentifierNamingCheck::NamingCheckId &Decl, SourceRange Range, SourceManager *SourceMgr = nullptr) { // Do nothing if the provided range is invalid. if (Range.getBegin().isInvalid() || Range.getEnd().isInvalid()) return; // If we have a source manager, use it to convert to the spelling location for // performing the fix. This is necessary because macros can map the same // spelling location to different source locations, and we only want to fix // the token once, before it is expanded by the macro. SourceLocation FixLocation = Range.getBegin(); if (SourceMgr) FixLocation = SourceMgr->getSpellingLoc(FixLocation); if (FixLocation.isInvalid()) return; // Try to insert the identifier location in the Usages map, and bail out if it // is already in there auto &Failure = Failures[Decl]; if (!Failure.RawUsageLocs.insert(FixLocation.getRawEncoding()).second) return; if (!Failure.ShouldFix) return; Failure.ShouldFix = utils::rangeCanBeFixed(Range, SourceMgr); } /// Convenience method when the usage to be added is a NamedDecl static void addUsage(IdentifierNamingCheck::NamingCheckFailureMap &Failures, const NamedDecl *Decl, SourceRange Range, SourceManager *SourceMgr = nullptr) { return addUsage(Failures, IdentifierNamingCheck::NamingCheckId(Decl->getLocation(), Decl->getNameAsString()), Range, SourceMgr); } void IdentifierNamingCheck::check(const MatchFinder::MatchResult &Result) { if (const auto *Decl = Result.Nodes.getNodeAs("classRef")) { if (Decl->isImplicit()) return; addUsage(NamingCheckFailures, Decl->getParent(), Decl->getNameInfo().getSourceRange()); for (const auto *Init : Decl->inits()) { if (!Init->isWritten() || Init->isInClassMemberInitializer()) continue; if (const auto *FD = Init->getAnyMember()) addUsage(NamingCheckFailures, FD, SourceRange(Init->getMemberLocation())); // Note: delegating constructors and base class initializers are handled // via the "typeLoc" matcher. } return; } if (const auto *Decl = Result.Nodes.getNodeAs("classRef")) { if (Decl->isImplicit()) return; SourceRange Range = Decl->getNameInfo().getSourceRange(); if (Range.getBegin().isInvalid()) return; // The first token that will be found is the ~ (or the equivalent trigraph), // we want instead to replace the next token, that will be the identifier. Range.setBegin(CharSourceRange::getTokenRange(Range).getEnd()); addUsage(NamingCheckFailures, Decl->getParent(), Range); return; } if (const auto *Loc = Result.Nodes.getNodeAs("typeLoc")) { NamedDecl *Decl = nullptr; if (const auto &Ref = Loc->getAs()) { Decl = Ref.getDecl(); } else if (const auto &Ref = Loc->getAs()) { Decl = Ref.getDecl(); } else if (const auto &Ref = Loc->getAs()) { Decl = Ref.getDecl(); } else if (const auto &Ref = Loc->getAs()) { Decl = Ref.getDecl(); } if (Decl) { addUsage(NamingCheckFailures, Decl, Loc->getSourceRange()); return; } if (const auto &Ref = Loc->getAs()) { const auto *Decl = Ref.getTypePtr()->getTemplateName().getAsTemplateDecl(); SourceRange Range(Ref.getTemplateNameLoc(), Ref.getTemplateNameLoc()); if (const auto *ClassDecl = dyn_cast(Decl)) { if (const auto *TemplDecl = ClassDecl->getTemplatedDecl()) addUsage(NamingCheckFailures, TemplDecl, Range); return; } } if (const auto &Ref = Loc->getAs()) { if (const auto *Decl = Ref.getTypePtr()->getAsTagDecl()) addUsage(NamingCheckFailures, Decl, Loc->getSourceRange()); return; } } if (const auto *Loc = Result.Nodes.getNodeAs("nestedNameLoc")) { if (NestedNameSpecifier *Spec = Loc->getNestedNameSpecifier()) { if (NamespaceDecl *Decl = Spec->getAsNamespace()) { addUsage(NamingCheckFailures, Decl, Loc->getLocalSourceRange()); return; } } } if (const auto *Decl = Result.Nodes.getNodeAs("using")) { for (const auto &Shadow : Decl->shadows()) { addUsage(NamingCheckFailures, Shadow->getTargetDecl(), Decl->getNameInfo().getSourceRange()); } return; } if (const auto *DeclRef = Result.Nodes.getNodeAs("declRef")) { SourceRange Range = DeclRef->getNameInfo().getSourceRange(); addUsage(NamingCheckFailures, DeclRef->getDecl(), Range, Result.SourceManager); return; } if (const auto *Decl = Result.Nodes.getNodeAs("decl")) { if (!Decl->getIdentifier() || Decl->getName().empty() || Decl->isImplicit()) return; // Fix type aliases in value declarations if (const auto *Value = Result.Nodes.getNodeAs("decl")) { if (const auto *Typedef = Value->getType().getTypePtr()->getAs()) { addUsage(NamingCheckFailures, Typedef->getDecl(), Value->getSourceRange()); } } // Fix type aliases in function declarations if (const auto *Value = Result.Nodes.getNodeAs("decl")) { if (const auto *Typedef = Value->getReturnType().getTypePtr()->getAs()) { addUsage(NamingCheckFailures, Typedef->getDecl(), Value->getSourceRange()); } for (unsigned i = 0; i < Value->getNumParams(); ++i) { if (const auto *Typedef = Value->parameters()[i] ->getType() .getTypePtr() ->getAs()) { addUsage(NamingCheckFailures, Typedef->getDecl(), Value->getSourceRange()); } } } // Ignore ClassTemplateSpecializationDecl which are creating duplicate // replacements with CXXRecordDecl if (isa(Decl)) return; StyleKind SK = findStyleKind(Decl, NamingStyles); if (SK == SK_Invalid) return; if (!NamingStyles[SK]) return; const NamingStyle &Style = *NamingStyles[SK]; StringRef Name = Decl->getName(); if (matchesStyle(Name, Style)) return; std::string KindName = fixupWithCase(StyleNames[SK], CT_LowerCase); std::replace(KindName.begin(), KindName.end(), '_', ' '); std::string Fixup = fixupWithStyle(Name, Style); if (StringRef(Fixup).equals(Name)) { if (!IgnoreFailedSplit) { LLVM_DEBUG(llvm::dbgs() << Decl->getBeginLoc().printToString(*Result.SourceManager) << llvm::format(": unable to split words for %s '%s'\n", KindName.c_str(), Name.str().c_str())); } } else { NamingCheckFailure &Failure = NamingCheckFailures[NamingCheckId( Decl->getLocation(), Decl->getNameAsString())]; SourceRange Range = DeclarationNameInfo(Decl->getDeclName(), Decl->getLocation()) .getSourceRange(); Failure.Fixup = std::move(Fixup); Failure.KindName = std::move(KindName); addUsage(NamingCheckFailures, Decl, Range); } } } void IdentifierNamingCheck::checkMacro(SourceManager &SourceMgr, const Token &MacroNameTok, const MacroInfo *MI) { if (!NamingStyles[SK_MacroDefinition]) return; StringRef Name = MacroNameTok.getIdentifierInfo()->getName(); const NamingStyle &Style = *NamingStyles[SK_MacroDefinition]; if (matchesStyle(Name, Style)) return; std::string KindName = fixupWithCase(StyleNames[SK_MacroDefinition], CT_LowerCase); std::replace(KindName.begin(), KindName.end(), '_', ' '); std::string Fixup = fixupWithStyle(Name, Style); if (StringRef(Fixup).equals(Name)) { if (!IgnoreFailedSplit) { LLVM_DEBUG(llvm::dbgs() << MacroNameTok.getLocation().printToString(SourceMgr) << llvm::format(": unable to split words for %s '%s'\n", KindName.c_str(), Name.str().c_str())); } } else { NamingCheckId ID(MI->getDefinitionLoc(), Name); NamingCheckFailure &Failure = NamingCheckFailures[ID]; SourceRange Range(MacroNameTok.getLocation(), MacroNameTok.getEndLoc()); Failure.Fixup = std::move(Fixup); Failure.KindName = std::move(KindName); addUsage(NamingCheckFailures, ID, Range); } } void IdentifierNamingCheck::expandMacro(const Token &MacroNameTok, const MacroInfo *MI) { StringRef Name = MacroNameTok.getIdentifierInfo()->getName(); NamingCheckId ID(MI->getDefinitionLoc(), Name); auto Failure = NamingCheckFailures.find(ID); if (Failure == NamingCheckFailures.end()) return; SourceRange Range(MacroNameTok.getLocation(), MacroNameTok.getEndLoc()); addUsage(NamingCheckFailures, ID, Range); } void IdentifierNamingCheck::onEndOfTranslationUnit() { for (const auto &Pair : NamingCheckFailures) { const NamingCheckId &Decl = Pair.first; const NamingCheckFailure &Failure = Pair.second; if (Failure.KindName.empty()) continue; if (Failure.ShouldFix) { auto Diag = diag(Decl.first, "invalid case style for %0 '%1'") << Failure.KindName << Decl.second; for (const auto &Loc : Failure.RawUsageLocs) { // We assume that the identifier name is made of one token only. This is // always the case as we ignore usages in macros that could build // identifier names by combining multiple tokens. // // For destructors, we alread take care of it by remembering the // location of the start of the identifier and not the start of the // tilde. // // Other multi-token identifiers, such as operators are not checked at // all. Diag << FixItHint::CreateReplacement( SourceRange(SourceLocation::getFromRawEncoding(Loc)), Failure.Fixup); } } } } } // namespace readability } // namespace tidy } // namespace clang