1 //===--- RenamerClangTidyCheck.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_RENAMERCLANGTIDYCHECK_H
10 #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_RENAMERCLANGTIDYCHECK_H
11 
12 #include "../ClangTidyCheck.h"
13 #include "llvm/ADT/DenseMap.h"
14 #include "llvm/ADT/DenseSet.h"
15 #include "llvm/ADT/FunctionExtras.h"
16 #include <optional>
17 #include <string>
18 #include <utility>
19 
20 namespace clang {
21 
22 class MacroInfo;
23 
24 namespace tidy {
25 
26 /// Base class for clang-tidy checks that want to flag declarations and/or
27 /// macros for renaming based on customizable criteria.
28 class RenamerClangTidyCheck : public ClangTidyCheck {
29 public:
30   RenamerClangTidyCheck(StringRef CheckName, ClangTidyContext *Context);
31   ~RenamerClangTidyCheck();
32 
33   /// Derived classes should not implement any matching logic themselves; this
34   /// class will do the matching and call the derived class'
35   /// getDeclFailureInfo() and getMacroFailureInfo() for determining whether a
36   /// given identifier passes or fails the check.
37   void registerMatchers(ast_matchers::MatchFinder *Finder) final;
38   void check(const ast_matchers::MatchFinder::MatchResult &Result) final;
39   void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
40                            Preprocessor *ModuleExpanderPP) final;
41   void onEndOfTranslationUnit() final;
42 
43   /// Derived classes that override this function should call this method from
44   /// the overridden method.
45   void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
46 
47   /// This enum will be used in %select of the diagnostic message.
48   /// Each value below IgnoreFailureThreshold should have an error message.
49   enum class ShouldFixStatus {
50     ShouldFix,
51 
52     /// The fixup will conflict with a language keyword,
53     /// so we can't fix it automatically.
54     ConflictsWithKeyword,
55 
56     /// The fixup will conflict with a macro
57     /// definition, so we can't fix it
58     /// automatically.
59     ConflictsWithMacroDefinition,
60 
61     /// The fixup results in an identifier that is not a valid c/c++ identifier.
62     FixInvalidIdentifier,
63 
64     /// Values pass this threshold will be ignored completely
65     /// i.e no message, no fixup.
66     IgnoreFailureThreshold,
67 
68     /// If the identifier was used or declared within a macro we
69     /// won't offer a fixup for safety reasons.
70     InsideMacro,
71   };
72 
73   /// Information describing a failed check
74   struct FailureInfo {
75     std::string KindName; // Tag or misc info to be used as derived classes need
76     std::string Fixup;    // The name that will be proposed as a fix-it hint
77   };
78 
79   /// Holds an identifier name check failure, tracking the kind of the
80   /// identifier, its possible fixup and the starting locations of all the
81   /// identifier usages.
82   struct NamingCheckFailure {
83     FailureInfo Info;
84 
85     /// Whether the failure should be fixed or not.
86     ///
87     /// e.g.: if the identifier was used or declared within a macro we won't
88     /// offer a fixup for safety reasons.
shouldFixNamingCheckFailure89     bool shouldFix() const {
90       return FixStatus == ShouldFixStatus::ShouldFix && !Info.Fixup.empty();
91     }
92 
shouldNotifyNamingCheckFailure93     bool shouldNotify() const {
94       return FixStatus < ShouldFixStatus::IgnoreFailureThreshold;
95     }
96 
97     ShouldFixStatus FixStatus = ShouldFixStatus::ShouldFix;
98 
99     /// A set of all the identifier usages starting SourceLocation.
100     llvm::DenseSet<SourceLocation> RawUsageLocs;
101 
102     NamingCheckFailure() = default;
103   };
104 
105   using NamingCheckId = std::pair<SourceLocation, StringRef>;
106 
107   using NamingCheckFailureMap =
108       llvm::DenseMap<NamingCheckId, NamingCheckFailure>;
109 
110   /// Check Macros for style violations.
111   void checkMacro(const Token &MacroNameTok, const MacroInfo *MI,
112                   const SourceManager &SourceMgr);
113 
114   /// Add a usage of a macro if it already has a violation.
115   void expandMacro(const Token &MacroNameTok, const MacroInfo *MI,
116                    const SourceManager &SourceMgr);
117 
118   void addUsage(const NamedDecl *Decl, SourceRange Range,
119                 const SourceManager &SourceMgr);
120 
121 protected:
122   /// Overridden by derived classes, returns information about if and how a Decl
123   /// failed the check. A 'std::nullopt' result means the Decl did not fail the
124   /// check.
125   virtual std::optional<FailureInfo>
126   getDeclFailureInfo(const NamedDecl *Decl, const SourceManager &SM) const = 0;
127 
128   /// Overridden by derived classes, returns information about if and how a
129   /// macro failed the check. A 'std::nullopt' result means the macro did not
130   /// fail the check.
131   virtual std::optional<FailureInfo>
132   getMacroFailureInfo(const Token &MacroNameTok,
133                       const SourceManager &SM) const = 0;
134 
135   /// Represents customized diagnostic text and how arguments should be applied.
136   /// Example usage:
137   ///
138   /// return DiagInfo{"my %1 very %2 special %3 text",
139   ///                  [=](DiagnosticBuilder &diag) {
140   ///                    diag << arg1 << arg2 << arg3;
141   ///                  }};
142   struct DiagInfo {
143     std::string Text;
144     llvm::unique_function<void(DiagnosticBuilder &)> ApplyArgs;
145   };
146 
147   /// Overridden by derived classes, returns a description of the diagnostic
148   /// that should be emitted for the given failure. The base class will then
149   /// further customize the diagnostic by adding info about whether the fix-it
150   /// can be automatically applied or not.
151   virtual DiagInfo getDiagInfo(const NamingCheckId &ID,
152                                const NamingCheckFailure &Failure) const = 0;
153 
154 private:
155   // Manage additions to the Failure/usage map
156   //
157   // return the result of NamingCheckFailures::try_emplace() if the usage was
158   // accepted.
159   std::pair<NamingCheckFailureMap::iterator, bool>
160   addUsage(const RenamerClangTidyCheck::NamingCheckId &FailureId,
161            SourceRange UsageRange, const SourceManager &SourceMgr);
162 
163   NamingCheckFailureMap NamingCheckFailures;
164   const bool AggressiveDependentMemberLookup;
165 };
166 
167 } // namespace tidy
168 } // namespace clang
169 
170 #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_RENAMERCLANGTIDYCHECK_H
171