Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support multiple import Cpp library in a single unit #4814

Merged
merged 9 commits into from
Jan 20, 2025
27 changes: 9 additions & 18 deletions toolchain/check/check_unit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::pair<llvm::StringRef, SemIRLoc>> 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,
Expand Down
48 changes: 37 additions & 11 deletions toolchain/check/import_cpp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,44 @@

namespace Carbon::Check {

auto ImportCppFile(Context& context, SemIRLoc loc,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
llvm::StringRef file_path, llvm::StringRef code) -> void {
RawStringOstream diagnostics_stream;
// Generates C++ file contents to #include all requested imports.
static auto GenerateCppIncludesHeaderCode(
llvm::ArrayRef<std::pair<llvm::StringRef, SemIRLoc>> imports)
-> std::string {
std::string code;
llvm::raw_string_ostream code_stream(code);
for (const auto& [path, _] : imports) {
code_stream << "#include \"";
code_stream.write_escaped(path);
code_stream << "\"\n";
}
return code;
}

auto ImportCppFiles(
Context& context, llvm::StringRef importing_file_path,
llvm::ArrayRef<std::pair<llvm::StringRef, SemIRLoc>> imports,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> 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;

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",
GenerateCppIncludesHeaderCode(imports), {},
(importing_file_path + ".generated.cpp_imports.h").str(), "clang-tool",
std::make_shared<clang::PCHContainerOperations>(),
clang::tooling::getClangStripDependencyFileAdjuster(),
clang::tooling::FileContentMappings(), &diagnostics_consumer, fs);
Expand All @@ -42,16 +68,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_stream.TakeStr());
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_stream.TakeStr());
num_imports, diagnostics_str);
}
}

Expand Down
10 changes: 6 additions & 4 deletions toolchain/check/import_cpp.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@

namespace Carbon::Check {

// Parses the C++ code and report errors and warnings.
auto ImportCppFile(Context& context, SemIRLoc loc,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> 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<std::pair<llvm::StringRef, SemIRLoc>> imports,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs) -> void;

} // namespace Carbon::Check

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,24 @@ import Cpp;

library "[[@TEST_NAME]]";

// CHECK:STDERR: fail_import_cpp_library_empty.carbon:[[@LINE+6]]: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]
// CHECK:STDERR:
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+8]]:1: error: 1 error and 0 warnings in 1 `Cpp` import:
// CHECK:STDERR: fail_import_cpp_library_file_with_quotes.carbon.generated.cpp_imports.h:1:10: fatal error: '\"foo.h\"' file not found
// CHECK:STDERR: 1 | #include "\"foo.h\""
// CHECK:STDERR: | ^~~~~~~~~~~
// CHECK:STDERR: [CppInteropParseError]
// CHECK:STDERR: import Cpp library "\"foo.h\"";
// CHECK:STDERR: ^~~~~~
// CHECK:STDERR:
import Cpp library "\"foo.h\"";

// CHECK:STDOUT: --- fail_import_cpp.carbon
Expand Down
119 changes: 99 additions & 20 deletions toolchain/check/testdata/interop/cpp/no_prelude/cpp_diagnostics.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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]
Expand All @@ -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]
Expand All @@ -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]
Expand All @@ -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]
Expand All @@ -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+20]]: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+28]]: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]
Expand Down Expand Up @@ -184,3 +251,15 @@ import Cpp library "multiple_errors_and_multiple_warnings.h";
// CHECK:STDOUT: package: <namespace> = namespace [template] {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- import_multiple_cpp_files_with_warnings.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = 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> = namespace [template] {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,19 @@
// 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]
// CHECK:STDERR:

// --- fail_cpp_file_not_found.carbon

library "[[@TEST_NAME]]";

// CHECK:STDERR: fail_cpp_file_not_found.carbon:[[@LINE+8]]: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: ^~~~~~
// CHECK:STDERR:
import Cpp library "not_found.h";

// CHECK:STDOUT: --- fail_cpp_file_not_found.carbon
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +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+4]]:1: error: semantics TODO: `multiple Cpp imports are not yet supported` [SemanticsTodo]
// CHECK:STDERR: import Cpp library "file2.h";
// CHECK:STDERR: ^~~~~~
// 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> = namespace [template] {}
Expand Down
Loading