#include "CxxParser.h" #include #include #include #include #include #include #include #include #include "ASTAction.h" #include "ApplicationSettings.h" #include "CanonicalFilePathCache.h" #include "ClangInvocationInfo.h" #include "CxxCompilationDatabaseSingle.h" #include "CxxDiagnosticConsumer.h" #include "FilePath.h" #include "FileRegister.h" #include "IndexerCommandCxx.h" #include "ParserClient.h" #include "ResourcePaths.h" #include "SingleFrontendActionFactory.h" #include "TextAccess.h" #include "logging.h" #include "utility.h" #include "utilityString.h" namespace { std::vector prependSyntaxOnlyToolArgs(const std::vector& args) { return utility::concat(std::vector({"clang-tool", "-fsyntax-only"}), args); } std::vector appendFilePath(const std::vector& args, llvm::StringRef filePath) { return utility::concat(args, {filePath.str()}); } // custom implementation of clang::runToolOnCodeWithArgs which also sets our custon DiagnosticConsumer bool runToolOnCodeWithArgs( clang::DiagnosticConsumer* DiagConsumer, std::unique_ptr ToolAction, const llvm::Twine& Code, const std::vector& Args, const llvm::Twine& FileName = "input.cc", const clang::tooling::FileContentMappings& VirtualMappedFiles = clang::tooling::FileContentMappings()) { CxxParser::initializeLLVM(); llvm::SmallString<16> FileNameStorage; llvm::StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage); llvm::IntrusiveRefCntPtr OverlayFileSystem( new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem())); llvm::IntrusiveRefCntPtr InMemoryFileSystem( new llvm::vfs::InMemoryFileSystem); OverlayFileSystem->pushOverlay(InMemoryFileSystem); llvm::IntrusiveRefCntPtr Files( new clang::FileManager(clang::FileSystemOptions(), OverlayFileSystem)); clang::tooling::ToolInvocation Invocation( prependSyntaxOnlyToolArgs(appendFilePath(Args, FileNameRef)), std::move(ToolAction), Files.get()); llvm::SmallString<1024> CodeStorage; llvm::StringRef CodeRef = Code.toNullTerminatedStringRef(CodeStorage); InMemoryFileSystem->addFile(FileNameRef, 0, llvm::MemoryBuffer::getMemBufferCopy(CodeRef)); Invocation.setDiagnosticConsumer(DiagConsumer); return Invocation.run(); } } // namespace std::vector CxxParser::getCommandlineArgumentsEssential( const std::vector& compilerFlags) { std::vector args; // The option -fno-delayed-template-parsing signals that templates that there should // be AST elements for unused template functions as well. args.push_back("-fno-delayed-template-parsing"); // The option -fexceptions signals that clang should watch out for exception-related code during // indexing. args.push_back("-fexceptions"); // The option -c signals that no executable is built. args.push_back("-c"); // The option -w disables all warnings. args.push_back("-w"); // This option tells clang just to continue parsing no matter how manny errors have been thrown. args.push_back("-ferror-limit=0"); for (const std::wstring& compilerFlag: compilerFlags) { args.push_back(utility::encodeToUtf8(compilerFlag)); } return args; } void CxxParser::initializeLLVM() { static bool intialized = false; if (!intialized) { llvm::InitializeAllTargets(); llvm::InitializeAllTargetMCs(); llvm::InitializeAllAsmPrinters(); llvm::InitializeAllAsmParsers(); intialized = true; } } CxxParser::CxxParser( std::shared_ptr client, std::shared_ptr fileRegister, std::shared_ptr indexerStateInfo) : Parser(client), m_fileRegister(fileRegister), m_indexerStateInfo(indexerStateInfo) { llvm::InitializeNativeTarget(); llvm::InitializeNativeTargetAsmParser(); } void CxxParser::buildIndex(std::shared_ptr indexerCommand) { clang::tooling::CompileCommand compileCommand; compileCommand.Filename = utility::encodeToUtf8(indexerCommand->getSourceFilePath().wstr()); compileCommand.Directory = utility::encodeToUtf8(indexerCommand->getWorkingDirectory().wstr()); std::vector args = indexerCommand->getCompilerFlags(); if (!args.empty() && !utility::isPrefix(L"-", args.front())) { args.erase(args.begin()); } compileCommand.CommandLine = getCommandlineArgumentsEssential(args); compileCommand.CommandLine = prependSyntaxOnlyToolArgs(compileCommand.CommandLine); CxxCompilationDatabaseSingle compilationDatabase(compileCommand); runTool(&compilationDatabase, indexerCommand->getSourceFilePath()); } void CxxParser::buildIndex( const std::wstring& fileName, std::shared_ptr fileContent, std::vector compilerFlags) { std::shared_ptr canonicalFilePathCache = std::make_shared(m_fileRegister); std::shared_ptr diagnostics = getDiagnostics( FilePath(), canonicalFilePathCache, false); std::unique_ptr action = std::make_unique( m_client, canonicalFilePathCache, m_indexerStateInfo); std::vector args = getCommandlineArgumentsEssential(compilerFlags); runToolOnCodeWithArgs( diagnostics.get(), std::move(action), fileContent->getText(), args, utility::encodeToUtf8(fileName)); } void CxxParser::runTool( clang::tooling::CompilationDatabase* compilationDatabase, const FilePath& sourceFilePath) { initializeLLVM(); clang::tooling::ClangTool tool( *compilationDatabase, std::vector(1, utility::encodeToUtf8(sourceFilePath.wstr()))); std::shared_ptr canonicalFilePathCache = std::make_shared(m_fileRegister); std::shared_ptr diagnostics = getDiagnostics( sourceFilePath, canonicalFilePathCache, true); tool.setDiagnosticConsumer(diagnostics.get()); ClangInvocationInfo info; if (LogManager::getInstance()->getLoggingEnabled()) { info = ClangInvocationInfo::getClangInvocationString(compilationDatabase); LOG_INFO( "Clang Invocation: " + info.invocation.substr( 0, ApplicationSettings::getInstance()->getVerboseIndexerLoggingEnabled() ? std::string::npos : 20000)); if (!info.errors.empty()) { LOG_INFO("Clang Invocation errors: " + info.errors); } } clang::ASTFrontendAction* action = new ASTAction( m_client, canonicalFilePathCache, m_indexerStateInfo); tool.run(new SingleFrontendActionFactory(action)); if (!m_client->hasContent()) { if (info.invocation.empty()) { info = ClangInvocationInfo::getClangInvocationString(compilationDatabase); } if (!info.errors.empty()) { Id fileId = m_client->recordFile(sourceFilePath, true); m_client->recordError( L"Clang Invocation errors: " + utility::decodeFromUtf8(info.errors), true, true, sourceFilePath, ParseLocation(fileId, 1, 1)); } } } std::shared_ptr CxxParser::getDiagnostics( const FilePath& sourceFilePath, std::shared_ptr canonicalFilePathCache, bool logErrors) const { llvm::IntrusiveRefCntPtr options = new clang::DiagnosticOptions(); return std::make_shared( llvm::errs(), &*options, m_client, canonicalFilePathCache, sourceFilePath, logErrors); }