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 /// A diagnostic consumer that turns each \c Diagnostic into a 230 /// \c SourceManager-independent \c ClangTidyError. 231 // 232 // FIXME: If we move away from unit-tests, this can be moved to a private 233 // implementation file. 234 class ClangTidyDiagnosticConsumer : public DiagnosticConsumer { 235 public: 236 ClangTidyDiagnosticConsumer(ClangTidyContext &Ctx, 237 DiagnosticsEngine *ExternalDiagEngine = nullptr, 238 bool RemoveIncompatibleErrors = true); 239 240 // FIXME: The concept of converting between FixItHints and Replacements is 241 // more generic and should be pulled out into a more useful Diagnostics 242 // library. 243 void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, 244 const Diagnostic &Info) override; 245 246 // Retrieve the diagnostics that were captured. 247 std::vector<ClangTidyError> take(); 248 249 private: 250 void finalizeLastError(); 251 void removeIncompatibleErrors(); 252 void removeDuplicatedDiagnosticsOfAliasCheckers(); 253 254 /// Returns the \c HeaderFilter constructed for the options set in the 255 /// context. 256 llvm::Regex *getHeaderFilter(); 257 258 /// Updates \c LastErrorRelatesToUserCode and LastErrorPassesLineFilter 259 /// according to the diagnostic \p Location. 260 void checkFilters(SourceLocation Location, const SourceManager &Sources); 261 bool passesLineFilter(StringRef FileName, unsigned LineNumber) const; 262 263 void forwardDiagnostic(const Diagnostic &Info); 264 265 ClangTidyContext &Context; 266 DiagnosticsEngine *ExternalDiagEngine; 267 bool RemoveIncompatibleErrors; 268 std::vector<ClangTidyError> Errors; 269 std::unique_ptr<llvm::Regex> HeaderFilter; 270 bool LastErrorRelatesToUserCode; 271 bool LastErrorPassesLineFilter; 272 bool LastErrorWasIgnored; 273 }; 274 275 } // end namespace tidy 276 } // end namespace clang 277 278 #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H 279