File indexing completed on 2024-04-28 16:57:52
0001 /* 0002 This file is part of the clazy static checker. 0003 0004 Copyright (C) 2016 Sergio Martins <smartins@kde.org> 0005 0006 This library is free software; you can redistribute it and/or 0007 modify it under the terms of the GNU Library General Public 0008 License as published by the Free Software Foundation; either 0009 version 2 of the License, or (at your option) any later version. 0010 0011 This library is distributed in the hope that it will be useful, 0012 but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0014 Library General Public License for more details. 0015 0016 You should have received a copy of the GNU Library General Public License 0017 along with this library; see the file COPYING.LIB. If not, write to 0018 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0019 Boston, MA 02110-1301, USA. 0020 */ 0021 0022 #include "SuppressionManager.h" 0023 #include "SourceCompatibilityHelpers.h" 0024 #include "clazy_stl.h" 0025 0026 #include <clang/Basic/SourceManager.h> 0027 #include <clang/Basic/SourceLocation.h> 0028 #include <clang/Basic/TokenKinds.h> 0029 #include <clang/Lex/Token.h> 0030 #include <llvm/Support/MemoryBuffer.h> 0031 #include <llvm/Support/raw_ostream.h> 0032 0033 #include <vector> 0034 0035 using namespace clang; 0036 using namespace std; 0037 0038 SuppressionManager::SuppressionManager() 0039 { 0040 } 0041 0042 bool SuppressionManager::isSuppressed(const std::string &checkName, clang::SourceLocation loc, 0043 const clang::SourceManager &sm, const clang::LangOptions &lo) const 0044 { 0045 if (loc.isMacroID()) 0046 loc = sm.getExpansionLoc(loc); 0047 0048 FileID fileID = sm.getFileID(loc); 0049 if (fileID.isInvalid()) 0050 return false; 0051 0052 auto it = m_processedFileIDs.find(fileID.getHashValue()); 0053 const bool notProcessedYet = (it == m_processedFileIDs.cend()); 0054 if (notProcessedYet) { 0055 parseFile(fileID, sm, lo); 0056 it = m_processedFileIDs.find(fileID.getHashValue()); 0057 } 0058 0059 Suppressions &suppressions = (*it).second; 0060 0061 // Case 1: clazy:skip, the whole file is skipped, regardless of which check 0062 if (suppressions.skipEntireFile) 0063 return true; 0064 0065 // Case 2: clazy:excludeall=foo, the check foo will be ignored for this file 0066 const bool checkIsSuppressed = suppressions.checksToSkip.find(checkName) != suppressions.checksToSkip.cend(); 0067 if (checkIsSuppressed) 0068 return true; 0069 0070 // Case 3: clazy:exclude=foo, the check foo will be ignored for this file in this line number 0071 if (loc.isInvalid()) 0072 return false; 0073 0074 const int lineNumber = sm.getSpellingLineNumber(loc); 0075 const bool checkIsSuppressedByLine = suppressions.checksToSkipByLine.find(LineAndCheckName(lineNumber, checkName)) != suppressions.checksToSkipByLine.cend(); 0076 return checkIsSuppressedByLine; 0077 } 0078 0079 void SuppressionManager::parseFile(FileID id, const SourceManager &sm, const clang::LangOptions &lo) const 0080 { 0081 const unsigned hash = id.getHashValue(); 0082 auto it = m_processedFileIDs.insert({hash, Suppressions()}).first; 0083 Suppressions &suppressions = (*it).second; 0084 0085 bool invalid = false; 0086 auto buffer = clazy::getBuffer(sm, id, &invalid); 0087 if (invalid) { 0088 llvm::errs() << "SuppressionManager::parseFile: Invalid buffer "; 0089 if (buffer) 0090 llvm::errs() << buffer->getBuffer() << "\n"; 0091 return; 0092 } 0093 0094 auto lexer = GET_LEXER(id, buffer, sm, lo); 0095 lexer.SetCommentRetentionState(true); 0096 0097 Token token; 0098 while (!lexer.LexFromRawLexer(token)) { 0099 if (token.getKind() == tok::comment) { 0100 std::string comment = Lexer::getSpelling(token, sm, lo); 0101 0102 if (clazy::contains(comment, "clazy:skip")) { 0103 suppressions.skipEntireFile = true; 0104 return; 0105 } 0106 0107 static regex rx(R"(clazy:excludeall=(.*?)(\s|$))"); 0108 smatch match; 0109 if (regex_search(comment, match, rx) && match.size() > 1) { 0110 vector<string> checks = clazy::splitString(match[1], ','); 0111 suppressions.checksToSkip.insert(checks.cbegin(), checks.cend()); 0112 } 0113 0114 const int lineNumber = sm.getSpellingLineNumber(token.getLocation()); 0115 if (lineNumber < 0) { 0116 llvm::errs() << "SuppressionManager::parseFile: Invalid line number " << lineNumber << "\n"; 0117 continue; 0118 } 0119 0120 static regex rx2(R"(clazy:exclude=(.*?)(\s|$))"); 0121 if (regex_search(comment, match, rx2) && match.size() > 1) { 0122 vector<string> checks = clazy::splitString(match[1], ','); 0123 0124 for (const string &checkName : checks) { 0125 suppressions.checksToSkipByLine.insert(LineAndCheckName(lineNumber, checkName)); 0126 } 0127 } 0128 } 0129 } 0130 }