1 //===--- tools/clang-check/ClangCheck.cpp - Clang check tool --------------===// 2 // 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 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file implements a clang-check tool that runs clang based on the info 10 // stored in a compilation database. 11 // 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. 15 // 16 //===----------------------------------------------------------------------===// 17 18 #include "clang/AST/ASTConsumer.h" 19 #include "clang/CodeGen/ObjectFilePCHContainerOperations.h" 20 #include "clang/Driver/Options.h" 21 #include "clang/Frontend/ASTConsumers.h" 22 #include "clang/Frontend/CompilerInstance.h" 23 #include "clang/Rewrite/Frontend/FixItRewriter.h" 24 #include "clang/Rewrite/Frontend/FrontendActions.h" 25 #include "clang/StaticAnalyzer/Frontend/FrontendActions.h" 26 #include "clang/Tooling/CommonOptionsParser.h" 27 #include "clang/Tooling/Syntax/BuildTree.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" 36 37 using namespace clang::driver; 38 using namespace clang::tooling; 39 using namespace llvm; 40 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" 45 "\n" 46 "\t find path/in/subtree -name '*.cpp'|xargs clang-check\n" 47 "\n" 48 "\tor using a specific build path:\n" 49 "\n" 50 "\t find path/in/subtree -name '*.cpp'|xargs clang-check -p build/path\n" 51 "\n" 52 "\tNote, that path/in/subtree and current directory should follow the\n" 53 "\trules described above.\n" 54 "\n" 55 ); 56 57 static cl::OptionCategory ClangCheckCategory("clang-check options"); 58 static const opt::OptTable &Options = getDriverOptTable(); 59 static cl::opt<bool> 60 ASTDump("ast-dump", 61 cl::desc(Options.getOptionHelpText(options::OPT_ast_dump)), 62 cl::cat(ClangCheckCategory)); 63 static cl::opt<bool> 64 ASTList("ast-list", 65 cl::desc(Options.getOptionHelpText(options::OPT_ast_list)), 66 cl::cat(ClangCheckCategory)); 67 static cl::opt<bool> 68 ASTPrint("ast-print", 69 cl::desc(Options.getOptionHelpText(options::OPT_ast_print)), 70 cl::cat(ClangCheckCategory)); 71 static cl::opt<std::string> ASTDumpFilter( 72 "ast-dump-filter", 73 cl::desc(Options.getOptionHelpText(options::OPT_ast_dump_filter)), 74 cl::cat(ClangCheckCategory)); 75 static cl::opt<bool> 76 Analyze("analyze", 77 cl::desc(Options.getOptionHelpText(options::OPT_analyze)), 78 cl::cat(ClangCheckCategory)); 79 80 static cl::opt<bool> 81 Fixit("fixit", cl::desc(Options.getOptionHelpText(options::OPT_fixit)), 82 cl::cat(ClangCheckCategory)); 83 static cl::opt<bool> FixWhatYouCan( 84 "fix-what-you-can", 85 cl::desc(Options.getOptionHelpText(options::OPT_fix_what_you_can)), 86 cl::cat(ClangCheckCategory)); 87 88 static cl::opt<bool> SyntaxTreeDump("syntax-tree-dump", 89 cl::desc("dump the syntax tree"), 90 cl::cat(ClangCheckCategory)); 91 static cl::opt<bool> TokensDump("tokens-dump", 92 cl::desc("dump the preprocessed tokens"), 93 cl::cat(ClangCheckCategory)); 94 95 namespace { 96 97 // FIXME: Move FixItRewriteInPlace from lib/Rewrite/Frontend/FrontendActions.cpp 98 // into a header file and reuse that. 99 class FixItOptions : public clang::FixItOptions { 100 public: 101 FixItOptions() { 102 FixWhatYouCan = ::FixWhatYouCan; 103 } 104 105 std::string RewriteFilename(const std::string& filename, int &fd) override { 106 // We don't need to do permission checking here since clang will diagnose 107 // any I/O errors itself. 108 109 fd = -1; // No file descriptor for file. 110 111 return filename; 112 } 113 }; 114 115 /// Subclasses \c clang::FixItRewriter to not count fixed errors/warnings 116 /// in the final error counts. 117 /// 118 /// This has the side-effect that clang-check -fixit exits with code 0 on 119 /// successfully fixing all errors. 120 class FixItRewriter : public clang::FixItRewriter { 121 public: 122 FixItRewriter(clang::DiagnosticsEngine& Diags, 123 clang::SourceManager& SourceMgr, 124 const clang::LangOptions& LangOpts, 125 clang::FixItOptions* FixItOpts) 126 : clang::FixItRewriter(Diags, SourceMgr, LangOpts, FixItOpts) { 127 } 128 129 bool IncludeInDiagnosticCounts() const override { return false; } 130 }; 131 132 /// Subclasses \c clang::FixItAction so that we can install the custom 133 /// \c FixItRewriter. 134 class ClangCheckFixItAction : public clang::FixItAction { 135 public: 136 bool BeginSourceFileAction(clang::CompilerInstance& CI) override { 137 FixItOpts.reset(new FixItOptions); 138 Rewriter.reset(new FixItRewriter(CI.getDiagnostics(), CI.getSourceManager(), 139 CI.getLangOpts(), FixItOpts.get())); 140 return true; 141 } 142 }; 143 144 class DumpSyntaxTree : public clang::ASTFrontendAction { 145 public: 146 std::unique_ptr<clang::ASTConsumer> 147 CreateASTConsumer(clang::CompilerInstance &CI, StringRef InFile) override { 148 class Consumer : public clang::ASTConsumer { 149 public: 150 Consumer(clang::CompilerInstance &CI) : Collector(CI.getPreprocessor()) {} 151 152 void HandleTranslationUnit(clang::ASTContext &AST) override { 153 clang::syntax::TokenBuffer TB = std::move(Collector).consume(); 154 if (TokensDump) 155 llvm::outs() << TB.dumpForTests(); 156 clang::syntax::Arena A(AST.getSourceManager(), AST.getLangOpts(), TB); 157 llvm::outs() << clang::syntax::buildSyntaxTree(A, AST)->dump( 158 AST.getSourceManager()); 159 } 160 161 private: 162 clang::syntax::TokenCollector Collector; 163 }; 164 return std::make_unique<Consumer>(CI); 165 } 166 }; 167 168 class ClangCheckActionFactory { 169 public: 170 std::unique_ptr<clang::ASTConsumer> newASTConsumer() { 171 if (ASTList) 172 return clang::CreateASTDeclNodeLister(); 173 if (ASTDump) 174 return clang::CreateASTDumper(nullptr /*Dump to stdout.*/, ASTDumpFilter, 175 /*DumpDecls=*/true, 176 /*Deserialize=*/false, 177 /*DumpLookups=*/false, 178 /*DumpDeclTypes=*/false, 179 clang::ADOF_Default); 180 if (ASTPrint) 181 return clang::CreateASTPrinter(nullptr, ASTDumpFilter); 182 return std::make_unique<clang::ASTConsumer>(); 183 } 184 }; 185 186 } // namespace 187 188 int main(int argc, const char **argv) { 189 llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); 190 191 // Initialize targets for clang module support. 192 llvm::InitializeAllTargets(); 193 llvm::InitializeAllTargetMCs(); 194 llvm::InitializeAllAsmPrinters(); 195 llvm::InitializeAllAsmParsers(); 196 197 auto ExpectedParser = 198 CommonOptionsParser::create(argc, argv, ClangCheckCategory); 199 if (!ExpectedParser) { 200 llvm::errs() << ExpectedParser.takeError(); 201 return 1; 202 } 203 CommonOptionsParser &OptionsParser = ExpectedParser.get(); 204 ClangTool Tool(OptionsParser.getCompilations(), 205 OptionsParser.getSourcePathList()); 206 207 // Clear adjusters because -fsyntax-only is inserted by the default chain. 208 Tool.clearArgumentsAdjusters(); 209 Tool.appendArgumentsAdjuster(getClangStripOutputAdjuster()); 210 Tool.appendArgumentsAdjuster(getClangStripDependencyFileAdjuster()); 211 212 // Running the analyzer requires --analyze. Other modes can work with the 213 // -fsyntax-only option. 214 Tool.appendArgumentsAdjuster(getInsertArgumentAdjuster( 215 Analyze ? "--analyze" : "-fsyntax-only", ArgumentInsertPosition::BEGIN)); 216 217 ClangCheckActionFactory CheckFactory; 218 std::unique_ptr<FrontendActionFactory> FrontendFactory; 219 220 // Choose the correct factory based on the selected mode. 221 if (Analyze) 222 FrontendFactory = newFrontendActionFactory<clang::ento::AnalysisAction>(); 223 else if (Fixit) 224 FrontendFactory = newFrontendActionFactory<ClangCheckFixItAction>(); 225 else if (SyntaxTreeDump || TokensDump) 226 FrontendFactory = newFrontendActionFactory<DumpSyntaxTree>(); 227 else 228 FrontendFactory = newFrontendActionFactory(&CheckFactory); 229 230 return Tool.run(FrontendFactory.get()); 231 } 232