| //===--- 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 "../GlobList.h" |
| #include "clang/AST/CXXInheritance.h" |
| #include "clang/Lex/PPCallbacks.h" |
| #include "clang/Lex/Preprocessor.h" |
| #include "llvm/ADT/ArrayRef.h" |
| #include "llvm/ADT/DenseMapInfo.h" |
| #include "llvm/Support/Debug.h" |
| #include "llvm/Support/Error.h" |
| #include "llvm/Support/FormatVariadic.h" |
| #include "llvm/Support/Path.h" |
| #include "llvm/Support/Regex.h" |
| #include "llvm/Support/YAMLParser.h" |
| |
| #define DEBUG_TYPE "clang-tidy" |
| |
| // FixItHint |
| |
| using namespace clang::ast_matchers; |
| |
| namespace clang { |
| namespace tidy { |
| |
| llvm::ArrayRef< |
| std::pair<readability::IdentifierNamingCheck::CaseType, StringRef>> |
| OptionEnumMapping< |
| readability::IdentifierNamingCheck::CaseType>::getEnumMapping() { |
| static constexpr std::pair<readability::IdentifierNamingCheck::CaseType, |
| StringRef> |
| Mapping[] = { |
| {readability::IdentifierNamingCheck::CT_AnyCase, "aNy_CasE"}, |
| {readability::IdentifierNamingCheck::CT_LowerCase, "lower_case"}, |
| {readability::IdentifierNamingCheck::CT_UpperCase, "UPPER_CASE"}, |
| {readability::IdentifierNamingCheck::CT_CamelBack, "camelBack"}, |
| {readability::IdentifierNamingCheck::CT_CamelCase, "CamelCase"}, |
| {readability::IdentifierNamingCheck::CT_CamelSnakeCase, |
| "Camel_Snake_Case"}, |
| {readability::IdentifierNamingCheck::CT_CamelSnakeBack, |
| "camel_Snake_Back"}}; |
| return llvm::makeArrayRef(Mapping); |
| } |
| |
| template <> |
| struct OptionEnumMapping< |
| readability::IdentifierNamingCheck::HungarianPrefixType> { |
| using HungarianPrefixType = |
| readability::IdentifierNamingCheck::HungarianPrefixType; |
| static llvm::ArrayRef<std::pair<HungarianPrefixType, StringRef>> |
| getEnumMapping() { |
| static constexpr std::pair<HungarianPrefixType, StringRef> Mapping[] = { |
| {HungarianPrefixType::HPT_Off, "Off"}, |
| {HungarianPrefixType::HPT_On, "On"}, |
| {HungarianPrefixType::HPT_LowerCase, "LowerCase"}, |
| {HungarianPrefixType::HPT_CamelCase, "CamelCase"}}; |
| return llvm::makeArrayRef(Mapping); |
| } |
| }; |
| |
| namespace readability { |
| |
| // clang-format off |
| #define NAMING_KEYS(m) \ |
| m(Namespace) \ |
| m(InlineNamespace) \ |
| m(EnumConstant) \ |
| m(ScopedEnumConstant) \ |
| 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 : int { |
| #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 |
| }; |
| |
| #define HUNGARIAN_NOTATION_PRIMITIVE_TYPES(m) \ |
| m(int8_t) \ |
| m(int16_t) \ |
| m(int32_t) \ |
| m(int64_t) \ |
| m(uint8_t) \ |
| m(uint16_t) \ |
| m(uint32_t) \ |
| m(uint64_t) \ |
| m(char8_t) \ |
| m(char16_t) \ |
| m(char32_t) \ |
| m(float) \ |
| m(double) \ |
| m(char) \ |
| m(bool) \ |
| m(_Bool) \ |
| m(int) \ |
| m(size_t) \ |
| m(wchar_t) \ |
| m(short-int) \ |
| m(short) \ |
| m(signed-int) \ |
| m(signed-short) \ |
| m(signed-short-int) \ |
| m(signed-long-long-int) \ |
| m(signed-long-long) \ |
| m(signed-long-int) \ |
| m(signed-long) \ |
| m(signed) \ |
| m(unsigned-long-long-int) \ |
| m(unsigned-long-long) \ |
| m(unsigned-long-int) \ |
| m(unsigned-long) \ |
| m(unsigned-short-int) \ |
| m(unsigned-short) \ |
| m(unsigned-int) \ |
| m(unsigned) \ |
| m(long-long-int) \ |
| m(long-double) \ |
| m(long-long) \ |
| m(long-int) \ |
| m(long) \ |
| m(ptrdiff_t) \ |
| |
| static StringRef const HungarainNotationPrimitiveTypes[] = { |
| #define STRINGIZE(v) #v, |
| HUNGARIAN_NOTATION_PRIMITIVE_TYPES(STRINGIZE) |
| #undef STRINGIZE |
| }; |
| |
| #define HUNGARIAN_NOTATION_USER_DEFINED_TYPES(m) \ |
| m(BOOL) \ |
| m(BOOLEAN) \ |
| m(BYTE) \ |
| m(CHAR) \ |
| m(UCHAR) \ |
| m(SHORT) \ |
| m(USHORT) \ |
| m(WORD) \ |
| m(DWORD) \ |
| m(DWORD32) \ |
| m(DWORD64) \ |
| m(LONG) \ |
| m(ULONG) \ |
| m(ULONG32) \ |
| m(ULONG64) \ |
| m(ULONGLONG) \ |
| m(HANDLE) \ |
| m(INT) \ |
| m(INT8) \ |
| m(INT16) \ |
| m(INT32) \ |
| m(INT64) \ |
| m(UINT) \ |
| m(UINT8) \ |
| m(UINT16) \ |
| m(UINT32) \ |
| m(UINT64) \ |
| m(PVOID) \ |
| |
| static StringRef const HungarainNotationUserDefinedTypes[] = { |
| #define STRINGIZE(v) #v, |
| HUNGARIAN_NOTATION_USER_DEFINED_TYPES(STRINGIZE) |
| #undef STRINGIZE |
| }; |
| |
| |
| #undef NAMING_KEYS |
| // clang-format on |
| |
| IdentifierNamingCheck::NamingStyle::NamingStyle( |
| llvm::Optional<IdentifierNamingCheck::CaseType> Case, |
| const std::string &Prefix, const std::string &Suffix, |
| const std::string &IgnoredRegexpStr, HungarianPrefixType HPType) |
| : Case(Case), Prefix(Prefix), Suffix(Suffix), |
| IgnoredRegexpStr(IgnoredRegexpStr), HPType(HPType) { |
| if (!IgnoredRegexpStr.empty()) { |
| IgnoredRegexp = |
| llvm::Regex(llvm::SmallString<128>({"^", IgnoredRegexpStr, "$"})); |
| if (!IgnoredRegexp.isValid()) |
| llvm::errs() << "Invalid IgnoredRegexp regular expression: " |
| << IgnoredRegexpStr; |
| } |
| } |
| |
| IdentifierNamingCheck::FileStyle IdentifierNamingCheck::getFileStyleFromOptions( |
| const ClangTidyCheck::OptionsView &Options) const { |
| IdentifierNamingCheck::HungarianNotationOption HNOption; |
| |
| HungarianNotation.loadDefaultConfig(HNOption); |
| HungarianNotation.loadFileConfig(Options, HNOption); |
| |
| SmallVector<llvm::Optional<IdentifierNamingCheck::NamingStyle>, 0> Styles; |
| Styles.resize(SK_Count); |
| SmallString<64> StyleString; |
| for (unsigned I = 0; I < SK_Count; ++I) { |
| StyleString = StyleNames[I]; |
| size_t StyleSize = StyleString.size(); |
| |
| StyleString.append("HungarianPrefix"); |
| auto HPTOpt = |
| Options.get<IdentifierNamingCheck::HungarianPrefixType>(StyleString); |
| if (!HungarianNotation.checkOptionValid(I, StyleString) && |
| HPTOpt.hasValue()) |
| configurationDiag("invalid identifier naming option '%0'") << StyleString; |
| StyleString.resize(StyleSize); |
| |
| StyleString.append("IgnoredRegexp"); |
| std::string IgnoredRegexpStr = Options.get(StyleString, ""); |
| StyleString.resize(StyleSize); |
| StyleString.append("Prefix"); |
| std::string Prefix(Options.get(StyleString, "")); |
| // Fast replacement of [Pre]fix -> [Suf]fix. |
| memcpy(&StyleString[StyleSize], "Suf", 3); |
| std::string Postfix(Options.get(StyleString, "")); |
| memcpy(&StyleString[StyleSize], "Case", 4); |
| StyleString.pop_back(); |
| StyleString.pop_back(); |
| auto CaseOptional = |
| Options.get<IdentifierNamingCheck::CaseType>(StyleString); |
| |
| if (CaseOptional || !Prefix.empty() || !Postfix.empty() || |
| !IgnoredRegexpStr.empty() || HPTOpt) |
| Styles[I].emplace(std::move(CaseOptional), std::move(Prefix), |
| std::move(Postfix), std::move(IgnoredRegexpStr), |
| HPTOpt.getValueOr(IdentifierNamingCheck::HPT_Off)); |
| } |
| bool IgnoreMainLike = Options.get("IgnoreMainLikeFunctions", false); |
| return {std::move(Styles), std::move(HNOption), IgnoreMainLike}; |
| } |
| |
| std::string IdentifierNamingCheck::HungarianNotation::getDeclTypeName( |
| const NamedDecl *ND) const { |
| const auto *VD = dyn_cast<ValueDecl>(ND); |
| if (!VD) |
| return {}; |
| |
| if (isa<FunctionDecl, EnumConstantDecl>(ND)) |
| return {}; |
| |
| // Get type text of variable declarations. |
| auto &SM = VD->getASTContext().getSourceManager(); |
| const char *Begin = SM.getCharacterData(VD->getBeginLoc()); |
| const char *End = SM.getCharacterData(VD->getEndLoc()); |
| intptr_t StrLen = End - Begin; |
| |
| // FIXME: Sometimes the value that returns from ValDecl->getEndLoc() |
| // is wrong(out of location of Decl). This causes `StrLen` will be assigned |
| // an unexpected large value. Current workaround to find the terminated |
| // character instead of the `getEndLoc()` function. |
| const char *EOL = strchr(Begin, '\n'); |
| if (!EOL) |
| EOL = Begin + strlen(Begin); |
| |
| const std::vector<const char *> PosList = { |
| strchr(Begin, '='), strchr(Begin, ';'), strchr(Begin, ','), |
| strchr(Begin, ')'), EOL}; |
| for (const auto &Pos : PosList) { |
| if (Pos > Begin) |
| EOL = std::min(EOL, Pos); |
| } |
| |
| StrLen = EOL - Begin; |
| std::string TypeName; |
| if (StrLen > 0) { |
| std::string Type(Begin, StrLen); |
| |
| static constexpr StringRef Keywords[] = { |
| // Constexpr specifiers |
| "constexpr", "constinit", "consteval", |
| // Qualifier |
| "const", "volatile", "restrict", "mutable", |
| // Storage class specifiers |
| "register", "static", "extern", "thread_local", |
| // Other keywords |
| "virtual"}; |
| |
| // Remove keywords |
| for (StringRef Kw : Keywords) { |
| for (size_t Pos = 0; |
| (Pos = Type.find(Kw.data(), Pos)) != std::string::npos;) { |
| Type.replace(Pos, Kw.size(), ""); |
| } |
| } |
| TypeName = Type.erase(0, Type.find_first_not_of(" ")); |
| |
| // Replace spaces with single space. |
| for (size_t Pos = 0; (Pos = Type.find(" ", Pos)) != std::string::npos; |
| Pos += strlen(" ")) { |
| Type.replace(Pos, strlen(" "), " "); |
| } |
| |
| // Replace " &" with "&". |
| for (size_t Pos = 0; (Pos = Type.find(" &", Pos)) != std::string::npos; |
| Pos += strlen("&")) { |
| Type.replace(Pos, strlen(" &"), "&"); |
| } |
| |
| // Replace " *" with "* ". |
| for (size_t Pos = 0; (Pos = Type.find(" *", Pos)) != std::string::npos; |
| Pos += strlen("*")) { |
| Type.replace(Pos, strlen(" *"), "* "); |
| } |
| |
| // Remove redundant tailing. |
| static constexpr StringRef TailsOfMultiWordType[] = { |
| " int", " char", " double", " long", " short"}; |
| bool RedundantRemoved = false; |
| for (auto Kw : TailsOfMultiWordType) { |
| size_t Pos = Type.rfind(Kw.data()); |
| if (Pos != std::string::npos) { |
| Type = Type.substr(0, Pos + Kw.size()); |
| RedundantRemoved = true; |
| break; |
| } |
| } |
| TypeName = Type.erase(0, Type.find_first_not_of(" ")); |
| if (!RedundantRemoved) { |
| std::size_t FoundSpace = Type.find(" "); |
| if (FoundSpace != std::string::npos) |
| Type = Type.substr(0, FoundSpace); |
| } |
| |
| TypeName = Type.erase(0, Type.find_first_not_of(" ")); |
| |
| QualType QT = VD->getType(); |
| if (!QT.isNull() && QT->isArrayType()) |
| TypeName.append("[]"); |
| } |
| |
| return TypeName; |
| } |
| |
| IdentifierNamingCheck::IdentifierNamingCheck(StringRef Name, |
| ClangTidyContext *Context) |
| : RenamerClangTidyCheck(Name, Context), Context(Context), CheckName(Name), |
| GetConfigPerFile(Options.get("GetConfigPerFile", true)), |
| IgnoreFailedSplit(Options.get("IgnoreFailedSplit", false)) { |
| |
| auto IterAndInserted = NamingStylesCache.try_emplace( |
| llvm::sys::path::parent_path(Context->getCurrentFile()), |
| getFileStyleFromOptions(Options)); |
| assert(IterAndInserted.second && "Couldn't insert Style"); |
| // Holding a reference to the data in the vector is safe as it should never |
| // move. |
| MainFileStyle = &IterAndInserted.first->getValue(); |
| } |
| |
| IdentifierNamingCheck::~IdentifierNamingCheck() = default; |
| |
| bool IdentifierNamingCheck::HungarianNotation::checkOptionValid( |
| int StyleKindIndex, StringRef StyleString) const { |
| if ((StyleKindIndex >= SK_EnumConstant) && |
| (StyleKindIndex <= SK_ConstantParameter)) |
| return true; |
| |
| if ((StyleKindIndex >= SK_Parameter) && (StyleKindIndex <= SK_Enum)) |
| return true; |
| |
| return false; |
| } |
| |
| bool IdentifierNamingCheck::HungarianNotation::isOptionEnabled( |
| StringRef OptionKey, const llvm::StringMap<std::string> &StrMap) const { |
| if (OptionKey.empty()) |
| return false; |
| |
| auto Iter = StrMap.find(OptionKey); |
| if (Iter == StrMap.end()) |
| return false; |
| |
| llvm::Optional<bool> Parsed = llvm::yaml::parseBool(Iter->getValue()); |
| return *Parsed; |
| } |
| |
| void IdentifierNamingCheck::HungarianNotation::loadFileConfig( |
| const ClangTidyCheck::OptionsView &Options, |
| IdentifierNamingCheck::HungarianNotationOption &HNOption) const { |
| |
| static constexpr StringRef HNOpts[] = {"TreatStructAsClass"}; |
| static constexpr StringRef HNDerivedTypes[] = {"Array", "Pointer", |
| "FunctionPointer"}; |
| |
| StringRef Section = "HungarianNotation."; |
| |
| SmallString<128> Buffer; |
| for (const auto &Opt : HNOpts) { |
| Buffer.assign({Section, "General.", Opt}); |
| std::string Val = Options.get(Buffer, ""); |
| if (!Val.empty()) |
| HNOption.General[Opt] = std::move(Val); |
| } |
| |
| for (const auto &Type : HNDerivedTypes) { |
| Buffer.assign({Section, "DerivedType.", Type}); |
| std::string Val = Options.get(Buffer, ""); |
| if (!Val.empty()) |
| HNOption.DerivedType[Type] = std::move(Val); |
| } |
| |
| static constexpr std::pair<StringRef, StringRef> HNCStrings[] = { |
| {"CharPrinter", "char*"}, |
| {"CharArray", "char[]"}, |
| {"WideCharPrinter", "wchar_t*"}, |
| {"WideCharArray", "wchar_t[]"}}; |
| |
| for (const auto &CStr : HNCStrings) { |
| Buffer.assign({Section, "CString.", CStr.first}); |
| std::string Val = Options.get(Buffer, ""); |
| if (!Val.empty()) |
| HNOption.CString[CStr.first] = std::move(Val); |
| } |
| |
| for (const auto &PrimType : HungarainNotationPrimitiveTypes) { |
| Buffer.assign({Section, "PrimitiveType.", PrimType}); |
| std::string Val = Options.get(Buffer, ""); |
| if (!Val.empty()) { |
| std::string Type = PrimType.str(); |
| std::replace(Type.begin(), Type.end(), '-', ' '); |
| HNOption.PrimitiveType[Type] = std::move(Val); |
| } |
| } |
| |
| for (const auto &Type : HungarainNotationUserDefinedTypes) { |
| Buffer.assign({Section, "UserDefinedType.", Type}); |
| std::string Val = Options.get(Buffer, ""); |
| if (!Val.empty()) |
| HNOption.UserDefinedType[Type] = std::move(Val); |
| } |
| } |
| |
| std::string IdentifierNamingCheck::HungarianNotation::getPrefix( |
| const Decl *D, |
| const IdentifierNamingCheck::HungarianNotationOption &HNOption) const { |
| if (!D) |
| return {}; |
| const auto *ND = dyn_cast<NamedDecl>(D); |
| if (!ND) |
| return {}; |
| |
| std::string Prefix; |
| if (const auto *ECD = dyn_cast<EnumConstantDecl>(ND)) { |
| Prefix = getEnumPrefix(ECD); |
| } else if (const auto *CRD = dyn_cast<CXXRecordDecl>(ND)) { |
| Prefix = getClassPrefix(CRD, HNOption); |
| } else if (isa<VarDecl, FieldDecl, RecordDecl>(ND)) { |
| std::string TypeName = getDeclTypeName(ND); |
| if (!TypeName.empty()) |
| Prefix = getDataTypePrefix(TypeName, ND, HNOption); |
| } |
| |
| return Prefix; |
| } |
| |
| bool IdentifierNamingCheck::HungarianNotation::removeDuplicatedPrefix( |
| SmallVector<StringRef, 8> &Words, |
| const IdentifierNamingCheck::HungarianNotationOption &HNOption) const { |
| if (Words.size() <= 1) |
| return true; |
| |
| std::string CorrectName = Words[0].str(); |
| std::vector<llvm::StringMap<std::string>> MapList = { |
| HNOption.CString, HNOption.DerivedType, HNOption.PrimitiveType, |
| HNOption.UserDefinedType}; |
| |
| for (const auto &Map : MapList) { |
| for (const auto &Str : Map) { |
| if (Str.getValue() == CorrectName) { |
| Words.erase(Words.begin(), Words.begin() + 1); |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| std::string IdentifierNamingCheck::HungarianNotation::getDataTypePrefix( |
| StringRef TypeName, const NamedDecl *ND, |
| const IdentifierNamingCheck::HungarianNotationOption &HNOption) const { |
| if (!ND || TypeName.empty()) |
| return TypeName.str(); |
| |
| std::string ModifiedTypeName(TypeName); |
| |
| // Derived types |
| std::string PrefixStr; |
| if (const auto *TD = dyn_cast<ValueDecl>(ND)) { |
| QualType QT = TD->getType(); |
| if (QT->isFunctionPointerType()) { |
| PrefixStr = HNOption.DerivedType.lookup("FunctionPointer"); |
| } else if (QT->isPointerType()) { |
| for (const auto &CStr : HNOption.CString) { |
| std::string Key = CStr.getKey().str(); |
| if (ModifiedTypeName.find(Key) == 0) { |
| PrefixStr = CStr.getValue(); |
| ModifiedTypeName = ModifiedTypeName.substr( |
| Key.size(), ModifiedTypeName.size() - Key.size()); |
| break; |
| } |
| } |
| } else if (QT->isArrayType()) { |
| for (const auto &CStr : HNOption.CString) { |
| std::string Key = CStr.getKey().str(); |
| if (ModifiedTypeName.find(Key) == 0) { |
| PrefixStr = CStr.getValue(); |
| break; |
| } |
| } |
| if (PrefixStr.empty()) |
| PrefixStr = HNOption.DerivedType.lookup("Array"); |
| } else if (QT->isReferenceType()) { |
| size_t Pos = ModifiedTypeName.find_last_of("&"); |
| if (Pos != std::string::npos) |
| ModifiedTypeName = ModifiedTypeName.substr(0, Pos); |
| } |
| } |
| |
| // Pointers |
| size_t PtrCount = [&](std::string TypeName) -> size_t { |
| size_t Pos = TypeName.find('*'); |
| size_t Count = 0; |
| for (; Pos < TypeName.length(); Pos++, Count++) { |
| if ('*' != TypeName[Pos]) |
| break; |
| } |
| return Count; |
| }(ModifiedTypeName); |
| if (PtrCount > 0) { |
| ModifiedTypeName = [&](std::string Str, StringRef From, StringRef To) { |
| size_t StartPos = 0; |
| while ((StartPos = Str.find(From.data(), StartPos)) != |
| std::string::npos) { |
| Str.replace(StartPos, From.size(), To.data()); |
| StartPos += To.size(); |
| } |
| return Str; |
| }(ModifiedTypeName, "*", ""); |
| } |
| |
| // Primitive types |
| if (PrefixStr.empty()) { |
| for (const auto &Type : HNOption.PrimitiveType) { |
| if (ModifiedTypeName == Type.getKey()) { |
| PrefixStr = Type.getValue(); |
| break; |
| } |
| } |
| } |
| |
| // User-Defined types |
| if (PrefixStr.empty()) { |
| for (const auto &Type : HNOption.UserDefinedType) { |
| if (ModifiedTypeName == Type.getKey()) { |
| PrefixStr = Type.getValue(); |
| break; |
| } |
| } |
| } |
| |
| for (size_t Idx = 0; Idx < PtrCount; Idx++) |
| PrefixStr.insert(0, HNOption.DerivedType.lookup("Pointer")); |
| |
| return PrefixStr; |
| } |
| |
| std::string IdentifierNamingCheck::HungarianNotation::getClassPrefix( |
| const CXXRecordDecl *CRD, |
| const IdentifierNamingCheck::HungarianNotationOption &HNOption) const { |
| |
| if (CRD->isUnion()) |
| return {}; |
| |
| if (CRD->isStruct() && |
| !isOptionEnabled("TreatStructAsClass", HNOption.General)) |
| return {}; |
| |
| return CRD->isAbstract() ? "I" : "C"; |
| } |
| |
| std::string IdentifierNamingCheck::HungarianNotation::getEnumPrefix( |
| const EnumConstantDecl *ECD) const { |
| std::string Name = ECD->getType().getAsString(); |
| if (std::string::npos != Name.find("enum")) { |
| Name = Name.substr(strlen("enum"), Name.length() - strlen("enum")); |
| Name = Name.erase(0, Name.find_first_not_of(" ")); |
| } |
| |
| static llvm::Regex Splitter( |
| "([a-z0-9A-Z]*)(_+)|([A-Z]?[a-z0-9]+)([A-Z]|$)|([A-Z]+)([A-Z]|$)"); |
| |
| StringRef EnumName(Name); |
| SmallVector<StringRef, 8> Substrs; |
| EnumName.split(Substrs, "_", -1, false); |
| |
| SmallVector<StringRef, 8> Words; |
| SmallVector<StringRef, 8> Groups; |
| for (auto Substr : Substrs) { |
| while (!Substr.empty()) { |
| Groups.clear(); |
| 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()); |
| } |
| } |
| } |
| |
| std::string Initial; |
| for (StringRef Word : Words) |
| Initial += tolower(Word[0]); |
| |
| return Initial; |
| } |
| |
| void IdentifierNamingCheck::HungarianNotation::loadDefaultConfig( |
| IdentifierNamingCheck::HungarianNotationOption &HNOption) const { |
| |
| // Options |
| static constexpr std::pair<StringRef, StringRef> General[] = { |
| {"TreatStructAsClass", "false"}}; |
| for (const auto &G : General) |
| HNOption.General.try_emplace(G.first, G.second); |
| |
| // Derived types |
| static constexpr std::pair<StringRef, StringRef> DerivedTypes[] = { |
| {"Array", "a"}, {"Pointer", "p"}, {"FunctionPointer", "fn"}}; |
| for (const auto &DT : DerivedTypes) |
| HNOption.DerivedType.try_emplace(DT.first, DT.second); |
| |
| // C strings |
| static constexpr std::pair<StringRef, StringRef> CStrings[] = { |
| {"char*", "sz"}, |
| {"char[]", "sz"}, |
| {"wchar_t*", "wsz"}, |
| {"wchar_t[]", "wsz"}}; |
| for (const auto &CStr : CStrings) |
| HNOption.CString.try_emplace(CStr.first, CStr.second); |
| |
| // clang-format off |
| static constexpr std::pair<StringRef, StringRef> PrimitiveTypes[] = { |
| {"int8_t", "i8" }, |
| {"int16_t", "i16" }, |
| {"int32_t", "i32" }, |
| {"int64_t", "i64" }, |
| {"uint8_t", "u8" }, |
| {"uint16_t", "u16" }, |
| {"uint32_t", "u32" }, |
| {"uint64_t", "u64" }, |
| {"char8_t", "c8" }, |
| {"char16_t", "c16" }, |
| {"char32_t", "c32" }, |
| {"float", "f" }, |
| {"double", "d" }, |
| {"char", "c" }, |
| {"bool", "b" }, |
| {"_Bool", "b" }, |
| {"int", "i" }, |
| {"size_t", "n" }, |
| {"wchar_t", "wc" }, |
| {"short int", "si" }, |
| {"short", "s" }, |
| {"signed int", "si" }, |
| {"signed short", "ss" }, |
| {"signed short int", "ssi" }, |
| {"signed long long int", "slli"}, |
| {"signed long long", "sll" }, |
| {"signed long int", "sli" }, |
| {"signed long", "sl" }, |
| {"signed", "s" }, |
| {"unsigned long long int", "ulli"}, |
| {"unsigned long long", "ull" }, |
| {"unsigned long int", "uli" }, |
| {"unsigned long", "ul" }, |
| {"unsigned short int", "usi" }, |
| {"unsigned short", "us" }, |
| {"unsigned int", "ui" }, |
| {"unsigned", "u" }, |
| {"long long int", "lli" }, |
| {"long double", "ld" }, |
| {"long long", "ll" }, |
| {"long int", "li" }, |
| {"long", "l" }, |
| {"ptrdiff_t", "p" }}; |
| // clang-format on |
| for (const auto &PT : PrimitiveTypes) |
| HNOption.PrimitiveType.try_emplace(PT.first, PT.second); |
| |
| // clang-format off |
| static constexpr std::pair<StringRef, StringRef> UserDefinedTypes[] = { |
| // Windows data types |
| {"BOOL", "b" }, |
| {"BOOLEAN", "b" }, |
| {"BYTE", "by" }, |
| {"CHAR", "c" }, |
| {"UCHAR", "uc" }, |
| {"SHORT", "s" }, |
| {"USHORT", "us" }, |
| {"WORD", "w" }, |
| {"DWORD", "dw" }, |
| {"DWORD32", "dw32"}, |
| {"DWORD64", "dw64"}, |
| {"LONG", "l" }, |
| {"ULONG", "ul" }, |
| {"ULONG32", "ul32"}, |
| {"ULONG64", "ul64"}, |
| {"ULONGLONG", "ull" }, |
| {"HANDLE", "h" }, |
| {"INT", "i" }, |
| {"INT8", "i8" }, |
| {"INT16", "i16" }, |
| {"INT32", "i32" }, |
| {"INT64", "i64" }, |
| {"UINT", "ui" }, |
| {"UINT8", "u8" }, |
| {"UINT16", "u16" }, |
| {"UINT32", "u32" }, |
| {"UINT64", "u64" }, |
| {"PVOID", "p" } }; |
| // clang-format on |
| for (const auto &UDT : UserDefinedTypes) |
| HNOption.UserDefinedType.try_emplace(UDT.first, UDT.second); |
| } |
| |
| void IdentifierNamingCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { |
| RenamerClangTidyCheck::storeOptions(Opts); |
| SmallString<64> StyleString; |
| ArrayRef<llvm::Optional<NamingStyle>> Styles = MainFileStyle->getStyles(); |
| for (size_t I = 0; I < SK_Count; ++I) { |
| if (!Styles[I]) |
| continue; |
| StyleString = StyleNames[I]; |
| size_t StyleSize = StyleString.size(); |
| |
| Options.store(Opts, (StyleString + "HungarianPrefix").str(), |
| Styles[I]->HPType); |
| |
| StyleString.append("IgnoredRegexp"); |
| Options.store(Opts, StyleString, Styles[I]->IgnoredRegexpStr); |
| StyleString.resize(StyleSize); |
| StyleString.append("Prefix"); |
| Options.store(Opts, StyleString, Styles[I]->Prefix); |
| // Fast replacement of [Pre]fix -> [Suf]fix. |
| memcpy(&StyleString[StyleSize], "Suf", 3); |
| Options.store(Opts, StyleString, Styles[I]->Suffix); |
| if (Styles[I]->Case) { |
| memcpy(&StyleString[StyleSize], "Case", 4); |
| StyleString.pop_back(); |
| StyleString.pop_back(); |
| Options.store(Opts, StyleString, *Styles[I]->Case); |
| } |
| } |
| Options.store(Opts, "GetConfigPerFile", GetConfigPerFile); |
| Options.store(Opts, "IgnoreFailedSplit", IgnoreFailedSplit); |
| Options.store(Opts, "IgnoreMainLikeFunctions", |
| MainFileStyle->isIgnoringMainLikeFunction()); |
| } |
| |
| bool IdentifierNamingCheck::matchesStyle( |
| StringRef Type, StringRef Name, |
| const IdentifierNamingCheck::NamingStyle &Style, |
| const IdentifierNamingCheck::HungarianNotationOption &HNOption, |
| const NamedDecl *Decl) const { |
| 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])?)*"), |
| }; |
| |
| if (!Name.consume_front(Style.Prefix)) |
| return false; |
| if (!Name.consume_back(Style.Suffix)) |
| return false; |
| if (IdentifierNamingCheck::HungarianPrefixType::HPT_Off != Style.HPType) { |
| std::string HNPrefix = HungarianNotation.getPrefix(Decl, HNOption); |
| if (!Name.consume_front(HNPrefix)) |
| return false; |
| } |
| |
| // Ensure the name doesn't have any extra underscores beyond those specified |
| // in the prefix and suffix. |
| if (Name.startswith("_") || Name.endswith("_")) |
| return false; |
| |
| if (Style.Case && !Matchers[static_cast<size_t>(*Style.Case)].match(Name)) |
| return false; |
| |
| return true; |
| } |
| |
| std::string IdentifierNamingCheck::fixupWithCase( |
| StringRef Type, StringRef Name, const Decl *D, |
| const IdentifierNamingCheck::NamingStyle &Style, |
| const IdentifierNamingCheck::HungarianNotationOption &HNOption, |
| IdentifierNamingCheck::CaseType Case) const { |
| 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; |
| SmallVector<StringRef, 8> Groups; |
| for (auto Substr : Substrs) { |
| while (!Substr.empty()) { |
| Groups.clear(); |
| 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.str(); |
| |
| if (IdentifierNamingCheck::HungarianPrefixType::HPT_Off != Style.HPType) { |
| HungarianNotation.removeDuplicatedPrefix(Words, HNOption); |
| } |
| |
| SmallString<128> Fixup; |
| switch (Case) { |
| case IdentifierNamingCheck::CT_AnyCase: |
| return Name.str(); |
| 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 += toupper(Word.front()); |
| Fixup += Word.substr(1).lower(); |
| } |
| break; |
| |
| case IdentifierNamingCheck::CT_CamelBack: |
| for (auto const &Word : Words) { |
| if (&Word == &Words.front()) { |
| Fixup += Word.lower(); |
| } else { |
| Fixup += toupper(Word.front()); |
| Fixup += Word.substr(1).lower(); |
| } |
| } |
| break; |
| |
| case IdentifierNamingCheck::CT_CamelSnakeCase: |
| for (auto const &Word : Words) { |
| if (&Word != &Words.front()) |
| Fixup += "_"; |
| Fixup += toupper(Word.front()); |
| Fixup += Word.substr(1).lower(); |
| } |
| break; |
| |
| case IdentifierNamingCheck::CT_CamelSnakeBack: |
| for (auto const &Word : Words) { |
| if (&Word != &Words.front()) { |
| Fixup += "_"; |
| Fixup += toupper(Word.front()); |
| } else { |
| Fixup += tolower(Word.front()); |
| } |
| Fixup += Word.substr(1).lower(); |
| } |
| break; |
| } |
| |
| return Fixup.str().str(); |
| } |
| |
| bool IdentifierNamingCheck::isParamInMainLikeFunction( |
| const ParmVarDecl &ParmDecl, bool IncludeMainLike) const { |
| const auto *FDecl = |
| dyn_cast_or_null<FunctionDecl>(ParmDecl.getParentFunctionOrMethod()); |
| if (!FDecl) |
| return false; |
| if (FDecl->isMain()) |
| return true; |
| if (!IncludeMainLike) |
| return false; |
| if (FDecl->getAccess() != AS_public && FDecl->getAccess() != AS_none) |
| return false; |
| // If the function doesn't have a name that's an identifier, can occur if the |
| // function is an operator overload, bail out early. |
| if (!FDecl->getDeclName().isIdentifier()) |
| return false; |
| enum MainType { None, Main, WMain }; |
| auto IsCharPtrPtr = [](QualType QType) -> MainType { |
| if (QType.isNull()) |
| return None; |
| if (QType = QType->getPointeeType(), QType.isNull()) |
| return None; |
| if (QType = QType->getPointeeType(), QType.isNull()) |
| return None; |
| if (QType->isCharType()) |
| return Main; |
| if (QType->isWideCharType()) |
| return WMain; |
| return None; |
| }; |
| auto IsIntType = [](QualType QType) { |
| if (QType.isNull()) |
| return false; |
| if (const auto *Builtin = |
| dyn_cast<BuiltinType>(QType->getUnqualifiedDesugaredType())) { |
| return Builtin->getKind() == BuiltinType::Int; |
| } |
| return false; |
| }; |
| if (!IsIntType(FDecl->getReturnType())) |
| return false; |
| if (FDecl->getNumParams() < 2 || FDecl->getNumParams() > 3) |
| return false; |
| if (!IsIntType(FDecl->parameters()[0]->getType())) |
| return false; |
| MainType Type = IsCharPtrPtr(FDecl->parameters()[1]->getType()); |
| if (Type == None) |
| return false; |
| if (FDecl->getNumParams() == 3 && |
| IsCharPtrPtr(FDecl->parameters()[2]->getType()) != Type) |
| return false; |
| |
| if (Type == Main) { |
| static llvm::Regex Matcher( |
| "(^[Mm]ain([_A-Z]|$))|([a-z0-9_]Main([_A-Z]|$))|(_main(_|$))"); |
| assert(Matcher.isValid() && "Invalid Matcher for main like functions."); |
| return Matcher.match(FDecl->getName()); |
| } |
| static llvm::Regex Matcher("(^((W[Mm])|(wm))ain([_A-Z]|$))|([a-z0-9_]W[Mm]" |
| "ain([_A-Z]|$))|(_wmain(_|$))"); |
| assert(Matcher.isValid() && "Invalid Matcher for wmain like functions."); |
| return Matcher.match(FDecl->getName()); |
| } |
| |
| std::string IdentifierNamingCheck::fixupWithStyle( |
| StringRef Type, StringRef Name, |
| const IdentifierNamingCheck::NamingStyle &Style, |
| const IdentifierNamingCheck::HungarianNotationOption &HNOption, |
| const Decl *D) const { |
| Name.consume_front(Style.Prefix); |
| Name.consume_back(Style.Suffix); |
| std::string Fixed = fixupWithCase( |
| Type, Name, D, Style, HNOption, |
| Style.Case.getValueOr(IdentifierNamingCheck::CaseType::CT_AnyCase)); |
| |
| std::string HungarianPrefix; |
| using HungarianPrefixType = IdentifierNamingCheck::HungarianPrefixType; |
| if (HungarianPrefixType::HPT_Off != Style.HPType) { |
| HungarianPrefix = HungarianNotation.getPrefix(D, HNOption); |
| if (!HungarianPrefix.empty()) { |
| if (Style.HPType == HungarianPrefixType::HPT_LowerCase) |
| HungarianPrefix += "_"; |
| |
| if (Style.HPType == HungarianPrefixType::HPT_CamelCase) |
| Fixed[0] = toupper(Fixed[0]); |
| } |
| } |
| StringRef Mid = StringRef(Fixed).trim("_"); |
| if (Mid.empty()) |
| Mid = "_"; |
| |
| return (Style.Prefix + HungarianPrefix + Mid + Style.Suffix).str(); |
| } |
| |
| StyleKind IdentifierNamingCheck::findStyleKind( |
| const NamedDecl *D, |
| ArrayRef<llvm::Optional<IdentifierNamingCheck::NamingStyle>> NamingStyles, |
| bool IgnoreMainLikeFunctions) const { |
| assert(D && D->getIdentifier() && !D->getName().empty() && !D->isImplicit() && |
| "Decl must be an explicit identifier with a name."); |
| |
| if (isa<ObjCIvarDecl>(D) && NamingStyles[SK_ObjcIvar]) |
| return SK_ObjcIvar; |
| |
| if (isa<TypedefDecl>(D) && NamingStyles[SK_Typedef]) |
| return SK_Typedef; |
| |
| if (isa<TypeAliasDecl>(D) && NamingStyles[SK_TypeAlias]) |
| return SK_TypeAlias; |
| |
| if (const auto *Decl = dyn_cast<NamespaceDecl>(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<EnumDecl>(D) && NamingStyles[SK_Enum]) |
| return SK_Enum; |
| |
| if (const auto *EnumConst = dyn_cast<EnumConstantDecl>(D)) { |
| if (cast<EnumDecl>(EnumConst->getDeclContext())->isScoped() && |
| NamingStyles[SK_ScopedEnumConstant]) |
| return SK_ScopedEnumConstant; |
| |
| if (NamingStyles[SK_EnumConstant]) |
| return SK_EnumConstant; |
| |
| if (NamingStyles[SK_Constant]) |
| return SK_Constant; |
| |
| return SK_Invalid; |
| } |
| |
| if (const auto *Decl = dyn_cast<CXXRecordDecl>(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<FieldDecl>(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<ParmVarDecl>(D)) { |
| if (isParamInMainLikeFunction(*Decl, IgnoreMainLikeFunctions)) |
| return SK_Invalid; |
| 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<VarDecl>(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<CXXMethodDecl>(D)) { |
| if (Decl->isMain() || !Decl->isUserProvided() || |
| Decl->size_overridden_methods() > 0 || Decl->hasAttr<OverrideAttr>()) |
| return SK_Invalid; |
| |
| // If this method has the same name as any base method, this is likely |
| // necessary even if it's not an override. e.g. CRTP. |
| for (const CXXBaseSpecifier &Base : Decl->getParent()->bases()) |
| if (const auto *RD = Base.getType()->getAsCXXRecordDecl()) |
| if (RD->hasMemberName(Decl->getDeclName())) |
| 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<FunctionDecl>(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<TemplateTypeParmDecl>(D)) { |
| if (NamingStyles[SK_TypeTemplateParameter]) |
| return SK_TypeTemplateParameter; |
| |
| if (NamingStyles[SK_TemplateParameter]) |
| return SK_TemplateParameter; |
| |
| return SK_Invalid; |
| } |
| |
| if (isa<NonTypeTemplateParmDecl>(D)) { |
| if (NamingStyles[SK_ValueTemplateParameter]) |
| return SK_ValueTemplateParameter; |
| |
| if (NamingStyles[SK_TemplateParameter]) |
| return SK_TemplateParameter; |
| |
| return SK_Invalid; |
| } |
| |
| if (isa<TemplateTemplateParmDecl>(D)) { |
| if (NamingStyles[SK_TemplateTemplateParameter]) |
| return SK_TemplateTemplateParameter; |
| |
| if (NamingStyles[SK_TemplateParameter]) |
| return SK_TemplateParameter; |
| |
| return SK_Invalid; |
| } |
| |
| return SK_Invalid; |
| } |
| |
| llvm::Optional<RenamerClangTidyCheck::FailureInfo> |
| IdentifierNamingCheck::getFailureInfo( |
| StringRef Type, StringRef Name, const NamedDecl *ND, |
| SourceLocation Location, |
| ArrayRef<llvm::Optional<IdentifierNamingCheck::NamingStyle>> NamingStyles, |
| const IdentifierNamingCheck::HungarianNotationOption &HNOption, |
| StyleKind SK, const SourceManager &SM, bool IgnoreFailedSplit) const { |
| if (SK == SK_Invalid || !NamingStyles[SK]) |
| return None; |
| |
| const IdentifierNamingCheck::NamingStyle &Style = *NamingStyles[SK]; |
| if (Style.IgnoredRegexp.isValid() && Style.IgnoredRegexp.match(Name)) |
| return None; |
| |
| if (matchesStyle(Type, Name, Style, HNOption, ND)) |
| return None; |
| |
| std::string KindName = |
| fixupWithCase(Type, StyleNames[SK], ND, Style, HNOption, |
| IdentifierNamingCheck::CT_LowerCase); |
| std::replace(KindName.begin(), KindName.end(), '_', ' '); |
| |
| std::string Fixup = fixupWithStyle(Type, Name, Style, HNOption, ND); |
| if (StringRef(Fixup).equals(Name)) { |
| if (!IgnoreFailedSplit) { |
| LLVM_DEBUG(Location.print(llvm::dbgs(), SM); |
| llvm::dbgs() |
| << llvm::formatv(": unable to split words for {0} '{1}'\n", |
| KindName, Name)); |
| } |
| return None; |
| } |
| return RenamerClangTidyCheck::FailureInfo{std::move(KindName), |
| std::move(Fixup)}; |
| } |
| |
| llvm::Optional<RenamerClangTidyCheck::FailureInfo> |
| IdentifierNamingCheck::getDeclFailureInfo(const NamedDecl *Decl, |
| const SourceManager &SM) const { |
| SourceLocation Loc = Decl->getLocation(); |
| const FileStyle &FileStyle = getStyleForFile(SM.getFilename(Loc)); |
| if (!FileStyle.isActive()) |
| return llvm::None; |
| |
| return getFailureInfo(HungarianNotation.getDeclTypeName(Decl), |
| Decl->getName(), Decl, Loc, FileStyle.getStyles(), |
| FileStyle.getHNOption(), |
| findStyleKind(Decl, FileStyle.getStyles(), |
| FileStyle.isIgnoringMainLikeFunction()), |
| SM, IgnoreFailedSplit); |
| } |
| |
| llvm::Optional<RenamerClangTidyCheck::FailureInfo> |
| IdentifierNamingCheck::getMacroFailureInfo(const Token &MacroNameTok, |
| const SourceManager &SM) const { |
| SourceLocation Loc = MacroNameTok.getLocation(); |
| const FileStyle &Style = getStyleForFile(SM.getFilename(Loc)); |
| if (!Style.isActive()) |
| return llvm::None; |
| |
| return getFailureInfo("", MacroNameTok.getIdentifierInfo()->getName(), NULL, |
| Loc, Style.getStyles(), Style.getHNOption(), |
| SK_MacroDefinition, SM, IgnoreFailedSplit); |
| } |
| |
| RenamerClangTidyCheck::DiagInfo |
| IdentifierNamingCheck::getDiagInfo(const NamingCheckId &ID, |
| const NamingCheckFailure &Failure) const { |
| return DiagInfo{"invalid case style for %0 '%1'", |
| [&](DiagnosticBuilder &Diag) { |
| Diag << Failure.Info.KindName << ID.second; |
| }}; |
| } |
| |
| const IdentifierNamingCheck::FileStyle & |
| IdentifierNamingCheck::getStyleForFile(StringRef FileName) const { |
| if (!GetConfigPerFile) |
| return *MainFileStyle; |
| StringRef Parent = llvm::sys::path::parent_path(FileName); |
| auto Iter = NamingStylesCache.find(Parent); |
| if (Iter != NamingStylesCache.end()) |
| return Iter->getValue(); |
| |
| ClangTidyOptions Options = Context->getOptionsForFile(FileName); |
| if (Options.Checks && GlobList(*Options.Checks).contains(CheckName)) { |
| auto It = NamingStylesCache.try_emplace( |
| Parent, |
| getFileStyleFromOptions({CheckName, Options.CheckOptions, Context})); |
| assert(It.second); |
| return It.first->getValue(); |
| } |
| // Default construction gives an empty style. |
| auto It = NamingStylesCache.try_emplace(Parent); |
| assert(It.second); |
| return It.first->getValue(); |
| } |
| |
| } // namespace readability |
| } // namespace tidy |
| } // namespace clang |