File indexing completed on 2024-04-28 16:57:48

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