diff options
Diffstat (limited to 'contrib/llvm/tools/clang/lib/Tooling')
8 files changed, 1360 insertions, 0 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/CommonOptionsParser.cpp b/contrib/llvm/tools/clang/lib/Tooling/CommonOptionsParser.cpp new file mode 100644 index 0000000..99aff9f --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Tooling/CommonOptionsParser.cpp @@ -0,0 +1,79 @@ +//===--- CommonOptionsParser.cpp - common options for clang tools ---------===// +// +// 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 CommonOptionsParser class used to parse common +// command-line options for clang tools, so that they can be run 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 and reads common command-line options. +// +// This class uses the Clang Tooling infrastructure, see +// http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html +// for details on setting it up with LLVM source tree. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/CommandLine.h" +#include "clang/Tooling/CommonOptionsParser.h" +#include "clang/Tooling/Tooling.h" + +using namespace clang::tooling; +using namespace llvm; + +const char *const CommonOptionsParser::HelpMessage = + "\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" + "\ta search for compile_commands.json will be attempted through all\n" + "\tparent paths 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\n" + "\tlooked up in the compile command database. If the path of a file is\n" + "\tabsolute, it needs to point into CMake's source tree. If the path is\n" + "\trelative, the current working directory needs to be in the CMake\n" + "\tsource tree and the file must be in a subdirectory of the current\n" + "\tworking directory. \"./\" prefixes in the relative files will be\n" + "\tautomatically removed, but the rest of a relative path must be a\n" + "\tsuffix of a path in the compile command database.\n" + "\n"; + +CommonOptionsParser::CommonOptionsParser(int &argc, const char **argv) { + static cl::opt<std::string> BuildPath( + "p", cl::desc("Build path"), cl::Optional); + + static cl::list<std::string> SourcePaths( + cl::Positional, cl::desc("<source0> [... <sourceN>]"), cl::OneOrMore); + + Compilations.reset(FixedCompilationDatabase::loadFromCommandLine(argc, + argv)); + cl::ParseCommandLineOptions(argc, argv); + SourcePathList = SourcePaths; + 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); + } +} diff --git a/contrib/llvm/tools/clang/lib/Tooling/CompilationDatabase.cpp b/contrib/llvm/tools/clang/lib/Tooling/CompilationDatabase.cpp new file mode 100644 index 0000000..b5b99cb --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Tooling/CompilationDatabase.cpp @@ -0,0 +1,146 @@ +//===--- CompilationDatabase.cpp - ----------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains implementations of the CompilationDatabase base class +// and the FixedCompilationDatabase. +// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/CompilationDatabase.h" +#include "clang/Tooling/CompilationDatabasePluginRegistry.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/system_error.h" +#include <sstream> + +namespace clang { +namespace tooling { + +CompilationDatabase::~CompilationDatabase() {} + +CompilationDatabase * +CompilationDatabase::loadFromDirectory(StringRef BuildDirectory, + std::string &ErrorMessage) { + std::stringstream ErrorStream; + for (CompilationDatabasePluginRegistry::iterator + It = CompilationDatabasePluginRegistry::begin(), + Ie = CompilationDatabasePluginRegistry::end(); + It != Ie; ++It) { + std::string DatabaseErrorMessage; + OwningPtr<CompilationDatabasePlugin> Plugin(It->instantiate()); + if (CompilationDatabase *DB = + Plugin->loadFromDirectory(BuildDirectory, DatabaseErrorMessage)) + return DB; + else + ErrorStream << It->getName() << ": " << DatabaseErrorMessage << "\n"; + } + ErrorMessage = ErrorStream.str(); + return NULL; +} + +static CompilationDatabase * +findCompilationDatabaseFromDirectory(StringRef Directory, + std::string &ErrorMessage) { + std::stringstream ErrorStream; + bool HasErrorMessage = false; + while (!Directory.empty()) { + std::string LoadErrorMessage; + + if (CompilationDatabase *DB = + CompilationDatabase::loadFromDirectory(Directory, LoadErrorMessage)) + return DB; + + if (!HasErrorMessage) { + ErrorStream << "No compilation database found in " << Directory.str() + << " or any parent directory\n" << LoadErrorMessage; + HasErrorMessage = true; + } + + Directory = llvm::sys::path::parent_path(Directory); + } + ErrorMessage = ErrorStream.str(); + return NULL; +} + +CompilationDatabase * +CompilationDatabase::autoDetectFromSource(StringRef SourceFile, + std::string &ErrorMessage) { + SmallString<1024> AbsolutePath(getAbsolutePath(SourceFile)); + StringRef Directory = llvm::sys::path::parent_path(AbsolutePath); + + CompilationDatabase *DB = findCompilationDatabaseFromDirectory(Directory, + ErrorMessage); + + if (!DB) + ErrorMessage = ("Could not auto-detect compilation database for file \"" + + SourceFile + "\"\n" + ErrorMessage).str(); + return DB; +} + +CompilationDatabase * +CompilationDatabase::autoDetectFromDirectory(StringRef SourceDir, + std::string &ErrorMessage) { + SmallString<1024> AbsolutePath(getAbsolutePath(SourceDir)); + + CompilationDatabase *DB = findCompilationDatabaseFromDirectory(AbsolutePath, + ErrorMessage); + + if (!DB) + ErrorMessage = ("Could not auto-detect compilation database from directory \"" + + SourceDir + "\"\n" + ErrorMessage).str(); + return DB; +} + +CompilationDatabasePlugin::~CompilationDatabasePlugin() {} + +FixedCompilationDatabase * +FixedCompilationDatabase::loadFromCommandLine(int &Argc, + const char **Argv, + Twine Directory) { + const char **DoubleDash = std::find(Argv, Argv + Argc, StringRef("--")); + if (DoubleDash == Argv + Argc) + return NULL; + std::vector<std::string> CommandLine(DoubleDash + 1, Argv + Argc); + Argc = DoubleDash - Argv; + return new FixedCompilationDatabase(Directory, CommandLine); +} + +FixedCompilationDatabase:: +FixedCompilationDatabase(Twine Directory, ArrayRef<std::string> CommandLine) { + std::vector<std::string> ToolCommandLine(1, "clang-tool"); + ToolCommandLine.insert(ToolCommandLine.end(), + CommandLine.begin(), CommandLine.end()); + CompileCommands.push_back(CompileCommand(Directory, ToolCommandLine)); +} + +std::vector<CompileCommand> +FixedCompilationDatabase::getCompileCommands(StringRef FilePath) const { + std::vector<CompileCommand> Result(CompileCommands); + Result[0].CommandLine.push_back(FilePath); + return Result; +} + +std::vector<std::string> +FixedCompilationDatabase::getAllFiles() const { + return std::vector<std::string>(); +} + +std::vector<CompileCommand> +FixedCompilationDatabase::getAllCompileCommands() const { + return std::vector<CompileCommand>(); +} + +// This anchor is used to force the linker to link in the generated object file +// and thus register the JSONCompilationDatabasePlugin. +extern volatile int JSONAnchorSource; +static int JSONAnchorDest = JSONAnchorSource; + +} // end namespace tooling +} // end namespace clang diff --git a/contrib/llvm/tools/clang/lib/Tooling/FileMatchTrie.cpp b/contrib/llvm/tools/clang/lib/Tooling/FileMatchTrie.cpp new file mode 100644 index 0000000..5eb4bb9 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Tooling/FileMatchTrie.cpp @@ -0,0 +1,188 @@ +//===--- FileMatchTrie.cpp - ----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the implementation of a FileMatchTrie. +// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/FileMatchTrie.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/PathV2.h" +#include "llvm/Support/raw_ostream.h" +#include <sstream> + +namespace clang { +namespace tooling { + +/// \brief Default \c PathComparator using \c llvm::sys::fs::equivalent(). +struct DefaultPathComparator : public PathComparator { + virtual ~DefaultPathComparator() {} + virtual bool equivalent(StringRef FileA, StringRef FileB) const { + return FileA == FileB || llvm::sys::fs::equivalent(FileA, FileB); + } +}; + +/// \brief A node of the \c FileMatchTrie. +/// +/// Each node has storage for up to one path and a map mapping a path segment to +/// child nodes. The trie starts with an empty root node. +class FileMatchTrieNode { +public: + /// \brief Inserts 'NewPath' into this trie. \c ConsumedLength denotes + /// the number of \c NewPath's trailing characters already consumed during + /// recursion. + /// + /// An insert of a path + /// 'p'starts at the root node and does the following: + /// - If the node is empty, insert 'p' into its storage and abort. + /// - If the node has a path 'p2' but no children, take the last path segment + /// 's' of 'p2', put a new child into the map at 's' an insert the rest of + /// 'p2' there. + /// - Insert a new child for the last segment of 'p' and insert the rest of + /// 'p' there. + /// + /// An insert operation is linear in the number of a path's segments. + void insert(StringRef NewPath, unsigned ConsumedLength = 0) { + // We cannot put relative paths into the FileMatchTrie as then a path can be + // a postfix of another path, violating a core assumption of the trie. + if (llvm::sys::path::is_relative(NewPath)) + return; + if (Path.empty()) { + // This is an empty leaf. Store NewPath and return. + Path = NewPath; + return; + } + if (Children.empty()) { + // This is a leaf, ignore duplicate entry if 'Path' equals 'NewPath'. + if (NewPath == Path) + return; + // Make this a node and create a child-leaf with 'Path'. + StringRef Element(llvm::sys::path::filename( + StringRef(Path).drop_back(ConsumedLength))); + Children[Element].Path = Path; + } + StringRef Element(llvm::sys::path::filename( + StringRef(NewPath).drop_back(ConsumedLength))); + Children[Element].insert(NewPath, ConsumedLength + Element.size() + 1); + } + + /// \brief Tries to find the node under this \c FileMatchTrieNode that best + /// matches 'FileName'. + /// + /// If multiple paths fit 'FileName' equally well, \c IsAmbiguous is set to + /// \c true and an empty string is returned. If no path fits 'FileName', an + /// empty string is returned. \c ConsumedLength denotes the number of + /// \c Filename's trailing characters already consumed during recursion. + /// + /// To find the best matching node for a given path 'p', the + /// \c findEquivalent() function is called recursively for each path segment + /// (back to fron) of 'p' until a node 'n' is reached that does not .. + /// - .. have children. In this case it is checked + /// whether the stored path is equivalent to 'p'. If yes, the best match is + /// found. Otherwise continue with the parent node as if this node did not + /// exist. + /// - .. a child matching the next path segment. In this case, all children of + /// 'n' are an equally good match for 'p'. All children are of 'n' are found + /// recursively and their equivalence to 'p' is determined. If none are + /// equivalent, continue with the parent node as if 'n' didn't exist. If one + /// is equivalent, the best match is found. Otherwise, report and ambigiuity + /// error. + StringRef findEquivalent(const PathComparator& Comparator, + StringRef FileName, + bool &IsAmbiguous, + unsigned ConsumedLength = 0) const { + if (Children.empty()) { + if (Comparator.equivalent(StringRef(Path), FileName)) + return StringRef(Path); + return StringRef(); + } + StringRef Element(llvm::sys::path::filename(FileName.drop_back( + ConsumedLength))); + llvm::StringMap<FileMatchTrieNode>::const_iterator MatchingChild = + Children.find(Element); + if (MatchingChild != Children.end()) { + StringRef Result = MatchingChild->getValue().findEquivalent( + Comparator, FileName, IsAmbiguous, + ConsumedLength + Element.size() + 1); + if (!Result.empty() || IsAmbiguous) + return Result; + } + std::vector<StringRef> AllChildren; + getAll(AllChildren, MatchingChild); + StringRef Result; + for (unsigned i = 0; i < AllChildren.size(); i++) { + if (Comparator.equivalent(AllChildren[i], FileName)) { + if (Result.empty()) { + Result = AllChildren[i]; + } else { + IsAmbiguous = true; + return StringRef(); + } + } + } + return Result; + } + +private: + /// \brief Gets all paths under this FileMatchTrieNode. + void getAll(std::vector<StringRef> &Results, + llvm::StringMap<FileMatchTrieNode>::const_iterator Except) const { + if (Path.empty()) + return; + if (Children.empty()) { + Results.push_back(StringRef(Path)); + return; + } + for (llvm::StringMap<FileMatchTrieNode>::const_iterator + It = Children.begin(), E = Children.end(); + It != E; ++It) { + if (It == Except) + continue; + It->getValue().getAll(Results, Children.end()); + } + } + + // The stored absolute path in this node. Only valid for leaf nodes, i.e. + // nodes where Children.empty(). + std::string Path; + + // The children of this node stored in a map based on the next path segment. + llvm::StringMap<FileMatchTrieNode> Children; +}; + +FileMatchTrie::FileMatchTrie() + : Root(new FileMatchTrieNode), Comparator(new DefaultPathComparator()) {} + +FileMatchTrie::FileMatchTrie(PathComparator *Comparator) + : Root(new FileMatchTrieNode), Comparator(Comparator) {} + +FileMatchTrie::~FileMatchTrie() { + delete Root; +} + +void FileMatchTrie::insert(StringRef NewPath) { + Root->insert(NewPath); +} + +StringRef FileMatchTrie::findEquivalent(StringRef FileName, + raw_ostream &Error) const { + if (llvm::sys::path::is_relative(FileName)) { + Error << "Cannot resolve relative paths"; + return StringRef(); + } + bool IsAmbiguous = false; + StringRef Result = Root->findEquivalent(*Comparator, FileName, IsAmbiguous); + if (IsAmbiguous) + Error << "Path is ambiguous"; + return Result; +} + +} // end namespace tooling +} // end namespace clang diff --git a/contrib/llvm/tools/clang/lib/Tooling/JSONCompilationDatabase.cpp b/contrib/llvm/tools/clang/lib/Tooling/JSONCompilationDatabase.cpp new file mode 100644 index 0000000..254b069 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Tooling/JSONCompilationDatabase.cpp @@ -0,0 +1,323 @@ +//===--- JSONCompilationDatabase.cpp - ------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the implementation of the JSONCompilationDatabase. +// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/JSONCompilationDatabase.h" +#include "clang/Tooling/CompilationDatabase.h" +#include "clang/Tooling/CompilationDatabasePluginRegistry.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/system_error.h" + +namespace clang { +namespace tooling { + +namespace { + +/// \brief A parser for escaped strings of command line arguments. +/// +/// Assumes \-escaping for quoted arguments (see the documentation of +/// unescapeCommandLine(...)). +class CommandLineArgumentParser { + public: + CommandLineArgumentParser(StringRef CommandLine) + : Input(CommandLine), Position(Input.begin()-1) {} + + std::vector<std::string> parse() { + bool HasMoreInput = true; + while (HasMoreInput && nextNonWhitespace()) { + std::string Argument; + HasMoreInput = parseStringInto(Argument); + CommandLine.push_back(Argument); + } + return CommandLine; + } + + private: + // All private methods return true if there is more input available. + + bool parseStringInto(std::string &String) { + do { + if (*Position == '"') { + if (!parseDoubleQuotedStringInto(String)) return false; + } else if (*Position == '\'') { + if (!parseSingleQuotedStringInto(String)) return false; + } else { + if (!parseFreeStringInto(String)) return false; + } + } while (*Position != ' '); + return true; + } + + bool parseDoubleQuotedStringInto(std::string &String) { + if (!next()) return false; + while (*Position != '"') { + if (!skipEscapeCharacter()) return false; + String.push_back(*Position); + if (!next()) return false; + } + return next(); + } + + bool parseSingleQuotedStringInto(std::string &String) { + if (!next()) return false; + while (*Position != '\'') { + String.push_back(*Position); + if (!next()) return false; + } + return next(); + } + + bool parseFreeStringInto(std::string &String) { + do { + if (!skipEscapeCharacter()) return false; + String.push_back(*Position); + if (!next()) return false; + } while (*Position != ' ' && *Position != '"' && *Position != '\''); + return true; + } + + bool skipEscapeCharacter() { + if (*Position == '\\') { + return next(); + } + return true; + } + + bool nextNonWhitespace() { + do { + if (!next()) return false; + } while (*Position == ' '); + return true; + } + + bool next() { + ++Position; + return Position != Input.end(); + } + + const StringRef Input; + StringRef::iterator Position; + std::vector<std::string> CommandLine; +}; + +std::vector<std::string> unescapeCommandLine( + StringRef EscapedCommandLine) { + CommandLineArgumentParser parser(EscapedCommandLine); + return parser.parse(); +} + +} // end namespace + +class JSONCompilationDatabasePlugin : public CompilationDatabasePlugin { + virtual CompilationDatabase *loadFromDirectory( + StringRef Directory, std::string &ErrorMessage) { + SmallString<1024> JSONDatabasePath(Directory); + llvm::sys::path::append(JSONDatabasePath, "compile_commands.json"); + OwningPtr<CompilationDatabase> Database( + JSONCompilationDatabase::loadFromFile(JSONDatabasePath, ErrorMessage)); + if (!Database) + return NULL; + return Database.take(); + } +}; + +// Register the JSONCompilationDatabasePlugin with the +// CompilationDatabasePluginRegistry using this statically initialized variable. +static CompilationDatabasePluginRegistry::Add<JSONCompilationDatabasePlugin> +X("json-compilation-database", "Reads JSON formatted compilation databases"); + +// This anchor is used to force the linker to link in the generated object file +// and thus register the JSONCompilationDatabasePlugin. +volatile int JSONAnchorSource = 0; + +JSONCompilationDatabase * +JSONCompilationDatabase::loadFromFile(StringRef FilePath, + std::string &ErrorMessage) { + OwningPtr<llvm::MemoryBuffer> DatabaseBuffer; + llvm::error_code Result = + llvm::MemoryBuffer::getFile(FilePath, DatabaseBuffer); + if (Result != 0) { + ErrorMessage = "Error while opening JSON database: " + Result.message(); + return NULL; + } + OwningPtr<JSONCompilationDatabase> Database( + new JSONCompilationDatabase(DatabaseBuffer.take())); + if (!Database->parse(ErrorMessage)) + return NULL; + return Database.take(); +} + +JSONCompilationDatabase * +JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString, + std::string &ErrorMessage) { + OwningPtr<llvm::MemoryBuffer> DatabaseBuffer( + llvm::MemoryBuffer::getMemBuffer(DatabaseString)); + OwningPtr<JSONCompilationDatabase> Database( + new JSONCompilationDatabase(DatabaseBuffer.take())); + if (!Database->parse(ErrorMessage)) + return NULL; + return Database.take(); +} + +std::vector<CompileCommand> +JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const { + SmallString<128> NativeFilePath; + llvm::sys::path::native(FilePath, NativeFilePath); + std::vector<StringRef> PossibleMatches; + std::string Error; + llvm::raw_string_ostream ES(Error); + StringRef Match = MatchTrie.findEquivalent(NativeFilePath.str(), ES); + if (Match.empty()) + return std::vector<CompileCommand>(); + llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator + CommandsRefI = IndexByFile.find(Match); + if (CommandsRefI == IndexByFile.end()) + return std::vector<CompileCommand>(); + std::vector<CompileCommand> Commands; + getCommands(CommandsRefI->getValue(), Commands); + 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; +} + +std::vector<CompileCommand> +JSONCompilationDatabase::getAllCompileCommands() const { + std::vector<CompileCommand> Commands; + for (llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator + CommandsRefI = IndexByFile.begin(), CommandsRefEnd = IndexByFile.end(); + CommandsRefI != CommandsRefEnd; ++CommandsRefI) { + getCommands(CommandsRefI->getValue(), Commands); + } + return Commands; +} + +void JSONCompilationDatabase::getCommands( + ArrayRef<CompileCommandRef> CommandsRef, + std::vector<CompileCommand> &Commands) const { + for (int I = 0, E = CommandsRef.size(); I != E; ++I) { + SmallString<8> DirectoryStorage; + SmallString<1024> CommandStorage; + Commands.push_back(CompileCommand( + // FIXME: Escape correctly: + CommandsRef[I].first->getValue(DirectoryStorage), + unescapeCommandLine(CommandsRef[I].second->getValue(CommandStorage)))); + } +} + +bool JSONCompilationDatabase::parse(std::string &ErrorMessage) { + llvm::yaml::document_iterator I = YAMLStream.begin(); + if (I == YAMLStream.end()) { + ErrorMessage = "Error while parsing YAML."; + return false; + } + llvm::yaml::Node *Root = I->getRoot(); + if (Root == NULL) { + ErrorMessage = "Error while parsing YAML."; + return false; + } + llvm::yaml::SequenceNode *Array = dyn_cast<llvm::yaml::SequenceNode>(Root); + if (Array == NULL) { + ErrorMessage = "Expected array."; + return false; + } + for (llvm::yaml::SequenceNode::iterator AI = Array->begin(), + AE = Array->end(); + AI != AE; ++AI) { + llvm::yaml::MappingNode *Object = dyn_cast<llvm::yaml::MappingNode>(&*AI); + if (Object == NULL) { + ErrorMessage = "Expected object."; + return false; + } + 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) { + llvm::yaml::Node *Value = (*KVI).getValue(); + if (Value == NULL) { + ErrorMessage = "Expected value."; + return false; + } + llvm::yaml::ScalarNode *ValueString = + dyn_cast<llvm::yaml::ScalarNode>(Value); + if (ValueString == NULL) { + ErrorMessage = "Expected string as value."; + return false; + } + llvm::yaml::ScalarNode *KeyString = + dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey()); + if (KeyString == NULL) { + ErrorMessage = "Expected strings as key."; + return false; + } + 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; + } else { + ErrorMessage = ("Unknown key: \"" + + KeyString->getRawValue() + "\"").str(); + return false; + } + } + if (!File) { + ErrorMessage = "Missing key: \"file\"."; + return false; + } + if (!Command) { + ErrorMessage = "Missing key: \"command\"."; + return false; + } + if (!Directory) { + ErrorMessage = "Missing key: \"directory\"."; + return false; + } + SmallString<8> FileStorage; + StringRef FileName = File->getValue(FileStorage); + SmallString<128> NativeFilePath; + if (llvm::sys::path::is_relative(FileName)) { + SmallString<8> DirectoryStorage; + SmallString<128> AbsolutePath( + Directory->getValue(DirectoryStorage)); + llvm::sys::path::append(AbsolutePath, FileName); + llvm::sys::path::native(AbsolutePath.str(), NativeFilePath); + } else { + llvm::sys::path::native(FileName, NativeFilePath); + } + IndexByFile[NativeFilePath].push_back( + CompileCommandRef(Directory, Command)); + MatchTrie.insert(NativeFilePath.str()); + } + return true; +} + +} // end namespace tooling +} // end namespace clang 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..d8440d6 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Tooling/Refactoring.cpp @@ -0,0 +1,190 @@ +//===--- 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/DiagnosticOptions.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Lex/Lexer.h" +#include "clang/Rewrite/Core/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(StringRef FilePath, unsigned Offset, + unsigned Length, StringRef ReplacementText) + : FilePath(FilePath), Offset(Offset), + Length(Length), ReplacementText(ReplacementText) {} + +Replacement::Replacement(SourceManager &Sources, SourceLocation Start, + unsigned Length, StringRef ReplacementText) { + setFromSourceLocation(Sources, Start, Length, ReplacementText); +} + +Replacement::Replacement(SourceManager &Sources, const CharSourceRange &Range, + 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, + 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, + 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; +} + +RefactoringTool::RefactoringTool(const CompilationDatabase &Compilations, + ArrayRef<std::string> SourcePaths) + : ClangTool(Compilations, SourcePaths) {} + +Replacements &RefactoringTool::getReplacements() { return Replace; } + +int RefactoringTool::runAndSave(FrontendActionFactory *ActionFactory) { + if (int Result = run(ActionFactory)) { + return Result; + } + + LangOptions DefaultLangOptions; + IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); + TextDiagnosticPrinter DiagnosticPrinter(llvm::errs(), &*DiagOpts); + DiagnosticsEngine Diagnostics( + IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), + &*DiagOpts, &DiagnosticPrinter, false); + SourceManager Sources(Diagnostics, getFiles()); + Rewriter Rewrite(Sources, DefaultLangOptions); + + if (!applyAllReplacements(Rewrite)) { + llvm::errs() << "Skipped some replacements.\n"; + } + + return saveRewrittenFiles(Rewrite); +} + +bool RefactoringTool::applyAllReplacements(Rewriter &Rewrite) { + return tooling::applyAllReplacements(Replace, Rewrite); +} + +int RefactoringTool::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 1; + I->second.write(FileStream); + FileStream.flush(); + } + return 0; +} + +} // 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 new file mode 100644 index 0000000..52855f6 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Tooling/Tooling.cpp @@ -0,0 +1,319 @@ +//===--- Tooling.cpp - Running clang standalone tools ---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements functions to run clang tools standalone instead +// of running them as a plugin. +// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/Tooling.h" +#include "clang/Driver/Compilation.h" +#include "clang/Driver/Driver.h" +#include "clang/Driver/Tool.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Tooling/ArgumentsAdjusters.h" +#include "clang/Tooling/CompilationDatabase.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/FileSystem.h" +#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 { + +FrontendActionFactory::~FrontendActionFactory() {} + +// FIXME: This file contains structural duplication with other parts of the +// code that sets up a compiler to run tools on it, and we should refactor +// it to be based on the same framework. + +/// \brief Builds a clang driver initialized for running clang tools. +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, *Diagnostics); + CompilerDriver->setTitle("clang_based_tool"); + return CompilerDriver; +} + +/// \brief Retrieves the clang CC1 specific flags out of the compilation's jobs. +/// +/// Returns NULL on error. +static const clang::driver::ArgStringList *getCC1Arguments( + clang::DiagnosticsEngine *Diagnostics, + clang::driver::Compilation *Compilation) { + // We expect to get back exactly one Command job, if we didn't something + // failed. Extract that job from the Compilation. + const clang::driver::JobList &Jobs = Compilation->getJobs(); + if (Jobs.size() != 1 || !isa<clang::driver::Command>(*Jobs.begin())) { + SmallString<256> error_msg; + llvm::raw_svector_ostream error_stream(error_msg); + Compilation->PrintJob(error_stream, Compilation->getJobs(), "; ", true); + Diagnostics->Report(clang::diag::err_fe_expected_compiler_job) + << error_stream.str(); + return NULL; + } + + // The one job we find should be to invoke clang again. + const clang::driver::Command *Cmd = + cast<clang::driver::Command>(*Jobs.begin()); + if (StringRef(Cmd->getCreator().getName()) != "clang") { + Diagnostics->Report(clang::diag::err_fe_expected_clang_command); + return NULL; + } + + return &Cmd->getArguments(); +} + +/// \brief Returns a clang build invocation initialized from the CC1 flags. +static clang::CompilerInvocation *newInvocation( + clang::DiagnosticsEngine *Diagnostics, + const clang::driver::ArgStringList &CC1Args) { + assert(!CC1Args.empty() && "Must at least contain the program name!"); + clang::CompilerInvocation *Invocation = new clang::CompilerInvocation; + clang::CompilerInvocation::CreateFromArgs( + *Invocation, CC1Args.data() + 1, CC1Args.data() + CC1Args.size(), + *Diagnostics); + Invocation->getFrontendOpts().DisableFree = false; + return Invocation; +} + +bool runToolOnCode(clang::FrontendAction *ToolAction, const Twine &Code, + const Twine &FileName) { + return runToolOnCodeWithArgs( + ToolAction, Code, std::vector<std::string>(), FileName); +} + +bool runToolOnCodeWithArgs(clang::FrontendAction *ToolAction, const Twine &Code, + const std::vector<std::string> &Args, + const Twine &FileName) { + SmallString<16> FileNameStorage; + StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage); + std::vector<std::string> Commands; + Commands.push_back("clang-tool"); + Commands.push_back("-fsyntax-only"); + Commands.insert(Commands.end(), Args.begin(), Args.end()); + Commands.push_back(FileNameRef.data()); + FileManager Files((FileSystemOptions())); + ToolInvocation Invocation(Commands, ToolAction, &Files); + + SmallString<1024> CodeStorage; + Invocation.mapVirtualFile(FileNameRef, + Code.toNullTerminatedStringRef(CodeStorage)); + return Invocation.run(); +} + +std::string getAbsolutePath(StringRef File) { + 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)) { + 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("./")); + } + SmallString<1024> AbsolutePath(BaseDirectory); + llvm::sys::path::append(AbsolutePath, RelativePath); + llvm::sys::path::native(Twine(AbsolutePath), PathStorage); + return PathStorage.str(); +} + +ToolInvocation::ToolInvocation( + ArrayRef<std::string> CommandLine, FrontendAction *ToolAction, + FileManager *Files) + : CommandLine(CommandLine.vec()), ToolAction(ToolAction), Files(Files) { +} + +void ToolInvocation::mapVirtualFile(StringRef FilePath, StringRef Content) { + SmallString<1024> PathStorage; + llvm::sys::path::native(FilePath, PathStorage); + MappedFileContents[PathStorage] = Content; +} + +bool ToolInvocation::run() { + std::vector<const char*> Argv; + for (int I = 0, E = CommandLine.size(); I != E; ++I) + Argv.push_back(CommandLine[I].c_str()); + const char *const BinaryName = Argv[0]; + IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); + TextDiagnosticPrinter DiagnosticPrinter( + llvm::errs(), &*DiagOpts); + DiagnosticsEngine Diagnostics( + IntrusiveRefCntPtr<clang::DiagnosticIDs>(new DiagnosticIDs()), + &*DiagOpts, &DiagnosticPrinter, false); + + const OwningPtr<clang::driver::Driver> Driver( + newDriver(&Diagnostics, BinaryName)); + // Since the input might only be virtual, don't check whether it exists. + Driver->setCheckInputsExist(false); + const OwningPtr<clang::driver::Compilation> Compilation( + Driver->BuildCompilation(llvm::makeArrayRef(Argv))); + const clang::driver::ArgStringList *const CC1Args = getCC1Arguments( + &Diagnostics, Compilation.get()); + if (CC1Args == NULL) { + return false; + } + OwningPtr<clang::CompilerInvocation> Invocation( + newInvocation(&Diagnostics, *CC1Args)); + return runInvocation(BinaryName, Compilation.get(), Invocation.take()); +} + +bool ToolInvocation::runInvocation( + const char *BinaryName, + clang::driver::Compilation *Compilation, + clang::CompilerInvocation *Invocation) { + // Show the invocation, with -v. + if (Invocation->getHeaderSearchOpts().Verbose) { + llvm::errs() << "clang Invocation:\n"; + Compilation->PrintJob(llvm::errs(), Compilation->getJobs(), "\n", true); + llvm::errs() << "\n"; + } + + // Create a compiler instance to handle the actual work. + clang::CompilerInstance Compiler; + Compiler.setInvocation(Invocation); + 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. + OwningPtr<FrontendAction> ScopedToolAction(ToolAction.take()); + + // Create the compilers actual diagnostics engine. + Compiler.createDiagnostics(); + if (!Compiler.hasDiagnostics()) + return false; + + Compiler.createSourceManager(*Files); + addFileMappingsTo(Compiler.getSourceManager()); + + const bool Success = Compiler.ExecuteAction(*ScopedToolAction); + + Compiler.resetAndLeakFileManager(); + Files->clearStatCaches(); + return Success; +} + +void ToolInvocation::addFileMappingsTo(SourceManager &Sources) { + for (llvm::StringMap<StringRef>::const_iterator + It = MappedFileContents.begin(), End = MappedFileContents.end(); + It != End; ++It) { + // Inject the code as the given file name into the preprocessor options. + const llvm::MemoryBuffer *Input = + llvm::MemoryBuffer::getMemBuffer(It->getValue()); + // FIXME: figure out what '0' stands for. + const FileEntry *FromFile = Files->getVirtualFile( + It->getKey(), Input->getBufferSize(), 0); + Sources.overrideFileContents(FromFile, Input); + } +} + +ClangTool::ClangTool(const CompilationDatabase &Compilations, + ArrayRef<std::string> SourcePaths) + : Files((FileSystemOptions())), + ArgsAdjuster(new ClangSyntaxOnlyAdjuster()) { + for (unsigned I = 0, E = SourcePaths.size(); I != E; ++I) { + SmallString<1024> File(getAbsolutePath(SourcePaths[I])); + + std::vector<CompileCommand> CompileCommandsForFile = + Compilations.getCompileCommands(File.str()); + 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 + // "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::outs() << "Skipping " << File << ". Command line not found.\n"; + } + } +} + +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 < 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; + // 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(CommandLine, ActionFactory->create(), &Files); + for (int I = 0, E = MappedFileContents.size(); I != E; ++I) { + Invocation.mapVirtualFile(MappedFileContents[I].first, + MappedFileContents[I].second); + } + if (!Invocation.run()) { + // FIXME: Diagnostics should be used instead. + llvm::errs() << "Error while processing " << File << ".\n"; + ProcessingFailed = true; + } + } + return ProcessingFailed ? 1 : 0; +} + +} // end namespace tooling +} // end namespace clang |