forked from OSchip/llvm-project
299 lines
11 KiB
C++
299 lines
11 KiB
C++
//===--- ClangTidyDiagnosticConsumer.h - clang-tidy -------------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H
|
|
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H
|
|
|
|
#include "ClangTidyOptions.h"
|
|
#include "ClangTidyProfiling.h"
|
|
#include "clang/Basic/Diagnostic.h"
|
|
#include "clang/Tooling/Core/Diagnostic.h"
|
|
#include "llvm/ADT/DenseMap.h"
|
|
#include "llvm/Support/Regex.h"
|
|
|
|
namespace clang {
|
|
|
|
class ASTContext;
|
|
class CompilerInstance;
|
|
class SourceManager;
|
|
namespace ast_matchers {
|
|
class MatchFinder;
|
|
} // namespace ast_matchers
|
|
namespace tooling {
|
|
class CompilationDatabase;
|
|
} // namespace tooling
|
|
|
|
namespace tidy {
|
|
class CachedGlobList;
|
|
|
|
/// A detected error complete with information to display diagnostic and
|
|
/// automatic fix.
|
|
///
|
|
/// This is used as an intermediate format to transport Diagnostics without a
|
|
/// dependency on a SourceManager.
|
|
///
|
|
/// FIXME: Make Diagnostics flexible enough to support this directly.
|
|
struct ClangTidyError : tooling::Diagnostic {
|
|
ClangTidyError(StringRef CheckName, Level DiagLevel, StringRef BuildDirectory,
|
|
bool IsWarningAsError);
|
|
|
|
bool IsWarningAsError;
|
|
std::vector<std::string> EnabledDiagnosticAliases;
|
|
};
|
|
|
|
/// Contains displayed and ignored diagnostic counters for a ClangTidy run.
|
|
struct ClangTidyStats {
|
|
unsigned ErrorsDisplayed = 0;
|
|
unsigned ErrorsIgnoredCheckFilter = 0;
|
|
unsigned ErrorsIgnoredNOLINT = 0;
|
|
unsigned ErrorsIgnoredNonUserCode = 0;
|
|
unsigned ErrorsIgnoredLineFilter = 0;
|
|
|
|
unsigned errorsIgnored() const {
|
|
return ErrorsIgnoredNOLINT + ErrorsIgnoredCheckFilter +
|
|
ErrorsIgnoredNonUserCode + ErrorsIgnoredLineFilter;
|
|
}
|
|
};
|
|
|
|
/// Every \c ClangTidyCheck reports errors through a \c DiagnosticsEngine
|
|
/// provided by this context.
|
|
///
|
|
/// A \c ClangTidyCheck always has access to the active context to report
|
|
/// warnings like:
|
|
/// \code
|
|
/// Context->Diag(Loc, "Single-argument constructors must be explicit")
|
|
/// << FixItHint::CreateInsertion(Loc, "explicit ");
|
|
/// \endcode
|
|
class ClangTidyContext {
|
|
public:
|
|
/// Initializes \c ClangTidyContext instance.
|
|
ClangTidyContext(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,
|
|
bool AllowEnablingAnalyzerAlphaCheckers = false);
|
|
/// Sets the DiagnosticsEngine that diag() will emit diagnostics to.
|
|
// FIXME: this is required initialization, and should be a constructor param.
|
|
// Fix the context -> diag engine -> consumer -> context initialization cycle.
|
|
void setDiagnosticsEngine(DiagnosticsEngine *DiagEngine) {
|
|
this->DiagEngine = DiagEngine;
|
|
}
|
|
|
|
~ClangTidyContext();
|
|
|
|
/// Report any errors detected using this method.
|
|
///
|
|
/// This is still under heavy development and will likely change towards using
|
|
/// tablegen'd diagnostic IDs.
|
|
/// FIXME: Figure out a way to manage ID spaces.
|
|
DiagnosticBuilder diag(StringRef CheckName, SourceLocation Loc,
|
|
StringRef Message,
|
|
DiagnosticIDs::Level Level = DiagnosticIDs::Warning);
|
|
|
|
DiagnosticBuilder diag(StringRef CheckName, StringRef Message,
|
|
DiagnosticIDs::Level Level = DiagnosticIDs::Warning);
|
|
|
|
DiagnosticBuilder diag(const ClangTidyError &Error);
|
|
|
|
/// Report any errors to do with reading the configuration using this method.
|
|
DiagnosticBuilder
|
|
configurationDiag(StringRef Message,
|
|
DiagnosticIDs::Level Level = DiagnosticIDs::Warning);
|
|
|
|
/// Sets the \c SourceManager of the used \c DiagnosticsEngine.
|
|
///
|
|
/// This is called from the \c ClangTidyCheck base class.
|
|
void setSourceManager(SourceManager *SourceMgr);
|
|
|
|
/// Should be called when starting to process new translation unit.
|
|
void setCurrentFile(StringRef File);
|
|
|
|
/// Returns the main file name of the current translation unit.
|
|
StringRef getCurrentFile() const { return CurrentFile; }
|
|
|
|
/// Sets ASTContext for the current translation unit.
|
|
void setASTContext(ASTContext *Context);
|
|
|
|
/// Gets the language options from the AST context.
|
|
const LangOptions &getLangOpts() const { return LangOpts; }
|
|
|
|
/// Returns the name of the clang-tidy check which produced this
|
|
/// diagnostic ID.
|
|
std::string getCheckName(unsigned DiagnosticID) const;
|
|
|
|
/// Returns \c true if the check is enabled for the \c CurrentFile.
|
|
///
|
|
/// The \c CurrentFile can be changed using \c setCurrentFile.
|
|
bool isCheckEnabled(StringRef CheckName) const;
|
|
|
|
/// Returns \c true if the check should be upgraded to error for the
|
|
/// \c CurrentFile.
|
|
bool treatAsError(StringRef CheckName) const;
|
|
|
|
/// Returns global options.
|
|
const ClangTidyGlobalOptions &getGlobalOptions() const;
|
|
|
|
/// Returns options for \c CurrentFile.
|
|
///
|
|
/// The \c CurrentFile can be changed using \c setCurrentFile.
|
|
const ClangTidyOptions &getOptions() const;
|
|
|
|
/// Returns options for \c File. Does not change or depend on
|
|
/// \c CurrentFile.
|
|
ClangTidyOptions getOptionsForFile(StringRef File) const;
|
|
|
|
/// Returns \c ClangTidyStats containing issued and ignored diagnostic
|
|
/// counters.
|
|
const ClangTidyStats &getStats() const { return Stats; }
|
|
|
|
/// Control profile collection in clang-tidy.
|
|
void setEnableProfiling(bool Profile);
|
|
bool getEnableProfiling() const { return Profile; }
|
|
|
|
/// Control storage of profile date.
|
|
void setProfileStoragePrefix(StringRef ProfilePrefix);
|
|
llvm::Optional<ClangTidyProfiling::StorageParams>
|
|
getProfileStorageParams() const;
|
|
|
|
/// Should be called when starting to process new translation unit.
|
|
void setCurrentBuildDirectory(StringRef BuildDirectory) {
|
|
CurrentBuildDirectory = std::string(BuildDirectory);
|
|
}
|
|
|
|
/// Returns build directory of the current translation unit.
|
|
const std::string &getCurrentBuildDirectory() const {
|
|
return CurrentBuildDirectory;
|
|
}
|
|
|
|
/// If the experimental alpha checkers from the static analyzer can be
|
|
/// enabled.
|
|
bool canEnableAnalyzerAlphaCheckers() const {
|
|
return AllowEnablingAnalyzerAlphaCheckers;
|
|
}
|
|
|
|
using DiagLevelAndFormatString = std::pair<DiagnosticIDs::Level, std::string>;
|
|
DiagLevelAndFormatString getDiagLevelAndFormatString(unsigned DiagnosticID,
|
|
SourceLocation Loc) {
|
|
return DiagLevelAndFormatString(
|
|
static_cast<DiagnosticIDs::Level>(
|
|
DiagEngine->getDiagnosticLevel(DiagnosticID, Loc)),
|
|
std::string(
|
|
DiagEngine->getDiagnosticIDs()->getDescription(DiagnosticID)));
|
|
}
|
|
|
|
private:
|
|
// Writes to Stats.
|
|
friend class ClangTidyDiagnosticConsumer;
|
|
|
|
DiagnosticsEngine *DiagEngine;
|
|
std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider;
|
|
|
|
std::string CurrentFile;
|
|
ClangTidyOptions CurrentOptions;
|
|
|
|
std::unique_ptr<CachedGlobList> CheckFilter;
|
|
std::unique_ptr<CachedGlobList> WarningAsErrorFilter;
|
|
|
|
LangOptions LangOpts;
|
|
|
|
ClangTidyStats Stats;
|
|
|
|
std::string CurrentBuildDirectory;
|
|
|
|
llvm::DenseMap<unsigned, std::string> CheckNamesByDiagnosticID;
|
|
|
|
bool Profile;
|
|
std::string ProfilePrefix;
|
|
|
|
bool AllowEnablingAnalyzerAlphaCheckers;
|
|
};
|
|
|
|
/// Check whether a given diagnostic should be suppressed due to the presence
|
|
/// of a "NOLINT" suppression comment.
|
|
/// This is exposed so that other tools that present clang-tidy diagnostics
|
|
/// (such as clangd) can respect the same suppression rules as clang-tidy.
|
|
/// This does not handle suppression of notes following a suppressed diagnostic;
|
|
/// that is left to the caller as it requires maintaining state in between calls
|
|
/// to this function.
|
|
/// If `AllowIO` is false, the function does not attempt to read source files
|
|
/// from disk which are not already mapped into memory; such files are treated
|
|
/// as not containing a suppression comment.
|
|
/// \param EnableNolintBlocks controls whether to honor NOLINTBEGIN/NOLINTEND
|
|
/// blocks; if false, only considers line-level disabling.
|
|
/// If suppression is not possible due to improper use of "NOLINT" comments -
|
|
/// for example, the use of a "NOLINTBEGIN" comment that is not followed by a
|
|
/// "NOLINTEND" comment - a diagnostic regarding the improper use is returned
|
|
/// via the output argument `SuppressionErrors`.
|
|
bool shouldSuppressDiagnostic(
|
|
DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info,
|
|
ClangTidyContext &Context,
|
|
SmallVectorImpl<ClangTidyError> &SuppressionErrors, bool AllowIO = true,
|
|
bool EnableNolintBlocks = true);
|
|
|
|
/// Gets the Fix attached to \p Diagnostic.
|
|
/// If there isn't a Fix attached to the diagnostic and \p AnyFix is true, Check
|
|
/// to see if exactly one note has a Fix and return it. Otherwise return
|
|
/// nullptr.
|
|
const llvm::StringMap<tooling::Replacements> *
|
|
getFixIt(const tooling::Diagnostic &Diagnostic, bool AnyFix);
|
|
|
|
/// A diagnostic consumer that turns each \c Diagnostic into a
|
|
/// \c SourceManager-independent \c ClangTidyError.
|
|
///
|
|
/// \param EnableNolintBlocks Enables diagnostic-disabling inside blocks of
|
|
/// code, delimited by NOLINTBEGIN and NOLINTEND.
|
|
//
|
|
// FIXME: If we move away from unit-tests, this can be moved to a private
|
|
// implementation file.
|
|
class ClangTidyDiagnosticConsumer : public DiagnosticConsumer {
|
|
public:
|
|
ClangTidyDiagnosticConsumer(ClangTidyContext &Ctx,
|
|
DiagnosticsEngine *ExternalDiagEngine = nullptr,
|
|
bool RemoveIncompatibleErrors = true,
|
|
bool GetFixesFromNotes = false,
|
|
bool EnableNolintBlocks = true);
|
|
|
|
// FIXME: The concept of converting between FixItHints and Replacements is
|
|
// more generic and should be pulled out into a more useful Diagnostics
|
|
// library.
|
|
void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
|
|
const Diagnostic &Info) override;
|
|
|
|
// Retrieve the diagnostics that were captured.
|
|
std::vector<ClangTidyError> take();
|
|
|
|
private:
|
|
void finalizeLastError();
|
|
void removeIncompatibleErrors();
|
|
void removeDuplicatedDiagnosticsOfAliasCheckers();
|
|
|
|
/// Returns the \c HeaderFilter constructed for the options set in the
|
|
/// context.
|
|
llvm::Regex *getHeaderFilter();
|
|
|
|
/// Updates \c LastErrorRelatesToUserCode and LastErrorPassesLineFilter
|
|
/// according to the diagnostic \p Location.
|
|
void checkFilters(SourceLocation Location, const SourceManager &Sources);
|
|
bool passesLineFilter(StringRef FileName, unsigned LineNumber) const;
|
|
|
|
void forwardDiagnostic(const Diagnostic &Info);
|
|
|
|
ClangTidyContext &Context;
|
|
DiagnosticsEngine *ExternalDiagEngine;
|
|
bool RemoveIncompatibleErrors;
|
|
bool GetFixesFromNotes;
|
|
bool EnableNolintBlocks;
|
|
std::vector<ClangTidyError> Errors;
|
|
std::unique_ptr<llvm::Regex> HeaderFilter;
|
|
bool LastErrorRelatesToUserCode;
|
|
bool LastErrorPassesLineFilter;
|
|
bool LastErrorWasIgnored;
|
|
};
|
|
|
|
} // end namespace tidy
|
|
} // end namespace clang
|
|
|
|
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H
|