Update the r# prefixing logic:

- Only prepend r# to fields when needed instead of always
- Append '__mangled_because_symbol_is_a_rust_raw_identifier' to names like 'Self' which can't be used legally even with an r# prefix

Also use the same check to prepend r# on:
- Message names (eg `message Self {}`)
- oneof names
- oneof case names
- enum names
- enum case names
- module names (e.g. 'package google.type')

PiperOrigin-RevId: 599153141
pull/15454/head
Protobuf Team Bot 1 year ago committed by Copybara-Service
parent 85972e505a
commit cd575718be
  1. 29
      rust/test/reserved.proto
  2. 13
      rust/test/shared/reserved_test.rs
  3. 11
      src/google/protobuf/compiler/rust/BUILD.bazel
  4. 10
      src/google/protobuf/compiler/rust/accessors/map.cc
  5. 10
      src/google/protobuf/compiler/rust/accessors/repeated_scalar.cc
  6. 4
      src/google/protobuf/compiler/rust/accessors/singular_message.cc
  7. 10
      src/google/protobuf/compiler/rust/accessors/singular_scalar.cc
  8. 8
      src/google/protobuf/compiler/rust/accessors/singular_string.cc
  9. 18
      src/google/protobuf/compiler/rust/generator.cc
  10. 6
      src/google/protobuf/compiler/rust/message.cc
  11. 32
      src/google/protobuf/compiler/rust/naming.cc
  12. 4
      src/google/protobuf/compiler/rust/naming.h
  13. 8
      src/google/protobuf/compiler/rust/oneof.cc
  14. 46
      src/google/protobuf/compiler/rust/rust_keywords.cc
  15. 33
      src/google/protobuf/compiler/rust/rust_keywords.h

