1 //===--- ClangTidyDiagnosticConsumer.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_CLANGTIDYDIAGNOSTICCONSUMER_H 10 #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H 11 12 #include "ClangTidyOptions.h" 13 #include "ClangTidyProfiling.h" 14 #include "clang/Basic/Diagnostic.h" 15 #include "clang/Tooling/Core/Diagnostic.h" 16 #include "llvm/ADT/DenseMap.h" 17 #include "llvm/Support/Regex.h" 18 19 namespace clang { 20 21 class ASTContext; 22 class CompilerInstance; 23 class SourceManager; 24 namespace ast_matchers { 25 class MatchFinder; 26 } 27 namespace tooling { 28 class CompilationDatabase; 29 } 30 31 namespace tidy { 32 33 /// A detected error complete with information to display diagnostic and 34 /// automatic fix. 35 /// 36 /// This is used as an intermediate format to transport Diagnostics without a 37 /// dependency on a SourceManager. 38 /// 39 /// FIXME: Make Diagnostics flexible enough to support this directly. 40 struct ClangTidyError : tooling::Diagnostic { 41 ClangTidyError(StringRef CheckName, Level DiagLevel, StringRef BuildDirectory, 42 bool IsWarningAsError); 43 44 bool IsWarningAsError; 45 std::vector<std::string> EnabledDiagnosticAliases; 46 }; 47 48 /// Contains displayed and ignored diagnostic counters for a ClangTidy 49 /// run. 50 struct ClangTidyStats { ClangTidyStatsClangTidyStats51 ClangTidyStats() 52 : ErrorsDisplayed(0), ErrorsIgnoredCheckFilter(0), ErrorsIgnoredNOLINT(0), 53 ErrorsIgnoredNonUserCode(0), ErrorsIgnoredLineFilter(0) {} 54 55 unsigned ErrorsDisplayed; 56 unsigned ErrorsIgnoredCheckFilter; 57 unsigned ErrorsIgnoredNOLINT; 58 unsigned ErrorsIgnoredNonUserCode; 59 unsigned ErrorsIgnoredLineFilter; 60 errorsIgnoredClangTidyStats61 unsigned errorsIgnored() const { 62 return ErrorsIgnoredNOLINT + ErrorsIgnoredCheckFilter + 63 ErrorsIgnoredNonUserCode + ErrorsIgnoredLineFilter; 64 } 65 }; 66 67 /// Every \c ClangTidyCheck reports errors through a \c DiagnosticsEngine 68 /// provided by this context. 69 /// 70 /// A \c ClangTidyCheck always has access to the active context to report 71 /// warnings like: 72 /// \code 73 /// Context->Diag(Loc, "Single-argument constructors must be explicit") 74 /// << FixItHint::CreateInsertion(Loc, "explicit "); 75 /// \endcode 76 class ClangTidyContext { 77 public: 78 /// Initializes \c ClangTidyContext instance. 79 ClangTidyContext(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider, 80 bool AllowEnablingAnalyzerAlphaCheckers = false); 81 /// Sets the DiagnosticsEngine that diag() will emit diagnostics to. 82 // FIXME: this is required initialization, and should be a constructor param. 83 // Fix the context -> diag engine -> consumer -> context initialization cycle. setDiagnosticsEngine(DiagnosticsEngine * DiagEngine)84 void setDiagnosticsEngine(DiagnosticsEngine *DiagEngine) { 85 this->DiagEngine = DiagEngine; 86 } 87 88 ~ClangTidyContext(); 89 90 /// Report any errors detected using this method. 91 /// 92 /// This is still under heavy development and will likely change towards using 93 /// tablegen'd diagnostic IDs. 94 /// FIXME: Figure out a way to manage ID spaces. 95 DiagnosticBuilder diag(StringRef CheckName, SourceLocation Loc, 96 StringRef Message, 97 DiagnosticIDs::Level Level = DiagnosticIDs::Warning); 98 99 DiagnosticBuilder diag(StringRef CheckName, StringRef Message, 100 DiagnosticIDs::Level Level = DiagnosticIDs::Warning); 101 102 /// Report any errors to do with reading the configuration using this method. 103 DiagnosticBuilder 104 configurationDiag(StringRef Message, 105 DiagnosticIDs::Level Level = DiagnosticIDs::Warning); 106 107 /// Sets the \c SourceManager of the used \c DiagnosticsEngine. 108 /// 109 /// This is called from the \c ClangTidyCheck base class. 110 void setSourceManager(SourceManager *SourceMgr); 111 112 /// Should be called when starting to process new translation unit. 113 void setCurrentFile(StringRef File); 114 115 /// Returns the main file name of the current translation unit. getCurrentFile()116 StringRef getCurrentFile() const { return CurrentFile; } 117 118 /// Sets ASTContext for the current translation unit. 119 void setASTContext(ASTContext *Context); 120 121 /// Gets the language options from the AST context. getLangOpts()122 const LangOptions &getLangOpts() const { return LangOpts; } 123 124 /// Returns the name of the clang-tidy check which produced this 125 /// diagnostic ID. 126 std::string getCheckName(unsigned DiagnosticID) const; 127 128 /// Returns \c true if the check is enabled for the \c CurrentFile. 129 /// 130 /// The \c CurrentFile can be changed using \c setCurrentFile. 131 bool isCheckEnabled(StringRef CheckName) const; 132 133 /// Returns \c true if the check should be upgraded to error for the 134 /// \c CurrentFile. 135 bool treatAsError(StringRef CheckName) const; 136 137 /// Returns global options. 138 const ClangTidyGlobalOptions &getGlobalOptions() const; 139 140 /// Returns options for \c CurrentFile. 141 /// 142 /// The \c CurrentFile can be changed using \c setCurrentFile. 143 const ClangTidyOptions &getOptions() const; 144 145 /// Returns options for \c File. Does not change or depend on 146 /// \c CurrentFile. 147 ClangTidyOptions getOptionsForFile(StringRef File) const; 148 149 /// Returns \c ClangTidyStats containing issued and ignored diagnostic 150 /// counters. getStats()151 const ClangTidyStats &getStats() const { return Stats; } 152 153 /// Control profile collection in clang-tidy. 154 void setEnableProfiling(bool Profile); getEnableProfiling()155 bool getEnableProfiling() const { return Profile; } 156 157 /// Control storage of profile date. 158 void setProfileStoragePrefix(StringRef ProfilePrefix); 159 llvm::Optional<ClangTidyProfiling::StorageParams> 160 getProfileStorageParams() const; 161 162 /// Should be called when starting to process new translation unit. setCurrentBuildDirectory(StringRef BuildDirectory)163 void setCurrentBuildDirectory(StringRef BuildDirectory) { 164 CurrentBuildDirectory = std::string(BuildDirectory); 165 } 166 167 /// Returns build directory of the current translation unit. getCurrentBuildDirectory()168 const std::string &getCurrentBuildDirectory() { 169 return CurrentBuildDirectory; 170 } 171 172 /// If the experimental alpha checkers from the static analyzer can be 173 /// enabled. canEnableAnalyzerAlphaCheckers()174 bool canEnableAnalyzerAlphaCheckers() const { 175 return AllowEnablingAnalyzerAlphaCheckers; 176 } 177 178 using DiagLevelAndFormatString = std::pair<DiagnosticIDs::Level, std::string>; getDiagLevelAndFormatString(unsigned DiagnosticID,SourceLocation Loc)179 DiagLevelAndFormatString getDiagLevelAndFormatString(unsigned DiagnosticID, 180 SourceLocation Loc) { 181 return DiagLevelAndFormatString( 182 static_cast<DiagnosticIDs::Level>( 183 DiagEngine->getDiagnosticLevel(DiagnosticID, Loc)), 184 std::string( 185 DiagEngine->getDiagnosticIDs()->getDescription(DiagnosticID))); 186 } 187 188 private: 189 // Writes to Stats. 190 friend class ClangTidyDiagnosticConsumer; 191 192 DiagnosticsEngine *DiagEngine; 193 std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider; 194 195 std::string CurrentFile; 196 ClangTidyOptions CurrentOptions; 197 class CachedGlobList; 198 std::unique_ptr<CachedGlobList> CheckFilter; 199 std::unique_ptr<CachedGlobList> WarningAsErrorFilter; 200 201 LangOptions LangOpts; 202 203 ClangTidyStats Stats; 204 205 std::string CurrentBuildDirectory; 206 207 llvm::DenseMap<unsigned, std::string> CheckNamesByDiagnosticID; 208 209 bool Profile; 210 std::string ProfilePrefix; 211 212 bool AllowEnablingAnalyzerAlphaCheckers; 213 }; 214 215 /// Check whether a given diagnostic should be suppressed due to the presence 216 /// of a "NOLINT" suppression comment. 217 /// This is exposed so that other tools that present clang-tidy diagnostics 218 /// (such as clangd) can respect the same suppression rules as clang-tidy. 219 /// This does not handle suppression of notes following a suppressed diagnostic; 220 /// that is left to the caller is it requires maintaining state in between calls 221 /// to this function. 222 /// If `AllowIO` is false, the function does not attempt to read source files 223 /// from disk which are not already mapped into memory; such files are treated 224 /// as not containing a suppression comment. 225 bool shouldSuppressDiagnostic(DiagnosticsEngine::Level DiagLevel, 226 const Diagnostic &Info, ClangTidyContext &Context, 227 bool AllowIO = true); 228 229 /// Gets the Fix attached to \p Diagnostic. 230 /// If there isn't a Fix attached to the diagnostic and \p AnyFix is true, Check 231 /// to see if exactly one note has a Fix and return it. Otherwise return 232 /// nullptr. 233 const llvm::StringMap<tooling::Replacements> * 234 getFixIt(const tooling::Diagnostic &Diagnostic, bool AnyFix); 235 236 /// A diagnostic consumer that turns each \c Diagnostic into a 237 /// \c SourceManager-independent \c ClangTidyError. 238 // 239 // FIXME: If we move away from unit-tests, this can be moved to a private 240 // implementation file. 241 class ClangTidyDiagnosticConsumer : public DiagnosticConsumer { 242 public: 243 ClangTidyDiagnosticConsumer(ClangTidyContext &Ctx, 244 DiagnosticsEngine *ExternalDiagEngine = nullptr, 245 bool RemoveIncompatibleErrors = true, 246 bool GetFixesFromNotes = false); 247 248 // FIXME: The concept of converting between FixItHints and Replacements is 249 // more generic and should be pulled out into a more useful Diagnostics 250 // library. 251 void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, 252 const Diagnostic &Info) override; 253 254 // Retrieve the diagnostics that were captured. 255 std::vector<ClangTidyError> take(); 256 257 private: 258 void finalizeLastError(); 259 void removeIncompatibleErrors(); 260 void removeDuplicatedDiagnosticsOfAliasCheckers(); 261 262 /// Returns the \c HeaderFilter constructed for the options set in the 263 /// context. 264 llvm::Regex *getHeaderFilter(); 265 266 /// Updates \c LastErrorRelatesToUserCode and LastErrorPassesLineFilter 267 /// according to the diagnostic \p Location. 268 void checkFilters(SourceLocation Location, const SourceManager &Sources); 269 bool passesLineFilter(StringRef FileName, unsigned LineNumber) const; 270 271 void forwardDiagnostic(const Diagnostic &Info); 272 273 ClangTidyContext &Context; 274 DiagnosticsEngine *ExternalDiagEngine; 275 bool RemoveIncompatibleErrors; 276 bool GetFixesFromNotes; 277 std::vector<ClangTidyError> Errors; 278 std::unique_ptr<llvm::Regex> HeaderFilter; 279 bool LastErrorRelatesToUserCode; 280 bool LastErrorPassesLineFilter; 281 bool LastErrorWasIgnored; 282 }; 283 284 } // end namespace tidy 285 } // end namespace clang 286 287 #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H 288