Files
Sourcetrail/src/lib_cxx/data/parser/cxx/CxxParser.cpp
T
Louis St-AmourandGitHub a12deb2056 Fixes for Clang 10 compatibility (closes #965) (#968)
Primarily FrontendAction now gets passed as unique_ptr, but also StringRef was added in a few places. See https://releases.llvm.org/10.0.0/tools/clang/docs/ReleaseNotes.html#internal-api-changes and http://llvm.org/docs/ProgrammersManual.html#the-stringref-class Also, llvm::make_unique is now std::make_unique: https://reviews.llvm.org/D66259 because LLVM requires C++14.
2020-06-07 16:18:58 +02:00

234 lines
7.5 KiB
C++

#include "CxxParser.h"
#include <clang/Driver/Compilation.h>
#include <clang/Driver/Driver.h>
#include <clang/Driver/Options.h>
#include <clang/Frontend/CompilerInvocation.h>
#include <clang/Tooling/Tooling.h>
#include <llvm/Option/ArgList.h>
#include <llvm/Support/TargetSelect.h>
#include <llvm/Support/VirtualFileSystem.h>
#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<std::string> prependSyntaxOnlyToolArgs(const std::vector<std::string>& args)
{
return utility::concat(std::vector<std::string>({"clang-tool", "-fsyntax-only"}), args);
}
std::vector<std::string> appendFilePath(const std::vector<std::string>& 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<clang::FrontendAction> ToolAction,
const llvm::Twine& Code,
const std::vector<std::string>& 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<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
new llvm::vfs::InMemoryFileSystem);
OverlayFileSystem->pushOverlay(InMemoryFileSystem);
llvm::IntrusiveRefCntPtr<clang::FileManager> 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<std::string> CxxParser::getCommandlineArgumentsEssential(
const std::vector<std::wstring>& compilerFlags)
{
std::vector<std::string> 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<ParserClient> client,
std::shared_ptr<FileRegister> fileRegister,
std::shared_ptr<IndexerStateInfo> indexerStateInfo)
: Parser(client), m_fileRegister(fileRegister), m_indexerStateInfo(indexerStateInfo)
{
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmParser();
}
void CxxParser::buildIndex(std::shared_ptr<IndexerCommandCxx> indexerCommand)
{
clang::tooling::CompileCommand compileCommand;
compileCommand.Filename = utility::encodeToUtf8(indexerCommand->getSourceFilePath().wstr());
compileCommand.Directory = utility::encodeToUtf8(indexerCommand->getWorkingDirectory().wstr());
std::vector<std::wstring> args = indexerCommand->getCompilerFlags();
if (!args.empty() && !utility::isPrefix<std::wstring>(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<TextAccess> fileContent,
std::vector<std::wstring> compilerFlags)
{
std::shared_ptr<CanonicalFilePathCache> canonicalFilePathCache =
std::make_shared<CanonicalFilePathCache>(m_fileRegister);
std::shared_ptr<CxxDiagnosticConsumer> diagnostics = getDiagnostics(
FilePath(), canonicalFilePathCache, false);
std::unique_ptr<clang::ASTFrontendAction> action = std::make_unique<ASTAction>(
m_client, canonicalFilePathCache, m_indexerStateInfo);
std::vector<std::string> 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<std::string>(1, utility::encodeToUtf8(sourceFilePath.wstr())));
std::shared_ptr<CanonicalFilePathCache> canonicalFilePathCache =
std::make_shared<CanonicalFilePathCache>(m_fileRegister);
std::shared_ptr<CxxDiagnosticConsumer> 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<CxxDiagnosticConsumer> CxxParser::getDiagnostics(
const FilePath& sourceFilePath,
std::shared_ptr<CanonicalFilePathCache> canonicalFilePathCache,
bool logErrors) const
{
llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> options = new clang::DiagnosticOptions();
return std::make_shared<CxxDiagnosticConsumer>(
llvm::errs(), &*options, m_client, canonicalFilePathCache, sourceFilePath, logErrors);
}