| 1 | //===--- IdentifierNamingCheck.h - clang-tidy -------------------*- C++ -*-===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IDENTIFIERNAMINGCHECK_H |
| 10 | #define |
| 11 | |
| 12 | #include "../utils/RenamerClangTidyCheck.h" |
| 13 | #include <optional> |
| 14 | #include <string> |
| 15 | namespace clang::tidy { |
| 16 | namespace readability { |
| 17 | |
| 18 | enum StyleKind : int; |
| 19 | |
| 20 | /// Checks for identifiers naming style mismatch. |
| 21 | /// |
| 22 | /// This check will try to enforce coding guidelines on the identifiers naming. |
| 23 | /// It supports `lower_case`, `UPPER_CASE`, `camelBack` and `CamelCase` casing |
| 24 | /// and tries to convert from one to another if a mismatch is detected. |
| 25 | /// |
| 26 | /// It also supports a fixed prefix and suffix that will be prepended or |
| 27 | /// appended to the identifiers, regardless of the casing. |
| 28 | /// |
| 29 | /// Many configuration options are available, in order to be able to create |
| 30 | /// different rules for different kind of identifier. In general, the |
| 31 | /// rules are falling back to a more generic rule if the specific case is not |
| 32 | /// configured. |
| 33 | class IdentifierNamingCheck final : public RenamerClangTidyCheck { |
| 34 | public: |
| 35 | IdentifierNamingCheck(StringRef Name, ClangTidyContext *Context); |
| 36 | ~IdentifierNamingCheck(); |
| 37 | |
| 38 | void storeOptions(ClangTidyOptions::OptionMap &Opts) override; |
| 39 | |
| 40 | enum CaseType { |
| 41 | CT_AnyCase = 0, |
| 42 | CT_LowerCase, |
| 43 | CT_CamelBack, |
| 44 | CT_UpperCase, |
| 45 | CT_CamelCase, |
| 46 | CT_CamelSnakeCase, |
| 47 | CT_CamelSnakeBack, |
| 48 | CT_LeadingUpperSnakeCase |
| 49 | }; |
| 50 | |
| 51 | enum HungarianPrefixType { |
| 52 | HPT_Off = 0, |
| 53 | HPT_On, |
| 54 | HPT_LowerCase, |
| 55 | HPT_CamelCase, |
| 56 | }; |
| 57 | |
| 58 | struct HungarianNotationOption { |
| 59 | HungarianNotationOption() = default; |
| 60 | |
| 61 | std::optional<CaseType> Case; |
| 62 | HungarianPrefixType HPType = HungarianPrefixType::HPT_Off; |
| 63 | llvm::StringMap<std::string> General; |
| 64 | llvm::StringMap<std::string> CString; |
| 65 | llvm::StringMap<std::string> PrimitiveType; |
| 66 | llvm::StringMap<std::string> UserDefinedType; |
| 67 | llvm::StringMap<std::string> DerivedType; |
| 68 | }; |
| 69 | |
| 70 | struct NamingStyle { |
| 71 | NamingStyle() = default; |
| 72 | |
| 73 | NamingStyle(std::optional<CaseType> Case, StringRef Prefix, |
| 74 | StringRef Suffix, StringRef IgnoredRegexpStr, |
| 75 | HungarianPrefixType HPType); |
| 76 | NamingStyle(const NamingStyle &O) = delete; |
| 77 | NamingStyle &operator=(NamingStyle &&O) = default; |
| 78 | NamingStyle(NamingStyle &&O) = default; |
| 79 | |
| 80 | std::optional<CaseType> Case; |
| 81 | std::string Prefix; |
| 82 | std::string Suffix; |
| 83 | // Store both compiled and non-compiled forms so original value can be |
| 84 | // serialized |
| 85 | llvm::Regex IgnoredRegexp; |
| 86 | std::string IgnoredRegexpStr; |
| 87 | |
| 88 | HungarianPrefixType HPType; |
| 89 | }; |
| 90 | |
| 91 | struct HungarianNotation { |
| 92 | public: |
| 93 | bool checkOptionValid(int StyleKindIndex) const; |
| 94 | bool isOptionEnabled(StringRef OptionKey, |
| 95 | const llvm::StringMap<std::string> &StrMap) const; |
| 96 | |
| 97 | size_t getAsteriskCount(const std::string &TypeName) const; |
| 98 | size_t getAsteriskCount(const std::string &TypeName, |
| 99 | const NamedDecl *ND) const; |
| 100 | |
| 101 | void loadDefaultConfig( |
| 102 | IdentifierNamingCheck::HungarianNotationOption &HNOption) const; |
| 103 | void loadFileConfig( |
| 104 | const ClangTidyCheck::OptionsView &Options, |
| 105 | IdentifierNamingCheck::HungarianNotationOption &HNOption) const; |
| 106 | |
| 107 | bool removeDuplicatedPrefix( |
| 108 | SmallVector<StringRef, 8> &Words, |
| 109 | const IdentifierNamingCheck::HungarianNotationOption &HNOption) const; |
| 110 | |
| 111 | std::string getPrefix( |
| 112 | const Decl *D, |
| 113 | const IdentifierNamingCheck::HungarianNotationOption &HNOption) const; |
| 114 | |
| 115 | std::string getDataTypePrefix( |
| 116 | StringRef TypeName, const NamedDecl *ND, |
| 117 | const IdentifierNamingCheck::HungarianNotationOption &HNOption) const; |
| 118 | |
| 119 | std::string getClassPrefix( |
| 120 | const CXXRecordDecl *CRD, |
| 121 | const IdentifierNamingCheck::HungarianNotationOption &HNOption) const; |
| 122 | |
| 123 | std::string getEnumPrefix(const EnumConstantDecl *ECD) const; |
| 124 | std::string getDeclTypeName(const NamedDecl *ND) const; |
| 125 | }; |
| 126 | |
| 127 | struct FileStyle { |
| 128 | FileStyle() : IsActive(false), IgnoreMainLikeFunctions(false) {} |
| 129 | FileStyle(SmallVectorImpl<std::optional<NamingStyle>> &&Styles, |
| 130 | HungarianNotationOption HNOption, bool IgnoreMainLike, |
| 131 | bool CheckAnonFieldInParent) |
| 132 | : Styles(std::move(Styles)), HNOption(std::move(HNOption)), |
| 133 | IsActive(true), IgnoreMainLikeFunctions(IgnoreMainLike), |
| 134 | CheckAnonFieldInParentScope(CheckAnonFieldInParent) {} |
| 135 | |
| 136 | ArrayRef<std::optional<NamingStyle>> getStyles() const { |
| 137 | assert(IsActive); |
| 138 | return Styles; |
| 139 | } |
| 140 | |
| 141 | const HungarianNotationOption &getHNOption() const { |
| 142 | assert(IsActive); |
| 143 | return HNOption; |
| 144 | } |
| 145 | |
| 146 | bool isActive() const { return IsActive; } |
| 147 | bool isIgnoringMainLikeFunction() const { return IgnoreMainLikeFunctions; } |
| 148 | |
| 149 | bool isCheckingAnonFieldInParentScope() const { |
| 150 | return CheckAnonFieldInParentScope; |
| 151 | } |
| 152 | |
| 153 | private: |
| 154 | SmallVector<std::optional<NamingStyle>, 0> Styles; |
| 155 | HungarianNotationOption HNOption; |
| 156 | bool IsActive; |
| 157 | bool IgnoreMainLikeFunctions; |
| 158 | bool CheckAnonFieldInParentScope; |
| 159 | }; |
| 160 | |
| 161 | IdentifierNamingCheck::FileStyle |
| 162 | getFileStyleFromOptions(const ClangTidyCheck::OptionsView &Options) const; |
| 163 | |
| 164 | bool |
| 165 | matchesStyle(StringRef Type, StringRef Name, |
| 166 | const IdentifierNamingCheck::NamingStyle &Style, |
| 167 | const IdentifierNamingCheck::HungarianNotationOption &HNOption, |
| 168 | const NamedDecl *Decl) const; |
| 169 | |
| 170 | std::string |
| 171 | fixupWithCase(StringRef Type, StringRef Name, const Decl *D, |
| 172 | const IdentifierNamingCheck::NamingStyle &Style, |
| 173 | const IdentifierNamingCheck::HungarianNotationOption &HNOption, |
| 174 | IdentifierNamingCheck::CaseType Case) const; |
| 175 | |
| 176 | std::string |
| 177 | fixupWithStyle(StringRef Type, StringRef Name, |
| 178 | const IdentifierNamingCheck::NamingStyle &Style, |
| 179 | const IdentifierNamingCheck::HungarianNotationOption &HNOption, |
| 180 | const Decl *D) const; |
| 181 | |
| 182 | StyleKind findStyleKind( |
| 183 | const NamedDecl *D, |
| 184 | ArrayRef<std::optional<IdentifierNamingCheck::NamingStyle>> NamingStyles, |
| 185 | bool IgnoreMainLikeFunctions, bool CheckAnonFieldInParentScope) const; |
| 186 | |
| 187 | std::optional<RenamerClangTidyCheck::FailureInfo> getFailureInfo( |
| 188 | StringRef Type, StringRef Name, const NamedDecl *ND, |
| 189 | SourceLocation Location, |
| 190 | ArrayRef<std::optional<IdentifierNamingCheck::NamingStyle>> NamingStyles, |
| 191 | const IdentifierNamingCheck::HungarianNotationOption &HNOption, |
| 192 | StyleKind SK, const SourceManager &SM, bool IgnoreFailedSplit) const; |
| 193 | |
| 194 | bool isParamInMainLikeFunction(const ParmVarDecl &ParmDecl, |
| 195 | bool IncludeMainLike) const; |
| 196 | |
| 197 | private: |
| 198 | std::optional<FailureInfo> |
| 199 | getDeclFailureInfo(const NamedDecl *Decl, |
| 200 | const SourceManager &SM) const override; |
| 201 | std::optional<FailureInfo> |
| 202 | getMacroFailureInfo(const Token &MacroNameTok, |
| 203 | const SourceManager &SM) const override; |
| 204 | DiagInfo getDiagInfo(const NamingCheckId &ID, |
| 205 | const NamingCheckFailure &Failure) const override; |
| 206 | |
| 207 | const FileStyle &getStyleForFile(StringRef FileName) const; |
| 208 | StringRef getRealFileName(StringRef FileName) const; |
| 209 | |
| 210 | /// Find the style kind of a field in an anonymous record. |
| 211 | StyleKind findStyleKindForAnonField( |
| 212 | const FieldDecl *AnonField, |
| 213 | ArrayRef<std::optional<NamingStyle>> NamingStyles) const; |
| 214 | |
| 215 | StyleKind findStyleKindForField( |
| 216 | const FieldDecl *Field, QualType Type, |
| 217 | ArrayRef<std::optional<NamingStyle>> NamingStyles) const; |
| 218 | |
| 219 | StyleKind |
| 220 | findStyleKindForVar(const VarDecl *Var, QualType Type, |
| 221 | ArrayRef<std::optional<NamingStyle>> NamingStyles) const; |
| 222 | |
| 223 | /// Stores the style options as a vector, indexed by the specified \ref |
| 224 | /// StyleKind, for a given directory. |
| 225 | mutable llvm::StringMap<FileStyle> NamingStylesCache; |
| 226 | mutable llvm::StringMap<SmallString<256U>> RealFileNameCache; |
| 227 | FileStyle *MainFileStyle; |
| 228 | ClangTidyContext *Context; |
| 229 | const bool GetConfigPerFile; |
| 230 | const bool IgnoreFailedSplit; |
| 231 | HungarianNotation HungarianNotation; |
| 232 | }; |
| 233 | |
| 234 | } // namespace readability |
| 235 | template <> |
| 236 | struct OptionEnumMapping<readability::IdentifierNamingCheck::CaseType> { |
| 237 | static llvm::ArrayRef< |
| 238 | std::pair<readability::IdentifierNamingCheck::CaseType, StringRef>> |
| 239 | getEnumMapping(); |
| 240 | }; |
| 241 | } // namespace clang::tidy |
| 242 | |
| 243 | #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IDENTIFIERNAMINGCHECK_H |
| 244 | |