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 SourceManager &SourceMgr, const Token &MacroNameTok,
112                   const MacroInfo *MI);
113 
114   /// Add a usage of a macro if it already has a violation.
115   void expandMacro(const Token &MacroNameTok, const MacroInfo *MI);
116 
117   void addUsage(const RenamerClangTidyCheck::NamingCheckId &Decl,
118                 SourceRange Range, const SourceManager *SourceMgr = nullptr);
119 
120   /// Convenience method when the usage to be added is a NamedDecl.
121   void addUsage(const NamedDecl *Decl, SourceRange Range,
122                 const SourceManager *SourceMgr = nullptr);
123 
124   void checkNamedDecl(const NamedDecl *Decl, const SourceManager &SourceMgr);
125 
126 protected:
127   /// Overridden by derived classes, returns information about if and how a Decl
128   /// failed the check. A 'std::nullopt' result means the Decl did not fail the
129   /// check.
130   virtual std::optional<FailureInfo>
131   getDeclFailureInfo(const NamedDecl *Decl, const SourceManager &SM) const = 0;
132 
133   /// Overridden by derived classes, returns information about if and how a
134   /// macro failed the check. A 'std::nullopt' result means the macro did not
135   /// fail the check.
136   virtual std::optional<FailureInfo>
137   getMacroFailureInfo(const Token &MacroNameTok,
138                       const SourceManager &SM) const = 0;
139 
140   /// Represents customized diagnostic text and how arguments should be applied.
141   /// Example usage:
142   ///
143   /// return DiagInfo{"my %1 very %2 special %3 text",
144   ///                  [=](DiagnosticBuilder &diag) {
145   ///                    diag << arg1 << arg2 << arg3;
146   ///                  }};
147   struct DiagInfo {
148     std::string Text;
149     llvm::unique_function<void(DiagnosticBuilder &)> ApplyArgs;
150   };
151 
152   /// Overridden by derived classes, returns a description of the diagnostic
153   /// that should be emitted for the given failure. The base class will then
154   /// further customize the diagnostic by adding info about whether the fix-it
155   /// can be automatically applied or not.
156   virtual DiagInfo getDiagInfo(const NamingCheckId &ID,
157                                const NamingCheckFailure &Failure) const = 0;
158 
159 private:
160   NamingCheckFailureMap NamingCheckFailures;
161   const bool AggressiveDependentMemberLookup;
162 };
163 
164 } // namespace tidy
165 } // namespace clang
166 
167 #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_RENAMERCLANGTIDYCHECK_H
168