Line data Source code
1 : /* 2 : * Cppcheck - A tool for static C/C++ code analysis 3 : * Copyright (C) 2007-2024 Cppcheck team. 4 : * 5 : * This program is free software: you can redistribute it and/or modify 6 : * it under the terms of the GNU General Public License as published by 7 : * the Free Software Foundation, either version 3 of the License, or 8 : * (at your option) any later version. 9 : * 10 : * This program is distributed in the hope that it will be useful, 11 : * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 : * GNU General Public License for more details. 14 : * 15 : * You should have received a copy of the GNU General Public License 16 : * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 : */ 18 : 19 : #include "checkersreport.h" 20 : 21 : #include "checkers.h" 22 : #include "errortypes.h" 23 : #include "settings.h" 24 : 25 : #include <map> 26 : #include <sstream> 27 : #include <unordered_set> 28 : #include <vector> 29 : 30 0 : static bool isCppcheckPremium(const Settings& settings) { 31 0 : return (settings.cppcheckCfgProductName.compare(0, 16, "Cppcheck Premium") == 0); 32 : } 33 : 34 0 : static bool isMisraRuleActive(const std::set<std::string>& activeCheckers, const std::string& rule) { 35 0 : if (activeCheckers.count("Misra C: " + rule)) 36 0 : return true; 37 0 : if (rule == "1.1") 38 0 : return true; // syntax error 39 0 : if (rule == "1.3") 40 0 : return true; // undefined behavior 41 0 : if (rule == "2.1") 42 0 : return activeCheckers.count("CheckCondition::alwaysTrueFalse") != 0; 43 0 : if (rule == "2.6") 44 0 : return activeCheckers.count("CheckOther::checkUnusedLabel") != 0; 45 0 : if (rule == "2.8") 46 0 : return activeCheckers.count("CheckUnusedVar::checkFunctionVariableUsage") != 0; 47 0 : if (rule == "5.3") 48 0 : return activeCheckers.count("CheckOther::checkShadowVariables") != 0; 49 0 : if (rule == "8.13") 50 0 : return activeCheckers.count("CheckOther::checkConstPointer") != 0; 51 0 : if (rule == "9.1") 52 0 : return true; // uninitvar 53 0 : if (rule == "12.5") 54 0 : return activeCheckers.count("CheckOther::checkConstPointer") != 0; 55 0 : if (rule == "14.3") 56 0 : return activeCheckers.count("CheckCondition::alwaysTrueFalse") != 0; 57 0 : if (rule == "17.5") 58 0 : return activeCheckers.count("CheckBufferOverrun::argumentSize") != 0; 59 0 : if (rule == "18.1") 60 0 : return activeCheckers.count("CheckBufferOverrun::pointerArithmetic") != 0; 61 0 : if (rule == "18.2") 62 0 : return activeCheckers.count("CheckOther::checkComparePointers") != 0; 63 0 : if (rule == "18.3") 64 0 : return activeCheckers.count("CheckOther::checkComparePointers") != 0; 65 0 : if (rule == "18.6") 66 0 : return true; // danlingLifetime => error 67 0 : if (rule == "19.1") 68 0 : return activeCheckers.count("CheckOther::checkOverlappingWrite") != 0; 69 0 : if (rule == "20.6") 70 0 : return true; // preprocessorErrorDirective 71 0 : if (rule == "21.13") 72 0 : return activeCheckers.count("CheckFunctions::invalidFunctionUsage") != 0; 73 0 : if (rule == "21.17") 74 0 : return activeCheckers.count("CheckBufferOverrun::bufferOverflow") != 0; 75 0 : if (rule == "21.18") 76 0 : return activeCheckers.count("CheckBufferOverrun::bufferOverflow") != 0; 77 0 : if (rule == "22.1") 78 0 : return true; // memleak => error 79 0 : if (rule == "22.2") 80 0 : return activeCheckers.count("CheckAutoVariables::autoVariables") != 0; 81 0 : if (rule == "22.3") 82 0 : return activeCheckers.count("CheckIO::checkFileUsage") != 0; 83 0 : if (rule == "22.4") 84 0 : return activeCheckers.count("CheckIO::checkFileUsage") != 0; 85 0 : if (rule == "22.6") 86 0 : return activeCheckers.count("CheckIO::checkFileUsage") != 0; 87 : 88 0 : return false; 89 : } 90 : 91 25 : CheckersReport::CheckersReport(const Settings& settings, const std::set<std::string>& activeCheckers) 92 25 : : mSettings(settings), mActiveCheckers(activeCheckers) 93 25 : {} 94 : 95 0 : int CheckersReport::getActiveCheckersCount() 96 : { 97 0 : if (mAllCheckersCount == 0) { 98 0 : countCheckers(); 99 : } 100 0 : return mActiveCheckersCount; 101 : } 102 : 103 0 : int CheckersReport::getAllCheckersCount() 104 : { 105 0 : if (mAllCheckersCount == 0) { 106 0 : countCheckers(); 107 : } 108 0 : return mAllCheckersCount; 109 : } 110 : 111 0 : void CheckersReport::countCheckers() 112 : { 113 0 : mActiveCheckersCount = mAllCheckersCount = 0; 114 : 115 0 : for (const auto& checkReq: checkers::allCheckers) { 116 0 : if (mActiveCheckers.count(checkReq.first) > 0) 117 0 : ++mActiveCheckersCount; 118 0 : ++mAllCheckersCount; 119 : } 120 0 : for (const auto& checkReq: checkers::premiumCheckers) { 121 0 : if (mActiveCheckers.count(checkReq.first) > 0) 122 0 : ++mActiveCheckersCount; 123 0 : ++mAllCheckersCount; 124 : } 125 0 : if (mSettings.premiumArgs.find("misra-c-") != std::string::npos || mSettings.addons.count("misra")) { 126 0 : for (const checkers::MisraInfo& info: checkers::misraC2012Rules) { 127 0 : const std::string rule = std::to_string(info.a) + "." + std::to_string(info.b); 128 0 : const bool active = isMisraRuleActive(mActiveCheckers, rule); 129 0 : if (active) 130 0 : ++mActiveCheckersCount; 131 0 : ++mAllCheckersCount; 132 : } 133 : } 134 0 : } 135 : 136 0 : std::string CheckersReport::getReport(const std::string& criticalErrors) const 137 : { 138 0 : std::ostringstream fout; 139 : 140 0 : fout << "Critical errors" << std::endl; 141 0 : fout << "---------------" << std::endl; 142 0 : if (!criticalErrors.empty()) { 143 0 : fout << "There was critical errors (" << criticalErrors << ")" << std::endl; 144 0 : fout << "All checking is skipped for a file with such error" << std::endl; 145 : } else { 146 0 : fout << "No critical errors, all files were checked." << std::endl; 147 0 : fout << "Important: Analysis is still not guaranteed to be 'complete' it is possible there are false negatives." << std::endl; 148 : } 149 : 150 0 : fout << std::endl << std::endl; 151 0 : fout << "Open source checkers" << std::endl; 152 0 : fout << "--------------------" << std::endl; 153 : 154 0 : int maxCheckerSize = 0; 155 0 : for (const auto& checkReq: checkers::allCheckers) { 156 0 : const std::string& checker = checkReq.first; 157 0 : if (checker.size() > maxCheckerSize) 158 0 : maxCheckerSize = checker.size(); 159 : } 160 0 : for (const auto& checkReq: checkers::allCheckers) { 161 0 : const std::string& checker = checkReq.first; 162 0 : const bool active = mActiveCheckers.count(checkReq.first) > 0; 163 0 : const std::string& req = checkReq.second; 164 0 : fout << (active ? "Yes " : "No ") << checker; 165 0 : if (!active && !req.empty()) 166 0 : fout << std::string(maxCheckerSize + 4 - checker.size(), ' ') << "require:" + req; 167 0 : fout << std::endl; 168 : } 169 : 170 0 : const bool cppcheckPremium = isCppcheckPremium(mSettings); 171 : 172 0 : auto reportSection = [&fout, cppcheckPremium] 173 : (const std::string& title, 174 : const Settings& settings, 175 : const std::set<std::string>& activeCheckers, 176 : const std::map<std::string, std::string>& premiumCheckers, 177 0 : const std::string& substring) { 178 0 : fout << std::endl << std::endl; 179 0 : fout << title << std::endl; 180 0 : fout << std::string(title.size(), '-') << std::endl; 181 0 : if (!cppcheckPremium) { 182 0 : fout << "Not available, Cppcheck Premium is not used" << std::endl; 183 0 : return; 184 : } 185 0 : int maxCheckerSize = 0; 186 0 : for (const auto& checkReq: premiumCheckers) { 187 0 : const std::string& checker = checkReq.first; 188 0 : if (checker.find(substring) != std::string::npos && checker.size() > maxCheckerSize) 189 0 : maxCheckerSize = checker.size(); 190 : } 191 0 : for (const auto& checkReq: premiumCheckers) { 192 0 : const std::string& checker = checkReq.first; 193 0 : if (checker.find(substring) == std::string::npos) 194 0 : continue; 195 0 : std::string req = checkReq.second; 196 0 : bool active = cppcheckPremium && activeCheckers.count(checker) > 0; 197 0 : if (substring == "::") { 198 0 : if (req == "warning") 199 0 : active &= settings.severity.isEnabled(Severity::warning); 200 0 : else if (req == "style") 201 0 : active &= settings.severity.isEnabled(Severity::style); 202 0 : else if (req == "portability") 203 0 : active &= settings.severity.isEnabled(Severity::portability); 204 0 : else if (!req.empty()) 205 0 : active = false; // FIXME: handle req 206 : } 207 0 : fout << (active ? "Yes " : "No ") << checker; 208 0 : if (!cppcheckPremium) { 209 0 : if (!req.empty()) 210 0 : req = "premium," + req; 211 : else 212 0 : req = "premium"; 213 : } 214 0 : if (!req.empty()) 215 0 : req = "require:" + req; 216 0 : if (!active) 217 0 : fout << std::string(maxCheckerSize + 4 - checker.size(), ' ') << req; 218 0 : fout << std::endl; 219 : } 220 0 : }; 221 : 222 0 : reportSection("Premium checkers", mSettings, mActiveCheckers, checkers::premiumCheckers, "::"); 223 0 : reportSection("Autosar", mSettings, mActiveCheckers, checkers::premiumCheckers, "Autosar: "); 224 0 : reportSection("Cert C", mSettings, mActiveCheckers, checkers::premiumCheckers, "Cert C: "); 225 0 : reportSection("Cert C++", mSettings, mActiveCheckers, checkers::premiumCheckers, "Cert C++: "); 226 : 227 0 : int misra = 0; 228 0 : if (mSettings.premiumArgs.find("misra-c-2012") != std::string::npos) 229 0 : misra = 2012; 230 0 : else if (mSettings.premiumArgs.find("misra-c-2023") != std::string::npos) 231 0 : misra = 2023; 232 0 : else if (mSettings.addons.count("misra")) 233 0 : misra = 2012; 234 : 235 0 : if (misra == 0) { 236 0 : fout << std::endl << std::endl; 237 0 : fout << "Misra C" << std::endl; 238 0 : fout << "-------" << std::endl; 239 0 : fout << "Misra is not enabled" << std::endl; 240 : } else { 241 0 : fout << std::endl << std::endl; 242 0 : fout << "Misra C " << misra << std::endl; 243 0 : fout << "------------" << std::endl; 244 0 : for (const checkers::MisraInfo& info: checkers::misraC2012Rules) { 245 0 : const std::string rule = std::to_string(info.a) + "." + std::to_string(info.b); 246 0 : const bool active = isMisraRuleActive(mActiveCheckers, rule); 247 0 : fout << (active ? "Yes " : "No ") << "Misra C " << misra << ": " << rule; 248 0 : std::string extra; 249 0 : if (misra == 2012 && info.amendment >= 1) 250 0 : extra = " amendment:" + std::to_string(info.amendment); 251 0 : std::string reqs; 252 0 : if (info.amendment >= 3) 253 0 : reqs += ",premium"; 254 0 : if (!active && !reqs.empty()) 255 0 : extra += " require:" + reqs.substr(1); 256 0 : if (!extra.empty()) 257 0 : fout << std::string(7 - rule.size(), ' ') << extra; 258 0 : fout << '\n'; 259 : } 260 : } 261 : 262 0 : reportSection("Misra C++ 2008", mSettings, mActiveCheckers, checkers::premiumCheckers, "Misra C++ 2008: "); 263 0 : reportSection("Misra C++ 2023", mSettings, mActiveCheckers, checkers::premiumCheckers, "Misra C++ 2023: "); 264 : 265 0 : return fout.str(); 266 : }