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 LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IDENTIFIERNAMINGCHECK_H
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 {
FileStyleFileStyle128     FileStyle() : IsActive(false), IgnoreMainLikeFunctions(false) {}
FileStyleFileStyle129     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 
getStylesFileStyle136     ArrayRef<std::optional<NamingStyle>> getStyles() const {
137       assert(IsActive);
138       return Styles;
139     }
140 
getHNOptionFileStyle141     const HungarianNotationOption &getHNOption() const {
142       assert(IsActive);
143       return HNOption;
144     }
145 
isActiveFileStyle146     bool isActive() const { return IsActive; }
isIgnoringMainLikeFunctionFileStyle147     bool isIgnoringMainLikeFunction() const { return IgnoreMainLikeFunctions; }
148 
isCheckingAnonFieldInParentScopeFileStyle149     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 
209   /// Find the style kind of a field in an anonymous record.
210   StyleKind findStyleKindForAnonField(
211       const FieldDecl *AnonField,
212       ArrayRef<std::optional<NamingStyle>> NamingStyles) const;
213 
214   StyleKind findStyleKindForField(
215       const FieldDecl *Field, QualType Type,
216       ArrayRef<std::optional<NamingStyle>> NamingStyles) const;
217 
218   StyleKind
219   findStyleKindForVar(const VarDecl *Var, QualType Type,
220                       ArrayRef<std::optional<NamingStyle>> NamingStyles) const;
221 
222   /// Stores the style options as a vector, indexed by the specified \ref
223   /// StyleKind, for a given directory.
224   mutable llvm::StringMap<FileStyle> NamingStylesCache;
225   FileStyle *MainFileStyle;
226   ClangTidyContext *Context;
227   const bool GetConfigPerFile;
228   const bool IgnoreFailedSplit;
229   HungarianNotation HungarianNotation;
230 };
231 
232 } // namespace readability
233 template <>
234 struct OptionEnumMapping<readability::IdentifierNamingCheck::CaseType> {
235   static llvm::ArrayRef<
236       std::pair<readability::IdentifierNamingCheck::CaseType, StringRef>>
237   getEnumMapping();
238 };
239 } // namespace clang::tidy
240 
241 #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IDENTIFIERNAMINGCHECK_H
242