I am working on an AST transformer using Clang. It is supposed to take a file-name as input, perform some transformations to the code in that file, and return the transformed code. It is based on a helpful example from Eli Bendersky.
Here is the code:
std::string transform(std::string fileName) {
// CompilerInstance will hold the instance of the Clang compiler for us,
// managing the various objects needed to run the compiler.
CompilerInstance compilerInstance;
compilerInstance.createDiagnostics();
auto& langOpts = compilerInstance.getLangOpts();
langOpts.CPlusPlus = true;
// Initialize target info with the default triple for our platform.
auto TO = std::make_shared<TargetOptions>();
TO->Triple = llvm::sys::getDefaultTargetTriple();
TargetInfo* targetInfo =
TargetInfo::CreateTargetInfo(compilerInstance.getDiagnostics(), TO);
compilerInstance.setTarget(targetInfo);
compilerInstance.createFileManager();
auto& fileManager = compilerInstance.getFileManager();
compilerInstance.createSourceManager(fileManager);
auto& sourceManager = compilerInstance.getSourceManager();
compilerInstance.createPreprocessor(TU_Module);
compilerInstance.createASTContext();
// A Rewriter helps us manage the code rewriting task.
auto rewriter = clang::Rewriter(sourceManager, compilerInstance.getLangOpts());
// Set the main file handled by the source manager to the input file.
const FileEntry* inputFile = fileManager.getFile(fileName);
sourceManager.setMainFileID(
sourceManager.createFileID(inputFile, SourceLocation(), SrcMgr::C_User));
compilerInstance.getDiagnosticClient().BeginSourceFile(
compilerInstance.getLangOpts(), &compilerInstance.getPreprocessor());
// Create an AST consumer instance which is going to get called by
// ParseAST.
MyASTConsumer consumer(rewriter);
// Parse the file to AST, registering our consumer as the AST consumer.
clang::ParseAST(
compilerInstance.getPreprocessor(),
&consumer,
compilerInstance.getASTContext());
// At this point the rewriter's buffer should be full with the rewritten
// file contents.
const RewriteBuffer* buffer = rewriter.getRewriteBufferFor(sourceManager.getMainFileID());
return std::string(buffer->begin(), buffer->end());
}
Here is my input program, negate.cpp
:
bool negate(bool b) {
if (b) {
return false;
} else {
return true;
}
}
When I run transform
on this code I get the following error:
negate.cpp:1:1: error: unknown type name 'bool'
bool negate(bool b) {
^
This suggests to me that it is working in C mode, not C++ mode. To confirm, I replaced bool
with int
, true
with 1
and false
with 0
:
int negate(int b) {
if (b) {
return 0;
} else {
return 1;
}
}
This worked, so my question is:
How do I put a Clang CompilerInstance
into C++ mode?
Update:
I tried changing the invocation, but no luck:
auto& langOpts = compilerInstance.getLangOpts();
langOpts.CPlusPlus = true;
langOpts.CPlusPlus11 = true;
auto* compilerInvocation = new CompilerInvocation();
compilerInvocation->setLangDefaults(
langOpts,
clang::InputKind::IK_CXX,
LangStandard::lang_gnu11);
The Clang tool is a front end compiler that is used to compile programming languages such as C++, C, Objective C++ and Objective C into machine code.
C++17 implementation status You can use Clang in C++17 mode with the -std=c++17 option (use -std=c++1z in Clang 4 and earlier).
clang is a C, C++, and Objective-C compiler which encompasses preprocessing, parsing, optimization, code generation, assembly, and linking. Depending on which high-level mode setting is passed, Clang will stop before doing a full link.
Before executing the compiler, you have to check/set the CompilerInvocation
structure with the getInvocation()
/ setInvocation()
methods. This structure defines the language options (calling convention, garbage collection, ...), the language kind (C, CXX, Asm, ObjC, ...), the target triple, the preprocessor options and the language standard. It is set with the method setLangDefaults()
.
This is the InputKind
class that holds which language you want to parse.
As written in the documentation, the setLangDefaults()
API
sets some properties which depend solely on the input kind
by inspecting the source code, you can see the following constructors call hierarchy:
CompilerInstance()
CompilerInvocation()
CompilerInvocationBase()
LangOptions()
In the LangOptions()
constructor, you can see that no specific/default language is set (LangOptions.def). Hence the need to go through the setLangDefaults() specific API.
you should thus go like:
std::string transform(std::string fileName) {
CompilerInstance compilerInstance;
compilerInstance.createDiagnostics();
CompilerInvocation & invocation = compilerInstance.getInvocation();
// Initialize target info with the default triple for our platform.
auto TO = std::make_shared<TargetOptions>();
TO->Triple = llvm::sys::getDefaultTargetTriple();
TargetInfo* targetInfo =
TargetInfo::CreateTargetInfo(compilerInstance.getDiagnostics(), TO);
compilerInstance.setTarget(targetInfo);
compilerInstance.createFileManager();
auto& fileManager = compilerInstance.getFileManager();
compilerInstance.createSourceManager(fileManager);
auto& sourceManager = compilerInstance.getSourceManager();
LangOptions langOpts;
langOpts.GNUMode = 1;
langOpts.CXXExceptions = 1;
langOpts.RTTI = 1;
langOpts.Bool = 1; // <-- Note the Bool option here !
langOpts.CPlusPlus = 1;
PreprocessorOptions &PPOpts = compilerInstance.getPreprocessorOpts();
invocation.setLangDefaults(langOpts,
clang::IK_CXX,
TO->Triple,
PPOpts,
clang::LangStandard::lang_cxx0x);
compilerInstance.createPreprocessor(TU_Module);
compilerInstance.createASTContext();
// A Rewriter helps us manage the code rewriting task.
auto rewriter = clang::Rewriter(sourceManager, compilerInstance.getLangOpts());
// Set the main file handled by the source manager to the input file.
const FileEntry* inputFile = fileManager.getFile(fileName);
sourceManager.setMainFileID(
sourceManager.createFileID(inputFile, SourceLocation(), SrcMgr::C_User));
compilerInstance.getDiagnosticClient().BeginSourceFile(
compilerInstance.getLangOpts(), &compilerInstance.getPreprocessor());
// Create an AST consumer instance which is going to get called by
// ParseAST.
MyASTConsumer consumer(rewriter);
// Parse the file to AST, registering our consumer as the AST consumer.
clang::ParseAST(
compilerInstance.getPreprocessor(),
&consumer,
compilerInstance.getASTContext());
// At this point the rewriter's buffer should be full with the rewritten
// file contents.
const RewriteBuffer* buffer = rewriter.getRewriteBufferFor(sourceManager.getMainFileID());
return std::string(buffer->begin(), buffer->end());
}
Reference taken in the clang tutorial CIRewriter.cpp, and which seems also a bit outdated.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With