Internal changes

PiperOrigin-RevId: 694172218
pull/19179/head
Mike Kruskal 3 weeks ago committed by Copybara-Service
parent 29ae2672fb
commit c1d55b4ea4
  1. 17
      csharp/src/Google.Protobuf/Reflection/FeatureSetDescriptor.g.cs
  2. 1
      src/google/protobuf/compiler/BUILD.bazel
  3. 15
      src/google/protobuf/compiler/code_generator.cc
  4. 11
      src/google/protobuf/compiler/code_generator.h
  5. 16
      src/google/protobuf/compiler/code_generator_unittest.cc
  6. 51
      src/google/protobuf/compiler/command_line_interface.cc
  7. 3
      src/google/protobuf/compiler/command_line_interface.h
  8. 66
      src/google/protobuf/compiler/command_line_interface_unittest.cc
  9. 78
      src/google/protobuf/compiler/java/BUILD.bazel
  10. 2
      src/google/protobuf/compiler/java/name_resolver.h
  11. 187
      src/google/protobuf/compiler/java/name_resolver_test.cc
  12. 7
      src/google/protobuf/compiler/java/test_file_name.proto
  13. 11
      src/google/protobuf/compiler/java/test_file_name_2024.proto
  14. 4
      src/google/protobuf/compiler/mock_code_generator.h

@ -1,17 +0,0 @@
#region Copyright notice and license
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#endregion
namespace Google.Protobuf.Reflection;
internal sealed partial class FeatureSetDescriptor
{
// Canonical serialized form of the edition defaults, generated by embed_edition_defaults.
private const string DefaultsBase64 =
"ChMYhAciACoMCAEQAhgCIAMoATACChMY5wciACoMCAIQARgBIAIoATABChMY6AciDAgBEAEYASACKAEwASoAIOYHKOgH";
}

@ -660,6 +660,7 @@ filegroup(
name = "test_proto_srcs",
srcs = [
"//src/google/protobuf/compiler/cpp:test_proto_srcs",
"//src/google/protobuf/compiler/java:test_proto_srcs",
],
visibility = ["//pkg:__pkg__"],
)

@ -64,17 +64,12 @@ bool CodeGenerator::GenerateAll(const std::vector<const FileDescriptor*>& files,
absl::StatusOr<FeatureSetDefaults> CodeGenerator::BuildFeatureSetDefaults()
const {
if ((GetSupportedFeatures() & FEATURE_SUPPORTS_EDITIONS) == 0) {
// For generators that don't fully support editions yet, provide an
// optimistic set of defaults. Protoc will check this condition later
// anyway.
return FeatureResolver::CompileDefaults(
FeatureSet::descriptor(), GetFeatureExtensions(),
MinimumAllowedEdition(), MaximumAllowedEdition());
}
// For generators that don't fully support editions yet, provide an
// optimistic set of defaults. Protoc will check this condition later
// anyway.
return FeatureResolver::CompileDefaults(
FeatureSet::descriptor(), GetFeatureExtensions(), GetMinimumEdition(),
GetMaximumEdition());
FeatureSet::descriptor(), GetFeatureExtensions(), ProtocMinimumEdition(),
MaximumKnownEdition());
}
GeneratorContext::~GeneratorContext() = default;

@ -162,8 +162,15 @@ class PROTOC_EXPORT CodeGenerator {
}
};
constexpr auto MinimumAllowedEdition() { return Edition::EDITION_PROTO2; }
constexpr auto MaximumAllowedEdition() { return Edition::EDITION_2023; }
// The minimum edition supported by protoc.
constexpr auto ProtocMinimumEdition() { return Edition::EDITION_PROTO2; }
// The maximum edition supported by protoc.
constexpr auto ProtocMaximumEdition() { return Edition::EDITION_2023; }
// The maximum edition known to protoc, which may or may not be officially
// supported yet. During development of a new edition, this will typically be
// set to that.
constexpr auto MaximumKnownEdition() { return Edition::EDITION_2024; }
// CodeGenerators generate one or more files in a given directory. This
// abstract interface represents the directory to which the CodeGenerator is

