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 ¯oNameTok, const MacroDefinition &md, 0062 SourceRange range, const MacroArgs *) 0063 { 0064 check->VisitMacroExpands(macroNameTok, range, md.getMacroInfo()); 0065 } 0066 0067 void ClazyPreprocessorCallbacks::Defined(const Token ¯oNameTok, const MacroDefinition &, SourceRange range) 0068 { 0069 check->VisitDefined(macroNameTok, range); 0070 } 0071 0072 void ClazyPreprocessorCallbacks::Ifdef(SourceLocation loc, const Token ¯oNameTok, const MacroDefinition &) 0073 { 0074 check->VisitIfdef(loc, macroNameTok); 0075 } 0076 0077 void ClazyPreprocessorCallbacks::Ifndef(SourceLocation loc, const Token ¯oNameTok, 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 ¯oNameTok, 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 }