summaryrefslogtreecommitdiffstats
path: root/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp
diff options
context:
space:
mode:
authorAlexander Kornienko <alexfh@google.com>2015-08-19 11:15:36 +0000
committerAlexander Kornienko <alexfh@google.com>2015-08-19 11:15:36 +0000
commit76c288062adfada1401c0b613a398519a3a1edd1 (patch)
tree53d48b0d59689d185cd25e61bf42b99a04c7f4da /clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp
parentdee4a867be1431eb0a25fc87c4960637acfaea2d (diff)
downloadbcm5719-llvm-76c288062adfada1401c0b613a398519a3a1edd1.tar.gz
bcm5719-llvm-76c288062adfada1401c0b613a398519a3a1edd1.zip
[clang-tidy] Add new IdentifierNaming check
This check will try to enforce coding guidelines on the identifiers naming. It supports lower_case, UPPER_CASE, camelBack and CamelCase casing and tries to convert from one to another if a mismatch is detected. It also supports a fixed prefix and suffix that will be prepended or appended to the identifiers, regardless of the casing. Many configuration options are available, in order to be able to create different rules for different kind of identifier. In general, the rules are falling back to a more generic rule if the specific case is not configured. http://reviews.llvm.org/D10933 Patch by Beren Minor! llvm-svn: 245429
Diffstat (limited to 'clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp')
-rw-r--r--clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp587
1 files changed, 587 insertions, 0 deletions
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
OpenPOWER on IntegriCloud