diff options
Diffstat (limited to 'contrib/llvm/tools/clang/lib/Tooling')
7 files changed, 596 insertions, 69 deletions
diff --git a/contrib/llvm/tools/clang/lib/Tooling/ArgumentsAdjusters.cpp b/contrib/llvm/tools/clang/lib/Tooling/ArgumentsAdjusters.cpp new file mode 100644 index 0000000..31dd4659 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Tooling/ArgumentsAdjusters.cpp @@ -0,0 +1,34 @@ +//===--- ArgumentsAdjusters.cpp - Command line arguments adjuster ---------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains definitions of classes which implement ArgumentsAdjuster +// interface. +// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/ArgumentsAdjusters.h" + +namespace clang { +namespace tooling { + +void ArgumentsAdjuster::anchor() { +} + +/// Add -fsyntax-only option to the commnand line arguments. +CommandLineArguments +ClangSyntaxOnlyAdjuster::Adjust(const CommandLineArguments &Args) { + CommandLineArguments AdjustedArgs = Args; + // FIXME: Remove options that generate output. + AdjustedArgs.push_back("-fsyntax-only"); + return AdjustedArgs; +} + +} // end namespace tooling +} // end namespace clang + diff --git a/contrib/llvm/tools/clang/lib/Tooling/CommandLineClangTool.cpp b/contrib/llvm/tools/clang/lib/Tooling/CommandLineClangTool.cpp new file mode 100644 index 0000000..8da2a33 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Tooling/CommandLineClangTool.cpp @@ -0,0 +1,80 @@ +//===--- CommandLineClangTool.cpp - command-line clang tools driver -------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the CommandLineClangTool class used to run clang +// tools as separate command-line applications with a consistent common +// interface for handling compilation database and input files. +// +// It provides a common subset of command-line options, common algorithm +// for locating a compilation database and source files, and help messages +// for the basic command-line interface. +// +// It creates a CompilationDatabase, initializes a ClangTool and runs a +// user-specified FrontendAction over all TUs in which the given files are +// compiled. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/FrontendActions.h" +#include "clang/Tooling/CommandLineClangTool.h" +#include "clang/Tooling/Tooling.h" + +using namespace clang::tooling; +using namespace llvm; + +static const char *MoreHelpText = + "\n" + "-p <build-path> is used to read a compile command database.\n" + "\n" + "\tFor example, it can be a CMake build directory in which a file named\n" + "\tcompile_commands.json exists (use -DCMAKE_EXPORT_COMPILE_COMMANDS=ON\n" + "\tCMake option to get this output). When no build path is specified,\n" + "\tclang-check will attempt to locate it automatically using all parent\n" + "\tpaths of the first input file. See:\n" + "\thttp://clang.llvm.org/docs/HowToSetupToolingForLLVM.html for an\n" + "\texample of setting up Clang Tooling on a source tree.\n" + "\n" + "<source0> ... specify the paths of source files. These paths are looked\n" + "\tup in the compile command database. If the path of a file is absolute,\n" + "\tit needs to point into CMake's source tree. If the path is relative,\n" + "\tthe current working directory needs to be in the CMake source tree and\n" + "\tthe file must be in a subdirectory of the current working directory.\n" + "\t\"./\" prefixes in the relative files will be automatically removed,\n" + "\tbut the rest of a relative path must be a suffix of a path in the\n" + "\tcompile command database.\n" + "\n"; + +CommandLineClangTool::CommandLineClangTool() : + BuildPath("p", cl::desc("Build path"), cl::Optional), + SourcePaths(cl::Positional, cl::desc("<source0> [... <sourceN>]"), + cl::OneOrMore), + MoreHelp(MoreHelpText) { +} + +void CommandLineClangTool::initialize(int argc, const char **argv) { + Compilations.reset(FixedCompilationDatabase::loadFromCommandLine(argc, argv)); + cl::ParseCommandLineOptions(argc, argv); + if (!Compilations) { + std::string ErrorMessage; + if (!BuildPath.empty()) { + Compilations.reset(CompilationDatabase::autoDetectFromDirectory( + BuildPath, ErrorMessage)); + } else { + Compilations.reset(CompilationDatabase::autoDetectFromSource( + SourcePaths[0], ErrorMessage)); + } + if (!Compilations) + llvm::report_fatal_error(ErrorMessage); + } +} + +int CommandLineClangTool::run(FrontendActionFactory *ActionFactory) { + ClangTool Tool(*Compilations, SourcePaths); + return Tool.run(ActionFactory); +} diff --git a/contrib/llvm/tools/clang/lib/Tooling/CompilationDatabase.cpp b/contrib/llvm/tools/clang/lib/Tooling/CompilationDatabase.cpp index dd9ccc0..3139cc2 100644 --- a/contrib/llvm/tools/clang/lib/Tooling/CompilationDatabase.cpp +++ b/contrib/llvm/tools/clang/lib/Tooling/CompilationDatabase.cpp @@ -12,11 +12,16 @@ //===----------------------------------------------------------------------===// #include "clang/Tooling/CompilationDatabase.h" +#include "clang/Tooling/Tooling.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/YAMLParser.h" #include "llvm/Support/Path.h" #include "llvm/Support/system_error.h" +#ifdef USE_CUSTOM_COMPILATION_DATABASE +#include "CustomCompilationDatabase.h" +#endif + namespace clang { namespace tooling { @@ -121,6 +126,52 @@ CompilationDatabase::loadFromDirectory(StringRef BuildDirectory, return Database.take(); } +static CompilationDatabase * +findCompilationDatabaseFromDirectory(StringRef Directory) { +#ifdef USE_CUSTOM_COMPILATION_DATABASE + if (CompilationDatabase *DB = + ::clang::tooling::findCompilationDatabaseForDirectory(Directory)) + return DB; +#endif + while (!Directory.empty()) { + std::string LoadErrorMessage; + + if (CompilationDatabase *DB = + CompilationDatabase::loadFromDirectory(Directory, LoadErrorMessage)) + return DB; + + Directory = llvm::sys::path::parent_path(Directory); + } + return NULL; +} + +CompilationDatabase * +CompilationDatabase::autoDetectFromSource(StringRef SourceFile, + std::string &ErrorMessage) { + llvm::SmallString<1024> AbsolutePath(getAbsolutePath(SourceFile)); + StringRef Directory = llvm::sys::path::parent_path(AbsolutePath); + + CompilationDatabase *DB = findCompilationDatabaseFromDirectory(Directory); + + if (!DB) + ErrorMessage = ("Could not auto-detect compilation database for file \"" + + SourceFile + "\"").str(); + return DB; +} + +CompilationDatabase * +CompilationDatabase::autoDetectFromDirectory(StringRef SourceDir, + std::string &ErrorMessage) { + llvm::SmallString<1024> AbsolutePath(getAbsolutePath(SourceDir)); + + CompilationDatabase *DB = findCompilationDatabaseFromDirectory(AbsolutePath); + + if (!DB) + ErrorMessage = ("Could not auto-detect compilation database from directory \"" + + SourceDir + "\"").str(); + return DB; +} + FixedCompilationDatabase * FixedCompilationDatabase::loadFromCommandLine(int &Argc, const char **Argv, @@ -148,6 +199,11 @@ FixedCompilationDatabase::getCompileCommands(StringRef FilePath) const { return Result; } +std::vector<std::string> +FixedCompilationDatabase::getAllFiles() const { + return std::vector<std::string>(); +} + JSONCompilationDatabase * JSONCompilationDatabase::loadFromFile(StringRef FilePath, std::string &ErrorMessage) { @@ -179,8 +235,10 @@ JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString, std::vector<CompileCommand> JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const { + llvm::SmallString<128> NativeFilePath; + llvm::sys::path::native(FilePath, NativeFilePath); llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator - CommandsRefI = IndexByFile.find(FilePath); + CommandsRefI = IndexByFile.find(NativeFilePath); if (CommandsRefI == IndexByFile.end()) return std::vector<CompileCommand>(); const std::vector<CompileCommandRef> &CommandsRef = CommandsRefI->getValue(); @@ -196,6 +254,21 @@ JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const { return Commands; } +std::vector<std::string> +JSONCompilationDatabase::getAllFiles() const { + std::vector<std::string> Result; + + llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator + CommandsRefI = IndexByFile.begin(); + const llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator + CommandsRefEnd = IndexByFile.end(); + for (; CommandsRefI != CommandsRefEnd; ++CommandsRefI) { + Result.push_back(CommandsRefI->first().str()); + } + + return Result; +} + bool JSONCompilationDatabase::parse(std::string &ErrorMessage) { llvm::yaml::document_iterator I = YAMLStream.begin(); if (I == YAMLStream.end()) { @@ -222,10 +295,9 @@ bool JSONCompilationDatabase::parse(std::string &ErrorMessage) { ErrorMessage = "Expected object."; return false; } - llvm::yaml::ScalarNode *Directory; - llvm::yaml::ScalarNode *Command; - llvm::SmallString<8> FileStorage; - llvm::StringRef File; + llvm::yaml::ScalarNode *Directory = NULL; + llvm::yaml::ScalarNode *Command = NULL; + llvm::yaml::ScalarNode *File = NULL; for (llvm::yaml::MappingNode::iterator KVI = Object->begin(), KVE = Object->end(); KVI != KVE; ++KVI) { @@ -242,20 +314,39 @@ bool JSONCompilationDatabase::parse(std::string &ErrorMessage) { } llvm::yaml::ScalarNode *KeyString = llvm::dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey()); + if (KeyString == NULL) { + ErrorMessage = "Expected strings as key."; + return false; + } llvm::SmallString<8> KeyStorage; if (KeyString->getValue(KeyStorage) == "directory") { Directory = ValueString; } else if (KeyString->getValue(KeyStorage) == "command") { Command = ValueString; } else if (KeyString->getValue(KeyStorage) == "file") { - File = ValueString->getValue(FileStorage); + File = ValueString; } else { ErrorMessage = ("Unknown key: \"" + KeyString->getRawValue() + "\"").str(); return false; } } - IndexByFile[File].push_back( + if (!File) { + ErrorMessage = "Missing key: \"file\"."; + return false; + } + if (!Command) { + ErrorMessage = "Missing key: \"command\"."; + return false; + } + if (!Directory) { + ErrorMessage = "Missing key: \"directory\"."; + return false; + } + llvm::SmallString<8> FileStorage; + llvm::SmallString<128> NativeFilePath; + llvm::sys::path::native(File->getValue(FileStorage), NativeFilePath); + IndexByFile[NativeFilePath].push_back( CompileCommandRef(Directory, Command)); } return true; @@ -263,4 +354,3 @@ bool JSONCompilationDatabase::parse(std::string &ErrorMessage) { } // end namespace tooling } // end namespace clang - diff --git a/contrib/llvm/tools/clang/lib/Tooling/CustomCompilationDatabase.h b/contrib/llvm/tools/clang/lib/Tooling/CustomCompilationDatabase.h new file mode 100644 index 0000000..b375f8d --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Tooling/CustomCompilationDatabase.h @@ -0,0 +1,42 @@ +//===--- CustomCompilationDatabase.h --------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains a hook to supply a custom \c CompilationDatabase +// implementation. +// +// The mechanism can be used by IDEs or non-public code bases to integrate with +// their build system. Currently we support statically linking in an +// implementation of \c findCompilationDatabaseForDirectory and enabling it +// with -DUSE_CUSTOM_COMPILATION_DATABASE when compiling the Tooling library. +// +// FIXME: The strategy forward is to provide a plugin system that can load +// custom compilation databases and make enabling that a build option. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_TOOLING_CUSTOM_COMPILATION_DATABASE_H +#define LLVM_CLANG_TOOLING_CUSTOM_COMPILATION_DATABASE_H + +#include "llvm/ADT/StringRef.h" + +namespace clang { +namespace tooling { +class CompilationDatabase; + +/// \brief Returns a CompilationDatabase for the given \c Directory. +/// +/// \c Directory can be any directory within a project. This methods will +/// then try to find compilation database files in \c Directory or any of its +/// parents. If a compilation database cannot be found or loaded, returns NULL. +clang::tooling::CompilationDatabase *findCompilationDatabaseForDirectory( + llvm::StringRef Directory); + +} // namespace tooling +} // namespace clang + +#endif // LLVM_CLANG_TOOLING_CUSTOM_COMPILATION_DATABASE_H diff --git a/contrib/llvm/tools/clang/lib/Tooling/Refactoring.cpp b/contrib/llvm/tools/clang/lib/Tooling/Refactoring.cpp new file mode 100644 index 0000000..6284353 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Tooling/Refactoring.cpp @@ -0,0 +1,186 @@ +//===--- Refactoring.cpp - Framework for clang refactoring tools ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implements tools to support refactorings. +// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Frontend/DiagnosticOptions.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Lex/Lexer.h" +#include "clang/Rewrite/Rewriter.h" +#include "clang/Tooling/Refactoring.h" +#include "llvm/Support/raw_os_ostream.h" + +namespace clang { +namespace tooling { + +static const char * const InvalidLocation = ""; + +Replacement::Replacement() + : FilePath(InvalidLocation), Offset(0), Length(0) {} + +Replacement::Replacement(llvm::StringRef FilePath, unsigned Offset, + unsigned Length, llvm::StringRef ReplacementText) + : FilePath(FilePath), Offset(Offset), + Length(Length), ReplacementText(ReplacementText) {} + +Replacement::Replacement(SourceManager &Sources, SourceLocation Start, + unsigned Length, llvm::StringRef ReplacementText) { + setFromSourceLocation(Sources, Start, Length, ReplacementText); +} + +Replacement::Replacement(SourceManager &Sources, const CharSourceRange &Range, + llvm::StringRef ReplacementText) { + setFromSourceRange(Sources, Range, ReplacementText); +} + +bool Replacement::isApplicable() const { + return FilePath != InvalidLocation; +} + +bool Replacement::apply(Rewriter &Rewrite) const { + SourceManager &SM = Rewrite.getSourceMgr(); + const FileEntry *Entry = SM.getFileManager().getFile(FilePath); + if (Entry == NULL) + return false; + FileID ID; + // FIXME: Use SM.translateFile directly. + SourceLocation Location = SM.translateFileLineCol(Entry, 1, 1); + ID = Location.isValid() ? + SM.getFileID(Location) : + SM.createFileID(Entry, SourceLocation(), SrcMgr::C_User); + // FIXME: We cannot check whether Offset + Length is in the file, as + // the remapping API is not public in the RewriteBuffer. + const SourceLocation Start = + SM.getLocForStartOfFile(ID). + getLocWithOffset(Offset); + // ReplaceText returns false on success. + // ReplaceText only fails if the source location is not a file location, in + // which case we already returned false earlier. + bool RewriteSucceeded = !Rewrite.ReplaceText(Start, Length, ReplacementText); + assert(RewriteSucceeded); + return RewriteSucceeded; +} + +std::string Replacement::toString() const { + std::string result; + llvm::raw_string_ostream stream(result); + stream << FilePath << ": " << Offset << ":+" << Length + << ":\"" << ReplacementText << "\""; + return result; +} + +bool Replacement::Less::operator()(const Replacement &R1, + const Replacement &R2) const { + if (R1.FilePath != R2.FilePath) return R1.FilePath < R2.FilePath; + if (R1.Offset != R2.Offset) return R1.Offset < R2.Offset; + if (R1.Length != R2.Length) return R1.Length < R2.Length; + return R1.ReplacementText < R2.ReplacementText; +} + +void Replacement::setFromSourceLocation(SourceManager &Sources, + SourceLocation Start, unsigned Length, + llvm::StringRef ReplacementText) { + const std::pair<FileID, unsigned> DecomposedLocation = + Sources.getDecomposedLoc(Start); + const FileEntry *Entry = Sources.getFileEntryForID(DecomposedLocation.first); + this->FilePath = Entry != NULL ? Entry->getName() : InvalidLocation; + this->Offset = DecomposedLocation.second; + this->Length = Length; + this->ReplacementText = ReplacementText; +} + +// FIXME: This should go into the Lexer, but we need to figure out how +// to handle ranges for refactoring in general first - there is no obvious +// good way how to integrate this into the Lexer yet. +static int getRangeSize(SourceManager &Sources, const CharSourceRange &Range) { + SourceLocation SpellingBegin = Sources.getSpellingLoc(Range.getBegin()); + SourceLocation SpellingEnd = Sources.getSpellingLoc(Range.getEnd()); + std::pair<FileID, unsigned> Start = Sources.getDecomposedLoc(SpellingBegin); + std::pair<FileID, unsigned> End = Sources.getDecomposedLoc(SpellingEnd); + if (Start.first != End.first) return -1; + if (Range.isTokenRange()) + End.second += Lexer::MeasureTokenLength(SpellingEnd, Sources, + LangOptions()); + return End.second - Start.second; +} + +void Replacement::setFromSourceRange(SourceManager &Sources, + const CharSourceRange &Range, + llvm::StringRef ReplacementText) { + setFromSourceLocation(Sources, Sources.getSpellingLoc(Range.getBegin()), + getRangeSize(Sources, Range), ReplacementText); +} + +bool applyAllReplacements(Replacements &Replaces, Rewriter &Rewrite) { + bool Result = true; + for (Replacements::const_iterator I = Replaces.begin(), + E = Replaces.end(); + I != E; ++I) { + if (I->isApplicable()) { + Result = I->apply(Rewrite) && Result; + } else { + Result = false; + } + } + return Result; +} + +bool saveRewrittenFiles(Rewriter &Rewrite) { + for (Rewriter::buffer_iterator I = Rewrite.buffer_begin(), + E = Rewrite.buffer_end(); + I != E; ++I) { + // FIXME: This code is copied from the FixItRewriter.cpp - I think it should + // go into directly into Rewriter (there we also have the Diagnostics to + // handle the error cases better). + const FileEntry *Entry = + Rewrite.getSourceMgr().getFileEntryForID(I->first); + std::string ErrorInfo; + llvm::raw_fd_ostream FileStream( + Entry->getName(), ErrorInfo, llvm::raw_fd_ostream::F_Binary); + if (!ErrorInfo.empty()) + return false; + I->second.write(FileStream); + FileStream.flush(); + } + return true; +} + +RefactoringTool::RefactoringTool(const CompilationDatabase &Compilations, + ArrayRef<std::string> SourcePaths) + : Tool(Compilations, SourcePaths) {} + +Replacements &RefactoringTool::getReplacements() { return Replace; } + +int RefactoringTool::run(FrontendActionFactory *ActionFactory) { + int Result = Tool.run(ActionFactory); + LangOptions DefaultLangOptions; + DiagnosticOptions DefaultDiagnosticOptions; + TextDiagnosticPrinter DiagnosticPrinter(llvm::errs(), + DefaultDiagnosticOptions); + DiagnosticsEngine Diagnostics( + llvm::IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), + &DiagnosticPrinter, false); + SourceManager Sources(Diagnostics, Tool.getFiles()); + Rewriter Rewrite(Sources, DefaultLangOptions); + if (!applyAllReplacements(Replace, Rewrite)) { + llvm::errs() << "Skipped some replacements.\n"; + } + if (!saveRewrittenFiles(Rewrite)) { + llvm::errs() << "Could not save rewritten files.\n"; + return 1; + } + return Result; +} + +} // end namespace tooling +} // end namespace clang diff --git a/contrib/llvm/tools/clang/lib/Tooling/RefactoringCallbacks.cpp b/contrib/llvm/tools/clang/lib/Tooling/RefactoringCallbacks.cpp new file mode 100644 index 0000000..4de125e --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Tooling/RefactoringCallbacks.cpp @@ -0,0 +1,81 @@ +//===--- RefactoringCallbacks.cpp - Structural query framework ------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// +//===----------------------------------------------------------------------===// +#include "clang/Lex/Lexer.h" +#include "clang/Tooling/RefactoringCallbacks.h" + +namespace clang { +namespace tooling { + +RefactoringCallback::RefactoringCallback() {} +tooling::Replacements &RefactoringCallback::getReplacements() { + return Replace; +} + +static Replacement replaceStmtWithText(SourceManager &Sources, + const Stmt &From, + StringRef Text) { + return tooling::Replacement(Sources, CharSourceRange::getTokenRange( + From.getSourceRange()), Text); +} +static Replacement replaceStmtWithStmt(SourceManager &Sources, + const Stmt &From, + const Stmt &To) { + return replaceStmtWithText(Sources, From, Lexer::getSourceText( + CharSourceRange::getTokenRange(To.getSourceRange()), + Sources, LangOptions())); +} + +ReplaceStmtWithText::ReplaceStmtWithText(StringRef FromId, StringRef ToText) + : FromId(FromId), ToText(ToText) {} + +void ReplaceStmtWithText::run( + const ast_matchers::MatchFinder::MatchResult &Result) { + if (const Stmt *FromMatch = Result.Nodes.getStmtAs<Stmt>(FromId)) { + Replace.insert(tooling::Replacement( + *Result.SourceManager, + CharSourceRange::getTokenRange(FromMatch->getSourceRange()), + ToText)); + } +} + +ReplaceStmtWithStmt::ReplaceStmtWithStmt(StringRef FromId, StringRef ToId) + : FromId(FromId), ToId(ToId) {} + +void ReplaceStmtWithStmt::run( + const ast_matchers::MatchFinder::MatchResult &Result) { + const Stmt *FromMatch = Result.Nodes.getStmtAs<Stmt>(FromId); + const Stmt *ToMatch = Result.Nodes.getStmtAs<Stmt>(ToId); + if (FromMatch && ToMatch) + Replace.insert(replaceStmtWithStmt( + *Result.SourceManager, *FromMatch, *ToMatch)); +} + +ReplaceIfStmtWithItsBody::ReplaceIfStmtWithItsBody(StringRef Id, + bool PickTrueBranch) + : Id(Id), PickTrueBranch(PickTrueBranch) {} + +void ReplaceIfStmtWithItsBody::run( + const ast_matchers::MatchFinder::MatchResult &Result) { + if (const IfStmt *Node = Result.Nodes.getStmtAs<IfStmt>(Id)) { + const Stmt *Body = PickTrueBranch ? Node->getThen() : Node->getElse(); + if (Body) { + Replace.insert(replaceStmtWithStmt(*Result.SourceManager, *Node, *Body)); + } else if (!PickTrueBranch) { + // If we want to use the 'else'-branch, but it doesn't exist, delete + // the whole 'if'. + Replace.insert(replaceStmtWithText(*Result.SourceManager, *Node, "")); + } + } +} + +} // end namespace tooling +} // end namespace clang diff --git a/contrib/llvm/tools/clang/lib/Tooling/Tooling.cpp b/contrib/llvm/tools/clang/lib/Tooling/Tooling.cpp index fa2374f..e93e0c9 100644 --- a/contrib/llvm/tools/clang/lib/Tooling/Tooling.cpp +++ b/contrib/llvm/tools/clang/lib/Tooling/Tooling.cpp @@ -12,13 +12,13 @@ // //===----------------------------------------------------------------------===// +#include "clang/Tooling/ArgumentsAdjusters.h" #include "clang/Tooling/Tooling.h" #include "clang/Tooling/CompilationDatabase.h" #include "clang/Driver/Compilation.h" #include "clang/Driver/Driver.h" #include "clang/Driver/Tool.h" #include "clang/Frontend/CompilerInstance.h" -#include "clang/Frontend/FrontendAction.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Frontend/TextDiagnosticPrinter.h" #include "llvm/ADT/STLExtras.h" @@ -26,6 +26,13 @@ #include "llvm/Support/Host.h" #include "llvm/Support/raw_ostream.h" +// For chdir, see the comment in ClangTool::run for more information. +#ifdef _WIN32 +# include <direct.h> +#else +# include <unistd.h> +#endif + namespace clang { namespace tooling { @@ -40,8 +47,8 @@ static clang::driver::Driver *newDriver(clang::DiagnosticsEngine *Diagnostics, const char *BinaryName) { const std::string DefaultOutputName = "a.out"; clang::driver::Driver *CompilerDriver = new clang::driver::Driver( - BinaryName, llvm::sys::getDefaultTargetTriple(), - DefaultOutputName, false, *Diagnostics); + BinaryName, llvm::sys::getDefaultTargetTriple(), + DefaultOutputName, false, *Diagnostics); CompilerDriver->setTitle("clang_based_tool"); return CompilerDriver; } @@ -108,29 +115,26 @@ bool runToolOnCode(clang::FrontendAction *ToolAction, const Twine &Code, return Invocation.run(); } -/// \brief Returns the absolute path of 'File', by prepending it with -/// 'BaseDirectory' if 'File' is not absolute. -/// -/// Otherwise returns 'File'. -/// If 'File' starts with "./", the returned path will not contain the "./". -/// Otherwise, the returned path will contain the literal path-concatenation of -/// 'BaseDirectory' and 'File'. -/// -/// \param File Either an absolute or relative path. -/// \param BaseDirectory An absolute path. -static std::string getAbsolutePath( - StringRef File, StringRef BaseDirectory) { - assert(llvm::sys::path::is_absolute(BaseDirectory)); +std::string getAbsolutePath(StringRef File) { + llvm::SmallString<1024> BaseDirectory; + if (const char *PWD = ::getenv("PWD")) + BaseDirectory = PWD; + else + llvm::sys::fs::current_path(BaseDirectory); + SmallString<1024> PathStorage; if (llvm::sys::path::is_absolute(File)) { - return File; + llvm::sys::path::native(File, PathStorage); + return PathStorage.str(); } StringRef RelativePath(File); + // FIXME: Should '.\\' be accepted on Win32? if (RelativePath.startswith("./")) { RelativePath = RelativePath.substr(strlen("./")); } llvm::SmallString<1024> AbsolutePath(BaseDirectory); llvm::sys::path::append(AbsolutePath, RelativePath); - return AbsolutePath.str(); + llvm::sys::path::native(Twine(AbsolutePath), PathStorage); + return PathStorage.str(); } ToolInvocation::ToolInvocation( @@ -140,7 +144,9 @@ ToolInvocation::ToolInvocation( } void ToolInvocation::mapVirtualFile(StringRef FilePath, StringRef Content) { - MappedFileContents[FilePath] = Content; + SmallString<1024> PathStorage; + llvm::sys::path::native(FilePath, PathStorage); + MappedFileContents[PathStorage] = Content; } bool ToolInvocation::run() { @@ -167,20 +173,15 @@ bool ToolInvocation::run() { } llvm::OwningPtr<clang::CompilerInvocation> Invocation( newInvocation(&Diagnostics, *CC1Args)); - return runInvocation(BinaryName, Compilation.get(), - Invocation.take(), *CC1Args, ToolAction.take()); + return runInvocation(BinaryName, Compilation.get(), Invocation.take(), + *CC1Args); } -// Exists solely for the purpose of lookup of the resource path. -static int StaticSymbol; - bool ToolInvocation::runInvocation( const char *BinaryName, clang::driver::Compilation *Compilation, clang::CompilerInvocation *Invocation, - const clang::driver::ArgStringList &CC1Args, - clang::FrontendAction *ToolAction) { - llvm::OwningPtr<clang::FrontendAction> ScopedToolAction(ToolAction); + const clang::driver::ArgStringList &CC1Args) { // Show the invocation, with -v. if (Invocation->getHeaderSearchOpts().Verbose) { llvm::errs() << "clang Invocation:\n"; @@ -194,6 +195,11 @@ bool ToolInvocation::runInvocation( Compiler.setFileManager(Files); // FIXME: What about LangOpts? + // ToolAction can have lifetime requirements for Compiler or its members, and + // we need to ensure it's deleted earlier than Compiler. So we pass it to an + // OwningPtr declared after the Compiler variable. + llvm::OwningPtr<FrontendAction> ScopedToolAction(ToolAction.take()); + // Create the compilers actual diagnostics engine. Compiler.createDiagnostics(CC1Args.size(), const_cast<char**>(CC1Args.data())); @@ -203,18 +209,10 @@ bool ToolInvocation::runInvocation( Compiler.createSourceManager(*Files); addFileMappingsTo(Compiler.getSourceManager()); - // Infer the builtin include path if unspecified. - if (Compiler.getHeaderSearchOpts().UseBuiltinIncludes && - Compiler.getHeaderSearchOpts().ResourceDir.empty()) { - // This just needs to be some symbol in the binary. - void *const SymbolAddr = &StaticSymbol; - Compiler.getHeaderSearchOpts().ResourceDir = - clang::CompilerInvocation::GetResourcesPath(BinaryName, SymbolAddr); - } - - const bool Success = Compiler.ExecuteAction(*ToolAction); + const bool Success = Compiler.ExecuteAction(*ScopedToolAction); Compiler.resetAndLeakFileManager(); + Files->clearStatCaches(); return Success; } @@ -228,35 +226,23 @@ void ToolInvocation::addFileMappingsTo(SourceManager &Sources) { // FIXME: figure out what '0' stands for. const FileEntry *FromFile = Files->getVirtualFile( It->getKey(), Input->getBufferSize(), 0); - // FIXME: figure out memory management ('true'). - Sources.overrideFileContents(FromFile, Input, true); + Sources.overrideFileContents(FromFile, Input); } } ClangTool::ClangTool(const CompilationDatabase &Compilations, ArrayRef<std::string> SourcePaths) - : Files((FileSystemOptions())) { - llvm::SmallString<1024> BaseDirectory; - if (const char *PWD = ::getenv("PWD")) - BaseDirectory = PWD; - else - llvm::sys::fs::current_path(BaseDirectory); + : Files((FileSystemOptions())), + ArgsAdjuster(new ClangSyntaxOnlyAdjuster()) { for (unsigned I = 0, E = SourcePaths.size(); I != E; ++I) { - llvm::SmallString<1024> File(getAbsolutePath( - SourcePaths[I], BaseDirectory)); + llvm::SmallString<1024> File(getAbsolutePath(SourcePaths[I])); - std::vector<CompileCommand> CompileCommands = + std::vector<CompileCommand> CompileCommandsForFile = Compilations.getCompileCommands(File.str()); - if (!CompileCommands.empty()) { - for (int I = 0, E = CompileCommands.size(); I != E; ++I) { - CompileCommand &Command = CompileCommands[I]; - if (!Command.Directory.empty()) { - // FIXME: What should happen if CommandLine includes -working-directory - // as well? - Command.CommandLine.push_back( - "-working-directory=" + Command.Directory); - } - CommandLines.push_back(std::make_pair(File.str(), Command.CommandLine)); + if (!CompileCommandsForFile.empty()) { + for (int I = 0, E = CompileCommandsForFile.size(); I != E; ++I) { + CompileCommands.push_back(std::make_pair(File.str(), + CompileCommandsForFile[I])); } } else { // FIXME: There are two use cases here: doing a fuzzy @@ -273,11 +259,39 @@ void ClangTool::mapVirtualFile(StringRef FilePath, StringRef Content) { MappedFileContents.push_back(std::make_pair(FilePath, Content)); } +void ClangTool::setArgumentsAdjuster(ArgumentsAdjuster *Adjuster) { + ArgsAdjuster.reset(Adjuster); +} + int ClangTool::run(FrontendActionFactory *ActionFactory) { + // Exists solely for the purpose of lookup of the resource path. + // This just needs to be some symbol in the binary. + static int StaticSymbol; + // The driver detects the builtin header path based on the path of the + // executable. + // FIXME: On linux, GetMainExecutable is independent of the value of the + // first argument, thus allowing ClangTool and runToolOnCode to just + // pass in made-up names here. Make sure this works on other platforms. + std::string MainExecutable = + llvm::sys::Path::GetMainExecutable("clang_tool", &StaticSymbol).str(); + bool ProcessingFailed = false; - for (unsigned I = 0; I < CommandLines.size(); ++I) { - std::string File = CommandLines[I].first; - std::vector<std::string> &CommandLine = CommandLines[I].second; + for (unsigned I = 0; I < CompileCommands.size(); ++I) { + std::string File = CompileCommands[I].first; + // 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(CompileCommands[I].second.Directory.c_str())) + llvm::report_fatal_error("Cannot chdir into \"" + + CompileCommands[I].second.Directory + "\n!"); + std::vector<std::string> CommandLine = + ArgsAdjuster->Adjust(CompileCommands[I].second.CommandLine); + assert(!CommandLine.empty()); + CommandLine[0] = MainExecutable; llvm::outs() << "Processing: " << File << ".\n"; ToolInvocation Invocation(CommandLine, ActionFactory->create(), &Files); for (int I = 0, E = MappedFileContents.size(); I != E; ++I) { |