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 ¯oNameTok, const MacroDefinition &md, SourceRange range, const MacroArgs *) 0046 { 0047 check->VisitMacroExpands(macroNameTok, range, md.getMacroInfo()); 0048 } 0049 0050 void ClazyPreprocessorCallbacks::Defined(const Token ¯oNameTok, const MacroDefinition &, SourceRange range) 0051 { 0052 check->VisitDefined(macroNameTok, range); 0053 } 0054 0055 void ClazyPreprocessorCallbacks::Ifdef(SourceLocation loc, const Token ¯oNameTok, const MacroDefinition &) 0056 { 0057 check->VisitIfdef(loc, macroNameTok); 0058 } 0059 0060 void ClazyPreprocessorCallbacks::Ifndef(SourceLocation loc, const Token ¯oNameTok, 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 ¯oNameTok, 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 }