diff options
Diffstat (limited to 'clang-tools-extra/clang-tidy')
4 files changed, 664 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt index 52486203712..1cdb368d554 100644 --- a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt @@ -5,6 +5,7 @@ add_clang_library(clangTidyReadabilityModule ContainerSizeEmptyCheck.cpp ElseAfterReturnCheck.cpp FunctionSizeCheck.cpp + IdentifierNamingCheck.cpp NamedParameterCheck.cpp NamespaceCommentCheck.cpp ReadabilityTidyModule.cpp diff --git a/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp b/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp new file mode 100644 index 00000000000..84fcb821de0 --- /dev/null +++ b/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp @@ -0,0 +1,587 @@ +//===--- IdentifierNamingCheck.cpp - clang-tidy ---------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "IdentifierNamingCheck.h" + +#include "llvm/Support/Debug.h" +#include "llvm/Support/Format.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +#define DEBUG_TYPE "clang-tidy" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace readability { + +#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(GlobalVariable) \ + m(LocalConstant) \ + m(LocalVariable) \ + m(StaticConstant) \ + m(StaticVariable) \ + m(Constant) \ + m(Variable) \ + m(ConstantParameter) \ + m(ParameterPack) \ + m(Parameter) \ + 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) \ + +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 + +IdentifierNamingCheck::IdentifierNamingCheck(StringRef Name, + ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) { + auto const fromString = [](StringRef Str) { + return llvm::StringSwitch<CaseType>(Str) + .Case("lower_case", CT_LowerCase) + .Case("UPPER_CASE", CT_UpperCase) + .Case("camelBack", CT_CamelBack) + .Case("CamelCase", CT_CamelCase) + .Default(CT_AnyCase); + }; + + for (auto const &Name : StyleNames) { + NamingStyles.push_back( + NamingStyle(fromString(Options.get((Name + "Case").str(), "")), + Options.get((Name + "Prefix").str(), ""), + Options.get((Name + "Suffix").str(), ""))); + } + + 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"; + } + + llvm_unreachable("Unknown Case Type"); + }; + + for (size_t i = 0; i < SK_Count; ++i) { + 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) { +// FIXME: For now, only Decl and DeclRefExpr nodes are visited for checking and +// replacement. There is a lot of missing cases, such as references to a class +// name (as in 'const int CMyClass::kClassConstant = 4;'), to an enclosing +// context (namespace, class, etc). + + Finder->addMatcher(namedDecl().bind("decl"), this); + Finder->addMatcher(declRefExpr().bind("declref"), 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]*$"), + }; + + 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; + + if (!Matchers[static_cast<size_t>(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<StringRef, 8> Substrs; + Name.split(Substrs, "_", -1, false); + + SmallVector<StringRef, 8> Words; + for (auto Substr : Substrs) { + while (!Substr.empty()) { + SmallVector<StringRef, 8> 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; + } + + return Fixup; +} + +static std::string fixupWithStyle(StringRef Name, + IdentifierNamingCheck::NamingStyle Style) { + return Style.Prefix + fixupWithCase(Name, Style.Case) + Style.Suffix; +} + +static StyleKind findStyleKind( + const NamedDecl *D, + const std::vector<IdentifierNamingCheck::NamingStyle> &NamingStyles) { + if (isa<TypedefDecl>(D) && NamingStyles[SK_Typedef].isSet()) + return SK_Typedef; + + if (const auto *Decl = dyn_cast<NamespaceDecl>(D)) { + if (Decl->isAnonymousNamespace()) + return SK_Invalid; + + if (Decl->isInline() && NamingStyles[SK_InlineNamespace].isSet()) + return SK_InlineNamespace; + + if (NamingStyles[SK_Namespace].isSet()) + return SK_Namespace; + } + + if (isa<EnumDecl>(D) && NamingStyles[SK_Enum].isSet()) + return SK_Enum; + + if (isa<EnumConstantDecl>(D)) { + if (NamingStyles[SK_EnumConstant].isSet()) + return SK_EnumConstant; + + if (NamingStyles[SK_Constant].isSet()) + return SK_Constant; + + return SK_Invalid; + } + + if (const auto *Decl = dyn_cast<CXXRecordDecl>(D)) { + if (Decl->isAnonymousStructOrUnion()) + return SK_Invalid; + + if (Decl->hasDefinition() && Decl->isAbstract() && + NamingStyles[SK_AbstractClass].isSet()) + return SK_AbstractClass; + + if (Decl->isStruct() && NamingStyles[SK_Struct].isSet()) + return SK_Struct; + + if (Decl->isStruct() && NamingStyles[SK_Class].isSet()) + return SK_Class; + + if (Decl->isClass() && NamingStyles[SK_Class].isSet()) + return SK_Class; + + if (Decl->isClass() && NamingStyles[SK_Struct].isSet()) + return SK_Struct; + + if (Decl->isUnion() && NamingStyles[SK_Union].isSet()) + return SK_Union; + + if (Decl->isEnum() && NamingStyles[SK_Enum].isSet()) + return SK_Enum; + + return SK_Invalid; + } + + if (const auto *Decl = dyn_cast<FieldDecl>(D)) { + QualType Type = Decl->getType(); + + if (!Type.isNull() && Type.isLocalConstQualified() && + NamingStyles[SK_ConstantMember].isSet()) + return SK_ConstantMember; + + if (!Type.isNull() && Type.isLocalConstQualified() && + NamingStyles[SK_Constant].isSet()) + return SK_Constant; + + if (Decl->getAccess() == AS_private && + NamingStyles[SK_PrivateMember].isSet()) + return SK_PrivateMember; + + if (Decl->getAccess() == AS_protected && + NamingStyles[SK_ProtectedMember].isSet()) + return SK_ProtectedMember; + + if (Decl->getAccess() == AS_public && NamingStyles[SK_PublicMember].isSet()) + return SK_PublicMember; + + if (NamingStyles[SK_Member].isSet()) + return SK_Member; + + return SK_Invalid; + } + + if (const auto *Decl = dyn_cast<ParmVarDecl>(D)) { + QualType Type = Decl->getType(); + + if (Decl->isConstexpr() && NamingStyles[SK_ConstexprVariable].isSet()) + return SK_ConstexprVariable; + + if (!Type.isNull() && Type.isLocalConstQualified() && + NamingStyles[SK_ConstantParameter].isSet()) + return SK_ConstantParameter; + + if (!Type.isNull() && Type.isLocalConstQualified() && + NamingStyles[SK_Constant].isSet()) + return SK_Constant; + + if (Decl->isParameterPack() && NamingStyles[SK_ParameterPack].isSet()) + return SK_ParameterPack; + + if (NamingStyles[SK_Parameter].isSet()) + return SK_Parameter; + + return SK_Invalid; + } + + if (const auto *Decl = dyn_cast<VarDecl>(D)) { + QualType Type = Decl->getType(); + + if (Decl->isConstexpr() && NamingStyles[SK_ConstexprVariable].isSet()) + return SK_ConstexprVariable; + + if (!Type.isNull() && Type.isLocalConstQualified() && + Decl->isStaticDataMember() && NamingStyles[SK_ClassConstant].isSet()) + return SK_ClassConstant; + + if (!Type.isNull() && Type.isLocalConstQualified() && + Decl->isFileVarDecl() && NamingStyles[SK_GlobalConstant].isSet()) + return SK_GlobalConstant; + + if (!Type.isNull() && Type.isLocalConstQualified() && + Decl->isStaticLocal() && NamingStyles[SK_StaticConstant].isSet()) + return SK_StaticConstant; + + if (!Type.isNull() && Type.isLocalConstQualified() && + Decl->isLocalVarDecl() && NamingStyles[SK_LocalConstant].isSet()) + return SK_LocalConstant; + + if (!Type.isNull() && Type.isLocalConstQualified() && + Decl->isFunctionOrMethodVarDecl() && + NamingStyles[SK_LocalConstant].isSet()) + return SK_LocalConstant; + + if (!Type.isNull() && Type.isLocalConstQualified() && + NamingStyles[SK_Constant].isSet()) + return SK_Constant; + + if (Decl->isStaticDataMember() && NamingStyles[SK_ClassMember].isSet()) + return SK_ClassMember; + + if (Decl->isFileVarDecl() && NamingStyles[SK_GlobalVariable].isSet()) + return SK_GlobalVariable; + + if (Decl->isStaticLocal() && NamingStyles[SK_StaticVariable].isSet()) + return SK_StaticVariable; + + if (Decl->isLocalVarDecl() && NamingStyles[SK_LocalVariable].isSet()) + return SK_LocalVariable; + + if (Decl->isFunctionOrMethodVarDecl() && + NamingStyles[SK_LocalVariable].isSet()) + return SK_LocalVariable; + + if (NamingStyles[SK_Variable].isSet()) + return SK_Variable; + + return SK_Invalid; + } + + if (const auto *Decl = dyn_cast<CXXMethodDecl>(D)) { + if (Decl->isMain() || !Decl->isUserProvided() || + Decl->isUsualDeallocationFunction() || + Decl->isCopyAssignmentOperator() || Decl->isMoveAssignmentOperator() || + Decl->size_overridden_methods() > 0) + return SK_Invalid; + + if (Decl->isConstexpr() && NamingStyles[SK_ConstexprMethod].isSet()) + return SK_ConstexprMethod; + + if (Decl->isConstexpr() && NamingStyles[SK_ConstexprFunction].isSet()) + return SK_ConstexprFunction; + + if (Decl->isStatic() && NamingStyles[SK_ClassMethod].isSet()) + return SK_ClassMethod; + + if (Decl->isVirtual() && NamingStyles[SK_VirtualMethod].isSet()) + return SK_VirtualMethod; + + if (Decl->getAccess() == AS_private && + NamingStyles[SK_PrivateMethod].isSet()) + return SK_PrivateMethod; + + if (Decl->getAccess() == AS_protected && + NamingStyles[SK_ProtectedMethod].isSet()) + return SK_ProtectedMethod; + + if (Decl->getAccess() == AS_public && NamingStyles[SK_PublicMethod].isSet()) + return SK_PublicMethod; + + if (NamingStyles[SK_Method].isSet()) + return SK_Method; + + if (NamingStyles[SK_Function].isSet()) + return SK_Function; + + return SK_Invalid; + } + + if (const auto *Decl = dyn_cast<FunctionDecl>(D)) { + if (Decl->isMain()) + return SK_Invalid; + + if (Decl->isConstexpr() && NamingStyles[SK_ConstexprFunction].isSet()) + return SK_ConstexprFunction; + + if (Decl->isGlobal() && NamingStyles[SK_GlobalFunction].isSet()) + return SK_GlobalFunction; + + if (NamingStyles[SK_Function].isSet()) + return SK_Function; + } + + if (isa<TemplateTypeParmDecl>(D)) { + if (NamingStyles[SK_TypeTemplateParameter].isSet()) + return SK_TypeTemplateParameter; + + if (NamingStyles[SK_TemplateParameter].isSet()) + return SK_TemplateParameter; + + return SK_Invalid; + } + + if (isa<NonTypeTemplateParmDecl>(D)) { + if (NamingStyles[SK_ValueTemplateParameter].isSet()) + return SK_ValueTemplateParameter; + + if (NamingStyles[SK_TemplateParameter].isSet()) + return SK_TemplateParameter; + + return SK_Invalid; + } + + if (isa<TemplateTemplateParmDecl>(D)) { + if (NamingStyles[SK_TemplateTemplateParameter].isSet()) + return SK_TemplateTemplateParameter; + + if (NamingStyles[SK_TemplateParameter].isSet()) + return SK_TemplateParameter; + + return SK_Invalid; + } + + return SK_Invalid; +} + +void IdentifierNamingCheck::check(const MatchFinder::MatchResult &Result) { + if (const auto *DeclRef = Result.Nodes.getNodeAs<DeclRefExpr>("declref")) { + auto It = NamingCheckFailures.find(DeclRef->getDecl()); + if (It == NamingCheckFailures.end()) + return; + + NamingCheckFailure &Failure = It->second; + SourceRange Range = DeclRef->getNameInfo().getSourceRange(); + + Failure.Usages.push_back(Range); + Failure.ShouldFix = Failure.ShouldFix && + Result.SourceManager->isInMainFile(Range.getBegin()) && + Result.SourceManager->isInMainFile(Range.getEnd()) && + !Range.getBegin().isMacroID() && + !Range.getEnd().isMacroID(); + + return; + } + + if (const auto *Decl = Result.Nodes.getNodeAs<NamedDecl>("decl")) { + if (!Decl->getIdentifier() || Decl->getName().empty() || Decl->isImplicit()) + return; + + StyleKind SK = findStyleKind(Decl, NamingStyles); + if (SK == SK_Invalid) + return; + + 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) { + DEBUG(llvm::dbgs() << Decl->getLocStart().printToString( + *Result.SourceManager) + << format(": unable to split words for %s '%s'\n", + KindName.c_str(), Name)); + } + } else { + NamingCheckFailure &Failure = NamingCheckFailures[Decl]; + SourceRange Range = + DeclarationNameInfo(Decl->getDeclName(), Decl->getLocation()) + .getSourceRange(); + + Failure.Fixup = std::move(Fixup); + Failure.KindName = std::move(KindName); + Failure.ShouldFix = + Failure.ShouldFix && + Result.SourceManager->isInMainFile(Range.getBegin()) && + Result.SourceManager->isInMainFile(Range.getEnd()) && + !Range.getBegin().isMacroID() && !Range.getEnd().isMacroID(); + } + } +} + +void IdentifierNamingCheck::onEndOfTranslationUnit() { + for (const auto &Pair : NamingCheckFailures) { + const NamedDecl &Decl = *Pair.first; + const NamingCheckFailure &Failure = Pair.second; + + SourceRange DeclRange = + DeclarationNameInfo(Decl.getDeclName(), Decl.getLocation()) + .getSourceRange(); + auto Diag = diag(Decl.getLocStart(), "invalid case style for %0 '%1'") + << Failure.KindName << Decl.getName(); + + if (Failure.ShouldFix) { + Diag << FixItHint::CreateReplacement( + CharSourceRange::getTokenRange(DeclRange), Failure.Fixup); + + for (const auto &Range : Failure.Usages) { + Diag << FixItHint::CreateReplacement( + CharSourceRange::getTokenRange(Range), Failure.Fixup); + } + } + } +} + +} // namespace readability +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.h b/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.h new file mode 100644 index 00000000000..048ba750911 --- /dev/null +++ b/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.h @@ -0,0 +1,73 @@ +//===--- IdentifierNamingCheck.h - clang-tidy -------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IDENTIFIERNAMINGCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IDENTIFIERNAMINGCHECK_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace readability { + +/// \brief Checks for identifiers naming style mismatch. +class IdentifierNamingCheck : public ClangTidyCheck { +public: + IdentifierNamingCheck(StringRef Name, ClangTidyContext *Context); + + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + void onEndOfTranslationUnit() override; + + enum CaseType { + CT_AnyCase = 0, + CT_LowerCase, + CT_CamelBack, + CT_UpperCase, + CT_CamelCase, + }; + + struct NamingStyle { + NamingStyle() : Case(CT_AnyCase) {} + + NamingStyle(CaseType Case, const std::string &Prefix, + const std::string &Suffix) + : Case(Case), Prefix(Prefix), Suffix(Suffix) {} + + CaseType Case; + std::string Prefix; + std::string Suffix; + + bool isSet() const { + return !(Case == CT_AnyCase && Prefix.empty() && Suffix.empty()); + } + }; + +private: + std::vector<NamingStyle> NamingStyles; + bool IgnoreFailedSplit; + + struct NamingCheckFailure { + std::string KindName; + std::string Fixup; + bool ShouldFix; + std::vector<SourceRange> Usages; + + NamingCheckFailure() : ShouldFix(true) {} + }; + + llvm::DenseMap<const NamedDecl *, NamingCheckFailure> NamingCheckFailures; +}; + +} // namespace readability +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IDENTIFIERNAMINGCHECK_H diff --git a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp index bf118f88ace..dd37e34fdd3 100644 --- a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp @@ -14,6 +14,7 @@ #include "ContainerSizeEmptyCheck.h" #include "ElseAfterReturnCheck.h" #include "FunctionSizeCheck.h" +#include "IdentifierNamingCheck.h" #include "NamedParameterCheck.h" #include "RedundantSmartptrGetCheck.h" #include "RedundantStringCStrCheck.h" @@ -35,6 +36,8 @@ public: "readability-else-after-return"); CheckFactories.registerCheck<FunctionSizeCheck>( "readability-function-size"); + CheckFactories.registerCheck<IdentifierNamingCheck>( + "readability-identifier-naming"); CheckFactories.registerCheck<readability::NamedParameterCheck>( "readability-named-parameter"); CheckFactories.registerCheck<RedundantSmartptrGetCheck>( |

