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