106f32e7eSjoerg //===--- tools/clang-check/ClangCheck.cpp - Clang check tool --------------===//
206f32e7eSjoerg //
306f32e7eSjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
406f32e7eSjoerg // See https://llvm.org/LICENSE.txt for license information.
506f32e7eSjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
606f32e7eSjoerg //
706f32e7eSjoerg //===----------------------------------------------------------------------===//
806f32e7eSjoerg //
906f32e7eSjoerg //  This file implements a clang-check tool that runs clang based on the info
1006f32e7eSjoerg //  stored in a compilation database.
1106f32e7eSjoerg //
1206f32e7eSjoerg //  This tool uses the Clang Tooling infrastructure, see
1306f32e7eSjoerg //    http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
1406f32e7eSjoerg //  for details on setting it up with LLVM source tree.
1506f32e7eSjoerg //
1606f32e7eSjoerg //===----------------------------------------------------------------------===//
1706f32e7eSjoerg 
1806f32e7eSjoerg #include "clang/AST/ASTConsumer.h"
1906f32e7eSjoerg #include "clang/CodeGen/ObjectFilePCHContainerOperations.h"
2006f32e7eSjoerg #include "clang/Driver/Options.h"
2106f32e7eSjoerg #include "clang/Frontend/ASTConsumers.h"
2206f32e7eSjoerg #include "clang/Frontend/CompilerInstance.h"
2306f32e7eSjoerg #include "clang/Rewrite/Frontend/FixItRewriter.h"
2406f32e7eSjoerg #include "clang/Rewrite/Frontend/FrontendActions.h"
2506f32e7eSjoerg #include "clang/StaticAnalyzer/Frontend/FrontendActions.h"
2606f32e7eSjoerg #include "clang/Tooling/CommonOptionsParser.h"
27*13fbcb42Sjoerg #include "clang/Tooling/Syntax/BuildTree.h"
28*13fbcb42Sjoerg #include "clang/Tooling/Syntax/Tokens.h"
29*13fbcb42Sjoerg #include "clang/Tooling/Syntax/Tree.h"
3006f32e7eSjoerg #include "clang/Tooling/Tooling.h"
3106f32e7eSjoerg #include "llvm/ADT/STLExtras.h"
3206f32e7eSjoerg #include "llvm/Option/OptTable.h"
3306f32e7eSjoerg #include "llvm/Support/Path.h"
3406f32e7eSjoerg #include "llvm/Support/Signals.h"
3506f32e7eSjoerg #include "llvm/Support/TargetSelect.h"
3606f32e7eSjoerg 
3706f32e7eSjoerg using namespace clang::driver;
3806f32e7eSjoerg using namespace clang::tooling;
3906f32e7eSjoerg using namespace llvm;
4006f32e7eSjoerg 
4106f32e7eSjoerg static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
4206f32e7eSjoerg static cl::extrahelp MoreHelp(
4306f32e7eSjoerg     "\tFor example, to run clang-check on all files in a subtree of the\n"
4406f32e7eSjoerg     "\tsource tree, use:\n"
4506f32e7eSjoerg     "\n"
4606f32e7eSjoerg     "\t  find path/in/subtree -name '*.cpp'|xargs clang-check\n"
4706f32e7eSjoerg     "\n"
4806f32e7eSjoerg     "\tor using a specific build path:\n"
4906f32e7eSjoerg     "\n"
5006f32e7eSjoerg     "\t  find path/in/subtree -name '*.cpp'|xargs clang-check -p build/path\n"
5106f32e7eSjoerg     "\n"
5206f32e7eSjoerg     "\tNote, that path/in/subtree and current directory should follow the\n"
5306f32e7eSjoerg     "\trules described above.\n"
5406f32e7eSjoerg     "\n"
5506f32e7eSjoerg );
5606f32e7eSjoerg 
5706f32e7eSjoerg static cl::OptionCategory ClangCheckCategory("clang-check options");
5806f32e7eSjoerg static const opt::OptTable &Options = getDriverOptTable();
5906f32e7eSjoerg static cl::opt<bool>
6006f32e7eSjoerg     ASTDump("ast-dump",
6106f32e7eSjoerg             cl::desc(Options.getOptionHelpText(options::OPT_ast_dump)),
6206f32e7eSjoerg             cl::cat(ClangCheckCategory));
6306f32e7eSjoerg static cl::opt<bool>
6406f32e7eSjoerg     ASTList("ast-list",
6506f32e7eSjoerg             cl::desc(Options.getOptionHelpText(options::OPT_ast_list)),
6606f32e7eSjoerg             cl::cat(ClangCheckCategory));
6706f32e7eSjoerg static cl::opt<bool>
6806f32e7eSjoerg     ASTPrint("ast-print",
6906f32e7eSjoerg              cl::desc(Options.getOptionHelpText(options::OPT_ast_print)),
7006f32e7eSjoerg              cl::cat(ClangCheckCategory));
7106f32e7eSjoerg static cl::opt<std::string> ASTDumpFilter(
7206f32e7eSjoerg     "ast-dump-filter",
7306f32e7eSjoerg     cl::desc(Options.getOptionHelpText(options::OPT_ast_dump_filter)),
7406f32e7eSjoerg     cl::cat(ClangCheckCategory));
7506f32e7eSjoerg static cl::opt<bool>
7606f32e7eSjoerg     Analyze("analyze",
7706f32e7eSjoerg             cl::desc(Options.getOptionHelpText(options::OPT_analyze)),
7806f32e7eSjoerg             cl::cat(ClangCheckCategory));
7906f32e7eSjoerg 
8006f32e7eSjoerg static cl::opt<bool>
8106f32e7eSjoerg     Fixit("fixit", cl::desc(Options.getOptionHelpText(options::OPT_fixit)),
8206f32e7eSjoerg           cl::cat(ClangCheckCategory));
8306f32e7eSjoerg static cl::opt<bool> FixWhatYouCan(
8406f32e7eSjoerg     "fix-what-you-can",
8506f32e7eSjoerg     cl::desc(Options.getOptionHelpText(options::OPT_fix_what_you_can)),
8606f32e7eSjoerg     cl::cat(ClangCheckCategory));
8706f32e7eSjoerg 
88*13fbcb42Sjoerg static cl::opt<bool> SyntaxTreeDump("syntax-tree-dump",
89*13fbcb42Sjoerg                                     cl::desc("dump the syntax tree"),
90*13fbcb42Sjoerg                                     cl::cat(ClangCheckCategory));
91*13fbcb42Sjoerg static cl::opt<bool> TokensDump("tokens-dump",
92*13fbcb42Sjoerg                                 cl::desc("dump the preprocessed tokens"),
93*13fbcb42Sjoerg                                 cl::cat(ClangCheckCategory));
94*13fbcb42Sjoerg 
9506f32e7eSjoerg namespace {
9606f32e7eSjoerg 
9706f32e7eSjoerg // FIXME: Move FixItRewriteInPlace from lib/Rewrite/Frontend/FrontendActions.cpp
9806f32e7eSjoerg // into a header file and reuse that.
9906f32e7eSjoerg class FixItOptions : public clang::FixItOptions {
10006f32e7eSjoerg public:
FixItOptions()10106f32e7eSjoerg   FixItOptions() {
10206f32e7eSjoerg     FixWhatYouCan = ::FixWhatYouCan;
10306f32e7eSjoerg   }
10406f32e7eSjoerg 
RewriteFilename(const std::string & filename,int & fd)10506f32e7eSjoerg   std::string RewriteFilename(const std::string& filename, int &fd) override {
10606f32e7eSjoerg     // We don't need to do permission checking here since clang will diagnose
10706f32e7eSjoerg     // any I/O errors itself.
10806f32e7eSjoerg 
10906f32e7eSjoerg     fd = -1;  // No file descriptor for file.
11006f32e7eSjoerg 
11106f32e7eSjoerg     return filename;
11206f32e7eSjoerg   }
11306f32e7eSjoerg };
11406f32e7eSjoerg 
11506f32e7eSjoerg /// Subclasses \c clang::FixItRewriter to not count fixed errors/warnings
11606f32e7eSjoerg /// in the final error counts.
11706f32e7eSjoerg ///
11806f32e7eSjoerg /// This has the side-effect that clang-check -fixit exits with code 0 on
11906f32e7eSjoerg /// successfully fixing all errors.
12006f32e7eSjoerg class FixItRewriter : public clang::FixItRewriter {
12106f32e7eSjoerg public:
FixItRewriter(clang::DiagnosticsEngine & Diags,clang::SourceManager & SourceMgr,const clang::LangOptions & LangOpts,clang::FixItOptions * FixItOpts)12206f32e7eSjoerg   FixItRewriter(clang::DiagnosticsEngine& Diags,
12306f32e7eSjoerg                 clang::SourceManager& SourceMgr,
12406f32e7eSjoerg                 const clang::LangOptions& LangOpts,
12506f32e7eSjoerg                 clang::FixItOptions* FixItOpts)
12606f32e7eSjoerg       : clang::FixItRewriter(Diags, SourceMgr, LangOpts, FixItOpts) {
12706f32e7eSjoerg   }
12806f32e7eSjoerg 
IncludeInDiagnosticCounts() const12906f32e7eSjoerg   bool IncludeInDiagnosticCounts() const override { return false; }
13006f32e7eSjoerg };
13106f32e7eSjoerg 
13206f32e7eSjoerg /// Subclasses \c clang::FixItAction so that we can install the custom
13306f32e7eSjoerg /// \c FixItRewriter.
13406f32e7eSjoerg class ClangCheckFixItAction : public clang::FixItAction {
13506f32e7eSjoerg public:
BeginSourceFileAction(clang::CompilerInstance & CI)13606f32e7eSjoerg   bool BeginSourceFileAction(clang::CompilerInstance& CI) override {
13706f32e7eSjoerg     FixItOpts.reset(new FixItOptions);
13806f32e7eSjoerg     Rewriter.reset(new FixItRewriter(CI.getDiagnostics(), CI.getSourceManager(),
13906f32e7eSjoerg                                      CI.getLangOpts(), FixItOpts.get()));
14006f32e7eSjoerg     return true;
14106f32e7eSjoerg   }
14206f32e7eSjoerg };
14306f32e7eSjoerg 
144*13fbcb42Sjoerg class DumpSyntaxTree : public clang::ASTFrontendAction {
145*13fbcb42Sjoerg public:
146*13fbcb42Sjoerg   std::unique_ptr<clang::ASTConsumer>
CreateASTConsumer(clang::CompilerInstance & CI,StringRef InFile)147*13fbcb42Sjoerg   CreateASTConsumer(clang::CompilerInstance &CI, StringRef InFile) override {
148*13fbcb42Sjoerg     class Consumer : public clang::ASTConsumer {
149*13fbcb42Sjoerg     public:
150*13fbcb42Sjoerg       Consumer(clang::CompilerInstance &CI) : Collector(CI.getPreprocessor()) {}
151*13fbcb42Sjoerg 
152*13fbcb42Sjoerg       void HandleTranslationUnit(clang::ASTContext &AST) override {
153*13fbcb42Sjoerg         clang::syntax::TokenBuffer TB = std::move(Collector).consume();
154*13fbcb42Sjoerg         if (TokensDump)
155*13fbcb42Sjoerg           llvm::outs() << TB.dumpForTests();
156*13fbcb42Sjoerg         clang::syntax::Arena A(AST.getSourceManager(), AST.getLangOpts(), TB);
157*13fbcb42Sjoerg         llvm::outs() << clang::syntax::buildSyntaxTree(A, AST)->dump(
158*13fbcb42Sjoerg             AST.getSourceManager());
159*13fbcb42Sjoerg       }
160*13fbcb42Sjoerg 
161*13fbcb42Sjoerg     private:
162*13fbcb42Sjoerg       clang::syntax::TokenCollector Collector;
163*13fbcb42Sjoerg     };
164*13fbcb42Sjoerg     return std::make_unique<Consumer>(CI);
165*13fbcb42Sjoerg   }
166*13fbcb42Sjoerg };
167*13fbcb42Sjoerg 
16806f32e7eSjoerg class ClangCheckActionFactory {
16906f32e7eSjoerg public:
newASTConsumer()17006f32e7eSjoerg   std::unique_ptr<clang::ASTConsumer> newASTConsumer() {
17106f32e7eSjoerg     if (ASTList)
17206f32e7eSjoerg       return clang::CreateASTDeclNodeLister();
17306f32e7eSjoerg     if (ASTDump)
17406f32e7eSjoerg       return clang::CreateASTDumper(nullptr /*Dump to stdout.*/, ASTDumpFilter,
17506f32e7eSjoerg                                     /*DumpDecls=*/true,
17606f32e7eSjoerg                                     /*Deserialize=*/false,
17706f32e7eSjoerg                                     /*DumpLookups=*/false,
178*13fbcb42Sjoerg                                     /*DumpDeclTypes=*/false,
17906f32e7eSjoerg                                     clang::ADOF_Default);
18006f32e7eSjoerg     if (ASTPrint)
18106f32e7eSjoerg       return clang::CreateASTPrinter(nullptr, ASTDumpFilter);
18206f32e7eSjoerg     return std::make_unique<clang::ASTConsumer>();
18306f32e7eSjoerg   }
18406f32e7eSjoerg };
18506f32e7eSjoerg 
18606f32e7eSjoerg } // namespace
18706f32e7eSjoerg 
main(int argc,const char ** argv)18806f32e7eSjoerg int main(int argc, const char **argv) {
18906f32e7eSjoerg   llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
19006f32e7eSjoerg 
19106f32e7eSjoerg   // Initialize targets for clang module support.
19206f32e7eSjoerg   llvm::InitializeAllTargets();
19306f32e7eSjoerg   llvm::InitializeAllTargetMCs();
19406f32e7eSjoerg   llvm::InitializeAllAsmPrinters();
19506f32e7eSjoerg   llvm::InitializeAllAsmParsers();
19606f32e7eSjoerg 
197*13fbcb42Sjoerg   auto ExpectedParser =
198*13fbcb42Sjoerg       CommonOptionsParser::create(argc, argv, ClangCheckCategory);
199*13fbcb42Sjoerg   if (!ExpectedParser) {
200*13fbcb42Sjoerg     llvm::errs() << ExpectedParser.takeError();
201*13fbcb42Sjoerg     return 1;
202*13fbcb42Sjoerg   }
203*13fbcb42Sjoerg   CommonOptionsParser &OptionsParser = ExpectedParser.get();
20406f32e7eSjoerg   ClangTool Tool(OptionsParser.getCompilations(),
20506f32e7eSjoerg                  OptionsParser.getSourcePathList());
20606f32e7eSjoerg 
20706f32e7eSjoerg   // Clear adjusters because -fsyntax-only is inserted by the default chain.
20806f32e7eSjoerg   Tool.clearArgumentsAdjusters();
20906f32e7eSjoerg   Tool.appendArgumentsAdjuster(getClangStripOutputAdjuster());
21006f32e7eSjoerg   Tool.appendArgumentsAdjuster(getClangStripDependencyFileAdjuster());
21106f32e7eSjoerg 
21206f32e7eSjoerg   // Running the analyzer requires --analyze. Other modes can work with the
21306f32e7eSjoerg   // -fsyntax-only option.
21406f32e7eSjoerg   Tool.appendArgumentsAdjuster(getInsertArgumentAdjuster(
21506f32e7eSjoerg       Analyze ? "--analyze" : "-fsyntax-only", ArgumentInsertPosition::BEGIN));
21606f32e7eSjoerg 
21706f32e7eSjoerg   ClangCheckActionFactory CheckFactory;
21806f32e7eSjoerg   std::unique_ptr<FrontendActionFactory> FrontendFactory;
21906f32e7eSjoerg 
22006f32e7eSjoerg   // Choose the correct factory based on the selected mode.
22106f32e7eSjoerg   if (Analyze)
22206f32e7eSjoerg     FrontendFactory = newFrontendActionFactory<clang::ento::AnalysisAction>();
22306f32e7eSjoerg   else if (Fixit)
22406f32e7eSjoerg     FrontendFactory = newFrontendActionFactory<ClangCheckFixItAction>();
225*13fbcb42Sjoerg   else if (SyntaxTreeDump || TokensDump)
226*13fbcb42Sjoerg     FrontendFactory = newFrontendActionFactory<DumpSyntaxTree>();
22706f32e7eSjoerg   else
22806f32e7eSjoerg     FrontendFactory = newFrontendActionFactory(&CheckFactory);
22906f32e7eSjoerg 
23006f32e7eSjoerg   return Tool.run(FrontendFactory.get());
23106f32e7eSjoerg }
232