Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I put Clang into C++ mode?

Tags:

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);
like image 664
sdgfsdh Avatar asked Jul 13 '16 15:07

sdgfsdh


People also ask

Does Clang work for C?

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.

How do you use C++17 in Clang?

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).

What is Clang command line?

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.


1 Answers

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.

like image 81
Heyji Avatar answered Sep 28 '22 03:09

Heyji