Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to create a virtual file in clang for codecompletion

Im trying to create virtual files for codecompletion in clang. Unfortunately, my application segfaults. I have the following setup:

auto createVirtualFile = [](
  clang::CompilerInstance& ci,
  std::string name,
  llvm::StringRef input
) {
  std::unique_ptr<llvm::MemoryBuffer>
    MB(llvm::MemoryBuffer::getMemBuffer(input, name));
  return std::move(MB);
};

Once the file is created i setup a CodeCompletConsumer:

auto setupCodeComplete = [](
  clang::CompilerInstance& ci,
  std::string File,
  int Line,
  int Column
) {
  auto& F = ci.getFrontendOpts();
  F.CodeCompletionAt.FileName = File;
  F.CodeCompletionAt.Line = Line;
  F.CodeCompletionAt.Column = Column;
  clang::FrontendInputFile FrontFile(File, clang::IK_CXX);
  //F.Inputs.push_back(FrontFile);
  ci.createCodeCompletionConsumer();
  return FrontFile;
};

I invoke those two functions the following way and execute a syntax only action:

auto runCodeCompleteAt = [] (
  clang::CompilerInstance& ci,
  std::string Filename,
  std::string Code,
  int Line,
  int Column
) {
  auto fid = createVirtualFile(ci, Filename, Code);
  auto File = setupCodeComplete(ci, Filename, Line, Column);
  clang::SyntaxOnlyAction Act;
  if (Act.BeginSourceFile(ci, File )) {
    Act.Execute(); // segfault
    Act.EndSourceFile();
  }
};

auto runExample = [](auto& ci){
  runCodeCompleterAt(ci, "test.cpp", "std::cou", 1, 7);
}

I appreciate any hints.

like image 522
Gaetano Avatar asked May 23 '16 10:05

Gaetano


1 Answers

The problem is although you can add llvm::MemoryBuffer into clang::FrontendInputFile, clang::SyntaxOnlyAction doesn't accept such input, it will cause assertion failure. As I said in previous answer, you may want to look into ASTUnit::CodeComplete in lib/Frontend/ASTUnit.cpp.

Actually you can use remappedFiles to link a filename with a buffer:

int main()
{
    unsigned Line = 2;
    unsigned Column = 13;
    clang::CompilerInstance ci;
    llvm::StringRef codeStr("void MyFoo() {}\nint main() {My return 0}");

    // Just a dummy filename, the file even doesnt' exist.
    std::string Filename("virtualFile.cpp");
    std::unique_ptr<llvm::MemoryBuffer> MB(llvm::MemoryBuffer::getMemBuffer(codeStr, Filename));

    // Create diagnostics and target
    ci.createDiagnostics();
    std::shared_ptr<clang::TargetOptions> to = std::make_shared<clang::TargetOptions>();
    // clang -v
    //to->Triple = "x86_64-pc-win32";
    //to->Triple = "x86_64-apple-darwin";
    to->Triple = "x86_64-apple-darwin15";
    ci.setTarget(clang::TargetInfo::CreateTargetInfo(ci.getDiagnostics(), to));

    // Create language options
    clang::LangOptions &lo = ci.getLangOpts();
    lo.CPlusPlus = true;
    lo.CPlusPlus11 = true;

    // Create code complete options
    clang::CodeCompleteOptions cco;
    cco.IncludeMacros = 0;
    cco.IncludeCodePatterns = 1;
    cco.IncludeGlobals = 1;
    cco.IncludeBriefComments = 1;

    // Set up the callback, I will go back to this callback class later
    auto pCustomCodeCompleteConsumer = new CustomCodeCompleteConsumer(cco);
    ci.setCodeCompletionConsumer(pCustomCodeCompleteConsumer);

    // Set up code complete postions & file
    // Until now I didn't find a way to pass in a string rather than a file name
    clang::FrontendOptions& frontendOpts = ci.getFrontendOpts();
    frontendOpts.CodeCompletionAt.FileName = Filename;
    frontendOpts.CodeCompletionAt.Line = Line;
    frontendOpts.CodeCompletionAt.Column = Column;
    frontendOpts.Inputs.push_back(clang::FrontendInputFile(Filename, clang::InputKind::IK_CXX));

    clang::PreprocessorOptions& preprocessorOpts = ci.getPreprocessorOpts();
    preprocessorOpts.clearRemappedFiles();
    preprocessorOpts.addRemappedFile(Filename, MB.release());

    // Execute
    clang::SyntaxOnlyAction Act;
    if (Act.BeginSourceFile(ci, ci.getFrontendOpts().Inputs[0])) {
        Act.Execute();
        Act.EndSourceFile();
    }
}

The output containsCOMPLETION: 50 Decl : MyFoo : [#void#]MyFoo().

I get this answer from code segment

  PreprocessorOpts.clearRemappedFiles();
  PreprocessorOpts.RetainRemappedFileBuffers = true;
  for (const auto &RemappedFile : RemappedFiles) {
    PreprocessorOpts.addRemappedFile(RemappedFile.first, RemappedFile.second);
    OwnedBuffers.push_back(RemappedFile.second);
  }

in ASTUnit::CodeComplete.

I believe you may want to read AugmentedCodeCompleteConsumer to make use of those completion hints.

like image 147
Shangtong Zhang Avatar answered Nov 02 '22 06:11

Shangtong Zhang