@ -72,8 +72,8 @@ class TestGenerator : public CodeGenerator {
private:
uint64_t features_ = CodeGenerator::Feature::FEATURE_SUPPORTS_EDITIONS;
Edition minimum_edition_ = MinimumAllowedEdition();
Edition maximum_edition_ = MaximumAllowedEdition();
Edition minimum_edition_ = ProtocMinimumEdition();
Edition maximum_edition_ = ProtocMaximumEdition();
std::vector<const FieldDescriptor*> feature_extensions_ = {
GetExtensionReflection(pb::test)};
};
@ -306,8 +306,8 @@ TEST_F(CodeGeneratorTest, BuildFeatureSetDefaults) {
}
fixed_features {}
}
minimum_edition: EDITION_99997_TEST_ONLY
maximum_edition: EDITION_99999_TEST_ONLY
minimum_edition: EDITION_PROTO2
maximum_edition: EDITION_2024
)pb")));
}
@ -320,13 +320,13 @@ TEST_F(CodeGeneratorTest, BuildFeatureSetDefaultsUnsupported) {
auto result = generator.BuildFeatureSetDefaults();
ASSERT_TRUE(result.ok()) << result.status().message();
EXPECT_EQ(result->minimum_edition(), MinimumAllowedEdition());
EXPECT_EQ(result->maximum_edition(), MaximumAllowedEdition());
EXPECT_EQ(result->minimum_edition(), ProtocMinimumEdition());
EXPECT_EQ(result->maximum_edition(), MaximumKnownEdition());
}
TEST_F(CodeGeneratorTest, SupportedEditionRangeIsDense) {
for (int i = static_cast<int>(MinimumAllowedEdition());
i <= static_cast<int>(MaximumAllowedEdition()); ++i) {
for (int i = static_cast<int>(ProtocMinimumEdition());
i <= static_cast<int>(ProtocMaximumEdition()); ++i) {
EXPECT_TRUE(Edition_IsValid(i));
}
}

@ -1318,6 +1318,10 @@ int CommandLineInterface::Run(int argc, const char* const argv[]) {
return 1;
}
if (!EnforceProtocEditionsSupport(parsed_files)) {
return 1;
}
// We construct a separate GeneratorContext for each output location. Note
// that two code generators may output to the same location, in which case
// they should share a single GeneratorContext so that OpenForInsert() works.
@ -1520,10 +1524,6 @@ bool CommandLineInterface::SetupFeatureResolution(DescriptorPool& pool) {
// Calculate the feature defaults for each built-in generator. All generators
// that support editions must agree on the supported edition range.
std::vector<const FieldDescriptor*> feature_extensions;
Edition minimum_edition = MinimumAllowedEdition();
// Override maximum_edition if experimental_editions is true.
Edition maximum_edition =
!experimental_editions_ ? MaximumAllowedEdition() : Edition::EDITION_MAX;
for (const auto& output : output_directives_) {
if (output.generator == nullptr) continue;
if (!experimental_editions_ &&
@ -1532,20 +1532,20 @@ bool CommandLineInterface::SetupFeatureResolution(DescriptorPool& pool) {
// Only validate min/max edition on generators that advertise editions
// support. Generators still under development will always use the
// correct values.
if (output.generator->GetMinimumEdition() != minimum_edition) {
if (output.generator->GetMinimumEdition() != ProtocMinimumEdition()) {
ABSL_LOG(ERROR) << "Built-in generator " << output.name
<< " specifies a minimum edition "
<< output.generator->GetMinimumEdition()
<< " which is not the protoc minimum "
<< minimum_edition << ".";
<< ProtocMinimumEdition() << ".";
return false;
}
if (output.generator->GetMaximumEdition() != maximum_edition) {
if (output.generator->GetMaximumEdition() != ProtocMaximumEdition()) {
ABSL_LOG(ERROR) << "Built-in generator " << output.name
<< " specifies a maximum edition "
<< output.generator->GetMaximumEdition()
<< " which is not the protoc maximum "
<< maximum_edition << ".";
<< ProtocMaximumEdition() << ".";
return false;
}
}
@ -1560,9 +1560,9 @@ bool CommandLineInterface::SetupFeatureResolution(DescriptorPool& pool) {
}
}
absl::StatusOr<FeatureSetDefaults> defaults =
FeatureResolver::CompileDefaults(FeatureSet::descriptor(),
feature_extensions, minimum_edition,
maximum_edition);
FeatureResolver::CompileDefaults(
FeatureSet::descriptor(), feature_extensions, ProtocMinimumEdition(),
MaximumKnownEdition());
if (!defaults.ok()) {
ABSL_LOG(ERROR) << defaults.status();
return false;
@ -2613,6 +2613,31 @@ bool CommandLineInterface::EnforceEditionsSupport(
return true;
}
bool CommandLineInterface::EnforceProtocEditionsSupport(
const std::vector<const FileDescriptor*>& parsed_files) const {
if (experimental_editions_) {
// The user has explicitly specified the experimental flag.
return true;
}
for (const auto* fd : parsed_files) {
Edition edition =
::google::protobuf::internal::InternalFeatureHelper::GetEdition(*fd);
if (CanSkipEditionCheck(fd->name())) {
// Legacy proto2/proto3 or exempted files don't need any checks.
continue;
}
if (edition > ProtocMaximumEdition()) {
std::cerr << absl::Substitute(
"$0: is a file using edition $1, which is later than the protoc "
"maximum supported edition $2.",
fd->name(), edition, ProtocMaximumEdition());
return false;
}
}
return true;
}
bool CommandLineInterface::GenerateOutput(
const std::vector<const FileDescriptor*>& parsed_files,
const OutputDirective& output_directive,
@ -3033,11 +3058,11 @@ bool CommandLineInterface::WriteEditionDefaults(const DescriptorPool& pool) {
std::vector<const FieldDescriptor*> extensions;
pool.FindAllExtensions(feature_set, &extensions);
Edition minimum = MinimumAllowedEdition();
Edition minimum = ProtocMinimumEdition();
if (edition_defaults_minimum_ != EDITION_UNKNOWN) {
minimum = edition_defaults_minimum_;
}
Edition maximum = MaximumAllowedEdition();
Edition maximum = ProtocMaximumEdition();
if (edition_defaults_maximum_ != EDITION_UNKNOWN) {
maximum = edition_defaults_maximum_;
}

@ -233,6 +233,9 @@ class PROTOC_EXPORT CommandLineInterface {
Edition minimum_edition, Edition maximum_edition,
const std::vector<const FileDescriptor*>& parsed_files) const;
bool EnforceProtocEditionsSupport(
const std::vector<const FileDescriptor*>& parsed_files) const;
// Return status for ParseArguments() and InterpretArgument().
enum ParseArgumentStatus {

@ -1633,7 +1633,7 @@ TEST_F(CommandLineInterfaceTest, Plugin_VersionSkewFuture) {
ExpectErrorSubstring(
"foo.proto:2:5: Edition 99997_TEST_ONLY is later than the maximum "
"supported edition 2023");
"supported edition 2024");
}
TEST_F(CommandLineInterfaceTest, Plugin_VersionSkewPast) {
@ -1927,6 +1927,70 @@ TEST_F(CommandLineInterfaceTest, PluginErrorAndNoEditionsSupport) {
"--plug_out: foo.proto: Saw message type MockCodeGenerator_Error.");
}
TEST_F(CommandLineInterfaceTest, AfterProtocMaximumEditionError) {
CreateTempFile("foo.proto",
R"schema(
edition = "2024";
package foo;
message Foo {
}
)schema");
Run("protocol_compiler --proto_path=$tmpdir --test_out=$tmpdir foo.proto");
ExpectErrorSubstring(
"foo.proto: is a file using edition 2024, which is later than the protoc "
"maximum supported edition 2023.");
}
TEST_F(CommandLineInterfaceTest, AfterProtocMaximumEditionAllowlisted) {
constexpr absl::string_view path = "google/protobuf";
CreateTempFile(absl::StrCat(path, "/foo.proto"),
R"schema(
edition = "2024";
package foo;
message Foo {
}
)schema");
Run(
absl::Substitute("protocol_compiler --proto_path=$$tmpdir "
"--test_out=$$tmpdir $0/foo.proto",
path));
ExpectNoErrors();
}
TEST_F(CommandLineInterfaceTest,
AfterProtocMaximumEditionExperimentalEditions) {
CreateTempFile("foo.proto",
R"schema(
edition = "2024";
package foo;
message Foo {
}
)schema");
Run("protocol_compiler --experimental_editions --proto_path=$tmpdir "
"--test_out=$tmpdir foo.proto");
ExpectNoErrors();
}
TEST_F(CommandLineInterfaceTest,
AfterMaximumKnownEditionErrorExperimentalEditions) {
CreateTempFile("foo.proto",
R"schema(
edition = "99997_TEST_ONLY";
package foo;
message Foo {
}
)schema");
Run("protocol_compiler --experimental_editions --proto_path=$tmpdir "
"--test_out=$tmpdir foo.proto");
ExpectErrorSubstring(
"foo.proto:2:5: Edition 99997_TEST_ONLY is later than the maximum "
"supported edition 2024\n");
}
TEST_F(CommandLineInterfaceTest, EditionDefaults) {
CreateTempFile("google/protobuf/descriptor.proto",
google::protobuf::DescriptorProto::descriptor()->file()->DebugString());

@ -4,6 +4,8 @@
load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
load("@rules_pkg//pkg:mappings.bzl", "pkg_files", "strip_prefix")
load("//bazel:cc_proto_library.bzl", "cc_proto_library")
load("//bazel:proto_library.bzl", "proto_library")
load("//build_defs:cpp_opts.bzl", "COPTS")
package(
@ -243,6 +245,76 @@ cc_test(
],
)
cc_test(
name = "name_resolver_test",
size = "small",
srcs = ["name_resolver_test.cc"],
deps = [
":helpers",
":java",
":test_file_name_2024_cc_proto",
":test_file_name_cc_proto",
":test_multiple_file_no_cc_proto",
":test_multiple_file_yes_cc_proto",
"//src/google/protobuf/compiler:command_line_interface",
"//src/google/protobuf/stubs:lite",
"//src/google/protobuf/testing",
"//src/google/protobuf/testing:file",
"@com_google_absl//absl/log:absl_check",
"@com_google_absl//absl/strings",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
proto_library(
name = "test_file_name_proto",
srcs = ["test_file_name.proto"],
strip_import_prefix = "/src",
)
cc_proto_library(
name = "test_file_name_cc_proto",
deps = [":test_file_name_proto"],
)
proto_library(
name = "test_file_name_2024_proto",
srcs = ["test_file_name_2024.proto"],
strip_import_prefix = "/src",
)
cc_proto_library(
name = "test_file_name_2024_cc_proto",
deps = [":test_file_name_2024_proto"],
)
proto_library(
name = "test_multiple_file_no_proto",
testonly = 1,
srcs = ["test_multiple_file_no.proto"],
strip_import_prefix = "/src",
)
cc_proto_library(
name = "test_multiple_file_no_cc_proto",
testonly = 1,
deps = [":test_multiple_file_no_proto"],
)
proto_library(
name = "test_multiple_file_yes_proto",
testonly = 1,
srcs = ["test_multiple_file_yes.proto"],
strip_import_prefix = "/src",
)
cc_proto_library(
name = "test_multiple_file_yes_cc_proto",
testonly = 1,
deps = [":test_multiple_file_yes_proto"],
)
################################################################################
# Distribution packaging
################################################################################
@ -265,3 +337,9 @@ filegroup(
),
visibility = ["//src/google/protobuf/compiler:__pkg__"],
)
filegroup(
name = "test_proto_srcs",
srcs = glob(["*.proto"]),
visibility = ["//src/google/protobuf/compiler:__pkg__"],
)

@ -34,7 +34,7 @@ enum NameEquality { NO_MATCH, EXACT_EQUAL, EQUAL_IGNORE_CASE };
// Used to get the Java class related names for a given descriptor. It caches
// the results to avoid redundant calculation across multiple name queries.
// Thread-safety note: This class is *not* thread-safe.
class ClassNameResolver {
class PROTOC_EXPORT ClassNameResolver {
public:
explicit ClassNameResolver(const Options& options = {}) : options_(options) {}
~ClassNameResolver() = default;

@ -0,0 +1,187 @@
#include "google/protobuf/compiler/java/name_resolver.h"
#include <string>
#include "google/protobuf/testing/file.h"
#include "google/protobuf/testing/file.h"
#include <gmock/gmock.h>
#include "google/protobuf/testing/googletest.h"
#include <gtest/gtest.h>
#include "absl/log/absl_check.h"
#include "absl/strings/str_cat.h"
#include "google/protobuf/compiler/command_line_interface.h"
#include "google/protobuf/compiler/java/generator.h"
#include "google/protobuf/compiler/java/test_file_name.pb.h"
#include "google/protobuf/compiler/java/test_file_name_2024.pb.h"
#include "google/protobuf/compiler/java/test_multiple_file_no.pb.h"
#include "google/protobuf/compiler/java/test_multiple_file_yes.pb.h"
namespace google {
namespace protobuf {
namespace compiler {
namespace java {
namespace {
using ::testing::HasSubstr;
#define PACKAGE_PREFIX ""
// Tests for Edition 2024 feature `use_old_outer_classname_default`.
TEST(NameResolverTest, GetFileDefaultImmutableClassNameEdition2024) {
ClassNameResolver resolver;
EXPECT_EQ(resolver.GetFileDefaultImmutableClassName(
protobuf_unittest::TestFileName2024::GetDescriptor()->file()),
"TestFileName2024Proto");
}
TEST(NameResolverTest, GetFileDefaultImmutableClassNameEdition2023) {
ClassNameResolver resolver;
EXPECT_EQ(resolver.GetFileDefaultImmutableClassName(
protobuf_unittest::TestFileName::GetDescriptor()->file()),
"TestFileName");
}
TEST(NameResolverTest, GetFileImmutableClassNameEdition2024) {
ClassNameResolver resolver;
EXPECT_EQ(resolver.GetFileImmutableClassName(
protobuf_unittest::TestFileName2024::GetDescriptor()->file()),
"TestFileName2024Proto");
}
TEST(NameResolverTest, GetFileImmutableClassNameEdition2023) {
ClassNameResolver resolver;
EXPECT_EQ(resolver.GetFileImmutableClassName(
protobuf_unittest::TestFileName::GetDescriptor()->file()),
"TestFileNameOuterClass");
}
TEST(NameResolverTest, GetFileImmutableClassNameConflictingEdition2024) {
ClassNameResolver resolver;
// Conflicting names in Edition 2024 will not add the "OuterClass" suffix.
EXPECT_EQ(
resolver.GetFileImmutableClassName(
protobuf_unittest::TestFileName2024Proto::GetDescriptor()->file()),
"TestFileName2024Proto");
}
TEST(NameResolverTest, InvalidConflictingProtoSuffixedMessageNameEdition2024) {
CommandLineInterface cli;
ABSL_CHECK_OK(File::SetContents(
absl::StrCat(::testing::TempDir(), "/test_file_name.proto"),
R"schema(
edition = "2024";
package foo;
message TestFileNameProto {
int32 field = 1;
}
)schema",
true));
JavaGenerator java_generator;
cli.RegisterGenerator("--java_out", &java_generator, "");
std::string java_out = absl::StrCat("--java_out=", ::testing::TempDir());
std::string proto_path = absl::StrCat("-I", ::testing::TempDir());
const char* argv[] = {"protoc", java_out.c_str(), proto_path.c_str(),
"test_file_name.proto", "--experimental_editions"};
CaptureTestStderr();
EXPECT_EQ(1, cli.Run(5, argv));
EXPECT_THAT(GetCapturedTestStderr(),
HasSubstr("Cannot generate Java output because the file's outer "
"class name, \"TestFileNameProto\", matches the name "
"of one of the types declared inside it"));
}
TEST(NameResolverTest, GetClassNameMultipleFilesServiceEdition2023) {
ClassNameResolver resolver;
EXPECT_EQ(resolver.GetClassName(
protobuf_unittest::MultipleFileYesService::descriptor(),
/* immutable = */ true),
PACKAGE_PREFIX "protobuf_unittest.MultipleFileYesService");
EXPECT_EQ(resolver.GetClassName(
protobuf_unittest::MultipleFileNoService::descriptor(),
/* immutable = */ true),
PACKAGE_PREFIX
"protobuf_unittest.TestMultipleFileNo."
"MultipleFileNoService");
}
TEST(NameResolverTest, GetJavaClassNameMultipleFilesServiceEdition2023) {
ClassNameResolver resolver;
EXPECT_EQ(resolver.GetJavaImmutableClassName(
protobuf_unittest::MultipleFileYesService::descriptor()),
PACKAGE_PREFIX "protobuf_unittest.MultipleFileYesService");
EXPECT_EQ(resolver.GetJavaImmutableClassName(
protobuf_unittest::MultipleFileNoService::descriptor()),
PACKAGE_PREFIX
"protobuf_unittest.TestMultipleFileNo$"
"MultipleFileNoService");
}
TEST(NameResolverTest, GetClassNameMultipleFilesMessageEdition2023) {
ClassNameResolver resolver;
EXPECT_EQ(resolver.GetClassName(
protobuf_unittest::MultipleFileYesMessage::descriptor(),
/* immutable = */ true),
PACKAGE_PREFIX "protobuf_unittest.MultipleFileYesMessage");
EXPECT_EQ(resolver.GetClassName(
protobuf_unittest::MultipleFileNoMessage::descriptor(),
/* immutable = */ true),
PACKAGE_PREFIX
"protobuf_unittest.TestMultipleFileNo."
"MultipleFileNoMessage");
}
TEST(NameResolverTest, GetJavaClassNameMultipleFilesMessageEdition2023) {
ClassNameResolver resolver;
EXPECT_EQ(resolver.GetJavaImmutableClassName(
protobuf_unittest::MultipleFileYesMessage::descriptor()),
PACKAGE_PREFIX "protobuf_unittest.MultipleFileYesMessage");
EXPECT_EQ(resolver.GetJavaImmutableClassName(
protobuf_unittest::MultipleFileNoMessage::descriptor()),
PACKAGE_PREFIX
"protobuf_unittest.TestMultipleFileNo$"
"MultipleFileNoMessage");
}
TEST(NameResolverTest, GetClassNameMultipleFilesEnumEdition2023) {
ClassNameResolver resolver;
EXPECT_EQ(
resolver.GetClassName(protobuf_unittest::MultipleFileYesEnum_descriptor(),
/* immutable = */ true),
PACKAGE_PREFIX "protobuf_unittest.MultipleFileYesEnum");
EXPECT_EQ(
resolver.GetClassName(protobuf_unittest::MultipleFileNoEnum_descriptor(),
/* immutable = */ true),
PACKAGE_PREFIX
"protobuf_unittest.TestMultipleFileNo."
"MultipleFileNoEnum");
}
TEST(NameResolverTest, GetJavaClassNameMultipleFilesEnumEdition2023) {
ClassNameResolver resolver;
EXPECT_EQ(resolver.GetJavaImmutableClassName(
protobuf_unittest::MultipleFileYesEnum_descriptor()),
PACKAGE_PREFIX "protobuf_unittest.MultipleFileYesEnum");
EXPECT_EQ(resolver.GetJavaImmutableClassName(
protobuf_unittest::MultipleFileNoEnum_descriptor()),
PACKAGE_PREFIX
"protobuf_unittest.TestMultipleFileNo$"
"MultipleFileNoEnum");
}
} // namespace
} // namespace java
} // namespace compiler
} // namespace protobuf
} // namespace google

@ -0,0 +1,7 @@
edition = "2023";
package protobuf_unittest;
message TestFileName {
int32 field = 1;
}

@ -0,0 +1,11 @@
edition = "2024";
package protobuf_unittest;
message TestFileName2024 {
int32 field = 1;
}
message TestFileName2024Proto {
int32 field = 1;
}

@ -119,8 +119,8 @@ class MockCodeGenerator : public CodeGenerator {
private:
std::string name_;
uint64_t suppressed_features_ = 0;
mutable Edition minimum_edition_ = MinimumAllowedEdition();
mutable Edition maximum_edition_ = MaximumAllowedEdition();
mutable Edition minimum_edition_ = ProtocMinimumEdition();
mutable Edition maximum_edition_ = ProtocMaximumEdition();
std::vector<const FieldDescriptor*> feature_extensions_ = {
GetExtensionReflection(pb::test)};

Loading…
Cancel
Save