File indexing completed on 2024-04-14 05:32:08
0001 /* 0002 SPDX-FileCopyrightText: 2016 Sergio Martins <smartins@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "SuppressionManager.h" 0008 #include "SourceCompatibilityHelpers.h" 0009 #include "clazy_stl.h" 0010 0011 #include <clang/Basic/SourceLocation.h> 0012 #include <clang/Basic/SourceManager.h> 0013 #include <clang/Basic/TokenKinds.h> 0014 #include <clang/Lex/Token.h> 0015 #include <llvm/Support/MemoryBuffer.h> 0016 #include <llvm/Support/raw_ostream.h> 0017 0018 #include <regex> 0019 #include <vector> 0020 0021 using namespace clang; 0022 0023 SuppressionManager::SuppressionManager() 0024 { 0025 } 0026 0027 bool SuppressionManager::isSuppressed(const std::string &checkName, 0028 clang::SourceLocation loc, 0029 const clang::SourceManager &sm, 0030 const clang::LangOptions &lo) const 0031 { 0032 if (loc.isMacroID()) { 0033 loc = sm.getExpansionLoc(loc); 0034 } 0035 0036 FileID fileID = sm.getFileID(loc); 0037 if (fileID.isInvalid()) { 0038 return false; 0039 } 0040 0041 auto it = m_processedFileIDs.find(fileID.getHashValue()); 0042 const bool notProcessedYet = (it == m_processedFileIDs.cend()); 0043 if (notProcessedYet) { 0044 parseFile(fileID, sm, lo); 0045 it = m_processedFileIDs.find(fileID.getHashValue()); 0046 } 0047 0048 Suppressions &suppressions = (*it).second; 0049 0050 // Case 1: clazy:skip, the whole file is skipped, regardless of which check 0051 if (suppressions.skipEntireFile) { 0052 return true; 0053 } 0054 0055 // Case 2: clazy:excludeall=foo, the check foo will be ignored for this file 0056 const bool checkIsSuppressed = suppressions.checksToSkip.find(checkName) != suppressions.checksToSkip.cend(); 0057 if (checkIsSuppressed) { 0058 return true; 0059 } 0060 0061 // Case 3: clazy:exclude=foo, the check foo will be ignored for this file in this line number 0062 if (loc.isInvalid()) { 0063 return false; 0064 } 0065 0066 const int lineNumber = sm.getSpellingLineNumber(loc); 0067 if (suppressions.skipNextLine.count(lineNumber) > 0) { 0068 suppressions.skipNextLine.erase(lineNumber); 0069 return true; 0070 } 0071 if (suppressions.checksToSkipByLine.find(LineAndCheckName(lineNumber, checkName)) != suppressions.checksToSkipByLine.cend()) 0072 return true; 0073 0074 return false; 0075 } 0076 0077 void SuppressionManager::parseFile(FileID id, const SourceManager &sm, const clang::LangOptions &lo) const 0078 { 0079 const unsigned hash = id.getHashValue(); 0080 auto it = m_processedFileIDs.insert({hash, Suppressions()}).first; 0081 Suppressions &suppressions = (*it).second; 0082 0083 bool invalid = false; 0084 auto buffer = clazy::getBuffer(sm, id, &invalid); 0085 if (invalid) { 0086 llvm::errs() << "SuppressionManager::parseFile: Invalid buffer "; 0087 if (buffer) { 0088 llvm::errs() << buffer->getBuffer() << "\n"; 0089 } 0090 return; 0091 } 0092 0093 auto lexer = GET_LEXER(id, buffer, sm, lo); 0094 lexer.SetCommentRetentionState(true); 0095 0096 Token token; 0097 while (!lexer.LexFromRawLexer(token)) { 0098 if (token.getKind() == tok::comment) { 0099 std::string comment = Lexer::getSpelling(token, sm, lo); 0100 0101 if (clazy::contains(comment, "clazy:skip")) { 0102 suppressions.skipEntireFile = true; 0103 return; 0104 } 0105 0106 if (clazy::contains(comment, "NOLINTNEXTLINE")) { 0107 bool invalid = false; 0108 const int nextLineNumber = sm.getSpellingLineNumber(token.getLocation(), &invalid) + 1; 0109 if (invalid) { 0110 llvm::errs() << "SuppressionManager::parseFile: Invalid line number for token location where NOLINTNEXTLINE was found\n"; 0111 continue; 0112 } 0113 0114 suppressions.skipNextLine.insert(nextLineNumber); 0115 } 0116 0117 static std::regex rx(R"(clazy:excludeall=(.*?)(\s|$))"); 0118 std::smatch match; 0119 if (regex_search(comment, match, rx) && match.size() > 1) { 0120 std::vector<std::string> checks = clazy::splitString(match[1], ','); 0121 suppressions.checksToSkip.insert(checks.cbegin(), checks.cend()); 0122 } 0123 0124 const int lineNumber = sm.getSpellingLineNumber(token.getLocation()); 0125 if (lineNumber < 0) { 0126 llvm::errs() << "SuppressionManager::parseFile: Invalid line number " << lineNumber << "\n"; 0127 continue; 0128 } 0129 0130 static std::regex rx2(R"(clazy:exclude=(.*?)(\s|$))"); 0131 if (regex_search(comment, match, rx2) && match.size() > 1) { 0132 std::vector<std::string> checks = clazy::splitString(match[1], ','); 0133 0134 for (const std::string &checkName : checks) { 0135 suppressions.checksToSkipByLine.insert(LineAndCheckName(lineNumber, checkName)); 0136 } 0137 } 0138 } 0139 } 0140 }