diff --git a/src/Clazy.cpp b/src/Clazy.cpp index fd3343e..3e21377 100644 --- a/src/Clazy.cpp +++ b/src/Clazy.cpp @@ -1,411 +1,413 @@ /* This file is part of the clazy static checker. Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Sérgio Martins Copyright (C) 2015-2017 Sergio Martins This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "Utils.h" #include "Clazy.h" #include "clazy_stl.h" #include "checkbase.h" #include "AccessSpecifierManager.h" #include "SourceCompatibilityHelpers.h" #include "FixItExporter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace clang; using namespace std; using namespace clang::ast_matchers; static void manuallyPopulateParentMap(ParentMap *map, Stmt *s) { if (!s) return; for (Stmt *child : s->children()) { llvm::errs() << "Patching " << child->getStmtClassName() << "\n"; map->setParent(child, s); manuallyPopulateParentMap(map, child); } } ClazyASTConsumer::ClazyASTConsumer(ClazyContext *context) : m_context(context) { #ifndef CLAZY_DISABLE_AST_MATCHERS m_matchFinder = new clang::ast_matchers::MatchFinder(); #endif } void ClazyASTConsumer::addCheck(const std::pair &check) { CheckBase *checkBase = check.first; #ifndef CLAZY_DISABLE_AST_MATCHERS checkBase->registerASTMatchers(*m_matchFinder); #endif //m_createdChecks.push_back(checkBase); const RegisteredCheck &rcheck = check.second; if (rcheck.options & RegisteredCheck::Option_VisitsStmts) m_checksToVisitStmts.push_back(checkBase); if (rcheck.options & RegisteredCheck::Option_VisitsDecls) m_checksToVisitDecls.push_back(checkBase); } ClazyASTConsumer::~ClazyASTConsumer() { #ifndef CLAZY_DISABLE_AST_MATCHERS delete m_matchFinder; #endif delete m_context; } bool ClazyASTConsumer::VisitDecl(Decl *decl) { if (AccessSpecifierManager *a = m_context->accessSpecifierManager) // Needs to visit system headers too (qobject.h for example) a->VisitDeclaration(decl); const SourceLocation locStart = clazy::getLocStart(decl); if (locStart.isInvalid() || m_context->sm.isInSystemHeader(locStart)) return true; const bool isFromIgnorableInclude = m_context->ignoresIncludedFiles() && !Utils::isMainFile(m_context->sm, locStart); m_context->lastDecl = decl; if (auto fdecl = dyn_cast(decl)) { m_context->lastFunctionDecl = fdecl; if (auto mdecl = dyn_cast(fdecl)) m_context->lastMethodDecl = mdecl; } for (CheckBase *check : m_checksToVisitDecls) { if (!(isFromIgnorableInclude && check->canIgnoreIncludes())) check->VisitDecl(decl); } return true; } bool ClazyASTConsumer::VisitStmt(Stmt *stm) { const SourceLocation locStart = clazy::getLocStart(stm); if (locStart.isInvalid() || m_context->sm.isInSystemHeader(locStart)) return true; if (!m_context->parentMap) { if (m_context->ci.getDiagnostics().hasUnrecoverableErrorOccurred()) return false; // ParentMap sometimes crashes when there were errors. Doesn't like a botched AST. m_context->parentMap = new ParentMap(stm); } ParentMap *parentMap = m_context->parentMap; // Workaround llvm bug: Crashes creating a parent map when encountering Catch Statements. if (lastStm && isa(lastStm) && !parentMap->hasParent(stm)) { parentMap->setParent(stm, lastStm); manuallyPopulateParentMap(parentMap, stm); } lastStm = stm; // clang::ParentMap takes a root statement, but there's no root statement in the AST, the root is a declaration // So add to parent map each time we go into a different hierarchy if (!parentMap->hasParent(stm)) parentMap->addStmt(stm); const bool isFromIgnorableInclude = m_context->ignoresIncludedFiles() && !Utils::isMainFile(m_context->sm, locStart); for (CheckBase *check : m_checksToVisitStmts) { if (!(isFromIgnorableInclude && check->canIgnoreIncludes())) check->VisitStmt(stm); } return true; } void ClazyASTConsumer::HandleTranslationUnit(ASTContext &ctx) { // FIXME: EndSourceFile() is called automatically, but not BeginsSourceFile() if (m_context->exporter) m_context->exporter->BeginSourceFile(clang::LangOptions()); if ((m_context->options & ClazyContext::ClazyOption_OnlyQt) && !m_context->isQt()) return; // Run our RecursiveAstVisitor based checks: TraverseDecl(ctx.getTranslationUnitDecl()); #ifndef CLAZY_DISABLE_AST_MATCHERS // Run our AstMatcher base checks: m_matchFinder->matchAST(ctx); #endif } static bool parseArgument(const string &arg, vector &args) { auto it = clazy::find(args, arg); if (it != args.end()) { args.erase(it); return true; } return false; } ClazyASTAction::ClazyASTAction() : PluginASTAction() , m_checkManager(CheckManager::instance()) { } std::unique_ptr ClazyASTAction::CreateASTConsumer(CompilerInstance &, llvm::StringRef) { // NOTE: This method needs to be kept reentrant (but not necessarily thread-safe) // Might be called from multiple threads via libclang, each thread operates on a different instance though std::lock_guard lock(CheckManager::lock()); auto astConsumer = std::unique_ptr(new ClazyASTConsumer(m_context)); auto createdChecks = m_checkManager->createChecks(m_checks, m_context); for (auto check : createdChecks) { astConsumer->addCheck(check); } return std::unique_ptr(astConsumer.release()); } static std::string getEnvVariable(const char *name) { const char *result = getenv(name); if (result) return result; else return std::string(); } bool ClazyASTAction::ParseArgs(const CompilerInstance &ci, const std::vector &args_) { // NOTE: This method needs to be kept reentrant (but not necessarily thread-safe) // Might be called from multiple threads via libclang, each thread operates on a different instance though std::vector args = args_; const string headerFilter = getEnvVariable("CLAZY_HEADER_FILTER"); const string ignoreDirs = getEnvVariable("CLAZY_IGNORE_DIRS"); std::string exportFixesFilename; if (parseArgument("help", args)) { m_context = new ClazyContext(ci, headerFilter, ignoreDirs, exportFixesFilename, {}, ClazyContext::ClazyOption_None); PrintHelp(llvm::errs()); return true; } if (parseArgument("export-fixes", args) || getenv("CLAZY_EXPORT_FIXES")) m_options |= ClazyContext::ClazyOption_ExportFixes; if (parseArgument("qt4-compat", args)) m_options |= ClazyContext::ClazyOption_Qt4Compat; if (parseArgument("only-qt", args)) m_options |= ClazyContext::ClazyOption_OnlyQt; if (parseArgument("qt-developer", args)) m_options |= ClazyContext::ClazyOption_QtDeveloper; if (parseArgument("visit-implicit-code", args)) m_options |= ClazyContext::ClazyOption_VisitImplicitCode; if (parseArgument("ignore-included-files", args)) m_options |= ClazyContext::ClazyOption_IgnoreIncludedFiles; if (parseArgument("export-fixes", args)) exportFixesFilename = args.at(0); m_context = new ClazyContext(ci, headerFilter, ignoreDirs, exportFixesFilename, {}, m_options); // This argument is for debugging purposes const bool dbgPrintRequestedChecks = parseArgument("print-requested-checks", args); { std::lock_guard lock(CheckManager::lock()); - m_checks = m_checkManager->requestedChecks(m_context, args); + m_checks = m_checkManager->requestedChecks(args, + m_options & ClazyContext::ClazyOption_Qt4Compat); } if (args.size() > 1) { // Too many arguments. llvm::errs() << "Too many arguments: "; for (const std::string &a : args) llvm::errs() << a << ' '; llvm::errs() << "\n"; PrintHelp(llvm::errs()); return false; } else if (args.size() == 1 && m_checks.empty()) { // Checks were specified but couldn't be found llvm::errs() << "Could not find checks in comma separated string " + args[0] + "\n"; PrintHelp(llvm::errs()); return false; } if (dbgPrintRequestedChecks) printRequestedChecks(); return true; } void ClazyASTAction::printRequestedChecks() const { llvm::errs() << "Requested checks: "; const unsigned int numChecks = m_checks.size(); for (unsigned int i = 0; i < numChecks; ++i) { llvm::errs() << m_checks.at(i).name; const bool isLast = i == numChecks - 1; if (!isLast) { llvm::errs() << ", "; } } llvm::errs() << "\n"; } void ClazyASTAction::PrintHelp(llvm::raw_ostream &ros) const { std::lock_guard lock(CheckManager::lock()); RegisteredCheck::List checks = m_checkManager->availableChecks(MaxCheckLevel); clazy::sort(checks, checkLessThanByLevel); ros << "Available checks and FixIts:\n\n"; int lastPrintedLevel = -1; const auto numChecks = checks.size(); for (unsigned int i = 0; i < numChecks; ++i) { const RegisteredCheck &check = checks[i]; const string levelStr = "level" + to_string(check.level); if (lastPrintedLevel < check.level) { lastPrintedLevel = check.level; if (check.level > 0) ros << "\n"; ros << "- Checks from " << levelStr << ":\n"; } const string relativeReadmePath = "src/checks/" + levelStr + "/README-" + check.name + ".md"; auto padded = check.name; padded.insert(padded.end(), 39 - padded.size(), ' '); ros << " - " << check.name;; auto fixits = m_checkManager->availableFixIts(check.name); if (!fixits.empty()) { ros << " ("; bool isFirst = true; for (const auto& fixit : fixits) { if (isFirst) { isFirst = false; } else { ros << ','; } ros << fixit.name; } ros << ')'; } ros << "\n"; } ros << "\nIf nothing is specified, all checks from level0 and level1 will be run.\n\n"; ros << "To specify which checks to enable set the CLAZY_CHECKS env variable, for example:\n"; ros << " export CLAZY_CHECKS=\"level0\"\n"; ros << " export CLAZY_CHECKS=\"level0,reserve-candidates,qstring-allocations\"\n"; ros << " export CLAZY_CHECKS=\"reserve-candidates\"\n\n"; ros << "or pass as compiler arguments, for example:\n"; ros << " -Xclang -plugin-arg-clazy -Xclang reserve-candidates,qstring-allocations\n"; ros << "\n"; } ClazyStandaloneASTAction::ClazyStandaloneASTAction(const string &checkList, const string &headerFilter, const string &ignoreDirs, const string &exportFixesFilename, const std::vector &translationUnitPaths, ClazyContext::ClazyOptions options) : clang::ASTFrontendAction() , m_checkList(checkList.empty() ? "level1" : checkList) , m_headerFilter(headerFilter.empty() ? getEnvVariable("CLAZY_HEADER_FILTER") : headerFilter) , m_ignoreDirs(ignoreDirs.empty() ? getEnvVariable("CLAZY_IGNORE_DIRS") : ignoreDirs) , m_exportFixesFilename(exportFixesFilename) , m_translationUnitPaths(translationUnitPaths) , m_options(options) { } unique_ptr ClazyStandaloneASTAction::CreateASTConsumer(CompilerInstance &ci, llvm::StringRef) { auto context = new ClazyContext(ci, m_headerFilter, m_ignoreDirs, m_exportFixesFilename, m_translationUnitPaths, m_options); auto astConsumer = new ClazyASTConsumer(context); auto cm = CheckManager::instance(); vector checks; checks.push_back(m_checkList); - const RegisteredCheck::List requestedChecks = cm->requestedChecks(context, checks); + const bool qt4Compat = m_options & ClazyContext::ClazyOption_Qt4Compat; + const RegisteredCheck::List requestedChecks = cm->requestedChecks(checks, qt4Compat); if (requestedChecks.size() == 0) { llvm::errs() << "No checks were requested!\n" << "\n"; return nullptr; } auto createdChecks = cm->createChecks(requestedChecks, context); for (const auto &check : createdChecks) { astConsumer->addCheck(check); } return unique_ptr(astConsumer); } volatile int ClazyPluginAnchorSource = 0; static FrontendPluginRegistry::Add X("clazy", "clang lazy plugin"); diff --git a/src/ClazyStandaloneMain.cpp b/src/ClazyStandaloneMain.cpp index 39af763..cd471a7 100644 --- a/src/ClazyStandaloneMain.cpp +++ b/src/ClazyStandaloneMain.cpp @@ -1,135 +1,155 @@ /* This file is part of the clazy static checker. Copyright (C) 2017 Sergio Martins This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // clazy:excludeall=non-pod-global-static #include "Clazy.h" #include "ClazyContext.h" #include "checks.json.h" #include #include #include #include #include #include #include namespace clang { class FrontendAction; } // namespace clang using namespace clang; using namespace clang::tooling; using namespace llvm; static llvm::cl::OptionCategory s_clazyCategory("clazy options"); static cl::opt s_checks("checks", cl::desc("Comma-separated list of clazy checks. Default is level1"), cl::init(""), cl::cat(s_clazyCategory)); static cl::opt s_exportFixes("export-fixes", cl::desc("YAML file to store suggested fixes in. The stored fixes can be applied to the input source code with clang-apply-replacements."), cl::init(""), cl::cat(s_clazyCategory)); static cl::opt s_qt4Compat("qt4-compat", cl::desc("Turns off checks not compatible with Qt 4"), cl::init(false), cl::cat(s_clazyCategory)); static cl::opt s_onlyQt("only-qt", cl::desc("Won't emit warnings for non-Qt files, or in other words, if -DQT_CORE_LIB is missing."), cl::init(false), cl::cat(s_clazyCategory)); static cl::opt s_qtDeveloper("qt-developer", cl::desc("For running clazy on Qt itself, optional, but honours specific guidelines"), cl::init(false), cl::cat(s_clazyCategory)); static cl::opt s_visitImplicitCode("visit-implicit-code", cl::desc("For visiting implicit code like compiler generated constructors. None of the built-in checks benefit from this, but can be useful for custom checks"), cl::init(false), cl::cat(s_clazyCategory)); static cl::opt s_ignoreIncludedFiles("ignore-included-files", cl::desc("Only emit warnings for the current file being compiled and ignore any includes. Useful for performance reasons."), cl::init(false), cl::cat(s_clazyCategory)); static cl::opt s_headerFilter("header-filter", cl::desc(R"(Regular expression matching the names of the headers to output diagnostics from. Diagnostics from the main file of each translation unit are always displayed.)"), cl::init(""), cl::cat(s_clazyCategory)); static cl::opt s_ignoreDirs("ignore-dirs", cl::desc(R"(Regular expression matching the names of the directories for which diagnostics should never be emitted. Useful for ignoring 3rdparty code.)"), cl::init(""), cl::cat(s_clazyCategory)); static cl::opt s_supportedChecks("supported-checks-json", cl::desc("Dump meta information about supported checks in JSON format."), cl::init(false), cl::cat(s_clazyCategory)); +static cl::opt s_listEnabledChecks("list-checks", cl::desc("List all enabled checks and exit."), + cl::init(false), cl::cat(s_clazyCategory)); + static cl::extrahelp s_commonHelp(CommonOptionsParser::HelpMessage); class ClazyToolActionFactory : public clang::tooling::FrontendActionFactory { public: ClazyToolActionFactory(std::vector paths) : FrontendActionFactory() , m_paths(std::move(paths)) { } FrontendAction *create() override { ClazyContext::ClazyOptions options = ClazyContext::ClazyOption_None; if (!s_exportFixes.getValue().empty()) options |= ClazyContext::ClazyOption_ExportFixes; if (s_qt4Compat.getValue()) options |= ClazyContext::ClazyOption_Qt4Compat; if (s_qtDeveloper.getValue()) options |= ClazyContext::ClazyOption_QtDeveloper; if (s_onlyQt.getValue()) options |= ClazyContext::ClazyOption_OnlyQt; if (s_visitImplicitCode.getValue()) options |= ClazyContext::ClazyOption_VisitImplicitCode; if (s_ignoreIncludedFiles.getValue()) options |= ClazyContext::ClazyOption_IgnoreIncludedFiles; // TODO: We need to agregate the fixes with previous run return new ClazyStandaloneASTAction(s_checks.getValue(), s_headerFilter.getValue(), s_ignoreDirs.getValue(), s_exportFixes.getValue(), m_paths, options); } std::vector m_paths; }; int main(int argc, const char **argv) { CommonOptionsParser optionsParser(argc, argv, s_clazyCategory, cl::ZeroOrMore); if (s_supportedChecks.getValue()) { std::cout << SUPPORTED_CHECKS_JSON_STR; return 0; } + if (s_listEnabledChecks.getValue()) { + std::string checksFromArgs = s_checks.getValue(); + std::vector checks = { checksFromArgs.empty() ? "level1" : checksFromArgs }; + const RegisteredCheck::List enabledChecks + = CheckManager::instance()->requestedChecks(checks, s_qt4Compat.getValue()); + + if (!enabledChecks.empty()) { + llvm::outs() << "Enabled checks:"; + for (const auto &check : enabledChecks) { + llvm::outs() << "\n " << check.name; + } + llvm::outs() << "\n"; + } + + return 0; + } + ClangTool tool(optionsParser.getCompilations(), optionsParser.getSourcePathList()); return tool.run(new ClazyToolActionFactory(optionsParser.getSourcePathList())); } diff --git a/src/checkmanager.cpp b/src/checkmanager.cpp index bb5c267..838e452 100644 --- a/src/checkmanager.cpp +++ b/src/checkmanager.cpp @@ -1,319 +1,319 @@ /* This file is part of the clazy static checker. Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Sérgio Martins Copyright (C) 2015 Sergio Martins This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "checkmanager.h" #include "clazy_stl.h" #include "ClazyContext.h" #include "Checks.h" #include #include #include #include #include #include #include using namespace clang; using namespace std; static const char * s_fixitNamePrefix = "fix-"; static const char * s_levelPrefix = "level"; std::mutex CheckManager::m_lock; CheckManager::CheckManager() { m_registeredChecks.reserve(100); registerChecks(); } bool CheckManager::checkExists(const string &name) const { return checkForName(m_registeredChecks, name) != m_registeredChecks.cend(); } CheckManager *CheckManager::instance() { static CheckManager s_instance; return &s_instance; } void CheckManager::registerCheck(const RegisteredCheck &check) { m_registeredChecks.push_back(check); } void CheckManager::registerFixIt(int id, const string &fixitName, const string &checkName) { if (!clazy::startsWith(fixitName, s_fixitNamePrefix)) { assert(false); return; } auto &fixits = m_fixitsByCheckName[checkName]; for (const auto& fixit : fixits) { if (fixit.name == fixitName) { // It can't exist assert(false); return; } } RegisteredFixIt fixit = {id, fixitName}; fixits.push_back(fixit); m_fixitByName.insert({fixitName, fixit}); } CheckBase* CheckManager::createCheck(const string &name, ClazyContext *context) { for (const auto& rc : m_registeredChecks) { if (rc.name == name) { return rc.factory(context); } } llvm::errs() << "Invalid check name " << name << "\n"; return nullptr; } string CheckManager::checkNameForFixIt(const string &fixitName) const { if (fixitName.empty()) return {}; for (auto ®isteredCheck : m_registeredChecks) { auto it = m_fixitsByCheckName.find(registeredCheck.name); if (it != m_fixitsByCheckName.end()) { auto &fixits = (*it).second; for (const RegisteredFixIt &fixit : fixits) { if (fixit.name == fixitName) return (*it).first; } } } return {}; } RegisteredCheck::List CheckManager::availableChecks(CheckLevel maxLevel) const { RegisteredCheck::List checks = m_registeredChecks; checks.erase(remove_if(checks.begin(), checks.end(), [maxLevel](const RegisteredCheck &r) { return r.level > maxLevel; }), checks.end()); return checks; } RegisteredCheck::List CheckManager::requestedChecksThroughEnv(vector &userDisabledChecks) const { static RegisteredCheck::List requestedChecksThroughEnv; static vector disabledChecksThroughEnv; if (requestedChecksThroughEnv.empty()) { const char *checksEnv = getenv("CLAZY_CHECKS"); if (checksEnv) { const string checksEnvStr = clazy::unquoteString(checksEnv); requestedChecksThroughEnv = checksEnvStr == "all_checks" ? availableChecks(CheckLevel2) : checksForCommaSeparatedString(checksEnvStr, /*by-ref=*/ disabledChecksThroughEnv); } } std::copy(disabledChecksThroughEnv.begin(), disabledChecksThroughEnv.end(), std::back_inserter(userDisabledChecks)); return requestedChecksThroughEnv; } RegisteredCheck::List::const_iterator CheckManager::checkForName(const RegisteredCheck::List &checks, const string &name) const { return clazy::find_if(checks, [name](const RegisteredCheck &r) { return r.name == name; } ); } RegisteredFixIt::List CheckManager::availableFixIts(const string &checkName) const { auto it = m_fixitsByCheckName.find(checkName); return it == m_fixitsByCheckName.end() ? RegisteredFixIt::List() : (*it).second; } static bool takeArgument(const string &arg, vector &args) { auto it = clazy::find(args, arg); if (it != args.end()) { args.erase(it, it + 1); return true; } return false; } -RegisteredCheck::List CheckManager::requestedChecks(const ClazyContext *context, std::vector &args) +RegisteredCheck::List CheckManager::requestedChecks(std::vector &args, bool qt4Compat) { RegisteredCheck::List result; // #1 Check if a level was specified static const vector levels = { "level0", "level1", "level2" }; const int numLevels = levels.size(); CheckLevel requestedLevel = CheckLevelUndefined; for (int i = 0; i < numLevels; ++i) { if (takeArgument(levels.at(i), args)) { requestedLevel = static_cast(i); break; } } if (args.size() > 1) // we only expect a level and a comma separated list of arguments return {}; if (args.size() == 1) { // #2 Process list of comma separated checks that were passed to compiler result = checksForCommaSeparatedString(args[0]); if (result.empty()) // User passed inexisting checks. return {}; } // #3 Append checks specified from env variable vector userDisabledChecks; RegisteredCheck::List checksFromEnv = requestedChecksThroughEnv(/*by-ref*/ userDisabledChecks); copy(checksFromEnv.cbegin(), checksFromEnv.cend(), back_inserter(result)); if (result.empty() && requestedLevel == CheckLevelUndefined) { // No checks or level specified, lets use the default level requestedLevel = DefaultCheckLevel; } // #4 Add checks from requested level RegisteredCheck::List checksFromRequestedLevel = checksForLevel(requestedLevel); clazy::append(checksFromRequestedLevel, result); clazy::sort_and_remove_dups(result, checkLessThan); CheckManager::removeChecksFromList(result, userDisabledChecks); - if (context->options & ClazyContext::ClazyOption_Qt4Compat) { + if (qt4Compat) { // #5 Remove Qt4 incompatible checks result.erase(remove_if(result.begin(), result.end(), [](const RegisteredCheck &c){ return c.options & RegisteredCheck::Option_Qt4Incompatible; }), result.end()); } return result; } RegisteredCheck::List CheckManager::checksForLevel(int level) const { RegisteredCheck::List result; if (level > CheckLevelUndefined && level <= MaxCheckLevel) { clazy::append_if(m_registeredChecks, result, [level](const RegisteredCheck &r) { return r.level <= level; }); } return result; } std::vector> CheckManager::createChecks(const RegisteredCheck::List &requestedChecks, ClazyContext *context) { assert(context); std::vector> checks; checks.reserve(requestedChecks.size() + 1); for (const auto& check : requestedChecks) { checks.push_back({createCheck(check.name, context), check }); } return checks; } /*static */ void CheckManager::removeChecksFromList(RegisteredCheck::List &list, vector &checkNames) { for (auto &name : checkNames) { list.erase(remove_if(list.begin(), list.end(), [name](const RegisteredCheck &c) { return c.name == name; }), list.end()); } } RegisteredCheck::List CheckManager::checksForCommaSeparatedString(const string &str) const { vector byRefDummy; return checksForCommaSeparatedString(str, byRefDummy); } RegisteredCheck::List CheckManager::checksForCommaSeparatedString(const string &str, vector &userDisabledChecks) const { vector checkNames = clazy::splitString(str, ','); RegisteredCheck::List result; for (const string &name : checkNames) { if (checkForName(result, name) != result.cend()) continue; // Already added. Duplicate check specified. continue. auto it = checkForName(m_registeredChecks, name); if (it == m_registeredChecks.cend()) { // Unknown, but might be a fixit name const string checkName = checkNameForFixIt(name); auto it = checkForName(m_registeredChecks, checkName); const bool checkDoesntExist = it == m_registeredChecks.cend(); if (checkDoesntExist) { if (clazy::startsWith(name, s_levelPrefix) && name.size() == strlen(s_levelPrefix) + 1) { auto lastChar = name.back(); const int digit = lastChar - '0'; if (digit > CheckLevelUndefined && digit <= MaxCheckLevel) { RegisteredCheck::List levelChecks = checksForLevel(digit); clazy::append(levelChecks, result); } else { llvm::errs() << "Invalid level: " << name << "\n"; } } else { if (clazy::startsWith(name, "no-")) { string checkName = name; checkName.erase(0, 3); if (checkExists(checkName)) { userDisabledChecks.push_back(checkName); } else { llvm::errs() << "Invalid check to disable: " << name << "\n"; } } else { llvm::errs() << "Invalid check: " << name << "\n"; } } } else { result.push_back(*it); } continue; } else { result.push_back(*it); } } removeChecksFromList(result, userDisabledChecks); return result; } diff --git a/src/checkmanager.h b/src/checkmanager.h index ffa5493..21061d0 100644 --- a/src/checkmanager.h +++ b/src/checkmanager.h @@ -1,129 +1,129 @@ /* This file is part of the clazy static checker. Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Sérgio Martins Copyright (C) 2015-2016 Sergio Martins This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef CLANG_LAZY_CHECK_MANAGER_H #define CLANG_LAZY_CHECK_MANAGER_H #include "checkbase.h" #include #include #include #include #include #include #include class ClazyContext; struct RegisteredFixIt { typedef std::vector List; RegisteredFixIt() : id(-1) {} RegisteredFixIt(int id, const std::string &name) : id(id), name(name) {} int id = -1; std::string name; bool operator==(const RegisteredFixIt &other) const { return id == other.id; } }; using FactoryFunction = std::function; struct RegisteredCheck { enum Option { Option_None = 0, Option_Qt4Incompatible = 1, Option_VisitsStmts = 2, Option_VisitsDecls = 4 }; typedef std::vector List; typedef int Options; std::string name; CheckLevel level; FactoryFunction factory; Options options; bool operator==(const RegisteredCheck &other) const { return name == other.name; } }; inline bool checkLessThan(const RegisteredCheck &c1, const RegisteredCheck &c2) { return c1.name < c2.name; } inline bool checkLessThanByLevel(const RegisteredCheck &c1, const RegisteredCheck &c2) { if (c1.level == c2.level) return checkLessThan(c1, c2); return c1.level < c2.level; } class CheckManager { public: /** * @note You must hold the CheckManager lock when operating on the instance * * @sa lock() */ static CheckManager *instance(); static std::mutex &lock() { return m_lock; } RegisteredCheck::List availableChecks(CheckLevel maxLevel) const; RegisteredCheck::List requestedChecksThroughEnv(std::vector &userDisabledChecks) const; RegisteredCheck::List::const_iterator checkForName(const RegisteredCheck::List &checks, const std::string &name) const; RegisteredCheck::List checksForCommaSeparatedString(const std::string &str) const; RegisteredCheck::List checksForCommaSeparatedString(const std::string &str, std::vector &userDisabledChecks) const; RegisteredFixIt::List availableFixIts(const std::string &checkName) const; /** * Returns all the requested checks. * This is a union of the requested checks via env variable and via arguments passed to compiler */ - RegisteredCheck::List requestedChecks(const ClazyContext *context, std::vector &args); + RegisteredCheck::List requestedChecks(std::vector &args, bool qt4Compat); std::vector> createChecks(const RegisteredCheck::List &requestedChecks, ClazyContext *context); static void removeChecksFromList(RegisteredCheck::List &list, std::vector &checkNames); private: CheckManager(); static std::mutex m_lock; void registerChecks(); void registerFixIt(int id, const std::string &fititName, const std::string &checkName); void registerCheck(const RegisteredCheck &check); bool checkExists(const std::string &name) const; RegisteredCheck::List checksForLevel(int level) const; CheckBase* createCheck(const std::string &name, ClazyContext *context); std::string checkNameForFixIt(const std::string &) const; RegisteredCheck::List m_registeredChecks; std::unordered_map> m_fixitsByCheckName; std::unordered_map m_fixitByName; }; #endif