blob: ee62242d591fcac392db386c2fd8dae9486d76e5 [file] [edit]
// Protocol Buffers - Google's data interchange format
// Copyright 2026 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
#include "google/protobuf/symbol.h"
#include <string>
#include <utility>
#include "absl/log/absl_check.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/descriptor.pb.h"
#include "google/protobuf/port.h"
// Must be included last.
#include "google/protobuf/port_def.inc"
namespace google {
namespace protobuf {
namespace internal {
const FileDescriptor* Symbol::GetFile() const {
switch (type()) {
case MESSAGE:
return descriptor()->file();
case FIELD:
return field_descriptor()->file();
case ONEOF:
return oneof_descriptor()->containing_type()->file();
case ENUM:
return enum_descriptor()->file();
case ENUM_VALUE:
return enum_value_descriptor()->type()->file();
case SERVICE:
return service_descriptor()->file();
case METHOD:
return method_descriptor()->service()->file();
case FULL_PACKAGE:
return file_descriptor();
case SUB_PACKAGE:
return sub_package_file_descriptor()->file;
default:
return nullptr;
}
}
absl::string_view Symbol::full_name() const {
switch (type()) {
case MESSAGE:
return descriptor()->full_name();
case FIELD:
return field_descriptor()->full_name();
case ONEOF:
return oneof_descriptor()->full_name();
case ENUM:
return enum_descriptor()->full_name();
case ENUM_VALUE:
return enum_value_descriptor()->full_name();
case SERVICE:
return service_descriptor()->full_name();
case METHOD:
return method_descriptor()->full_name();
case FULL_PACKAGE:
return file_descriptor()->package();
case SUB_PACKAGE:
return sub_package_file_descriptor()->file->package().substr(
0, sub_package_file_descriptor()->name_size);
default:
ABSL_CHECK(false);
}
return "";
}
std::pair<const void*, absl::string_view> Symbol::parent_name_key() const {
const auto or_file = [&](const void* p) { return p ? p : GetFile(); };
switch (type()) {
case MESSAGE:
return {or_file(descriptor()->containing_type()), descriptor()->name()};
case FIELD: {
auto* field = field_descriptor();
return {or_file(field->is_extension() ? field->extension_scope()
: field->containing_type()),
field->name()};
}
case ONEOF:
return {oneof_descriptor()->containing_type(),
oneof_descriptor()->name()};
case ENUM:
return {or_file(enum_descriptor()->containing_type()),
enum_descriptor()->name()};
case ENUM_VALUE:
return {or_file(enum_value_descriptor()->type()->containing_type()),
enum_value_descriptor()->name()};
case ENUM_VALUE_OTHER_PARENT:
return {enum_value_descriptor()->type(), enum_value_descriptor()->name()};
case SERVICE:
return {GetFile(), service_descriptor()->name()};
case METHOD:
return {method_descriptor()->service(), method_descriptor()->name()};
default:
ABSL_CHECK(false);
}
return {};
}
const FeatureSet& Symbol::features() const {
switch (type()) {
case MESSAGE:
return descriptor()->features();
case FIELD:
return field_descriptor()->features();
case ONEOF:
return oneof_descriptor()->features();
case ENUM:
return enum_descriptor()->features();
case ENUM_VALUE:
return enum_value_descriptor()->features();
case SERVICE:
return service_descriptor()->features();
case METHOD:
return method_descriptor()->features();
case FULL_PACKAGE:
return file_descriptor()->features();
case SUB_PACKAGE:
default:
internal::Unreachable();
}
}
bool Symbol::is_placeholder() const {
switch (type()) {
case MESSAGE:
return descriptor()->is_placeholder();
case ENUM:
return enum_descriptor()->is_placeholder();
case FULL_PACKAGE:
return file_descriptor()->is_placeholder();
default:
return false;
}
}
SymbolVisibility Symbol::visibility_keyword() const {
switch (type()) {
case MESSAGE:
return descriptor()->visibility_keyword();
case ENUM:
return enum_descriptor()->visibility_keyword();
default:
return SymbolVisibility::VISIBILITY_UNSET;
}
}
bool Symbol::IsNestedDefinition() const {
switch (type()) {
case MESSAGE:
return descriptor()->containing_type() != nullptr;
case ENUM:
return enum_descriptor()->containing_type() != nullptr;
case FIELD: // For extension fields
return field_descriptor()->containing_type() != nullptr;
default:
return false;
}
}
SymbolVisibility Symbol::GetEffectiveVisibility() const {
// Only Types have visibility
if (!IsType()) {
return SymbolVisibility::VISIBILITY_UNSET;
}
SymbolVisibility effective_visibility = visibility_keyword();
// If our visibility is specifically set we can return that. We'll validate
// whether it's reasonable or not later.
if (effective_visibility == SymbolVisibility::VISIBILITY_UNSET) {
switch (features().default_symbol_visibility()) {
case FeatureSet::VisibilityFeature::EXPORT_ALL:
return SymbolVisibility::VISIBILITY_EXPORT;
case FeatureSet::VisibilityFeature::EXPORT_TOP_LEVEL:
return IsNestedDefinition() ? SymbolVisibility::VISIBILITY_LOCAL
: SymbolVisibility::VISIBILITY_EXPORT;
case FeatureSet::VisibilityFeature::LOCAL_ALL:
case FeatureSet::VisibilityFeature::STRICT:
return SymbolVisibility::VISIBILITY_LOCAL;
// Unset shouldn't be possible from the compiler without there being an
// error (recursive import, for example), but happens in unit
// tests so assume it represents pre-edition 2024 defaults. In either
// case we want to fail open. We have a DCHECK here to make sure it can
// fail in tests, but not released code.
case FeatureSet::VisibilityFeature::DEFAULT_SYMBOL_VISIBILITY_UNKNOWN:
default:
ABSL_DCHECK(false);
return SymbolVisibility::VISIBILITY_EXPORT;
}
}
return effective_visibility;
}
/*
* Calculate whether this symbol can be accessed from the given
* FileDescriptor*.
*
* Returns true if the symbol is in the same file OR the symbol is `export`
*/
bool Symbol::IsVisibleFrom(FileDescriptor* other) const {
if (other == nullptr || GetFile() == nullptr) {
return false;
}
// Only Types (message/enum) have visibility.
if (!IsType()) {
return true;
}
// If we're dealing with a placeholder then just stop now, visibility can't
// be determined and we have to rely on the proto-compiler previously having
// checked the validity.
if (is_placeholder()) {
return true;
}
if (GetFile() == other) {
return true;
}
SymbolVisibility effective_visibility = GetEffectiveVisibility();
return effective_visibility == SymbolVisibility::VISIBILITY_EXPORT;
}
std::string Symbol::GetVisibilityError(FileDescriptor* other,
absl::string_view usage) const {
const absl::string_view file_path =
GetFile() != nullptr ? GetFile()->name() : "unknown_file";
const absl::string_view symbol_name = full_name();
if (!IsType()) {
return absl::StrCat(
"Attempt to get a visibility error for a non-message/enum symbol \"",
symbol_name, "\", defined in \"", file_path, "\"");
}
SymbolVisibility explicit_visibility = visibility_keyword();
std::string reason =
explicit_visibility == SymbolVisibility::VISIBILITY_LOCAL
? "It is explicitly marked 'local'"
: absl::StrCat(
"It defaulted to local from file-level 'option "
"features.default_symbol_visibility = '",
FeatureSet_VisibilityFeature_DefaultSymbolVisibility_Name(
features().default_symbol_visibility()),
"';");
return absl::StrCat("Symbol \"", symbol_name, "\", defined in \"", file_path,
"\" ", usage, " is ", "not visible from \"",
other->name(), "\". ", reason,
" and cannot be accessed outside its own file");
}
} // namespace internal
} // namespace protobuf
} // namespace google
#include "google/protobuf/port_undef.inc"