@ -5,13 +5,32 @@
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
// LINT: LEGACY_NAMES
// The purpose of this file is to be as hostile as possible to reserved words
// to the Rust language and ensure it still works.
syntax = "proto2";
package naming;
// Note: Ideally this test could be 'package type.if.else.true.false'
// which would work in Rust but would break the C++ codegen.
package type.type;
message Reserved {
optional int32 for = 1; // for is a reserved word in rust, let's make sure we can compile it
message Self {
optional int32 for = 1;
optional Self self = 2;
optional bool true = 3;
optional string false = 4;
}
message pub {
enum Self {
enum = 0;
}
}
message Inner {}
optional Inner pub = 2; // pub is reserved too!
message enum {
oneof self {
.type.type.pub.Self const = 3;
}
}

@ -5,19 +5,20 @@
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
use googletest::prelude::*;
/// Test covering proto compilation with reserved words.
use reserved_proto::naming::Reserved;
use googletest::prelude::*;
use reserved_proto::r#type::r#type::r#enum;
use reserved_proto::r#type::r#type::Self__mangled_because_symbol_is_a_rust_raw_identifier;
#[test]
fn test_reserved_keyword_in_accessors() {
let msg = Reserved::new();
let res = msg.r#for();
let msg = Self__mangled_because_symbol_is_a_rust_raw_identifier::new();
let res = msg.self__mangled_because_symbol_is_a_rust_raw_identifier().r#for();
assert_that!(res, eq(0));
}
#[test]
fn test_reserved_keyword_in_messages() {
let msg = Reserved::new();
let _ = msg.r#pub();
let msg = r#enum::new();
let _ = msg.r#const();
}

@ -182,8 +182,14 @@ cc_test(
cc_library(
name = "naming",
srcs = ["naming.cc"],
hdrs = ["naming.h"],
srcs = [
"naming.cc",
"rust_keywords.cc",
],
hdrs = [
"naming.h",
"rust_keywords.h",
],
copts = COPTS,
strip_include_prefix = "/src",
deps = [
@ -191,6 +197,7 @@ cc_library(
"//src/google/protobuf",
"//src/google/protobuf/compiler:code_generator",
"//src/google/protobuf/compiler/cpp:names_internal",
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/log:absl_check",
"@com_google_absl//absl/log:absl_log",
"@com_google_absl//absl/strings",

@ -23,7 +23,7 @@ void Map::InMsgImpl(Context& ctx, const FieldDescriptor& field,
auto& key_type = *field.message_type()->map_key();
auto& value_type = *field.message_type()->map_value();
ctx.Emit({{"field", field.name()},
ctx.Emit({{"field", RsSafeName(field.name())},
{"Key", RsTypePath(ctx, key_type)},
{"Value", RsTypePath(ctx, value_type)},
{"getter_thunk", ThunkName(ctx, field, "get")},
@ -32,7 +32,7 @@ void Map::InMsgImpl(Context& ctx, const FieldDescriptor& field,
[&] {
if (ctx.is_upb()) {
ctx.Emit({}, R"rs(
pub fn r#$field$(&self)
pub fn $field$(&self)
-> $pb$::MapView<'_, $Key$, $Value$> {
unsafe {
$getter_thunk$(self.raw_msg())
@ -44,7 +44,7 @@ void Map::InMsgImpl(Context& ctx, const FieldDescriptor& field,
})rs");
} else {
ctx.Emit({}, R"rs(
pub fn r#$field$(&self)
pub fn $field$(&self)
-> $pb$::MapView<'_, $Key$, $Value$> {
unsafe {
$pb$::MapView::from_raw($pbi$::Private,
@ -60,7 +60,7 @@ void Map::InMsgImpl(Context& ctx, const FieldDescriptor& field,
}
if (ctx.is_upb()) {
ctx.Emit({}, R"rs(
pub fn r#$field$_mut(&mut self)
pub fn $field$_mut(&mut self)
-> $pb$::MapMut<'_, $Key$, $Value$> {
let raw = unsafe {
$getter_mut_thunk$(self.raw_msg(),
@ -72,7 +72,7 @@ void Map::InMsgImpl(Context& ctx, const FieldDescriptor& field,
})rs");
} else {
ctx.Emit({}, R"rs(
pub fn r#$field$_mut(&mut self)
pub fn $field$_mut(&mut self)
-> $pb$::MapMut<'_, $Key$, $Value$> {
let inner = $pbr$::InnerMapMut::new($pbi$::Private,
unsafe { $getter_mut_thunk$(self.raw_msg()) });

@ -20,7 +20,7 @@ namespace rust {
void RepeatedScalar::InMsgImpl(Context& ctx, const FieldDescriptor& field,
AccessorCase accessor_case) const {
ctx.Emit({{"field", field.name()},
ctx.Emit({{"field", RsSafeName(field.name())},
{"Scalar", RsTypePath(ctx, field)},
{"getter_thunk", ThunkName(ctx, field, "get")},
{"getter_mut_thunk", ThunkName(ctx, field, "get_mut")},
@ -28,7 +28,7 @@ void RepeatedScalar::InMsgImpl(Context& ctx, const FieldDescriptor& field,
[&] {
if (ctx.is_upb()) {
ctx.Emit({}, R"rs(
pub fn r#$field$(&self) -> $pb$::RepeatedView<'_, $Scalar$> {
pub fn $field$(&self) -> $pb$::RepeatedView<'_, $Scalar$> {
unsafe {
$getter_thunk$(
self.raw_msg(),
@ -44,7 +44,7 @@ void RepeatedScalar::InMsgImpl(Context& ctx, const FieldDescriptor& field,
)rs");
} else {
ctx.Emit({}, R"rs(
pub fn r#$field$(&self) -> $pb$::RepeatedView<'_, $Scalar$> {
pub fn $field$(&self) -> $pb$::RepeatedView<'_, $Scalar$> {
unsafe {
$pb$::RepeatedView::from_raw(
$pbi$::Private,
@ -63,7 +63,7 @@ void RepeatedScalar::InMsgImpl(Context& ctx, const FieldDescriptor& field,
}
if (ctx.is_upb()) {
ctx.Emit({}, R"rs(
pub fn r#$field$_mut(&mut self) -> $pb$::RepeatedMut<'_, $Scalar$> {
pub fn $field$_mut(&mut self) -> $pb$::RepeatedMut<'_, $Scalar$> {
unsafe {
$pb$::RepeatedMut::from_inner(
$pbi$::Private,
@ -82,7 +82,7 @@ void RepeatedScalar::InMsgImpl(Context& ctx, const FieldDescriptor& field,
)rs");
} else {
ctx.Emit({}, R"rs(
pub fn r#$field$_mut(&mut self) -> $pb$::RepeatedMut<'_, $Scalar$> {
pub fn $field$_mut(&mut self) -> $pb$::RepeatedMut<'_, $Scalar$> {
unsafe {
$pb$::RepeatedMut::from_inner(
$pbi$::Private,

@ -25,7 +25,7 @@ void SingularMessage::InMsgImpl(Context& ctx, const FieldDescriptor& field,
// fully qualified message name with modules prefixed
std::string msg_type = RsTypePath(ctx, field);
ctx.Emit({{"msg_type", msg_type},
{"field", field.name()},
{"field", RsSafeName(field.name())},
{"getter_thunk", ThunkName(ctx, field, "get")},
{"getter_mut_thunk", ThunkName(ctx, field, "get_mut")},
{"clearer_thunk", ThunkName(ctx, field, "clear")},
@ -58,7 +58,7 @@ void SingularMessage::InMsgImpl(Context& ctx, const FieldDescriptor& field,
{"getter",
[&] {
ctx.Emit({}, R"rs(
pub fn r#$field$(&self) -> $msg_type$View {
pub fn $field$(&self) -> $msg_type$View {
$getter_body$
}
)rs");

@ -25,14 +25,14 @@ void SingularScalar::InMsgImpl(Context& ctx, const FieldDescriptor& field,
AccessorCase accessor_case) const {
ctx.Emit(
{
{"field", field.name()},
{"field", RsSafeName(field.name())},
{"Scalar", RsTypePath(ctx, field)},
{"hazzer_thunk", ThunkName(ctx, field, "has")},
{"default_value", DefaultValue(ctx, field)},
{"getter",
[&] {
ctx.Emit({}, R"rs(
pub fn r#$field$(&self) -> $Scalar$ {
pub fn $field$(&self) -> $Scalar$ {
unsafe { $getter_thunk$(self.raw_msg()) }
}
)rs");
@ -42,7 +42,7 @@ void SingularScalar::InMsgImpl(Context& ctx, const FieldDescriptor& field,
if (!field.is_optional()) return;
if (!field.has_presence()) return;
ctx.Emit({}, R"rs(
pub fn r#$field$_opt(&self) -> $pb$::Optional<$Scalar$> {
pub fn $field$_opt(&self) -> $pb$::Optional<$Scalar$> {
if !unsafe { $hazzer_thunk$(self.raw_msg()) } {
return $pb$::Optional::Unset($default_value$);
}
@ -61,7 +61,7 @@ void SingularScalar::InMsgImpl(Context& ctx, const FieldDescriptor& field,
}
if (field.has_presence()) {
ctx.Emit({}, R"rs(
pub fn r#$field$_mut(&mut self) -> $pb$::FieldEntry<'_, $Scalar$> {
pub fn $field$_mut(&mut self) -> $pb$::FieldEntry<'_, $Scalar$> {
static VTABLE: $pbi$::PrimitiveOptionalMutVTable<$Scalar$> =
$pbi$::PrimitiveOptionalMutVTable::new(
$pbi$::Private,
@ -84,7 +84,7 @@ void SingularScalar::InMsgImpl(Context& ctx, const FieldDescriptor& field,
)rs");
} else {
ctx.Emit({}, R"rs(
pub fn r#$field$_mut(&mut self) -> $pb$::Mut<'_, $Scalar$> {
pub fn $field$_mut(&mut self) -> $pb$::Mut<'_, $Scalar$> {
static VTABLE: $pbi$::PrimitiveVTable<$Scalar$> =
$pbi$::PrimitiveVTable::new(
$pbi$::Private,

@ -39,7 +39,7 @@ void SingularString::InMsgImpl(Context& ctx, const FieldDescriptor& field,
};
ctx.Emit(
{
{"field", field.name()},
{"field", RsSafeName(field.name())},
{"hazzer_thunk", hazzer_thunk},
{"getter_thunk", getter_thunk},
{"setter_thunk", setter_thunk},
@ -70,7 +70,7 @@ void SingularString::InMsgImpl(Context& ctx, const FieldDescriptor& field,
if (field.has_presence()) {
ctx.Emit(
{
{"field", field.name()},
{"field", RsSafeName(field.name())},
{"proxied_type", proxied_type},
{"default_val", DefaultValue(ctx, field)},
{"view_type", proxied_type},
@ -115,7 +115,7 @@ void SingularString::InMsgImpl(Context& ctx, const FieldDescriptor& field,
}
)rs");
} else {
ctx.Emit({{"field", field.name()},
ctx.Emit({{"field", RsSafeName(field.name())},
{"proxied_type", proxied_type},
{"getter_thunk", getter_thunk},
{"setter_thunk", setter_thunk}},
@ -143,7 +143,7 @@ void SingularString::InMsgImpl(Context& ctx, const FieldDescriptor& field,
}},
},
R"rs(
pub fn r#$field$(&self) -> &$proxied_type$ {
pub fn $field$(&self) -> &$proxied_type$ {
let view = unsafe { $getter_thunk$(self.raw_msg()).as_ref() };
$transform_view$
}

@ -52,10 +52,10 @@ namespace {
void EmitOpeningOfPackageModules(Context& ctx, absl::string_view pkg) {
if (pkg.empty()) return;
for (absl::string_view segment : absl::StrSplit(pkg, '.')) {
ctx.Emit({{"segment", segment}},
ctx.Emit({{"segment", RsSafeName(segment)}},
R"rs(
#[allow(non_snake_case)]
pub mod r#$segment$ {
pub mod $segment$ {
)rs");
}
}
@ -77,8 +77,8 @@ void EmitClosingOfPackageModules(Context& ctx, absl::string_view pkg) {
absl::c_reverse(segments);
for (absl::string_view segment : segments) {
ctx.Emit({{"segment", segment}}, R"rs(
} // mod r#$segment$
ctx.Emit({{"segment", RsSafeName(segment)}}, R"rs(
} // mod $segment$
)rs");
}
}
@ -92,19 +92,19 @@ void EmitPubUseOfOwnTypes(Context& ctx, const FileDescriptor& primary_file,
auto mod = RustInternalModuleName(ctx, non_primary_src);
for (int i = 0; i < non_primary_src.message_type_count(); ++i) {
auto& msg = *non_primary_src.message_type(i);
ctx.Emit({{"mod", mod}, {"Msg", msg.name()}},
ctx.Emit({{"mod", mod}, {"Msg", RsSafeName(msg.name())}},
R"rs(
pub use crate::r#$mod$::$Msg$;
pub use crate::$mod$::$Msg$;
// TODO Address use for imported crates
pub use crate::r#$mod$::$Msg$View;
pub use crate::r#$mod$::$Msg$Mut;
pub use crate::$mod$::$Msg$View;
pub use crate::$mod$::$Msg$Mut;
)rs");
}
for (int i = 0; i < non_primary_src.enum_type_count(); ++i) {
auto& enum_ = *non_primary_src.enum_type(i);
ctx.Emit({{"mod", mod}, {"Enum", EnumRsName(enum_)}},
R"rs(
pub use crate::r#$mod$::$Enum$;
pub use crate::$mod$::$Enum$;
)rs");
}
}

@ -220,7 +220,7 @@ void GenerateRs(Context& ctx, const Descriptor& msg) {
ABSL_LOG(WARNING) << "unsupported map field: " << msg.full_name();
return;
}
ctx.Emit({{"Msg", msg.name()},
ctx.Emit({{"Msg", RsSafeName(msg.name())},
{"Msg::new", [&] { MessageNew(ctx, msg); }},
{"Msg::serialize", [&] { MessageSerialize(ctx, msg); }},
{"Msg::deserialize", [&] { MessageDeserialize(ctx, msg); }},
@ -260,7 +260,7 @@ void GenerateRs(Context& ctx, const Descriptor& msg) {
return;
}
ctx.Emit(
{{"Msg", msg.name()},
{{"Msg", RsSafeName(msg.name())},
{"nested_msgs",
[&] {
for (int i = 0; i < msg.nested_type_count(); ++i) {
@ -505,7 +505,7 @@ void GenerateRs(Context& ctx, const Descriptor& msg) {
if (ctx.is_cpp()) {
ctx.printer().PrintRaw("\n");
ctx.Emit({{"Msg", msg.name()}}, R"rs(
ctx.Emit({{"Msg", RsSafeName(msg.name())}}, R"rs(
impl $Msg$ {
pub fn __unstable_wrap_cpp_grant_permission_to_break(msg: $pbi$::RawMessage) -> Self {
Self { inner: $pbr$::MessageInner { msg } }

@ -24,6 +24,7 @@
#include "google/protobuf/compiler/code_generator.h"
#include "google/protobuf/compiler/cpp/helpers.h"
#include "google/protobuf/compiler/rust/context.h"
#include "google/protobuf/compiler/rust/rust_keywords.h"
#include "google/protobuf/descriptor.h"
namespace google {
@ -130,8 +131,10 @@ std::string RustModule(Context& ctx, const FileDescriptor& file,
std::vector<std::string> package_modules =
absl::StrSplit(file.package(), '.', absl::SkipEmpty());
modules.insert(modules.begin(), package_modules.begin(),
package_modules.end());
modules.reserve(package_modules.size());
for (const auto& module : package_modules) {
modules.push_back(RsSafeName(module));
}
// Innermost to outermost order.
std::vector<std::string> modules_from_containing_types;
@ -227,12 +230,12 @@ std::string RustModule(Context& ctx, const EnumDescriptor& enum_) {
}
std::string RustInternalModuleName(Context& ctx, const FileDescriptor& file) {
return absl::StrReplaceAll(StripProto(file.name()),
{{"_", "__"}, {"/", "_s"}});
return RsSafeName(
absl::StrReplaceAll(StripProto(file.name()), {{"_", "__"}, {"/", "_s"}}));
}
std::string GetCrateRelativeQualifiedPath(Context& ctx, const Descriptor& msg) {
return absl::StrCat(RustModule(ctx, msg), msg.name());
return absl::StrCat(RustModule(ctx, msg), RsSafeName(msg.name()));
}
std::string GetCrateRelativeQualifiedPath(Context& ctx,
@ -255,8 +258,19 @@ std::string FieldInfoComment(Context& ctx, const FieldDescriptor& field) {
return comment;
}
std::string RsSafeName(absl::string_view name) {
if (IsNotLegalEvenWithRPoundPrefix(name)) {
return absl::StrCat(name,
"__mangled_because_symbol_is_a_rust_raw_identifier");
}
if (IsRustKeyword(name)) {
return absl::StrCat("r#", name);
}
return std::string(name);
}
std::string EnumRsName(const EnumDescriptor& desc) {
return SnakeToUpperCamelCase(desc.name());
return RsSafeName(SnakeToUpperCamelCase(desc.name()));
}
std::string EnumValueRsName(const EnumValueDescriptor& value) {
@ -279,11 +293,11 @@ std::string EnumValueRsName(const MultiCasePrefixStripper& stripper,
if (absl::ascii_isdigit(name[0])) {
name = absl::StrCat("_", name);
}
return name;
return RsSafeName(name);
}
std::string OneofViewEnumRsName(const OneofDescriptor& oneof) {
return SnakeToUpperCamelCase(oneof.name());
return RsSafeName(SnakeToUpperCamelCase(oneof.name()));
}
std::string OneofMutEnumRsName(const OneofDescriptor& oneof) {
@ -297,7 +311,7 @@ std::string OneofCaseEnumRsName(const OneofDescriptor& oneof) {
}
std::string OneofCaseRsName(const FieldDescriptor& oneof_field) {
return SnakeToUpperCamelCase(oneof_field.name());
return RsSafeName(SnakeToUpperCamelCase(oneof_field.name()));
}
std::string CamelToSnakeCase(absl::string_view input) {

@ -49,6 +49,10 @@ std::string OneofCaseRsName(const FieldDescriptor& oneof_field);
std::string FieldInfoComment(Context& ctx, const FieldDescriptor& field);
// Returns how to 'spell' the provided name in Rust, which is the provided name
// verbatim unless it is a Rust keyword that isn't a legal symbol name.
std::string RsSafeName(absl::string_view name);
// Constructs a string of the Rust modules which will contain the message.
//
// Example: Given a message 'NestedMessage' which is defined in package 'x.y'

@ -257,7 +257,7 @@ void GenerateOneofDefinition(Context& ctx, const OneofDescriptor& oneof) {
void GenerateOneofAccessors(Context& ctx, const OneofDescriptor& oneof) {
ctx.Emit(
{{"oneof_name", oneof.name()},
{{"oneof_name", RsSafeName(oneof.name())},
{"view_enum_name", OneofViewEnumRsName(oneof)},
{"mut_enum_name", OneofMutEnumRsName(oneof)},
{"case_enum_name", OneofCaseEnumRsName(oneof)},
@ -272,7 +272,7 @@ void GenerateOneofAccessors(Context& ctx, const OneofDescriptor& oneof) {
ctx.Emit(
{
{"case", OneofCaseRsName(field)},
{"rs_getter", field.name()},
{"rs_getter", RsSafeName(field.name())},
{"type", rs_type},
},
R"rs(
@ -321,14 +321,14 @@ void GenerateOneofAccessors(Context& ctx, const OneofDescriptor& oneof) {
}},
{"case_thunk", ThunkName(ctx, oneof, "case")}},
R"rs(
pub fn r#$oneof_name$(&self) -> $Msg$_::$view_enum_name$ {
pub fn $oneof_name$(&self) -> $Msg$_::$view_enum_name$ {
match unsafe { $case_thunk$(self.raw_msg()) } {
$view_cases$
_ => $Msg$_::$view_enum_name$::not_set(std::marker::PhantomData)
}
}
pub fn r#$oneof_name$_mut(&mut self) -> $Msg$_::$mut_enum_name$ {
pub fn $oneof_name$_mut(&mut self) -> $Msg$_::$mut_enum_name$ {
match unsafe { $case_thunk$(self.raw_msg()) } {
$mut_cases$
_ => $Msg$_::$mut_enum_name$::not_set(std::marker::PhantomData)

@ -0,0 +1,46 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2024 Google LLC. 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/compiler/rust/rust_keywords.h"
#include <string>
#include "absl/container/flat_hash_set.h"
#include "absl/strings/string_view.h"
namespace google {
namespace protobuf {
namespace compiler {
namespace rust {
bool IsNotLegalEvenWithRPoundPrefix(absl::string_view str) {
// These keywords cannot be used even with an r# prefix.
// https://doc.rust-lang.org/reference/identifiers.html
static const auto* rust_raw_identifiers =
new absl::flat_hash_set<std::string>{"crate", "self", "super", "Self"};
return rust_raw_identifiers->contains(str);
}
bool IsRustKeyword(absl::string_view str) {
// https://doc.rust-lang.org/reference/keywords.html#reserved-keywords
static const auto* rust_keywords = new absl::flat_hash_set<std::string>{
"as", "async", "await", "break", "const", "continue",
"crate", "dyn", "else", "enum", "extern", "false",
"fn", "for", "if", "impl", "in", "let",
"loop", "match", "mod", "move", "mut", "pub",
"ref", "return", "Self", "self", "static", "struct",
"super", "trait", "true", "type", "union", "unsafe",
"use", "where", "while", "abstract", "become", "box",
"do", "final", "macro", "override", "priv", "try",
"typeof", "unsized", "virtual", "yield"};
return rust_keywords->contains(str);
}
} // namespace rust
} // namespace compiler
} // namespace protobuf
} // namespace google

@ -0,0 +1,33 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2024 Google LLC. 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
#ifndef GOOGLE_PROTOBUF_COMPILER_RUST_RUST_KEYWORDS_H__
#define GOOGLE_PROTOBUF_COMPILER_RUST_RUST_KEYWORDS_H__
#include "absl/strings/string_view.h"
namespace google {
namespace protobuf {
namespace compiler {
namespace rust {
// Returns true if the provided str is a 'Raw Identifier', which is a symbol
// which cannot be used even with r# prefix.
bool IsNotLegalEvenWithRPoundPrefix(absl::string_view str);
// Returns true if the provided str is a Rust 2021 Edition keyword and cannot be
// used as a symbol. These symbols can can be used with an r# prefix unless
// IsNotLegalEvenWithRPoundPrefix is true. This function should always match the
// behavior for the corresponding Edition that our emitted crates use.
bool IsRustKeyword(absl::string_view str);
} // namespace rust
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_RUST_RUST_KEYWORDS_H__
Loading…
Cancel
Save