diff --git a/toolchain/check/BUILD b/toolchain/check/BUILD index 48ee3d8687db3..790df1c18cb4b 100644 --- a/toolchain/check/BUILD +++ b/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", ], ) diff --git a/toolchain/check/check.cpp b/toolchain/check/check.cpp index 2113deed47916..c070b3e271218 100644 --- a/toolchain/check/check.cpp +++ b/toolchain/check/check.cpp @@ -61,16 +61,36 @@ static auto RenderImportKey(ImportKey import_key) -> std::string { static auto TrackImport(Map& api_map, Map* 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& 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 units, bool prelude_import, - llvm::raw_ostream* vlog_stream) -> void { + llvm::IntrusiveRefCntPtr 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 unit_infos; @@ -316,7 +337,7 @@ auto CheckParseTrees(llvm::MutableArrayRef 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 explicit_import_map; @@ -332,11 +353,12 @@ auto CheckParseTrees(llvm::MutableArrayRef 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 units, bool prelude_import, for (int check_index = 0; check_index < static_cast(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 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(); } } } diff --git a/toolchain/check/check.h b/toolchain/check/check.h index 5820d8030e745..9618fb20a9880 100644 --- a/toolchain/check/check.h +++ b/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 units, bool prelude_import, - llvm::raw_ostream* vlog_stream) -> void; + llvm::IntrusiveRefCntPtr fs, + llvm::raw_ostream* vlog_stream, bool fuzzing) -> void; } // namespace Carbon::Check diff --git a/toolchain/check/check_unit.cpp b/toolchain/check/check_unit.cpp index 4470ae19de924..1005b32bc640b 100644 --- a/toolchain/check/check_unit.cpp +++ b/toolchain/check/check_unit.cpp @@ -4,12 +4,18 @@ #include "toolchain/check/check_unit.h" +#include + +#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 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) diff --git a/toolchain/check/check_unit.h b/toolchain/check/check_unit.h index 2aba6799aa048..31a39fe63af68 100644 --- a/toolchain/check/check_unit.h +++ b/toolchain/check/check_unit.h @@ -74,6 +74,9 @@ struct UnitAndImports { // A map of the package names to the outgoing imports above. Map package_imports_map; + // List of the `import Cpp` imports. + llvm::SmallVector 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 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 fs_; llvm::raw_ostream* vlog_stream_; Context::DiagnosticEmitter emitter_; diff --git a/toolchain/check/import_cpp.cpp b/toolchain/check/import_cpp.cpp new file mode 100644 index 0000000000000..9a9204346129b --- /dev/null +++ b/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 +#include + +#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 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::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 diff --git a/toolchain/check/import_cpp.h b/toolchain/check/import_cpp.h new file mode 100644 index 0000000000000..10a380d2994e5 --- /dev/null +++ b/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_ diff --git a/toolchain/check/testdata/interop/cpp/no_prelude/bad_import.carbon b/toolchain/check/testdata/interop/cpp/no_prelude/bad_import.carbon new file mode 100644 index 0000000000000..93c587bb80b0e --- /dev/null +++ b/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 [template] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_import_cpp_library_empty.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = 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 [template] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/interop/cpp/no_prelude/cpp_diagnostics.carbon b/toolchain/check/testdata/interop/cpp/no_prelude/cpp_diagnostics.carbon new file mode 100644 index 0000000000000..62d411780cc66 --- /dev/null +++ b/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 [template] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_import_cpp_file_with_multiple_errors.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- import_cpp_file_with_one_warning.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- import_cpp_file_with_multiple_warnings.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = 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 [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 [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 new file mode 100644 index 0000000000000..876270079770f --- /dev/null +++ b/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 [template] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/interop/cpp/no_prelude/test_support.carbon b/toolchain/check/testdata/interop/cpp/no_prelude/function_decl.carbon similarity index 72% rename from toolchain/check/testdata/interop/cpp/no_prelude/test_support.carbon rename to toolchain/check/testdata/interop/cpp/no_prelude/function_decl.carbon index 8beed8f838bd7..79b06f2ced7b4 100644 --- a/toolchain/check/testdata/interop/cpp/no_prelude/test_support.carbon +++ b/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 [template] {} diff --git a/toolchain/check/testdata/interop/cpp/no_prelude/multiple_imports.carbon b/toolchain/check/testdata/interop/cpp/no_prelude/multiple_imports.carbon new file mode 100644 index 0000000000000..7e96e21e85fba --- /dev/null +++ b/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 [template] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/packages/no_prelude/restricted_package_names.carbon b/toolchain/check/testdata/packages/no_prelude/restricted_package_names.carbon index e02d02f26539b..b6286957e80ca 100644 --- a/toolchain/check/testdata/packages/no_prelude/restricted_package_names.carbon +++ b/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 [template] { -// CHECK:STDOUT: has_error -// CHECK:STDOUT: } -// CHECK:STDOUT: %default.import = import +// CHECK:STDOUT: package: = namespace [template] {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_raw_cpp.carbon diff --git a/toolchain/diagnostics/coverage_test.cpp b/toolchain/diagnostics/coverage_test.cpp index f22d50b1bfeac..bd6fe368c5eb4 100644 --- a/toolchain/diagnostics/coverage_test.cpp +++ b/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)); } diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index 781f396d9e584..561350aa111db 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/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) diff --git a/toolchain/driver/compile_subcommand.cpp b/toolchain/driver/compile_subcommand.cpp index c5b105139c429..e102990261728 100644 --- a/toolchain/driver/compile_subcommand.cpp +++ b/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) {