Repository: carbon-language/carbon-lang
File: toolchain/check/import_cpp.cpp (C++)

Code before:
// Returns an AST for the C++ imports and a bool that represents whether
// compilation errors where encountered or the generated AST is null due to an
// error. Sets the AST in the context's `sem_ir`.
// TODO: Consider to always have a (non-null) AST.
static auto GenerateAst(Context& context,
                        llvm::ArrayRef<Parse::Tree::PackagingNames> imports,
                        llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
                        std::shared_ptr<clang::CompilerInvocation> invocation)
    -> std::pair<std::unique_ptr<clang::ASTUnit>, bool> {
  // Build a diagnostics engine.
  CarbonClangDiagnosticConsumer diagnostics_consumer(&context,
                                                     invocation.get());
  llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diags(
      clang::CompilerInstance::createDiagnostics(
          *fs, invocation->getDiagnosticOpts(), &diagnostics_consumer,
          /*ShouldOwnClient=*/false));

  // Remap the imports file name to the corresponding `#include`s.
  std::string includes = GenerateCppIncludesHeaderCode(context, imports);
  auto includes_buffer =
      llvm::MemoryBuffer::getMemBuffer(includes, IncludesFileName);
  invocation->getPreprocessorOpts().addRemappedFile(IncludesFileName,
                                                    includes_buffer.get());

  // Create the AST unit.
  auto ast = clang::ASTUnit::LoadFromCompilerInvocation(
      invocation, std::make_shared<clang::PCHContainerOperations>(), nullptr,
      diags, new clang::FileManager(invocation->getFileSystemOpts(), fs));

  // Remove link to the diagnostics consumer before its destruction.
  ast->getDiagnostics().setClient(nullptr);

  // Remove remapped file before its underlying storage is destroyed.
  invocation->getPreprocessorOpts().clearRemappedFiles();

  // Attach the AST to SemIR. This needs to be done before we can emit any
  // diagnostics, so their locations can be properly interpreted by our
  // diagnostics machinery.
  context.sem_ir().set_cpp_ast(ast.get());

  // Emit any diagnostics we queued up while building the AST.
  diagnostics_consumer.EmitDiagnostics();

  return {std::move(ast), !ast || diagnostics_consumer.getNumErrors() > 0};
}

// Adds a namespace for the `Cpp` import and returns its `NameScopeId`.
static auto AddNamespace(Context& context, PackageNameId cpp_package_id,
                         llvm::ArrayRef<Parse::Tree::PackagingNames> imports)
    -> SemIR::NameScopeId {
  auto& import_cpps = context.sem_ir().import_cpps();

Review comment (suggestion):
I assume this is where the `std::shared_ptr` came from originally.... do we really need a shared ptr everywhere, or could we eliminate it (or localize it to just this routine)? Hmm, maybe that's hard/impossible to do with `std::shared_ptr`....

Code after:
auto ImportCppFiles(Context& context,
                    llvm::ArrayRef<Parse::Tree::PackagingNames> imports,
                    llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
                    std::shared_ptr<clang::CompilerInvocation> invocation)
    -> std::unique_ptr<clang::ASTUnit> {
  if (imports.empty()) {
    return nullptr;
  }

  CARBON_CHECK(!context.sem_ir().cpp_ast());

  PackageNameId package_id = imports.front().package_id;
  CARBON_CHECK(
      llvm::all_of(imports, [&](const Parse::Tree::PackagingNames& import) {
        return import.package_id == package_id;
      }));
  auto name_scope_id = AddNamespace(context, package_id, imports);

  auto [generated_ast, ast_has_error] =
      GenerateAst(context, imports, fs, std::move(invocation));

  SemIR::NameScope& name_scope = context.name_scopes().Get(name_scope_id);
  name_scope.set_is_closed_import(true);
  name_scope.set_clang_decl_context_id(context.sem_ir().clang_decls().Add(
      {.decl = generated_ast->getASTContext().getTranslationUnitDecl(),
       .inst_id = name_scope.inst_id()}));

  if (ast_has_error) {
    name_scope.set_has_error();
  }

  return std::move(generated_ast);
}

// Look ups the given name in the Clang AST in a specific scope. Returns the
// lookup result if lookup was successful.
static auto ClangLookup(Context& context, SemIR::NameScopeId scope_id,
                        SemIR::NameId name_id)
    -> std::optional<clang::LookupResult> {
  std::optional<llvm::StringRef> name =
      context.names().GetAsStringIfIdentifier(name_id);
  if (!name) {
    // Special names never exist in C++ code.
    return std::nullopt;
  }

  clang::ASTUnit* ast = context.sem_ir().cpp_ast();
  CARBON_CHECK(ast);
  clang::Sema& sema = ast->getSema();

  clang::LookupResult lookup(