diff options
Diffstat (limited to 'contrib/llvm/tools/clang/lib/Tooling/Tooling.cpp')
-rw-r--r-- | contrib/llvm/tools/clang/lib/Tooling/Tooling.cpp | 162 |
1 files changed, 86 insertions, 76 deletions
diff --git a/contrib/llvm/tools/clang/lib/Tooling/Tooling.cpp b/contrib/llvm/tools/clang/lib/Tooling/Tooling.cpp index 0db38db..60371fb 100644 --- a/contrib/llvm/tools/clang/lib/Tooling/Tooling.cpp +++ b/contrib/llvm/tools/clang/lib/Tooling/Tooling.cpp @@ -79,14 +79,14 @@ static const llvm::opt::ArgStringList *getCC1Arguments( } // The one job we find should be to invoke clang again. - const clang::driver::Command *Cmd = + const clang::driver::Command &Cmd = cast<clang::driver::Command>(*Jobs.begin()); - if (StringRef(Cmd->getCreator().getName()) != "clang") { + if (StringRef(Cmd.getCreator().getName()) != "clang") { Diagnostics->Report(clang::diag::err_fe_expected_clang_command); return nullptr; } - return &Cmd->getArguments(); + return &Cmd.getArguments(); } /// \brief Returns a clang build invocation initialized from the CC1 flags. @@ -123,17 +123,25 @@ getSyntaxOnlyToolArgs(const std::vector<std::string> &ExtraArgs, bool runToolOnCodeWithArgs(clang::FrontendAction *ToolAction, const Twine &Code, const std::vector<std::string> &Args, - const Twine &FileName) { + const Twine &FileName, + const FileContentMappings &VirtualMappedFiles) { + SmallString<16> FileNameStorage; StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage); llvm::IntrusiveRefCntPtr<FileManager> Files( new FileManager(FileSystemOptions())); - ToolInvocation Invocation(getSyntaxOnlyToolArgs(Args, FileNameRef), ToolAction, - Files.get()); + ToolInvocation Invocation(getSyntaxOnlyToolArgs(Args, FileNameRef), + ToolAction, Files.get()); SmallString<1024> CodeStorage; Invocation.mapVirtualFile(FileNameRef, Code.toNullTerminatedStringRef(CodeStorage)); + + for (auto &FilenameWithContent : VirtualMappedFiles) { + Invocation.mapVirtualFile(FilenameWithContent.first, + FilenameWithContent.second); + } + return Invocation.run(); } @@ -186,10 +194,6 @@ ToolInvocation::~ToolInvocation() { delete Action; } -void ToolInvocation::setDiagnosticConsumer(DiagnosticConsumer *D) { - DiagConsumer = D; -} - void ToolInvocation::mapVirtualFile(StringRef FilePath, StringRef Content) { SmallString<1024> PathStorage; llvm::sys::path::native(FilePath, PathStorage); @@ -223,8 +227,10 @@ bool ToolInvocation::run() { newInvocation(&Diagnostics, *CC1Args)); for (const auto &It : MappedFileContents) { // Inject the code as the given file name into the preprocessor options. - auto *Input = llvm::MemoryBuffer::getMemBuffer(It.getValue()); - Invocation->getPreprocessorOpts().addRemappedFile(It.getKey(), Input); + std::unique_ptr<llvm::MemoryBuffer> Input = + llvm::MemoryBuffer::getMemBuffer(It.getValue()); + Invocation->getPreprocessorOpts().addRemappedFile(It.getKey(), + Input.release()); } return runInvocation(BinaryName, Compilation.get(), Invocation.release()); } @@ -271,51 +277,27 @@ bool FrontendActionFactory::runInvocation(CompilerInvocation *Invocation, ClangTool::ClangTool(const CompilationDatabase &Compilations, ArrayRef<std::string> SourcePaths) - : Files(new FileManager(FileSystemOptions())), DiagConsumer(nullptr) { - ArgsAdjusters.push_back(new ClangStripOutputAdjuster()); - ArgsAdjusters.push_back(new ClangSyntaxOnlyAdjuster()); - for (const auto &SourcePath : SourcePaths) { - std::string File(getAbsolutePath(SourcePath)); - - std::vector<CompileCommand> CompileCommandsForFile = - Compilations.getCompileCommands(File); - if (!CompileCommandsForFile.empty()) { - for (CompileCommand &CompileCommand : CompileCommandsForFile) { - CompileCommands.push_back( - std::make_pair(File, std::move(CompileCommand))); - } - } else { - // FIXME: There are two use cases here: doing a fuzzy - // "find . -name '*.cc' |xargs tool" match, where as a user I don't care - // about the .cc files that were not found, and the use case where I - // specify all files I want to run over explicitly, where this should - // be an error. We'll want to add an option for this. - llvm::errs() << "Skipping " << File << ". Compile command not found.\n"; - } - } + : Compilations(Compilations), SourcePaths(SourcePaths), + Files(new FileManager(FileSystemOptions())), DiagConsumer(nullptr) { + appendArgumentsAdjuster(getClangStripOutputAdjuster()); + appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster()); } -void ClangTool::setDiagnosticConsumer(DiagnosticConsumer *D) { - DiagConsumer = D; -} +ClangTool::~ClangTool() {} void ClangTool::mapVirtualFile(StringRef FilePath, StringRef Content) { MappedFileContents.push_back(std::make_pair(FilePath, Content)); } -void ClangTool::setArgumentsAdjuster(ArgumentsAdjuster *Adjuster) { - clearArgumentsAdjusters(); - appendArgumentsAdjuster(Adjuster); -} - -void ClangTool::appendArgumentsAdjuster(ArgumentsAdjuster *Adjuster) { - ArgsAdjusters.push_back(Adjuster); +void ClangTool::appendArgumentsAdjuster(ArgumentsAdjuster Adjuster) { + if (ArgsAdjuster) + ArgsAdjuster = combineAdjusters(ArgsAdjuster, Adjuster); + else + ArgsAdjuster = Adjuster; } void ClangTool::clearArgumentsAdjusters() { - for (unsigned I = 0, E = ArgsAdjusters.size(); I != E; ++I) - delete ArgsAdjusters[I]; - ArgsAdjusters.clear(); + ArgsAdjuster = nullptr; } int ClangTool::run(ToolAction *Action) { @@ -330,37 +312,65 @@ int ClangTool::run(ToolAction *Action) { std::string MainExecutable = llvm::sys::fs::getMainExecutable("clang_tool", &StaticSymbol); + llvm::SmallString<128> InitialDirectory; + if (std::error_code EC = llvm::sys::fs::current_path(InitialDirectory)) + llvm::report_fatal_error("Cannot detect current path: " + + Twine(EC.message())); bool ProcessingFailed = false; - for (const auto &Command : CompileCommands) { - // FIXME: chdir is thread hostile; on the other hand, creating the same - // behavior as chdir is complex: chdir resolves the path once, thus - // guaranteeing that all subsequent relative path operations work - // on the same path the original chdir resulted in. This makes a difference - // for example on network filesystems, where symlinks might be switched - // during runtime of the tool. Fixing this depends on having a file system - // abstraction that allows openat() style interactions. - if (chdir(Command.second.Directory.c_str())) - llvm::report_fatal_error("Cannot chdir into \"" + - Twine(Command.second.Directory) + "\n!"); - std::vector<std::string> CommandLine = Command.second.CommandLine; - for (ArgumentsAdjuster *Adjuster : ArgsAdjusters) - CommandLine = Adjuster->Adjust(CommandLine); - assert(!CommandLine.empty()); - CommandLine[0] = MainExecutable; - // FIXME: We need a callback mechanism for the tool writer to output a - // customized message for each file. - DEBUG({ - llvm::dbgs() << "Processing: " << Command.first << ".\n"; - }); - ToolInvocation Invocation(std::move(CommandLine), Action, Files.get()); - Invocation.setDiagnosticConsumer(DiagConsumer); - for (const auto &MappedFile : MappedFileContents) { - Invocation.mapVirtualFile(MappedFile.first, MappedFile.second); + for (const auto &SourcePath : SourcePaths) { + std::string File(getAbsolutePath(SourcePath)); + + // Currently implementations of CompilationDatabase::getCompileCommands can + // change the state of the file system (e.g. prepare generated headers), so + // this method needs to run right before we invoke the tool, as the next + // file may require a different (incompatible) state of the file system. + // + // FIXME: Make the compilation database interface more explicit about the + // requirements to the order of invocation of its members. + std::vector<CompileCommand> CompileCommandsForFile = + Compilations.getCompileCommands(File); + if (CompileCommandsForFile.empty()) { + // FIXME: There are two use cases here: doing a fuzzy + // "find . -name '*.cc' |xargs tool" match, where as a user I don't care + // about the .cc files that were not found, and the use case where I + // specify all files I want to run over explicitly, where this should + // be an error. We'll want to add an option for this. + llvm::errs() << "Skipping " << File << ". Compile command not found.\n"; + continue; } - if (!Invocation.run()) { - // FIXME: Diagnostics should be used instead. - llvm::errs() << "Error while processing " << Command.first << ".\n"; - ProcessingFailed = true; + for (CompileCommand &CompileCommand : CompileCommandsForFile) { + // FIXME: chdir is thread hostile; on the other hand, creating the same + // behavior as chdir is complex: chdir resolves the path once, thus + // guaranteeing that all subsequent relative path operations work + // on the same path the original chdir resulted in. This makes a + // difference for example on network filesystems, where symlinks might be + // switched during runtime of the tool. Fixing this depends on having a + // file system abstraction that allows openat() style interactions. + if (chdir(CompileCommand.Directory.c_str())) + llvm::report_fatal_error("Cannot chdir into \"" + + Twine(CompileCommand.Directory) + "\n!"); + std::vector<std::string> CommandLine = CompileCommand.CommandLine; + if (ArgsAdjuster) + CommandLine = ArgsAdjuster(CommandLine); + assert(!CommandLine.empty()); + CommandLine[0] = MainExecutable; + // FIXME: We need a callback mechanism for the tool writer to output a + // customized message for each file. + DEBUG({ llvm::dbgs() << "Processing: " << File << ".\n"; }); + ToolInvocation Invocation(std::move(CommandLine), Action, Files.get()); + Invocation.setDiagnosticConsumer(DiagConsumer); + for (const auto &MappedFile : MappedFileContents) + Invocation.mapVirtualFile(MappedFile.first, MappedFile.second); + if (!Invocation.run()) { + // FIXME: Diagnostics should be used instead. + llvm::errs() << "Error while processing " << File << ".\n"; + ProcessingFailed = true; + } + // Return to the initial directory to correctly resolve next file by + // relative path. + if (chdir(InitialDirectory.c_str())) + llvm::report_fatal_error("Cannot chdir into \"" + + Twine(InitialDirectory) + "\n!"); } } return ProcessingFailed ? 1 : 0; |