Просмотр исходного кода

Generate AST when importing a cpp file (#4790)

Ignore the AST and support a single Cpp import, for now.
Report cpp compilation errors and warnings.
Part of #4666
Boaz Brickner 1 год назад
Родитель
Сommit
5b70a3ea91

+ 4 - 0
toolchain/check/BUILD

@@ -26,6 +26,7 @@ cc_library(
         "global_init.cpp",
         "impl_lookup.cpp",
         "import.cpp",
+        "import_cpp.cpp",
         "import_ref.cpp",
         "inst_block_stack.cpp",
         "literal.cpp",
@@ -52,6 +53,7 @@ cc_library(
         "global_init.h",
         "impl_lookup.h",
         "import.h",
+        "import_cpp.h",
         "import_ref.h",
         "inst_block_stack.h",
         "keyword_modifier_set.h",
@@ -88,6 +90,8 @@ cc_library(
         "//toolchain/sem_ir:formatter",
         "//toolchain/sem_ir:inst",
         "//toolchain/sem_ir:typed_insts",
+        "@llvm-project//clang:frontend",
+        "@llvm-project//clang:tooling",
         "@llvm-project//llvm:Support",
     ],
 )

+ 32 - 10
toolchain/check/check.cpp

@@ -61,16 +61,36 @@ static auto RenderImportKey(ImportKey import_key) -> std::string {
 static auto TrackImport(Map<ImportKey, UnitAndImports*>& api_map,
                         Map<ImportKey, Parse::NodeId>* explicit_import_map,
                         UnitAndImports& unit_info,
-                        Parse::Tree::PackagingNames import) -> void {
+                        Parse::Tree::PackagingNames import, bool fuzzing)
+    -> void {
   const auto& packaging = unit_info.parse_tree().packaging_decl();
 
   IdentifierId file_package_id =
       packaging ? packaging->names.package_id : IdentifierId::Invalid;
-  auto import_key = GetImportKey(unit_info, file_package_id, import);
+  const auto import_key = GetImportKey(unit_info, file_package_id, import);
+  const auto& [import_package_name, import_library_name] = import_key;
+
+  if (import_package_name == CppPackageName) {
+    if (import_library_name.empty()) {
+      CARBON_DIAGNOSTIC(CppInteropMissingLibrary, Error,
+                        "`Cpp` import missing library");
+      unit_info.emitter.Emit(import.node_id, CppInteropMissingLibrary);
+      return;
+    }
+    if (fuzzing) {
+      // Clang is not crash-resilient.
+      CARBON_DIAGNOSTIC(CppInteropFuzzing, Error,
+                        "`Cpp` import found during fuzzing");
+      unit_info.emitter.Emit(import.node_id, CppInteropFuzzing);
+      return;
+    }
+    unit_info.cpp_imports.push_back(import);
+    return;
+  }
 
   // True if the import has `Main` as the package name, even if it comes from
   // the file's packaging (diagnostics may differentiate).
-  bool is_explicit_main = import_key.first == MainPackageName;
+  bool is_explicit_main = import_package_name == MainPackageName;
 
   // Explicit imports need more validation than implicit ones. We try to do
   // these in an order of imports that should be removed, followed by imports
@@ -185,7 +205,7 @@ static auto TrackImport(Map<ImportKey, UnitAndImports*>& api_map,
   } else {
     // The imported api is missing.
     package_imports.has_load_error = true;
-    if (!explicit_import_map && import_key.first == CppPackageName) {
+    if (!explicit_import_map && import_package_name == CppPackageName) {
       // Don't diagnose the implicit import in `impl package Cpp`, because we'll
       // have diagnosed the use of `Cpp` in the declaration.
       return;
@@ -295,7 +315,8 @@ static auto BuildApiMapAndDiagnosePackaging(
 }
 
 auto CheckParseTrees(llvm::MutableArrayRef<Unit> units, bool prelude_import,
-                     llvm::raw_ostream* vlog_stream) -> void {
+                     llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
+                     llvm::raw_ostream* vlog_stream, bool fuzzing) -> void {
   // UnitAndImports is big due to its SmallVectors, so we default to 0 on the
   // stack.
   llvm::SmallVector<UnitAndImports, 0> unit_infos;
@@ -316,7 +337,7 @@ auto CheckParseTrees(llvm::MutableArrayRef<Unit> units, bool prelude_import,
       // An `impl` has an implicit import of its `api`.
       auto implicit_names = packaging->names;
       implicit_names.package_id = IdentifierId::Invalid;
-      TrackImport(api_map, nullptr, unit_info, implicit_names);
+      TrackImport(api_map, nullptr, unit_info, implicit_names, fuzzing);
     }
 
     Map<ImportKey, Parse::NodeId> explicit_import_map;
@@ -332,11 +353,12 @@ auto CheckParseTrees(llvm::MutableArrayRef<Unit> units, bool prelude_import,
       TrackImport(api_map, &explicit_import_map, unit_info,
                   {.node_id = Parse::InvalidNodeId(),
                    .package_id = core_ident_id,
-                   .library_id = prelude_id});
+                   .library_id = prelude_id},
+                  fuzzing);
     }
 
     for (const auto& import : unit_info.parse_tree().imports()) {
-      TrackImport(api_map, &explicit_import_map, unit_info, import);
+      TrackImport(api_map, &explicit_import_map, unit_info, import, fuzzing);
     }
 
     // If there were no imports, mark the file as ready to check for below.
@@ -350,7 +372,7 @@ auto CheckParseTrees(llvm::MutableArrayRef<Unit> units, bool prelude_import,
   for (int check_index = 0;
        check_index < static_cast<int>(ready_to_check.size()); ++check_index) {
     auto* unit_info = ready_to_check[check_index];
-    CheckUnit(unit_info, units.size(), vlog_stream).Run();
+    CheckUnit(unit_info, units.size(), fs, vlog_stream).Run();
     for (auto* incoming_import : unit_info->incoming_imports) {
       --incoming_import->imports_remaining;
       if (incoming_import->imports_remaining == 0) {
@@ -397,7 +419,7 @@ auto CheckParseTrees(llvm::MutableArrayRef<Unit> units, bool prelude_import,
     // incomplete imports.
     for (auto& unit_info : unit_infos) {
       if (unit_info.imports_remaining > 0) {
-        CheckUnit(&unit_info, units.size(), vlog_stream).Run();
+        CheckUnit(&unit_info, units.size(), fs, vlog_stream).Run();
       }
     }
   }

+ 2 - 1
toolchain/check/check.h

@@ -37,7 +37,8 @@ struct Unit {
 // Checks a group of parse trees. This will use imports to decide the order of
 // checking.
 auto CheckParseTrees(llvm::MutableArrayRef<Unit> units, bool prelude_import,
-                     llvm::raw_ostream* vlog_stream) -> void;
+                     llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
+                     llvm::raw_ostream* vlog_stream, bool fuzzing) -> void;
 
 }  // namespace Carbon::Check
 

+ 37 - 0
toolchain/check/check_unit.cpp

@@ -4,12 +4,18 @@
 
 #include "toolchain/check/check_unit.h"
 
+#include <string>
+
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/VirtualFileSystem.h"
 #include "toolchain/base/kind_switch.h"
 #include "toolchain/base/pretty_stack_trace_function.h"
 #include "toolchain/check/generic.h"
 #include "toolchain/check/handle.h"
 #include "toolchain/check/impl.h"
 #include "toolchain/check/import.h"
+#include "toolchain/check/import_cpp.h"
 #include "toolchain/check/import_ref.h"
 #include "toolchain/check/node_id_traversal.h"
 
@@ -29,9 +35,11 @@ static auto GetImportedIRCount(UnitAndImports* unit_and_imports) -> int {
 }
 
 CheckUnit::CheckUnit(UnitAndImports* unit_and_imports, int total_ir_count,
+                     llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
                      llvm::raw_ostream* vlog_stream)
     : unit_and_imports_(unit_and_imports),
       total_ir_count_(total_ir_count),
+      fs_(std::move(fs)),
       vlog_stream_(vlog_stream),
       emitter_(*unit_and_imports_->unit->sem_ir_converter,
                unit_and_imports_->err_tracker),
@@ -130,6 +138,7 @@ auto CheckUnit::InitPackageScopeAndImports() -> void {
   ImportCurrentPackage(package_inst_id, namespace_type_id);
   CARBON_CHECK(context_.scope_stack().PeekIndex() == ScopeIndex::Package);
   ImportOtherPackages(namespace_type_id);
+  ImportCppPackages();
 }
 
 auto CheckUnit::CollectDirectImports(
@@ -325,6 +334,34 @@ auto CheckUnit::ImportOtherPackages(SemIR::TypeId namespace_type_id) -> void {
   }
 }
 
+auto CheckUnit::ImportCppPackages() -> void {
+  const auto& imports = unit_and_imports_->cpp_imports;
+  if (imports.empty()) {
+    return;
+  }
+
+  if (imports.size() >= 2) {
+    context_.TODO(imports[1].node_id,
+                  "multiple Cpp imports are not yet supported");
+    return;
+  }
+
+  const auto& import = imports.front();
+  llvm::StringRef filename =
+      unit_and_imports_->unit->value_stores->string_literal_values().Get(
+          import.library_id);
+
+  // TODO: Pass the import location so that diagnostics would point to it.
+  auto source_buffer = SourceBuffer::MakeFromFile(
+      *fs_, filename, unit_and_imports_->err_tracker);
+  if (!source_buffer) {
+    return;
+  }
+
+  ImportCppFile(context_, import.node_id, source_buffer->filename(),
+                source_buffer->text());
+}
+
 // Loops over all nodes in the tree. On some errors, this may return early,
 // for example if an unrecoverable state is encountered.
 // NOLINTNEXTLINE(readability-function-size)

+ 9 - 1
toolchain/check/check_unit.h

@@ -74,6 +74,9 @@ struct UnitAndImports {
   // A map of the package names to the outgoing imports above.
   Map<IdentifierId, int32_t> package_imports_map;
 
+  // List of the `import Cpp` imports.
+  llvm::SmallVector<Parse::Tree::PackagingNames> cpp_imports;
+
   // The remaining number of imports which must be checked before this unit can
   // be processed.
   int32_t imports_remaining = 0;
@@ -98,6 +101,7 @@ struct UnitAndImports {
 class CheckUnit {
  public:
   explicit CheckUnit(UnitAndImports* unit_and_imports, int total_ir_count,
+                     llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
                      llvm::raw_ostream* vlog_stream);
 
   // Produces and checks the IR for the provided unit.
@@ -125,9 +129,12 @@ class CheckUnit {
   auto ImportCurrentPackage(SemIR::InstId package_inst_id,
                             SemIR::TypeId namespace_type_id) -> void;
 
-  // Imports all other packages (excluding the current package).
+  // Imports all other Carbon packages (excluding the current package).
   auto ImportOtherPackages(SemIR::TypeId namespace_type_id) -> void;
 
+  // Imports all C++ packages.
+  auto ImportCppPackages() -> void;
+
   // Checks that each required definition is available. If the definition can be
   // generated by resolving a specific, does so, otherwise emits a diagnostic
   // for each declaration in context.definitions_required() that doesn't have a
@@ -142,6 +149,7 @@ class CheckUnit {
   UnitAndImports* unit_and_imports_;
   // The number of IRs being checked in total.
   int total_ir_count_;
+  llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs_;
   llvm::raw_ostream* vlog_stream_;
 
   Context::DiagnosticEmitter emitter_;

+ 57 - 0
toolchain/check/import_cpp.cpp

@@ -0,0 +1,57 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#include "toolchain/check/import_cpp.h"
+
+#include <memory>
+#include <string>
+
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/raw_ostream.h"
+#include "toolchain/check/context.h"
+#include "toolchain/check/diagnostic_helpers.h"
+#include "toolchain/diagnostics/diagnostic.h"
+#include "toolchain/diagnostics/format_providers.h"
+
+namespace Carbon::Check {
+
+auto ImportCppFile(Context& context, SemIRLoc loc, llvm::StringRef file_path,
+                   llvm::StringRef code) -> void {
+  std::string diagnostics_str;
+  llvm::raw_string_ostream diagnostics_stream(diagnostics_str);
+
+  llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> diagnostic_options(
+      new clang::DiagnosticOptions());
+  clang::TextDiagnosticPrinter diagnostics_consumer(diagnostics_stream,
+                                                    diagnostic_options.get());
+  // TODO: Share compilation flags with ClangRunner.
+  auto ast = clang::tooling::buildASTFromCodeWithArgs(
+      code, {}, file_path, "clang-tool",
+      std::make_shared<clang::PCHContainerOperations>(),
+      clang::tooling::getClangStripDependencyFileAdjuster(),
+      clang::tooling::FileContentMappings(), &diagnostics_consumer);
+  // TODO: Implement and use a DynamicRecursiveASTVisitor to traverse the AST.
+  int num_errors = diagnostics_consumer.getNumErrors();
+  int num_warnings = diagnostics_consumer.getNumWarnings();
+  if (num_errors > 0) {
+    // TODO: Remove the warnings part when there are no warnings.
+    CARBON_DIAGNOSTIC(
+        CppInteropParseError, Error,
+        "{0} error{0:s} and {1} warning{1:s} in `Cpp` import `{2}`:\n{3}",
+        IntAsSelect, IntAsSelect, std::string, std::string);
+    context.emitter().Emit(loc, CppInteropParseError, num_errors, num_warnings,
+                           file_path.str(), diagnostics_str);
+  } else if (num_warnings > 0) {
+    CARBON_DIAGNOSTIC(CppInteropParseWarning, Warning,
+                      "{0} warning{0:s} in `Cpp` import `{1}`:\n{2}",
+                      IntAsSelect, std::string, std::string);
+    context.emitter().Emit(loc, CppInteropParseWarning, num_warnings,
+                           file_path.str(), diagnostics_str);
+  }
+}
+
+}  // namespace Carbon::Check

+ 20 - 0
toolchain/check/import_cpp.h

@@ -0,0 +1,20 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#ifndef CARBON_TOOLCHAIN_CHECK_IMPORT_CPP_H_
+#define CARBON_TOOLCHAIN_CHECK_IMPORT_CPP_H_
+
+#include "llvm/ADT/StringRef.h"
+#include "toolchain/check/context.h"
+#include "toolchain/check/diagnostic_helpers.h"
+
+namespace Carbon::Check {
+
+// Parses the C++ code and report errors and warnings.
+auto ImportCppFile(Context& context, SemIRLoc loc, llvm::StringRef file_path,
+                   llvm::StringRef code) -> void;
+
+}  // namespace Carbon::Check
+
+#endif  // CARBON_TOOLCHAIN_CHECK_IMPORT_CPP_H_

+ 55 - 0
toolchain/check/testdata/interop/cpp/no_prelude/bad_import.carbon

@@ -0,0 +1,55 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/no_prelude/bad_import.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/no_prelude/bad_import.carbon
+
+// --- fail_import_cpp.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_import_cpp.carbon:[[@LINE+4]]:1: error: `Cpp` import missing library [CppInteropMissingLibrary]
+// CHECK:STDERR: import Cpp;
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR:
+import Cpp;
+
+// --- fail_import_cpp_library_empty.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_import_cpp_library_empty.carbon:[[@LINE+5]]:1: error: `Cpp` import missing library [CppInteropMissingLibrary]
+// CHECK:STDERR: import Cpp library "";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR:
+// CHECK:STDERR: "foo.h": error: error opening file for read: No such file or directory [ErrorOpeningFile]
+import Cpp library "";
+
+// --- fail_import_cpp_library_file_with_quotes.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "\"foo.h\"";
+
+// CHECK:STDOUT: --- fail_import_cpp.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_import_cpp_library_empty.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_import_cpp_library_file_with_quotes.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 185 - 0
toolchain/check/testdata/interop/cpp/no_prelude/cpp_diagnostics.carbon

@@ -0,0 +1,185 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/no_prelude/cpp_diagnostics.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/no_prelude/cpp_diagnostics.carbon
+
+// --- one_error.h
+
+#error "error1"
+
+// --- fail_import_cpp_file_with_one_error.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_import_cpp_file_with_one_error.carbon:[[@LINE+8]]:1: error: 1 error and 0 warnings in `Cpp` import `one_error.h`:
+// CHECK:STDERR: one_error.h:2:2: error: "error1"
+// CHECK:STDERR:     2 | #error "error1"
+// CHECK:STDERR:       |  ^
+// CHECK:STDERR:  [CppInteropParseError]
+// CHECK:STDERR: import Cpp library "one_error.h";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR:
+import Cpp library "one_error.h";
+
+// --- multiple_errors.h
+
+#error "error1"
+#error "error2"
+
+// --- fail_import_cpp_file_with_multiple_errors.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_import_cpp_file_with_multiple_errors.carbon:[[@LINE+11]]:1: error: 2 errors and 0 warnings in `Cpp` import `multiple_errors.h`:
+// CHECK:STDERR: multiple_errors.h:2:2: error: "error1"
+// CHECK:STDERR:     2 | #error "error1"
+// CHECK:STDERR:       |  ^
+// CHECK:STDERR: multiple_errors.h:3:2: error: "error2"
+// CHECK:STDERR:     3 | #error "error2"
+// CHECK:STDERR:       |  ^
+// CHECK:STDERR:  [CppInteropParseError]
+// CHECK:STDERR: import Cpp library "multiple_errors.h";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR:
+import Cpp library "multiple_errors.h";
+
+// --- one_warning.h
+
+#warning "warning1"
+
+// --- import_cpp_file_with_one_warning.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: import_cpp_file_with_one_warning.carbon:[[@LINE+8]]:1: warning: 1 warning in `Cpp` import `one_warning.h`:
+// CHECK:STDERR: one_warning.h:2:2: warning: "warning1"
+// CHECK:STDERR:     2 | #warning "warning1"
+// CHECK:STDERR:       |  ^
+// CHECK:STDERR:  [CppInteropParseWarning]
+// CHECK:STDERR: import Cpp library "one_warning.h";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR:
+import Cpp library "one_warning.h";
+
+// --- multiple_warnings.h
+
+#warning "warning1"
+#warning "warning2"
+#warning "warning3"
+
+// --- import_cpp_file_with_multiple_warnings.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: import_cpp_file_with_multiple_warnings.carbon:[[@LINE+14]]:1: warning: 3 warnings in `Cpp` import `multiple_warnings.h`:
+// CHECK:STDERR: multiple_warnings.h:2:2: warning: "warning1"
+// CHECK:STDERR:     2 | #warning "warning1"
+// CHECK:STDERR:       |  ^
+// CHECK:STDERR: multiple_warnings.h:3:2: warning: "warning2"
+// CHECK:STDERR:     3 | #warning "warning2"
+// CHECK:STDERR:       |  ^
+// CHECK:STDERR: multiple_warnings.h:4:2: warning: "warning3"
+// CHECK:STDERR:     4 | #warning "warning3"
+// CHECK:STDERR:       |  ^
+// CHECK:STDERR:  [CppInteropParseWarning]
+// CHECK:STDERR: import Cpp library "multiple_warnings.h";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR:
+import Cpp library "multiple_warnings.h";
+
+// --- one_error_and_one_warning.h
+
+#error "error1"
+#warning "warning1"
+
+// --- fail_import_cpp_file_with_one_error_and_one_warning.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_import_cpp_file_with_one_error_and_one_warning.carbon:[[@LINE+11]]:1: error: 1 error and 1 warning in `Cpp` import `one_error_and_one_warning.h`:
+// CHECK:STDERR: one_error_and_one_warning.h:2:2: error: "error1"
+// CHECK:STDERR:     2 | #error "error1"
+// CHECK:STDERR:       |  ^
+// CHECK:STDERR: one_error_and_one_warning.h:3:2: warning: "warning1"
+// CHECK:STDERR:     3 | #warning "warning1"
+// CHECK:STDERR:       |  ^
+// CHECK:STDERR:  [CppInteropParseError]
+// CHECK:STDERR: import Cpp library "one_error_and_one_warning.h";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR:
+import Cpp library "one_error_and_one_warning.h";
+
+// --- multiple_errors_and_multiple_warnings.h
+
+#error "error1"
+#error "error2"
+#warning "warning1"
+#warning "warning2"
+#warning "warning3"
+
+// --- fail_import_cpp_file_with_multiple_errors_and_multiple_warnings.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_import_cpp_file_with_multiple_errors_and_multiple_warnings.carbon:[[@LINE+19]]:1: error: 2 errors and 3 warnings in `Cpp` import `multiple_errors_and_multiple_warnings.h`:
+// CHECK:STDERR: multiple_errors_and_multiple_warnings.h:2:2: error: "error1"
+// CHECK:STDERR:     2 | #error "error1"
+// CHECK:STDERR:       |  ^
+// CHECK:STDERR: multiple_errors_and_multiple_warnings.h:3:2: error: "error2"
+// CHECK:STDERR:     3 | #error "error2"
+// CHECK:STDERR:       |  ^
+// CHECK:STDERR: multiple_errors_and_multiple_warnings.h:4:2: warning: "warning1"
+// CHECK:STDERR:     4 | #warning "warning1"
+// CHECK:STDERR:       |  ^
+// CHECK:STDERR: multiple_errors_and_multiple_warnings.h:5:2: warning: "warning2"
+// CHECK:STDERR:     5 | #warning "warning2"
+// CHECK:STDERR:       |  ^
+// CHECK:STDERR: multiple_errors_and_multiple_warnings.h:6:2: warning: "warning3"
+// CHECK:STDERR:     6 | #warning "warning3"
+// CHECK:STDERR:       |  ^
+// CHECK:STDERR:  [CppInteropParseError]
+// CHECK:STDERR: import Cpp library "multiple_errors_and_multiple_warnings.h";
+// CHECK:STDERR: ^~~~~~
+import Cpp library "multiple_errors_and_multiple_warnings.h";
+
+// CHECK:STDOUT: --- fail_import_cpp_file_with_one_error.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_import_cpp_file_with_multiple_errors.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- import_cpp_file_with_one_warning.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- import_cpp_file_with_multiple_warnings.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_import_cpp_file_with_one_error_and_one_warning.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_import_cpp_file_with_multiple_errors_and_multiple_warnings.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 23 - 0
toolchain/check/testdata/interop/cpp/no_prelude/file_not_found.carbon

@@ -0,0 +1,23 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/no_prelude/file_not_found.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/no_prelude/file_not_found.carbon
+// CHECK:STDERR: not_found.h: error: error opening file for read: No such file or directory [ErrorOpeningFile]
+
+// --- fail_cpp_file_not_found.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "not_found.h";
+
+// CHECK:STDOUT: --- fail_cpp_file_not_found.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 7 - 5
toolchain/check/testdata/interop/cpp/no_prelude/test_support.carbon → toolchain/check/testdata/interop/cpp/no_prelude/function_decl.carbon

@@ -4,19 +4,21 @@
 //
 // AUTOUPDATE
 // TIP: To test this file alone, run:
-// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/no_prelude/test_support.carbon
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/no_prelude/function_decl.carbon
 // TIP: To dump output, run:
-// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/no_prelude/test_support.carbon
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/no_prelude/function_decl.carbon
 
-// --- cpp_file.h
+// --- function_decl.h
 
 void foo();
 
-// --- carbon_file.carbon
+// --- import_function_decl.carbon
 
 library "[[@TEST_NAME]]";
 
-// CHECK:STDOUT: --- carbon_file.carbon
+import Cpp library "function_decl.h";
+
+// CHECK:STDOUT: --- import_function_decl.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace [template] {}

+ 34 - 0
toolchain/check/testdata/interop/cpp/no_prelude/multiple_imports.carbon

@@ -0,0 +1,34 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/no_prelude/multiple_imports.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/no_prelude/multiple_imports.carbon
+
+// --- file1.h
+
+void foo1();
+
+// --- file2.h
+
+void foo1();
+
+// --- fail_multiple_imports.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "file1.h";
+// CHECK:STDERR: fail_multiple_imports.carbon:[[@LINE+3]]:1: error: semantics TODO: `multiple Cpp imports are not yet supported` [SemanticsTodo]
+// CHECK:STDERR: import Cpp library "file2.h";
+// CHECK:STDERR: ^~~~~~
+import Cpp library "file2.h";
+
+// CHECK:STDOUT: --- fail_multiple_imports.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 6 - 5
toolchain/check/testdata/packages/no_prelude/restricted_package_names.carbon

@@ -61,7 +61,11 @@ package Cpp;
 
 // --- fail_cpp.impl.carbon
 
-// CHECK:STDERR: fail_cpp.impl.carbon:[[@LINE+4]]:6: error: `Cpp` cannot be used by a `package` declaration [CppPackageDeclaration]
+// CHECK:STDERR: fail_cpp.impl.carbon:[[@LINE+8]]:6: error: `Cpp` cannot be used by a `package` declaration [CppPackageDeclaration]
+// CHECK:STDERR: impl package Cpp;
+// CHECK:STDERR:      ^~~~~~~
+// CHECK:STDERR:
+// CHECK:STDERR: fail_cpp.impl.carbon:[[@LINE+4]]:6: error: `Cpp` import missing library [CppInteropMissingLibrary]
 // CHECK:STDERR: impl package Cpp;
 // CHECK:STDERR:      ^~~~~~~
 // CHECK:STDERR:
@@ -128,10 +132,7 @@ package Cpp library "[[@TEST_NAME]]";
 // CHECK:STDOUT: --- fail_cpp.impl.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
-// CHECK:STDOUT:   package: <namespace> = namespace [template] {
-// CHECK:STDOUT:     has_error
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %default.import = import <invalid>
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- fail_raw_cpp.carbon

+ 4 - 1
toolchain/diagnostics/coverage_test.cpp

@@ -24,6 +24,9 @@ constexpr DiagnosticKind UntestedDiagnosticKinds[] = {
     DiagnosticKind::TestDiagnostic,
     DiagnosticKind::TestDiagnosticNote,
 
+    // Driver specific.
+    DiagnosticKind::CppInteropFuzzing,
+
     // These diagnose filesystem issues that are hard to unit test.
     DiagnosticKind::ErrorReadingFile,
     DiagnosticKind::ErrorStattingFile,
@@ -42,7 +45,7 @@ constexpr DiagnosticKind UntestedDiagnosticKinds[] = {
 // Looks for diagnostic kinds that aren't covered by a file_test.
 TEST(Coverage, DiagnosticKind) {
   Testing::TestKindCoverage(absl::GetFlag(FLAGS_testdata_manifest),
-                            R"(^ *// CHECK:STDERR: .*\.carbon:.* \[(\w+)\]$)",
+                            R"(^ *// CHECK:STDERR: .* \[(\w+)\]$)",
                             llvm::ArrayRef(DiagnosticKinds),
                             llvm::ArrayRef(UntestedDiagnosticKinds));
 }

+ 4 - 0
toolchain/diagnostics/diagnostic_kind.def

@@ -145,6 +145,10 @@ CARBON_DIAGNOSTIC_KIND(InImport)
 CARBON_DIAGNOSTIC_KIND(ResolvingSpecificHere)
 
 // Package/import checking diagnostics.
+CARBON_DIAGNOSTIC_KIND(CppInteropFuzzing)
+CARBON_DIAGNOSTIC_KIND(CppInteropMissingLibrary)
+CARBON_DIAGNOSTIC_KIND(CppInteropParseError)
+CARBON_DIAGNOSTIC_KIND(CppInteropParseWarning)
 CARBON_DIAGNOSTIC_KIND(IncorrectExtension)
 CARBON_DIAGNOSTIC_KIND(IncorrectExtensionImplNote)
 CARBON_DIAGNOSTIC_KIND(DuplicateLibraryApi)

+ 2 - 2
toolchain/driver/compile_subcommand.cpp

@@ -841,8 +841,8 @@ auto CompileSubcommand::Run(DriverEnv& driver_env) -> DriverResult {
 
   // Execute the actual checking.
   CARBON_VLOG_TO(driver_env.vlog_stream, "*** Check::CheckParseTrees ***\n");
-  Check::CheckParseTrees(check_units, options_.prelude_import,
-                         driver_env.vlog_stream);
+  Check::CheckParseTrees(check_units, options_.prelude_import, driver_env.fs,
+                         driver_env.vlog_stream, driver_env.fuzzing);
   CARBON_VLOG_TO(driver_env.vlog_stream,
                  "*** Check::CheckParseTrees done ***\n");
   for (auto& unit : units) {