diff --git a/toolchain/check/check_unit.cpp b/toolchain/check/check_unit.cpp index 3eaa9efe69732..3e7933c87ac30 100644 --- a/toolchain/check/check_unit.cpp +++ b/toolchain/check/check_unit.cpp @@ -340,26 +340,17 @@ auto CheckUnit::ImportCppPackages() -> void { 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; + llvm::SmallVector> import_pairs; + import_pairs.reserve(imports.size()); + for (const auto& import : imports) { + import_pairs.push_back( + {unit_and_imports_->unit->value_stores->string_literal_values().Get( + import.library_id), + import.node_id}); } - ImportCppFile(context_, import.node_id, fs_, source_buffer->filename(), - source_buffer->text()); + ImportCppFiles(context_, unit_and_imports_->unit->sem_ir->filename(), + import_pairs, fs_); } // Loops over all nodes in the tree. On some errors, this may return early, diff --git a/toolchain/check/import_cpp.cpp b/toolchain/check/import_cpp.cpp index fd4cf37cc0748..1d3ae78200661 100644 --- a/toolchain/check/import_cpp.cpp +++ b/toolchain/check/import_cpp.cpp @@ -18,10 +18,43 @@ #include "toolchain/diagnostics/format_providers.h" namespace Carbon::Check { +namespace { + +auto GenerateCppIncludesHeaderFilename(llvm::StringRef importing_file_path) + -> std::string { + return importing_file_path.str() + ".generated.cpp_imports.h"; +} + +auto GenerateCppIncludesHeaderCode( + llvm::ArrayRef> imports) + -> std::string { + constexpr llvm::StringLiteral include_prefix(R"(#include ")"); + constexpr llvm::StringLiteral include_suffix(R"(" +)"); + + std::string code; + for (const auto& [path, _] : imports) { + code.append( + llvm::Twine(include_prefix).concat(path).concat(include_suffix).str()); + } + return code; +} + +} // namespace + +auto ImportCppFiles( + Context& context, llvm::StringRef importing_file_path, + llvm::ArrayRef> imports, + llvm::IntrusiveRefCntPtr fs) -> void { + size_t num_imports = imports.size(); + if (num_imports == 0) { + return; + } + + // TODO: Use all import locations by referring each Clang diagnostic to the + // relevant import. + SemIRLoc loc = imports.back().second; -auto ImportCppFile(Context& context, SemIRLoc loc, - llvm::IntrusiveRefCntPtr fs, - llvm::StringRef file_path, llvm::StringRef code) -> void { std::string diagnostics_str; llvm::raw_string_ostream diagnostics_stream(diagnostics_str); @@ -29,9 +62,11 @@ auto ImportCppFile(Context& context, SemIRLoc loc, 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", + GenerateCppIncludesHeaderCode(imports), {}, + GenerateCppIncludesHeaderFilename(importing_file_path), "clang-tool", std::make_shared(), clang::tooling::getClangStripDependencyFileAdjuster(), clang::tooling::FileContentMappings(), &diagnostics_consumer, fs); @@ -42,16 +77,16 @@ auto ImportCppFile(Context& context, SemIRLoc loc, // 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); + "{0} error{0:s} and {1} warning{1:s} in {2} `Cpp` import{2:s}:\n{3}", + IntAsSelect, IntAsSelect, IntAsSelect, std::string); context.emitter().Emit(loc, CppInteropParseError, num_errors, num_warnings, - file_path.str(), diagnostics_str); + num_imports, 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); + "{0} warning{0:s} in `Cpp` {1} import{1:s}:\n{2}", + IntAsSelect, IntAsSelect, std::string); context.emitter().Emit(loc, CppInteropParseWarning, num_warnings, - file_path.str(), diagnostics_str); + num_imports, diagnostics_str); } } diff --git a/toolchain/check/import_cpp.h b/toolchain/check/import_cpp.h index a3fbfcca5687d..db638e964b801 100644 --- a/toolchain/check/import_cpp.h +++ b/toolchain/check/import_cpp.h @@ -11,10 +11,12 @@ namespace Carbon::Check { -// Parses the C++ code and report errors and warnings. -auto ImportCppFile(Context& context, SemIRLoc loc, - llvm::IntrusiveRefCntPtr fs, - llvm::StringRef file_path, llvm::StringRef code) -> void; +// Generates a C++ header that includes the imported cpp files, parses it and +// report errors and warnings. +auto ImportCppFiles( + Context& context, llvm::StringRef importing_file_path, + llvm::ArrayRef> imports, + llvm::IntrusiveRefCntPtr fs) -> void; } // namespace Carbon::Check diff --git a/toolchain/check/testdata/interop/cpp/no_prelude/bad_import.carbon b/toolchain/check/testdata/interop/cpp/no_prelude/bad_import.carbon index 93c587bb80b0e..5b9d452d571ae 100644 --- a/toolchain/check/testdata/interop/cpp/no_prelude/bad_import.carbon +++ b/toolchain/check/testdata/interop/cpp/no_prelude/bad_import.carbon @@ -22,17 +22,27 @@ import Cpp; library "[[@TEST_NAME]]"; -// CHECK:STDERR: fail_import_cpp_library_empty.carbon:[[@LINE+5]]:1: error: `Cpp` import missing library [CppInteropMissingLibrary] +// CHECK:STDERR: fail_import_cpp_library_empty.carbon:[[@LINE+4]]: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]]"; +// CHECK:STDERR: fail_import_cpp_library_file_with_quotes.carbon:[[@LINE+11]]:1: error: 1 error and 1 warning in 1 `Cpp` import: +// CHECK:STDERR: fail_import_cpp_library_file_with_quotes.carbon.generated.cpp_imports.h:1:12: warning: extra tokens at end of #include directive +// CHECK:STDERR: 1 | #include ""foo.h"" +// CHECK:STDERR: | ^ +// CHECK:STDERR: | // +// CHECK:STDERR: fail_import_cpp_library_file_with_quotes.carbon.generated.cpp_imports.h:1:10: error: empty filename +// CHECK:STDERR: 1 | #include ""foo.h"" +// CHECK:STDERR: | ^ +// CHECK:STDERR: [CppInteropParseError] +// CHECK:STDERR: import Cpp library "\"foo.h\""; +// CHECK:STDERR: ^~~~~~ import Cpp library "\"foo.h\""; // CHECK:STDOUT: --- fail_import_cpp.carbon diff --git a/toolchain/check/testdata/interop/cpp/no_prelude/cpp_diagnostics.carbon b/toolchain/check/testdata/interop/cpp/no_prelude/cpp_diagnostics.carbon index 62d411780cc66..fa5f907cd18ff 100644 --- a/toolchain/check/testdata/interop/cpp/no_prelude/cpp_diagnostics.carbon +++ b/toolchain/check/testdata/interop/cpp/no_prelude/cpp_diagnostics.carbon @@ -16,8 +16,9 @@ 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: fail_import_cpp_file_with_one_error.carbon:[[@LINE+9]]:1: error: 1 error and 0 warnings in 1 `Cpp` import: +// CHECK:STDERR: In file included from fail_import_cpp_file_with_one_error.carbon.generated.cpp_imports.h:1: +// CHECK:STDERR: ./one_error.h:2:2: error: "error1" // CHECK:STDERR: 2 | #error "error1" // CHECK:STDERR: | ^ // CHECK:STDERR: [CppInteropParseError] @@ -35,11 +36,12 @@ import Cpp library "one_error.h"; 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: fail_import_cpp_file_with_multiple_errors.carbon:[[@LINE+12]]:1: error: 2 errors and 0 warnings in 1 `Cpp` import: +// CHECK:STDERR: In file included from fail_import_cpp_file_with_multiple_errors.carbon.generated.cpp_imports.h:1: +// 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: ./multiple_errors.h:3:2: error: "error2" // CHECK:STDERR: 3 | #error "error2" // CHECK:STDERR: | ^ // CHECK:STDERR: [CppInteropParseError] @@ -56,8 +58,9 @@ import Cpp library "multiple_errors.h"; 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: import_cpp_file_with_one_warning.carbon:[[@LINE+9]]:1: warning: 1 warning in `Cpp` 1 import: +// CHECK:STDERR: In file included from import_cpp_file_with_one_warning.carbon.generated.cpp_imports.h:1: +// CHECK:STDERR: ./one_warning.h:2:2: warning: "warning1" // CHECK:STDERR: 2 | #warning "warning1" // CHECK:STDERR: | ^ // CHECK:STDERR: [CppInteropParseWarning] @@ -76,14 +79,15 @@ import Cpp library "one_warning.h"; 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: import_cpp_file_with_multiple_warnings.carbon:[[@LINE+15]]:1: warning: 3 warnings in `Cpp` 1 import: +// CHECK:STDERR: In file included from import_cpp_file_with_multiple_warnings.carbon.generated.cpp_imports.h:1: +// 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: ./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: ./multiple_warnings.h:4:2: warning: "warning3" // CHECK:STDERR: 4 | #warning "warning3" // CHECK:STDERR: | ^ // CHECK:STDERR: [CppInteropParseWarning] @@ -101,11 +105,12 @@ import Cpp library "multiple_warnings.h"; 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: fail_import_cpp_file_with_one_error_and_one_warning.carbon:[[@LINE+12]]:1: error: 1 error and 1 warning in 1 `Cpp` import: +// CHECK:STDERR: In file included from fail_import_cpp_file_with_one_error_and_one_warning.carbon.generated.cpp_imports.h:1: +// 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: ./one_error_and_one_warning.h:3:2: warning: "warning1" // CHECK:STDERR: 3 | #warning "warning1" // CHECK:STDERR: | ^ // CHECK:STDERR: [CppInteropParseError] @@ -126,20 +131,82 @@ import Cpp library "one_error_and_one_warning.h"; 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: fail_import_cpp_file_with_multiple_errors_and_multiple_warnings.carbon:[[@LINE+21]]:1: error: 2 errors and 3 warnings in 1 `Cpp` import: +// CHECK:STDERR: In file included from fail_import_cpp_file_with_multiple_errors_and_multiple_warnings.carbon.generated.cpp_imports.h:1: +// 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: ./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: ./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: ./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: ./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: ^~~~~~ +// CHECK:STDERR: +import Cpp library "multiple_errors_and_multiple_warnings.h"; + +// --- import_multiple_cpp_files_with_warnings.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "one_warning.h"; +// CHECK:STDERR: import_multiple_cpp_files_with_warnings.carbon:[[@LINE+19]]:1: warning: 4 warnings in `Cpp` 2 imports: +// CHECK:STDERR: In file included from import_multiple_cpp_files_with_warnings.carbon.generated.cpp_imports.h:1: +// CHECK:STDERR: ./one_warning.h:2:2: warning: "warning1" +// CHECK:STDERR: 2 | #warning "warning1" +// CHECK:STDERR: | ^ +// CHECK:STDERR: In file included from import_multiple_cpp_files_with_warnings.carbon.generated.cpp_imports.h:2: +// 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"; + +// --- fail_import_multiple_cpp_files_with_warnings_and_errors.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "one_error_and_one_warning.h"; +// CHECK:STDERR: fail_import_multiple_cpp_files_with_warnings_and_errors.carbon:[[@LINE+27]]:1: error: 3 errors and 4 warnings in 2 `Cpp` imports: +// CHECK:STDERR: In file included from fail_import_multiple_cpp_files_with_warnings_and_errors.carbon.generated.cpp_imports.h:1: +// 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: In file included from fail_import_multiple_cpp_files_with_warnings_and_errors.carbon.generated.cpp_imports.h:2: +// 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] @@ -183,3 +250,15 @@ import Cpp library "multiple_errors_and_multiple_warnings.h"; // CHECK:STDOUT: package: = namespace [template] {} // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: --- import_multiple_cpp_files_with_warnings.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_import_multiple_cpp_files_with_warnings_and_errors.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/interop/cpp/no_prelude/file_not_found.carbon b/toolchain/check/testdata/interop/cpp/no_prelude/file_not_found.carbon index 876270079770f..62734140b3565 100644 --- a/toolchain/check/testdata/interop/cpp/no_prelude/file_not_found.carbon +++ b/toolchain/check/testdata/interop/cpp/no_prelude/file_not_found.carbon @@ -7,12 +7,18 @@ // 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]]"; +// CHECK:STDERR: fail_cpp_file_not_found.carbon:[[@LINE+7]]:1: error: 1 error and 0 warnings in 1 `Cpp` import: +// CHECK:STDERR: fail_cpp_file_not_found.carbon.generated.cpp_imports.h:1:10: fatal error: 'not_found.h' file not found +// CHECK:STDERR: 1 | #include "not_found.h" +// CHECK:STDERR: | ^~~~~~~~~~~~~ +// CHECK:STDERR: [CppInteropParseError] +// CHECK:STDERR: import Cpp library "not_found.h"; +// CHECK:STDERR: ^~~~~~ import Cpp library "not_found.h"; // CHECK:STDOUT: --- fail_cpp_file_not_found.carbon diff --git a/toolchain/check/testdata/interop/cpp/no_prelude/multiple_imports.carbon b/toolchain/check/testdata/interop/cpp/no_prelude/multiple_imports.carbon index 7e96e21e85fba..4ae55ebb3c986 100644 --- a/toolchain/check/testdata/interop/cpp/no_prelude/multiple_imports.carbon +++ b/toolchain/check/testdata/interop/cpp/no_prelude/multiple_imports.carbon @@ -16,17 +16,14 @@ void foo1(); void foo1(); -// --- fail_multiple_imports.carbon +// --- 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: --- multiple_imports.carbon // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [template] {}