File indexing completed on 2024-04-28 16:57:50

0001 /*
0002     This file is part of the clazy static checker.
0003 
0004     Copyright (C) 2017 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 // clazy:excludeall=non-pod-global-static
0023 
0024 #include "Clazy.h"
0025 #include "ClazyContext.h"
0026 
0027 #include "checks.json.h"
0028 
0029 #include <clang/Tooling/CommonOptionsParser.h>
0030 #include <clang/Tooling/Tooling.h>
0031 #include <llvm/Support/CommandLine.h>
0032 #include <llvm/ADT/ArrayRef.h>
0033 #include <llvm/ADT/StringRef.h>
0034 
0035 #include <iostream>
0036 #include <string>
0037 
0038 namespace clang {
0039 class FrontendAction;
0040 }  // namespace clang
0041 
0042 using namespace clang;
0043 using namespace clang::tooling;
0044 using namespace llvm;
0045 
0046 static llvm::cl::OptionCategory s_clazyCategory("clazy options");
0047 static cl::opt<std::string> s_checks("checks", cl::desc("Comma-separated list of clazy checks. Default is level1"),
0048                                      cl::init(""), cl::cat(s_clazyCategory));
0049 
0050 static cl::opt<std::string> 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."),
0051                                           cl::init(""), cl::cat(s_clazyCategory));
0052 
0053 static cl::opt<bool> s_qt4Compat("qt4-compat", cl::desc("Turns off checks not compatible with Qt 4"),
0054                                  cl::init(false), cl::cat(s_clazyCategory));
0055 
0056 static cl::opt<bool> s_onlyQt("only-qt", cl::desc("Won't emit warnings for non-Qt files, or in other words, if -DQT_CORE_LIB is missing."),
0057                               cl::init(false), cl::cat(s_clazyCategory));
0058 
0059 static cl::opt<bool> s_qtDeveloper("qt-developer", cl::desc("For running clazy on Qt itself, optional, but honours specific guidelines"),
0060                                    cl::init(false), cl::cat(s_clazyCategory));
0061 
0062 static cl::opt<bool> 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"),
0063                                          cl::init(false), cl::cat(s_clazyCategory));
0064 
0065 static cl::opt<bool> s_ignoreIncludedFiles("ignore-included-files", cl::desc("Only emit warnings for the current file being compiled and ignore any includes. Useful for performance reasons."),
0066                                            cl::init(false), cl::cat(s_clazyCategory));
0067 
0068 static cl::opt<std::string> s_headerFilter("header-filter", cl::desc(R"(Regular expression matching the names of the
0069 headers to output diagnostics from. Diagnostics
0070 from the main file of each translation unit are
0071 always displayed.)"),
0072                                            cl::init(""), cl::cat(s_clazyCategory));
0073 
0074 static cl::opt<std::string> s_ignoreDirs("ignore-dirs", cl::desc(R"(Regular expression matching the names of the
0075 directories for which diagnostics should never be emitted. Useful for ignoring 3rdparty code.)"),
0076                                          cl::init(""), cl::cat(s_clazyCategory));
0077 
0078 static cl::opt<bool> s_supportedChecks("supported-checks-json", cl::desc("Dump meta information about supported checks in JSON format."),
0079                                        cl::init(false), cl::cat(s_clazyCategory));
0080 
0081 static cl::opt<bool> s_listEnabledChecks("list-checks", cl::desc("List all enabled checks and exit."),
0082                                          cl::init(false), cl::cat(s_clazyCategory));
0083 
0084 static cl::opt<std::string> s_vfsoverlay("vfsoverlay", cl::desc("YAML file to overlay the virtual filesystem described by file over the real file system."),
0085                                           cl::init(""), cl::cat(s_clazyCategory));
0086 
0087 static cl::extrahelp s_commonHelp(CommonOptionsParser::HelpMessage);
0088 
0089 class ClazyToolActionFactory
0090     : public clang::tooling::FrontendActionFactory
0091 {
0092 public:
0093     ClazyToolActionFactory(std::vector<std::string> paths)
0094         : FrontendActionFactory()
0095         , m_paths(std::move(paths))
0096     {
0097     }
0098 
0099 #if LLVM_VERSION_MAJOR >= 10
0100     std::unique_ptr<FrontendAction> create() override
0101 #else
0102     FrontendAction *create() override
0103 #endif
0104     {
0105         ClazyContext::ClazyOptions options = ClazyContext::ClazyOption_None;
0106 
0107         if (!s_exportFixes.getValue().empty())
0108             options |= ClazyContext::ClazyOption_ExportFixes;
0109 
0110         if (s_qt4Compat.getValue())
0111             options |= ClazyContext::ClazyOption_Qt4Compat;
0112 
0113         if (s_qtDeveloper.getValue())
0114             options |= ClazyContext::ClazyOption_QtDeveloper;
0115 
0116         if (s_onlyQt.getValue())
0117             options |= ClazyContext::ClazyOption_OnlyQt;
0118 
0119         if (s_visitImplicitCode.getValue())
0120             options |= ClazyContext::ClazyOption_VisitImplicitCode;
0121 
0122         if (s_ignoreIncludedFiles.getValue())
0123             options |= ClazyContext::ClazyOption_IgnoreIncludedFiles;
0124 
0125         // TODO: We need to agregate the fixes with previous run
0126 #if LLVM_VERSION_MAJOR >= 10
0127         return std::make_unique<ClazyStandaloneASTAction>(s_checks.getValue(), s_headerFilter.getValue(),
0128                                                           s_ignoreDirs.getValue(), s_exportFixes.getValue(),
0129                                                           m_paths, options);
0130 #else
0131         return new ClazyStandaloneASTAction(s_checks.getValue(), s_headerFilter.getValue(),
0132                                             s_ignoreDirs.getValue(), s_exportFixes.getValue(),
0133                                             m_paths, options);
0134 #endif
0135     }
0136     std::vector<std::string> m_paths;
0137 };
0138 
0139 llvm::IntrusiveRefCntPtr<vfs::FileSystem> getVfsFromFile(const std::string &overlayFile, llvm::IntrusiveRefCntPtr<vfs::FileSystem> BaseFS)
0140 {
0141     llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> buffer = BaseFS->getBufferForFile(
0142         overlayFile);
0143     if (!buffer) {
0144         llvm::errs() << "Can't load virtual filesystem overlay file '" << overlayFile
0145                      << "': " << buffer.getError().message() << ".\n";
0146         return nullptr;
0147     }
0148 
0149     IntrusiveRefCntPtr<vfs::FileSystem> fs = vfs::getVFSFromYAML(std::move(buffer.get()),
0150                                                                  /*DiagHandler*/ nullptr,
0151                                                                  overlayFile);
0152     if (!fs) {
0153         llvm::errs() << "Error: invalid virtual filesystem overlay file '" << overlayFile << "'.\n";
0154         return nullptr;
0155     }
0156     return fs;
0157 }
0158 
0159 int main(int argc, const char **argv)
0160 {
0161     for (int i = 1; i < argc; i++) {
0162         if (strcmp(argv[i], "--version") == 0) {
0163             std::cout << "clazy version 1.11\n";
0164             break;
0165         }
0166     }
0167 
0168     auto expectedParser = CommonOptionsParser::create(argc, argv, s_clazyCategory, cl::ZeroOrMore);
0169     if (!expectedParser) {
0170        llvm::errs() << expectedParser.takeError();
0171        return 1;
0172     }
0173 
0174     auto &optionsParser = expectedParser.get();
0175     // llvm::errs() << optionsParser.getSourcePathList().size() << "\n";
0176 
0177     if (s_supportedChecks.getValue()) {
0178         std::cout << SUPPORTED_CHECKS_JSON_STR;
0179         return 0;
0180     }
0181 
0182     if (s_listEnabledChecks.getValue()) {
0183         std::string checksFromArgs = s_checks.getValue();
0184         std::vector<std::string> checks = { checksFromArgs.empty() ? "level1" : checksFromArgs };
0185         const RegisteredCheck::List enabledChecks
0186                 = CheckManager::instance()->requestedChecks(checks, s_qt4Compat.getValue());
0187 
0188         if (!enabledChecks.empty()) {
0189             llvm::outs() << "Enabled checks:";
0190             for (const auto &check : enabledChecks) {
0191                 llvm::outs() << "\n    " << check.name;
0192             }
0193             llvm::outs() << "\n";
0194         }
0195 
0196         return 0;
0197     }
0198 
0199     llvm::IntrusiveRefCntPtr<vfs::OverlayFileSystem> fs(
0200         new vfs::OverlayFileSystem(vfs::getRealFileSystem()));
0201     const std::string &overlayFile = s_vfsoverlay.getValue();
0202     if (!s_vfsoverlay.getValue().empty()) {
0203         llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> buffer = fs->getBufferForFile(
0204             overlayFile);
0205         if (!buffer) {
0206             llvm::errs() << "Can't load virtual filesystem overlay file '" << overlayFile
0207                          << "': " << buffer.getError().message() << ".\n";
0208             return 0;
0209         }
0210 
0211         IntrusiveRefCntPtr<vfs::FileSystem> vfso = vfs::getVFSFromYAML(std::move(buffer.get()),
0212                                                                        /*DiagHandler*/ nullptr,
0213                                                                        overlayFile);
0214         if (!vfso) {
0215             llvm::errs() << "Error: invalid virtual filesystem overlay file '" << overlayFile
0216                          << "'.\n";
0217             return 0;
0218         }
0219         fs->pushOverlay(vfso);
0220     }
0221 
0222     ClangTool tool(optionsParser.getCompilations(),
0223                    optionsParser.getSourcePathList(),
0224                    std::make_shared<PCHContainerOperations>(),
0225                    fs);
0226 
0227     return tool.run(new ClazyToolActionFactory(optionsParser.getSourcePathList()));
0228 }