1 //===--- tools/clang-check/ClangCheck.cpp - Clang check tool --------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This file implements a clang-check tool that runs clang based on the info
10 // stored in a compilation database.
12 // This tool uses the Clang Tooling infrastructure, see
13 // http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
14 // for details on setting it up with LLVM source tree.
16 //===----------------------------------------------------------------------===//
18 #include "clang/AST/ASTConsumer.h"
19 #include "clang/Driver/Options.h"
20 #include "clang/Frontend/ASTConsumers.h"
21 #include "clang/Frontend/CompilerInstance.h"
22 #include "clang/Rewrite/Frontend/FixItRewriter.h"
23 #include "clang/Rewrite/Frontend/FrontendActions.h"
24 #include "clang/StaticAnalyzer/Frontend/FrontendActions.h"
25 #include "clang/Tooling/CommonOptionsParser.h"
26 #include "clang/Tooling/Syntax/BuildTree.h"
27 #include "clang/Tooling/Syntax/TokenBufferTokenManager.h"
28 #include "clang/Tooling/Syntax/Tokens.h"
29 #include "clang/Tooling/Syntax/Tree.h"
30 #include "clang/Tooling/Tooling.h"
31 #include "llvm/ADT/STLExtras.h"
32 #include "llvm/Option/OptTable.h"
33 #include "llvm/Support/Path.h"
34 #include "llvm/Support/Signals.h"
35 #include "llvm/Support/TargetSelect.h"
37 using namespace clang::driver
;
38 using namespace clang::tooling
;
41 static cl::extrahelp
CommonHelp(CommonOptionsParser::HelpMessage
);
42 static cl::extrahelp
MoreHelp(
43 "\tFor example, to run clang-check on all files in a subtree of the\n"
44 "\tsource tree, use:\n"
46 "\t find path/in/subtree -name '*.cpp'|xargs clang-check\n"
48 "\tor using a specific build path:\n"
50 "\t find path/in/subtree -name '*.cpp'|xargs clang-check -p build/path\n"
52 "\tNote, that path/in/subtree and current directory should follow the\n"
53 "\trules described above.\n"
57 static cl::OptionCategory
ClangCheckCategory("clang-check options");
58 static const opt::OptTable
&Options
= getDriverOptTable();
61 cl::desc(Options
.getOptionHelpText(options::OPT_ast_dump
)),
62 cl::cat(ClangCheckCategory
));
65 cl::desc(Options
.getOptionHelpText(options::OPT_ast_list
)),
66 cl::cat(ClangCheckCategory
));
69 cl::desc(Options
.getOptionHelpText(options::OPT_ast_print
)),
70 cl::cat(ClangCheckCategory
));
71 static cl::opt
<std::string
> ASTDumpFilter(
73 cl::desc(Options
.getOptionHelpText(options::OPT_ast_dump_filter
)),
74 cl::cat(ClangCheckCategory
));
77 cl::desc(Options
.getOptionHelpText(options::OPT_analyze
)),
78 cl::cat(ClangCheckCategory
));
79 static cl::opt
<std::string
>
80 AnalyzerOutput("analyzer-output-path",
81 cl::desc(Options
.getOptionHelpText(options::OPT_o
)),
82 cl::cat(ClangCheckCategory
));
85 Fixit("fixit", cl::desc(Options
.getOptionHelpText(options::OPT_fixit
)),
86 cl::cat(ClangCheckCategory
));
87 static cl::opt
<bool> FixWhatYouCan(
89 cl::desc(Options
.getOptionHelpText(options::OPT_fix_what_you_can
)),
90 cl::cat(ClangCheckCategory
));
92 static cl::opt
<bool> SyntaxTreeDump("syntax-tree-dump",
93 cl::desc("dump the syntax tree"),
94 cl::cat(ClangCheckCategory
));
95 static cl::opt
<bool> TokensDump("tokens-dump",
96 cl::desc("dump the preprocessed tokens"),
97 cl::cat(ClangCheckCategory
));
101 // FIXME: Move FixItRewriteInPlace from lib/Rewrite/Frontend/FrontendActions.cpp
102 // into a header file and reuse that.
103 class FixItOptions
: public clang::FixItOptions
{
106 FixWhatYouCan
= ::FixWhatYouCan
;
109 std::string
RewriteFilename(const std::string
& filename
, int &fd
) override
{
110 // We don't need to do permission checking here since clang will diagnose
111 // any I/O errors itself.
113 fd
= -1; // No file descriptor for file.
119 /// Subclasses \c clang::FixItRewriter to not count fixed errors/warnings
120 /// in the final error counts.
122 /// This has the side-effect that clang-check -fixit exits with code 0 on
123 /// successfully fixing all errors.
124 class FixItRewriter
: public clang::FixItRewriter
{
126 FixItRewriter(clang::DiagnosticsEngine
& Diags
,
127 clang::SourceManager
& SourceMgr
,
128 const clang::LangOptions
& LangOpts
,
129 clang::FixItOptions
* FixItOpts
)
130 : clang::FixItRewriter(Diags
, SourceMgr
, LangOpts
, FixItOpts
) {
133 bool IncludeInDiagnosticCounts() const override
{ return false; }
136 /// Subclasses \c clang::FixItAction so that we can install the custom
137 /// \c FixItRewriter.
138 class ClangCheckFixItAction
: public clang::FixItAction
{
140 bool BeginSourceFileAction(clang::CompilerInstance
& CI
) override
{
141 FixItOpts
.reset(new FixItOptions
);
142 Rewriter
.reset(new FixItRewriter(CI
.getDiagnostics(), CI
.getSourceManager(),
143 CI
.getLangOpts(), FixItOpts
.get()));
148 class DumpSyntaxTree
: public clang::ASTFrontendAction
{
150 std::unique_ptr
<clang::ASTConsumer
>
151 CreateASTConsumer(clang::CompilerInstance
&CI
, StringRef InFile
) override
{
152 class Consumer
: public clang::ASTConsumer
{
154 Consumer(clang::CompilerInstance
&CI
) : Collector(CI
.getPreprocessor()) {}
156 void HandleTranslationUnit(clang::ASTContext
&AST
) override
{
157 clang::syntax::TokenBuffer TB
= std::move(Collector
).consume();
159 llvm::outs() << TB
.dumpForTests();
160 clang::syntax::TokenBufferTokenManager
TBTM(TB
, AST
.getLangOpts(),
161 AST
.getSourceManager());
162 clang::syntax::Arena A
;
164 << clang::syntax::buildSyntaxTree(A
, TBTM
, AST
)->dump(TBTM
);
168 clang::syntax::TokenCollector Collector
;
170 return std::make_unique
<Consumer
>(CI
);
174 class ClangCheckActionFactory
{
176 std::unique_ptr
<clang::ASTConsumer
> newASTConsumer() {
178 return clang::CreateASTDeclNodeLister();
180 return clang::CreateASTDumper(nullptr /*Dump to stdout.*/, ASTDumpFilter
,
182 /*Deserialize=*/false,
183 /*DumpLookups=*/false,
184 /*DumpDeclTypes=*/false,
185 clang::ADOF_Default
);
187 return clang::CreateASTPrinter(nullptr, ASTDumpFilter
);
188 return std::make_unique
<clang::ASTConsumer
>();
194 int main(int argc
, const char **argv
) {
195 llvm::sys::PrintStackTraceOnErrorSignal(argv
[0]);
197 // Initialize targets for clang module support.
198 llvm::InitializeAllTargets();
199 llvm::InitializeAllTargetMCs();
200 llvm::InitializeAllAsmPrinters();
201 llvm::InitializeAllAsmParsers();
203 auto ExpectedParser
=
204 CommonOptionsParser::create(argc
, argv
, ClangCheckCategory
);
205 if (!ExpectedParser
) {
206 llvm::errs() << ExpectedParser
.takeError();
209 CommonOptionsParser
&OptionsParser
= ExpectedParser
.get();
210 ClangTool
Tool(OptionsParser
.getCompilations(),
211 OptionsParser
.getSourcePathList());
214 // Set output path if is provided by user.
216 // As the original -o options have been removed by default via the
217 // strip-output adjuster, we only need to add the analyzer -o options here
218 // when it is provided by users.
219 if (!AnalyzerOutput
.empty())
220 Tool
.appendArgumentsAdjuster(
221 getInsertArgumentAdjuster(CommandLineArguments
{"-o", AnalyzerOutput
},
222 ArgumentInsertPosition::END
));
224 // Running the analyzer requires --analyze. Other modes can work with the
225 // -fsyntax-only option.
227 // The syntax-only adjuster is installed by default.
228 // Good: It also strips options that trigger extra output, like -save-temps.
229 // Bad: We don't want the -fsyntax-only when executing the static analyzer.
231 // To enable the static analyzer, we first strip all -fsyntax-only options
232 // and then add an --analyze option to the front.
233 Tool
.appendArgumentsAdjuster(
234 [&](const CommandLineArguments
&Args
, StringRef
/*unused*/) {
235 CommandLineArguments AdjustedArgs
;
236 for (const std::string
&Arg
: Args
)
237 if (Arg
!= "-fsyntax-only")
238 AdjustedArgs
.emplace_back(Arg
);
241 Tool
.appendArgumentsAdjuster(
242 getInsertArgumentAdjuster("--analyze", ArgumentInsertPosition::BEGIN
));
245 ClangCheckActionFactory CheckFactory
;
246 std::unique_ptr
<FrontendActionFactory
> FrontendFactory
;
248 // Choose the correct factory based on the selected mode.
250 FrontendFactory
= newFrontendActionFactory
<clang::ento::AnalysisAction
>();
252 FrontendFactory
= newFrontendActionFactory
<ClangCheckFixItAction
>();
253 else if (SyntaxTreeDump
|| TokensDump
)
254 FrontendFactory
= newFrontendActionFactory
<DumpSyntaxTree
>();
256 FrontendFactory
= newFrontendActionFactory(&CheckFactory
);
258 return Tool
.run(FrontendFactory
.get());