|
85 | 85 | #include "google/protobuf/parse_context.h" |
86 | 86 | #include "google/protobuf/port.h" |
87 | 87 | #include "google/protobuf/repeated_ptr_field.h" |
| 88 | +#include "google/protobuf/symbol_checker.h" |
88 | 89 | #include "google/protobuf/text_format.h" |
89 | 90 | #include "google/protobuf/unknown_field_set.h" |
90 | 91 |
|
@@ -2361,6 +2362,8 @@ absl::string_view DescriptorPool::ErrorCollector::ErrorLocationName( |
2361 | 2362 | return "IMPORT"; |
2362 | 2363 | case EDITIONS: |
2363 | 2364 | return "EDITIONS"; |
| 2365 | + case SYMBOL: |
| 2366 | + return "SYMBOL"; |
2364 | 2367 | case OTHER: |
2365 | 2368 | return "OTHER"; |
2366 | 2369 | } |
@@ -4802,57 +4805,6 @@ class DescriptorBuilder { |
4802 | 4805 | const MethodDescriptorProto& proto); |
4803 | 4806 | void SuggestFieldNumbers(FileDescriptor* file, |
4804 | 4807 | const FileDescriptorProto& proto); |
4805 | | - void CheckVisibilityRules(FileDescriptor* file, |
4806 | | - const FileDescriptorProto& proto); |
4807 | | - |
4808 | | - // Internal State used for checking visibility rules. |
4809 | | - struct DescriptorAndProto { |
4810 | | - const Descriptor* descriptor; |
4811 | | - const DescriptorProto* proto; |
4812 | | - }; |
4813 | | - |
4814 | | - struct EnumDescriptorAndProto { |
4815 | | - const EnumDescriptor* descriptor; |
4816 | | - const EnumDescriptorProto* proto; |
4817 | | - }; |
4818 | | - |
4819 | | - struct VisibilityCheckerState { |
4820 | | - FileDescriptor* containing_file; |
4821 | | - |
4822 | | - std::vector<DescriptorAndProto> nested_messages; |
4823 | | - std::vector<EnumDescriptorAndProto> nested_enums; |
4824 | | - std::vector<EnumDescriptorAndProto> namespaced_enums; |
4825 | | - }; |
4826 | | - |
4827 | | - void CheckVisibilityRulesVisit(const Descriptor& message, |
4828 | | - const DescriptorProto& proto, |
4829 | | - VisibilityCheckerState& state); |
4830 | | - void CheckVisibilityRulesVisit(const EnumDescriptor& enm, |
4831 | | - const EnumDescriptorProto& proto, |
4832 | | - VisibilityCheckerState& state); |
4833 | | - void CheckVisibilityRulesVisit(const FileDescriptor&, |
4834 | | - const FileDescriptorProto& proto, |
4835 | | - VisibilityCheckerState& state) {} |
4836 | | - void CheckVisibilityRulesVisit(const FieldDescriptor&, |
4837 | | - const FieldDescriptorProto& proto, |
4838 | | - VisibilityCheckerState& state) {} |
4839 | | - void CheckVisibilityRulesVisit(const EnumValueDescriptor&, |
4840 | | - const EnumValueDescriptorProto& proto, |
4841 | | - VisibilityCheckerState& state) {} |
4842 | | - void CheckVisibilityRulesVisit(const OneofDescriptor&, |
4843 | | - const OneofDescriptorProto& proto, |
4844 | | - VisibilityCheckerState& state) {} |
4845 | | - void CheckVisibilityRulesVisit(const Descriptor::ExtensionRange&, |
4846 | | - const DescriptorProto::ExtensionRange& proto, |
4847 | | - VisibilityCheckerState& state) {} |
4848 | | - void CheckVisibilityRulesVisit(const MethodDescriptor&, |
4849 | | - const MethodDescriptorProto& proto, |
4850 | | - VisibilityCheckerState& state) {} |
4851 | | - void CheckVisibilityRulesVisit(const ServiceDescriptor&, |
4852 | | - const ServiceDescriptorProto& proto, |
4853 | | - VisibilityCheckerState& state) {} |
4854 | | - |
4855 | | - bool IsEnumNamespaceMessage(const EnumDescriptor& enm) const; |
4856 | 4808 |
|
4857 | 4809 |
|
4858 | 4810 | // Checks that the extension field matches what is declared. |
@@ -6732,8 +6684,16 @@ FileDescriptor* DescriptorBuilder::BuildFileImpl( |
6732 | 6684 | }); |
6733 | 6685 | } |
6734 | 6686 | if (!had_errors_ && pool_->enforce_symbol_visibility_) { |
6735 | | - // Check Symbol Visibility Rules. |
6736 | | - CheckVisibilityRules(result, proto); |
| 6687 | + SymbolChecker symbol_checker(result, proto); |
| 6688 | + // Check Symbol Visibility and future co-location Rules. |
| 6689 | + auto errors = symbol_checker.CheckSymbolVisibilityRules(); |
| 6690 | + if (!errors.empty()) { |
| 6691 | + for (const auto& error : errors) { |
| 6692 | + AddError(error.symbol_name(), *error.descriptor(), |
| 6693 | + DescriptorPool::ErrorCollector::SYMBOL, |
| 6694 | + error.message().data()); |
| 6695 | + } |
| 6696 | + } |
6737 | 6697 | } |
6738 | 6698 |
|
6739 | 6699 | if (had_errors_) { |
@@ -8370,139 +8330,6 @@ void DescriptorBuilder::SuggestFieldNumbers(FileDescriptor* file, |
8370 | 8330 | } |
8371 | 8331 | } |
8372 | 8332 |
|
8373 | | -// Populate VisibilityCheckerState for messages. |
8374 | | -void DescriptorBuilder::CheckVisibilityRulesVisit( |
8375 | | - const Descriptor& message, const DescriptorProto& proto, |
8376 | | - VisibilityCheckerState& state) { |
8377 | | - if (message.containing_type() != nullptr) { |
8378 | | - state.nested_messages.push_back(DescriptorAndProto{&message, &proto}); |
8379 | | - } |
8380 | | -} |
8381 | | - |
8382 | | -// Populate VisibilityCheckerState for enums. |
8383 | | -void DescriptorBuilder::CheckVisibilityRulesVisit( |
8384 | | - const EnumDescriptor& enm, const EnumDescriptorProto& proto, |
8385 | | - VisibilityCheckerState& state) { |
8386 | | - if (enm.containing_type() != nullptr) { |
8387 | | - if (IsEnumNamespaceMessage(enm)) { |
8388 | | - state.namespaced_enums.push_back(EnumDescriptorAndProto{&enm, &proto}); |
8389 | | - } else { |
8390 | | - state.nested_enums.push_back(EnumDescriptorAndProto{&enm, &proto}); |
8391 | | - } |
8392 | | - } |
8393 | | -} |
8394 | | - |
8395 | | -// Returns true iff the message is a pure zero field message used only for Enum |
8396 | | -// namespacing. AKA it is: |
8397 | | -// * top-level |
8398 | | -// * visibility local either explicitly or by file default |
8399 | | -// * has reserved range of 1 to max. |
8400 | | -bool DescriptorBuilder::IsEnumNamespaceMessage( |
8401 | | - const EnumDescriptor& enm) const { |
8402 | | - const Descriptor* container = enm.containing_type(); |
8403 | | - const FeatureSet::VisibilityFeature::DefaultSymbolVisibility |
8404 | | - default_visibility = enm.features().default_symbol_visibility(); |
8405 | | - // Only allowed for top-level messages |
8406 | | - if (container->containing_type() != nullptr) { |
8407 | | - return false; |
8408 | | - } |
8409 | | - |
8410 | | - bool default_to_local = |
8411 | | - default_visibility == FeatureSet::VisibilityFeature::STRICT || |
8412 | | - default_visibility == FeatureSet::VisibilityFeature::LOCAL_ALL; |
8413 | | - |
8414 | | - bool is_local = |
8415 | | - container->visibility_keyword() == SymbolVisibility::VISIBILITY_LOCAL || |
8416 | | - (container->visibility_keyword() == SymbolVisibility::VISIBILITY_UNSET && |
8417 | | - default_to_local); |
8418 | | - |
8419 | | - // must either be marked local, or unset with file default making it local |
8420 | | - if (!is_local) { |
8421 | | - return false; |
8422 | | - } |
8423 | | - |
8424 | | - if (container->reserved_range_count() != 1) { |
8425 | | - return false; |
8426 | | - } |
8427 | | - |
8428 | | - const Descriptor::ReservedRange* range = container->reserved_range(0); |
8429 | | - if (range == nullptr || |
8430 | | - (range->start != 1 && |
8431 | | - range->end != FieldDescriptor::kLastReservedNumber)) { |
8432 | | - return false; |
8433 | | - } |
8434 | | - |
8435 | | - return true; |
8436 | | -} |
8437 | | - |
8438 | | -// Enforce File-wide visibility and co-location rules. |
8439 | | -void DescriptorBuilder::CheckVisibilityRules(FileDescriptor* file, |
8440 | | - const FileDescriptorProto& proto) { |
8441 | | - // Check DefaultSymbolVisibility first. |
8442 | | - // |
8443 | | - // For Edition 2024: |
8444 | | - // If DefaultSymbolVisibility is STRICT enforce it with caveats for: |
8445 | | - // |
8446 | | - |
8447 | | - VisibilityCheckerState state; |
8448 | | - |
8449 | | - // Build our state object so we can apply rules based on type. |
8450 | | - internal::VisitDescriptors( |
8451 | | - *file, proto, [&](const auto& descriptor, const auto& proto) { |
8452 | | - CheckVisibilityRulesVisit(descriptor, proto, state); |
8453 | | - }); |
8454 | | - |
8455 | | - // In edition 2024 we only enforce STRICT visibility rules. There are possibly |
8456 | | - // more rules to come in future editions, but for now just apply the rule for |
8457 | | - // enforcing nested symbol local visibility. There is a single caveat for, |
8458 | | - // allowing nested enums to have visibility set only when |
8459 | | - // |
8460 | | - // local msg { export enum {} reserved 1 to max; } |
8461 | | - for (auto& nested : state.nested_messages) { |
8462 | | - if (nested.descriptor->visibility_keyword() == |
8463 | | - SymbolVisibility::VISIBILITY_EXPORT && |
8464 | | - nested.descriptor->features().default_symbol_visibility() == |
8465 | | - FeatureSet::VisibilityFeature::STRICT) { |
8466 | | - AddError( |
8467 | | - nested.descriptor->full_name(), *nested.proto, |
8468 | | - DescriptorPool::ErrorCollector::INPUT_TYPE, [&] { |
8469 | | - return absl::StrCat( |
8470 | | - "\"", nested.descriptor->name(), |
8471 | | - "\" is a nested message and cannot be `export` with STRICT " |
8472 | | - "default_symbol_visibility. It must be moved to top-level, " |
8473 | | - "ideally " |
8474 | | - "in its own file in order to be `export`."); |
8475 | | - }); |
8476 | | - } |
8477 | | - } |
8478 | | - |
8479 | | - for (auto& nested : state.nested_enums) { |
8480 | | - if (nested.descriptor->visibility_keyword() == |
8481 | | - SymbolVisibility::VISIBILITY_EXPORT && |
8482 | | - nested.descriptor->features().default_symbol_visibility() == |
8483 | | - FeatureSet::VisibilityFeature::STRICT) { |
8484 | | - // This list contains only enums not considered 'namespaced' by |
8485 | | - // IsEnumNamespaceMessage |
8486 | | - |
8487 | | - AddError(nested.descriptor->full_name(), *nested.proto, |
8488 | | - DescriptorPool::ErrorCollector::INPUT_TYPE, [&] { |
8489 | | - return absl::StrCat( |
8490 | | - "\"", nested.descriptor->name(), |
8491 | | - "\" is a nested enum and cannot be marked `export` with " |
8492 | | - "STRICT " |
8493 | | - "default_symbol_visibility. It must be moved to " |
8494 | | - "top-level, ideally " |
8495 | | - "in its own file in order to be `export`. For C++ " |
8496 | | - "namespacing of enums in a messages use: `local " |
8497 | | - "message <OuterNamespace> { export enum ", |
8498 | | - nested.descriptor->name(), " {...} reserved 1 to max; }`"); |
8499 | | - }); |
8500 | | - } |
8501 | | - |
8502 | | - // Enforce Future rules here: |
8503 | | - } |
8504 | | -} |
8505 | | - |
8506 | 8333 | // ------------------------------------------------------------------- |
8507 | 8334 |
|
8508 | 8335 | // Determine if the file uses optimize_for = LITE_RUNTIME, being careful to |
|
0 commit comments