File indexing completed on 2024-04-21 05:38:45

0001 /*
0002     SPDX-FileCopyrightText: 2015 Klarälvdalens Datakonsult AB a KDAB Group company info@kdab.com
0003     SPDX-FileContributor: SĂ©rgio Martins <sergio.martins@kdab.com>
0004 
0005     SPDX-FileCopyrightText: 2015-2017 Sergio Martins <smartins@kde.org>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-or-later
0008 */
0009 
0010 #include "checkbase.h"
0011 #include "ClazyContext.h"
0012 #include "SuppressionManager.h"
0013 #include "Utils.h"
0014 #include "clazy_stl.h"
0015 
0016 #include <clang/AST/DeclBase.h>
0017 #include <clang/AST/Stmt.h>
0018 #include <clang/Basic/Diagnostic.h>
0019 #include <clang/Basic/DiagnosticIDs.h>
0020 #include <clang/Basic/SourceManager.h>
0021 #include <clang/Frontend/CompilerInstance.h>
0022 #include <clang/Lex/MacroInfo.h>
0023 #include <clang/Lex/Preprocessor.h>
0024 #include <llvm/ADT/IntrusiveRefCntPtr.h>
0025 #include <llvm/ADT/StringRef.h>
0026 #include <llvm/Support/raw_ostream.h>
0027 
0028 #include <memory>
0029 #include <vector>
0030 
0031 namespace clang
0032 {
0033 class MacroArgs;
0034 class Token;
0035 } // namespace clang
0036 
0037 using namespace clang;
0038 using namespace clang::ast_matchers;
0039 
0040 ClazyPreprocessorCallbacks::ClazyPreprocessorCallbacks(CheckBase *check)
0041     : check(check)
0042 {
0043 }
0044 
0045 void ClazyPreprocessorCallbacks::MacroExpands(const Token &macroNameTok, const MacroDefinition &md, SourceRange range, const MacroArgs *)
0046 {
0047     check->VisitMacroExpands(macroNameTok, range, md.getMacroInfo());
0048 }
0049 
0050 void ClazyPreprocessorCallbacks::Defined(const Token &macroNameTok, const MacroDefinition &, SourceRange range)
0051 {
0052     check->VisitDefined(macroNameTok, range);
0053 }
0054 
0055 void ClazyPreprocessorCallbacks::Ifdef(SourceLocation loc, const Token &macroNameTok, const MacroDefinition &)
0056 {
0057     check->VisitIfdef(loc, macroNameTok);
0058 }
0059 
0060 void ClazyPreprocessorCallbacks::Ifndef(SourceLocation loc, const Token &macroNameTok, const MacroDefinition &)
0061 {
0062     check->VisitIfndef(loc, macroNameTok);
0063 }
0064 
0065 void ClazyPreprocessorCallbacks::If(SourceLocation loc, SourceRange conditionRange, PPCallbacks::ConditionValueKind conditionValue)
0066 {
0067     check->VisitIf(loc, conditionRange, conditionValue);
0068 }
0069 
0070 void ClazyPreprocessorCallbacks::Elif(SourceLocation loc, SourceRange conditionRange, PPCallbacks::ConditionValueKind conditionValue, SourceLocation ifLoc)
0071 {
0072     check->VisitElif(loc, conditionRange, conditionValue, ifLoc);
0073 }
0074 
0075 void ClazyPreprocessorCallbacks::Else(SourceLocation loc, SourceLocation ifLoc)
0076 {
0077     check->VisitElse(loc, ifLoc);
0078 }
0079 
0080 void ClazyPreprocessorCallbacks::Endif(SourceLocation loc, SourceLocation ifLoc)
0081 {
0082     check->VisitEndif(loc, ifLoc);
0083 }
0084 
0085 void ClazyPreprocessorCallbacks::MacroDefined(const Token &macroNameTok, const MacroDirective *)
0086 {
0087     check->VisitMacroDefined(macroNameTok);
0088 }
0089 
0090 void ClazyPreprocessorCallbacks::InclusionDirective(clang::SourceLocation HashLoc,
0091                                                     const clang::Token &IncludeTok,
0092                                                     clang::StringRef FileName,
0093                                                     bool IsAngled,
0094                                                     clang::CharSourceRange FilenameRange,
0095                                                     clazy::OptionalFileEntryRef File,
0096                                                     clang::StringRef SearchPath,
0097                                                     clang::StringRef RelativePath,
0098                                                     const clang::Module *Imported,
0099                                                     clang::SrcMgr::CharacteristicKind FileType)
0100 {
0101     check->VisitInclusionDirective(HashLoc, IncludeTok, FileName, IsAngled, FilenameRange, File, SearchPath, RelativePath, Imported, FileType);
0102 }
0103 
0104 CheckBase::CheckBase(const std::string &name, const ClazyContext *context, Options options)
0105     : m_sm(context->ci.getSourceManager())
0106     , m_name(name)
0107     , m_context(context)
0108     , m_astContext(context->astContext)
0109     , m_preprocessorCallbacks(new ClazyPreprocessorCallbacks(this))
0110     , m_options(options)
0111     , m_tag(" [-Wclazy-" + m_name + ']')
0112 {
0113 }
0114 
0115 CheckBase::~CheckBase()
0116 {
0117 }
0118 
0119 void CheckBase::VisitStmt(Stmt *)
0120 {
0121     // Overriden in derived classes
0122 }
0123 
0124 void CheckBase::VisitDecl(Decl *)
0125 {
0126     // Overriden in derived classes
0127 }
0128 
0129 void CheckBase::VisitMacroExpands(const Token &, const SourceRange &, const clang::MacroInfo *)
0130 {
0131     // Overriden in derived classes
0132 }
0133 
0134 void CheckBase::VisitMacroDefined(const Token &)
0135 {
0136     // Overriden in derived classes
0137 }
0138 
0139 void CheckBase::VisitDefined(const Token &, const SourceRange &)
0140 {
0141     // Overriden in derived classes
0142 }
0143 
0144 void CheckBase::VisitIfdef(clang::SourceLocation, const clang::Token &)
0145 {
0146     // Overriden in derived classes
0147 }
0148 
0149 void CheckBase::VisitIfndef(SourceLocation, const Token &)
0150 {
0151     // Overriden in derived classes
0152 }
0153 
0154 void CheckBase::VisitIf(SourceLocation, SourceRange, clang::PPCallbacks::ConditionValueKind)
0155 {
0156     // Overriden in derived classes
0157 }
0158 
0159 void CheckBase::VisitElif(SourceLocation, SourceRange, clang::PPCallbacks::ConditionValueKind, SourceLocation)
0160 {
0161     // Overriden in derived classes
0162 }
0163 
0164 void CheckBase::VisitElse(SourceLocation, SourceLocation)
0165 {
0166     // Overriden in derived classes
0167 }
0168 
0169 void CheckBase::VisitEndif(SourceLocation, SourceLocation)
0170 {
0171     // Overriden in derived classes
0172 }
0173 
0174 void CheckBase::VisitInclusionDirective(clang::SourceLocation,
0175                                         const clang::Token &,
0176                                         clang::StringRef,
0177                                         bool,
0178                                         clang::CharSourceRange,
0179                                         clazy::OptionalFileEntryRef,
0180                                         clang::StringRef,
0181                                         clang::StringRef,
0182                                         const clang::Module *,
0183                                         clang::SrcMgr::CharacteristicKind)
0184 {
0185     // Overriden in derived classes
0186 }
0187 
0188 void CheckBase::enablePreProcessorCallbacks()
0189 {
0190     Preprocessor &pi = m_context->ci.getPreprocessor();
0191     pi.addPPCallbacks(std::unique_ptr<PPCallbacks>(m_preprocessorCallbacks));
0192 }
0193 
0194 bool CheckBase::shouldIgnoreFile(SourceLocation loc) const
0195 {
0196     if (m_filesToIgnore.empty()) {
0197         return false;
0198     }
0199 
0200     if (!loc.isValid()) {
0201         return true;
0202     }
0203 
0204     std::string filename = static_cast<std::string>(sm().getFilename(loc));
0205 
0206     return clazy::any_of(m_filesToIgnore, [filename](const std::string &ignored) {
0207         return clazy::contains(filename, ignored);
0208     });
0209 }
0210 
0211 void CheckBase::emitWarning(const clang::Decl *d, const std::string &error, bool printWarningTag)
0212 {
0213     emitWarning(d->getBeginLoc(), error, printWarningTag);
0214 }
0215 
0216 void CheckBase::emitWarning(const clang::Stmt *s, const std::string &error, bool printWarningTag)
0217 {
0218     emitWarning(s->getBeginLoc(), error, printWarningTag);
0219 }
0220 
0221 void CheckBase::emitWarning(clang::SourceLocation loc, const std::string &error, bool printWarningTag)
0222 {
0223     emitWarning(loc, error, {}, printWarningTag);
0224 }
0225 
0226 void CheckBase::emitWarning(clang::SourceLocation loc, std::string error, const std::vector<FixItHint> &fixits, bool printWarningTag)
0227 {
0228     if (m_context->suppressionManager.isSuppressed(m_name, loc, sm(), lo())) {
0229         return;
0230     }
0231 
0232     if (m_context->shouldIgnoreFile(loc)) {
0233         return;
0234     }
0235 
0236     if (loc.isMacroID()) {
0237         if (warningAlreadyEmitted(loc)) {
0238             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
0239         }
0240         m_emittedWarningsInMacro.push_back(loc.getRawEncoding());
0241     }
0242 
0243     if (printWarningTag) {
0244         error += m_tag;
0245     }
0246 
0247     reallyEmitWarning(loc, error, fixits);
0248 
0249     for (const auto &l : m_queuedManualInterventionWarnings) {
0250         std::string msg("FixIt failed, requires manual intervention: ");
0251         if (!l.second.empty()) {
0252             msg += ' ' + l.second;
0253         }
0254 
0255         reallyEmitWarning(l.first, msg + m_tag, {});
0256     }
0257 
0258     m_queuedManualInterventionWarnings.clear();
0259 }
0260 
0261 void CheckBase::emitInternalError(SourceLocation loc, std::string error)
0262 {
0263     llvm::errs() << m_tag << ": internal error: " << error << " at " << loc.printToString(sm()) << "\n";
0264 }
0265 
0266 void CheckBase::reallyEmitWarning(clang::SourceLocation loc, const std::string &error, const std::vector<FixItHint> &fixits)
0267 {
0268     FullSourceLoc full(loc, sm());
0269     auto &engine = m_context->ci.getDiagnostics();
0270     auto severity =
0271         (m_context->treatAsError(m_name) || (engine.getWarningsAsErrors() && !m_context->userDisabledWError())) ? DiagnosticIDs::Error : DiagnosticIDs::Warning;
0272     unsigned id = engine.getDiagnosticIDs()->getCustomDiagID(severity, error.c_str());
0273     DiagnosticBuilder B = engine.Report(full, id);
0274     for (const FixItHint &fixit : fixits) {
0275         if (!fixit.isNull()) {
0276             B.AddFixItHint(fixit);
0277         }
0278     }
0279 }
0280 
0281 void CheckBase::queueManualFixitWarning(clang::SourceLocation loc, const std::string &message)
0282 {
0283     if (!manualFixitAlreadyQueued(loc)) {
0284         m_queuedManualInterventionWarnings.push_back({loc, message});
0285         m_emittedManualFixItsWarningsInMacro.push_back(loc.getRawEncoding());
0286     }
0287 }
0288 
0289 bool CheckBase::warningAlreadyEmitted(SourceLocation loc) const
0290 {
0291     PresumedLoc ploc = sm().getPresumedLoc(loc);
0292     for (auto rawLoc : m_emittedWarningsInMacro) {
0293         SourceLocation l = SourceLocation::getFromRawEncoding(rawLoc);
0294         PresumedLoc p = sm().getPresumedLoc(l);
0295         if (Utils::presumedLocationsEqual(p, ploc)) {
0296             return true;
0297         }
0298     }
0299 
0300     return false;
0301 }
0302 
0303 bool CheckBase::manualFixitAlreadyQueued(SourceLocation loc) const
0304 {
0305     PresumedLoc ploc = sm().getPresumedLoc(loc);
0306     for (auto loc : m_emittedManualFixItsWarningsInMacro) {
0307         SourceLocation l = SourceLocation::getFromRawEncoding(loc);
0308         PresumedLoc p = sm().getPresumedLoc(l);
0309         if (Utils::presumedLocationsEqual(p, ploc)) {
0310             return true;
0311         }
0312     }
0313 
0314     return false;
0315 }
0316 
0317 bool CheckBase::isOptionSet(const std::string &optionName) const
0318 {
0319     const std::string qualifiedName = name() + '-' + optionName;
0320     return m_context->isOptionSet(qualifiedName);
0321 }
0322 
0323 ClazyAstMatcherCallback::ClazyAstMatcherCallback(CheckBase *check)
0324     : MatchFinder::MatchCallback()
0325     , m_check(check)
0326 {
0327 }