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 /// Sets the \c SourceManager of the used \c DiagnosticsEngine. 100 /// 101 /// This is called from the \c ClangTidyCheck base class. 102 void setSourceManager(SourceManager *SourceMgr); 103 104 /// Should be called when starting to process new translation unit. 105 void setCurrentFile(StringRef File); 106 107 /// Returns the main file name of the current translation unit. getCurrentFile()108 StringRef getCurrentFile() const { return CurrentFile; } 109 110 /// Sets ASTContext for the current translation unit. 111 void setASTContext(ASTContext *Context); 112 113 /// Gets the language options from the AST context. getLangOpts()114 const LangOptions &getLangOpts() const { return LangOpts; } 115 116 /// Returns the name of the clang-tidy check which produced this 117 /// diagnostic ID. 118 std::string getCheckName(unsigned DiagnosticID) const; 119 120 /// Returns \c true if the check is enabled for the \c CurrentFile. 121 /// 122 /// The \c CurrentFile can be changed using \c setCurrentFile. 123 bool isCheckEnabled(StringRef CheckName) const; 124 125 /// Returns \c true if the check should be upgraded to error for the 126 /// \c CurrentFile. 127 bool treatAsError(StringRef CheckName) const; 128 129 /// Returns global options. 130 const ClangTidyGlobalOptions &getGlobalOptions() const; 131 132 /// Returns options for \c CurrentFile. 133 /// 134 /// The \c CurrentFile can be changed using \c setCurrentFile. 135 const ClangTidyOptions &getOptions() const; 136 137 /// Returns options for \c File. Does not change or depend on 138 /// \c CurrentFile. 139 ClangTidyOptions getOptionsForFile(StringRef File) const; 140 141 /// Returns \c ClangTidyStats containing issued and ignored diagnostic 142 /// counters. getStats()143 const ClangTidyStats &getStats() const { return Stats; } 144 145 /// Control profile collection in clang-tidy. 146 void setEnableProfiling(bool Profile); getEnableProfiling()147 bool getEnableProfiling() const { return Profile; } 148 149 /// Control storage of profile date. 150 void setProfileStoragePrefix(StringRef ProfilePrefix); 151 llvm::Optional<ClangTidyProfiling::StorageParams> 152 getProfileStorageParams() const; 153 154 /// Should be called when starting to process new translation unit. setCurrentBuildDirectory(StringRef BuildDirectory)155 void setCurrentBuildDirectory(StringRef BuildDirectory) { 156 CurrentBuildDirectory = std::string(BuildDirectory); 157 } 158 159 /// Returns build directory of the current translation unit. getCurrentBuildDirectory()160 const std::string &getCurrentBuildDirectory() { 161 return CurrentBuildDirectory; 162 } 163 164 /// If the experimental alpha checkers from the static analyzer can be 165 /// enabled. canEnableAnalyzerAlphaCheckers()166 bool canEnableAnalyzerAlphaCheckers() const { 167 return AllowEnablingAnalyzerAlphaCheckers; 168 } 169 170 using DiagLevelAndFormatString = std::pair<DiagnosticIDs::Level, std::string>; getDiagLevelAndFormatString(unsigned DiagnosticID,SourceLocation Loc)171 DiagLevelAndFormatString getDiagLevelAndFormatString(unsigned DiagnosticID, 172 SourceLocation Loc) { 173 return DiagLevelAndFormatString( 174 static_cast<DiagnosticIDs::Level>( 175 DiagEngine->getDiagnosticLevel(DiagnosticID, Loc)), 176 std::string( 177 DiagEngine->getDiagnosticIDs()->getDescription(DiagnosticID))); 178 } 179 180 private: 181 // Writes to Stats. 182 friend class ClangTidyDiagnosticConsumer; 183 184 DiagnosticsEngine *DiagEngine; 185 std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider; 186 187 std::string CurrentFile; 188 ClangTidyOptions CurrentOptions; 189 class CachedGlobList; 190 std::unique_ptr<CachedGlobList> CheckFilter; 191 std::unique_ptr<CachedGlobList> WarningAsErrorFilter; 192 193 LangOptions LangOpts; 194 195 ClangTidyStats Stats; 196 197 std::string CurrentBuildDirectory; 198 199 llvm::DenseMap<unsigned, std::string> CheckNamesByDiagnosticID; 200 201 bool Profile; 202 std::string ProfilePrefix; 203 204 bool AllowEnablingAnalyzerAlphaCheckers; 205 }; 206 207 /// Check whether a given diagnostic should be suppressed due to the presence 208 /// of a "NOLINT" suppression comment. 209 /// This is exposed so that other tools that present clang-tidy diagnostics 210 /// (such as clangd) can respect the same suppression rules as clang-tidy. 211 /// This does not handle suppression of notes following a suppressed diagnostic; 212 /// that is left to the caller is it requires maintaining state in between calls 213 /// to this function. 214 /// If `AllowIO` is false, the function does not attempt to read source files 215 /// from disk which are not already mapped into memory; such files are treated 216 /// as not containing a suppression comment. 217 bool shouldSuppressDiagnostic(DiagnosticsEngine::Level DiagLevel, 218 const Diagnostic &Info, ClangTidyContext &Context, 219 bool AllowIO = true); 220 221 /// A diagnostic consumer that turns each \c Diagnostic into a 222 /// \c SourceManager-independent \c ClangTidyError. 223 // 224 // FIXME: If we move away from unit-tests, this can be moved to a private 225 // implementation file. 226 class ClangTidyDiagnosticConsumer : public DiagnosticConsumer { 227 public: 228 ClangTidyDiagnosticConsumer(ClangTidyContext &Ctx, 229 DiagnosticsEngine *ExternalDiagEngine = nullptr, 230 bool RemoveIncompatibleErrors = true); 231 232 // FIXME: The concept of converting between FixItHints and Replacements is 233 // more generic and should be pulled out into a more useful Diagnostics 234 // library. 235 void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, 236 const Diagnostic &Info) override; 237 238 // Retrieve the diagnostics that were captured. 239 std::vector<ClangTidyError> take(); 240 241 private: 242 void finalizeLastError(); 243 void removeIncompatibleErrors(); 244 void removeDuplicatedDiagnosticsOfAliasCheckers(); 245 246 /// Returns the \c HeaderFilter constructed for the options set in the 247 /// context. 248 llvm::Regex *getHeaderFilter(); 249 250 /// Updates \c LastErrorRelatesToUserCode and LastErrorPassesLineFilter 251 /// according to the diagnostic \p Location. 252 void checkFilters(SourceLocation Location, const SourceManager &Sources); 253 bool passesLineFilter(StringRef FileName, unsigned LineNumber) const; 254 255 void forwardDiagnostic(const Diagnostic &Info); 256 257 ClangTidyContext &Context; 258 DiagnosticsEngine *ExternalDiagEngine; 259 bool RemoveIncompatibleErrors; 260 std::vector<ClangTidyError> Errors; 261 std::unique_ptr<llvm::Regex> HeaderFilter; 262 bool LastErrorRelatesToUserCode; 263 bool LastErrorPassesLineFilter; 264 bool LastErrorWasIgnored; 265 }; 266 267 } // end namespace tidy 268 } // end namespace clang 269 270 #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H 271