src/checkbase.cpp (276 lines of code) (raw):
/*
SPDX-FileCopyrightText: 2015 Klarälvdalens Datakonsult AB a KDAB Group company info@kdab.com
SPDX-FileContributor: Sérgio Martins <sergio.martins@kdab.com>
SPDX-FileCopyrightText: 2015-2017 Sergio Martins <smartins@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "checkbase.h"
#include "ClazyContext.h"
#include "SuppressionManager.h"
#include "Utils.h"
#include "clazy_stl.h"
#include <clang/AST/DeclBase.h>
#include <clang/AST/Stmt.h>
#include <clang/Basic/Diagnostic.h>
#include <clang/Basic/DiagnosticIDs.h>
#include <clang/Basic/SourceManager.h>
#include <clang/Frontend/CompilerInstance.h>
#include <clang/Lex/MacroInfo.h>
#include <clang/Lex/Preprocessor.h>
#include <llvm/ADT/IntrusiveRefCntPtr.h>
#include <llvm/ADT/StringRef.h>
#include <llvm/Support/raw_ostream.h>
#include <memory>
#include <vector>
namespace clang
{
class MacroArgs;
class Token;
} // namespace clang
using namespace clang;
using namespace clang::ast_matchers;
ClazyPreprocessorCallbacks::ClazyPreprocessorCallbacks(CheckBase *check)
: check(check)
{
}
void ClazyPreprocessorCallbacks::MacroExpands(const Token ¯oNameTok, const MacroDefinition &md, SourceRange range, const MacroArgs *)
{
check->VisitMacroExpands(macroNameTok, range, md.getMacroInfo());
}
void ClazyPreprocessorCallbacks::Defined(const Token ¯oNameTok, const MacroDefinition &, SourceRange range)
{
check->VisitDefined(macroNameTok, range);
}
void ClazyPreprocessorCallbacks::Ifdef(SourceLocation loc, const Token ¯oNameTok, const MacroDefinition &)
{
check->VisitIfdef(loc, macroNameTok);
}
void ClazyPreprocessorCallbacks::Ifndef(SourceLocation loc, const Token ¯oNameTok, const MacroDefinition &)
{
check->VisitIfndef(loc, macroNameTok);
}
void ClazyPreprocessorCallbacks::If(SourceLocation loc, SourceRange conditionRange, PPCallbacks::ConditionValueKind conditionValue)
{
check->VisitIf(loc, conditionRange, conditionValue);
}
void ClazyPreprocessorCallbacks::Elif(SourceLocation loc, SourceRange conditionRange, PPCallbacks::ConditionValueKind conditionValue, SourceLocation ifLoc)
{
check->VisitElif(loc, conditionRange, conditionValue, ifLoc);
}
void ClazyPreprocessorCallbacks::Else(SourceLocation loc, SourceLocation ifLoc)
{
check->VisitElse(loc, ifLoc);
}
void ClazyPreprocessorCallbacks::Endif(SourceLocation loc, SourceLocation ifLoc)
{
check->VisitEndif(loc, ifLoc);
}
void ClazyPreprocessorCallbacks::MacroDefined(const Token ¯oNameTok, const MacroDirective *)
{
check->VisitMacroDefined(macroNameTok);
}
void ClazyPreprocessorCallbacks::InclusionDirective(clang::SourceLocation HashLoc,
const clang::Token &IncludeTok,
llvm::StringRef FileName,
bool IsAngled,
clang::CharSourceRange FilenameRange,
clazy::OptionalFileEntryRef File,
llvm::StringRef SearchPath,
llvm::StringRef RelativePath,
#if LLVM_VERSION_MAJOR >= 19
const clang::Module *SuggestedModule,
bool ModuleImported,
#else
const clang::Module *Imported,
#endif
clang::SrcMgr::CharacteristicKind FileType)
{
check->VisitInclusionDirective(HashLoc,
IncludeTok,
FileName,
IsAngled,
FilenameRange,
File,
SearchPath,
RelativePath,
#if LLVM_VERSION_MAJOR >= 19
SuggestedModule,
ModuleImported,
#else
Imported,
#endif
FileType);
}
CheckBase::CheckBase(const std::string &name, const ClazyContext *context, Options options)
: m_sm(context->ci.getSourceManager())
, m_name(name)
, m_context(context)
, m_astContext(context->astContext)
, m_preprocessorCallbacks(new ClazyPreprocessorCallbacks(this))
, m_options(options)
, m_tag(" [-Wclazy-" + m_name + ']')
{
}
CheckBase::~CheckBase()
{
}
void CheckBase::VisitStmt(Stmt *)
{
// Overriden in derived classes
}
void CheckBase::VisitDecl(Decl *)
{
// Overriden in derived classes
}
void CheckBase::VisitMacroExpands(const Token &, const SourceRange &, const clang::MacroInfo *)
{
// Overriden in derived classes
}
void CheckBase::VisitMacroDefined(const Token &)
{
// Overriden in derived classes
}
void CheckBase::VisitDefined(const Token &, const SourceRange &)
{
// Overriden in derived classes
}
void CheckBase::VisitIfdef(clang::SourceLocation, const clang::Token &)
{
// Overriden in derived classes
}
void CheckBase::VisitIfndef(SourceLocation, const Token &)
{
// Overriden in derived classes
}
void CheckBase::VisitIf(SourceLocation, SourceRange, clang::PPCallbacks::ConditionValueKind)
{
// Overriden in derived classes
}
void CheckBase::VisitElif(SourceLocation, SourceRange, clang::PPCallbacks::ConditionValueKind, SourceLocation)
{
// Overriden in derived classes
}
void CheckBase::VisitElse(SourceLocation, SourceLocation)
{
// Overriden in derived classes
}
void CheckBase::VisitEndif(SourceLocation, SourceLocation)
{
// Overriden in derived classes
}
void CheckBase::VisitInclusionDirective(clang::SourceLocation,
const clang::Token &,
llvm::StringRef,
bool,
clang::CharSourceRange,
clazy::OptionalFileEntryRef,
llvm::StringRef,
llvm::StringRef,
const clang::Module *,
#if LLVM_VERSION_MAJOR >= 19
bool ModuleImported,
#endif
clang::SrcMgr::CharacteristicKind)
{
// Overriden in derived classes
}
void CheckBase::enablePreProcessorCallbacks()
{
Preprocessor &pi = m_context->ci.getPreprocessor();
pi.addPPCallbacks(std::unique_ptr<PPCallbacks>(m_preprocessorCallbacks));
}
bool CheckBase::shouldIgnoreFile(SourceLocation loc) const
{
if (m_filesToIgnore.empty()) {
return false;
}
if (!loc.isValid()) {
return true;
}
std::string filename = static_cast<std::string>(sm().getFilename(loc));
return clazy::any_of(m_filesToIgnore, [filename](const std::string &ignored) {
return clazy::contains(filename, ignored);
});
}
void CheckBase::emitWarning(const clang::Decl *d, const std::string &error, bool printWarningTag)
{
emitWarning(d->getBeginLoc(), error, printWarningTag);
}
void CheckBase::emitWarning(const clang::Stmt *s, const std::string &error, bool printWarningTag)
{
emitWarning(s->getBeginLoc(), error, printWarningTag);
}
void CheckBase::emitWarning(clang::SourceLocation loc, const std::string &error, bool printWarningTag)
{
emitWarning(loc, error, {}, printWarningTag);
}
void CheckBase::emitWarning(clang::SourceLocation loc, std::string error, const std::vector<FixItHint> &fixits, bool printWarningTag)
{
loc = sm().getFileLoc(loc);
if (m_context->suppressionManager.isSuppressed(m_name, loc, sm(), lo())) {
return;
}
if (m_context->shouldIgnoreFile(loc)) {
return;
}
if (loc.isMacroID()) {
if (warningAlreadyEmitted(loc)) {
return; // For warnings in macro arguments we get a warning in each place the argument is used within the expanded macro, so filter all the dups
}
m_emittedWarningsInMacro.push_back(loc.getRawEncoding());
}
if (printWarningTag) {
error += m_tag;
}
reallyEmitWarning(loc, error, fixits);
for (const auto &l : m_queuedManualInterventionWarnings) {
std::string msg("FixIt failed, requires manual intervention: ");
if (!l.second.empty()) {
msg += ' ' + l.second;
}
auto curLoc = sm().getFileLoc(l.first);
reallyEmitWarning(curLoc, msg + m_tag, {});
}
m_queuedManualInterventionWarnings.clear();
}
void CheckBase::emitInternalError(SourceLocation loc, std::string error)
{
llvm::errs() << m_tag << ": internal error: " << error << " at " << loc.printToString(sm()) << "\n";
}
void CheckBase::reallyEmitWarning(clang::SourceLocation loc, const std::string &error, const std::vector<FixItHint> &fixits)
{
FullSourceLoc full(loc, sm());
auto &engine = m_context->ci.getDiagnostics();
auto severity =
(m_context->treatAsError(m_name) || (engine.getWarningsAsErrors() && !m_context->userDisabledWError())) ? DiagnosticIDs::Error : DiagnosticIDs::Warning;
unsigned id = engine.getDiagnosticIDs()->getCustomDiagID(severity, error.c_str());
DiagnosticBuilder B = engine.Report(full, id);
for (const FixItHint &fixit : fixits) {
if (!fixit.isNull()) {
B.AddFixItHint(fixit);
}
}
}
void CheckBase::queueManualFixitWarning(clang::SourceLocation loc, const std::string &message)
{
if (!manualFixitAlreadyQueued(loc)) {
m_queuedManualInterventionWarnings.push_back({loc, message});
m_emittedManualFixItsWarningsInMacro.push_back(loc.getRawEncoding());
}
}
bool CheckBase::warningAlreadyEmitted(SourceLocation loc) const
{
PresumedLoc ploc = sm().getPresumedLoc(loc);
for (auto rawLoc : m_emittedWarningsInMacro) {
SourceLocation l = SourceLocation::getFromRawEncoding(rawLoc);
PresumedLoc p = sm().getPresumedLoc(l);
if (Utils::presumedLocationsEqual(p, ploc)) {
return true;
}
}
return false;
}
bool CheckBase::manualFixitAlreadyQueued(SourceLocation loc) const
{
PresumedLoc ploc = sm().getPresumedLoc(loc);
for (auto loc : m_emittedManualFixItsWarningsInMacro) {
SourceLocation l = SourceLocation::getFromRawEncoding(loc);
PresumedLoc p = sm().getPresumedLoc(l);
if (Utils::presumedLocationsEqual(p, ploc)) {
return true;
}
}
return false;
}
bool CheckBase::isOptionSet(const std::string &optionName) const
{
const std::string qualifiedName = name() + '-' + optionName;
return m_context->isOptionSet(qualifiedName);
}
ClazyAstMatcherCallback::ClazyAstMatcherCallback(CheckBase *check)
: MatchFinder::MatchCallback()
, m_check(check)
{
}