File indexing completed on 2024-04-14 05:32:06

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