Skip to content

Commit

Permalink
Generate Kythe annotations for objc messages.
Browse files Browse the repository at this point in the history
This introduces both the basic plumbing for generating metadata (guarded by compiler flags), as well as the initial output of metadata for messages and fields. We largely follows the outline used in the C++ generator.

The second part requires a bit of refactoring on the `FieldGenerator` class since it assumed it was only storing plain string substitutions.

This does not implement cross-references for all symbols (e.g. enums and oneofs); these will be done in followups.

PiperOrigin-RevId: 716698791
  • Loading branch information
protobuf-github-bot authored and copybara-github committed Jan 24, 2025
1 parent b9ec7e0 commit 3cf7321
Show file tree
Hide file tree
Showing 15 changed files with 273 additions and 128 deletions.
33 changes: 16 additions & 17 deletions src/google/protobuf/compiler/objectivec/enum_field.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
#include <string>

#include "absl/container/btree_set.h"
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "google/protobuf/compiler/objectivec/field.h"
#include "google/protobuf/compiler/objectivec/helpers.h"
#include "google/protobuf/compiler/objectivec/names.h"
#include "google/protobuf/compiler/objectivec/options.h"
#include "google/protobuf/descriptor.h"
Expand All @@ -27,38 +27,37 @@ namespace objectivec {

namespace {

void SetEnumVariables(
const FieldDescriptor* descriptor,
const GenerationOptions& generation_options,
absl::flat_hash_map<absl::string_view, std::string>* variables) {
void SetEnumVariables(const FieldDescriptor* descriptor,
const GenerationOptions& generation_options,
SubstitutionMap& variables) {
const std::string type = EnumName(descriptor->enum_type());
const std::string enum_desc_func = absl::StrCat(type, "_EnumDescriptor");
(*variables)["enum_name"] = type;
variables.Set("enum_name", type);
// When using fwd decls, for non repeated fields, if it was defined in a
// different file, the property decls need to use "enum NAME" rather than just
// "NAME" to support the forward declaration of the enums.
if (generation_options.headers_use_forward_declarations &&
!descriptor->is_repeated() &&
!IsProtobufLibraryBundledProtoFile(descriptor->enum_type()->file()) &&
(descriptor->file() != descriptor->enum_type()->file())) {
(*variables)["property_type"] = absl::StrCat("enum ", type, " ");
variables.Set("property_type", absl::StrCat("enum ", type, " "));
}
(*variables)["enum_verifier"] = absl::StrCat(type, "_IsValidValue");
(*variables)["enum_desc_func"] = enum_desc_func;
variables.Set("enum_verifier", absl::StrCat(type, "_IsValidValue"));
variables.Set("enum_desc_func", enum_desc_func);

(*variables)["dataTypeSpecific_name"] = "enumDescFunc";
(*variables)["dataTypeSpecific_value"] = enum_desc_func;
variables.Set("dataTypeSpecific_name", "enumDescFunc");
variables.Set("dataTypeSpecific_value", enum_desc_func);

const Descriptor* msg_descriptor = descriptor->containing_type();
(*variables)["owning_message_class"] = ClassName(msg_descriptor);
variables.Set("owning_message_class", ClassName(msg_descriptor));
}
} // namespace

EnumFieldGenerator::EnumFieldGenerator(
const FieldDescriptor* descriptor,
const GenerationOptions& generation_options)
: SingleFieldGenerator(descriptor, generation_options) {
SetEnumVariables(descriptor, generation_options, &variables_);
SetEnumVariables(descriptor, generation_options, variables_);
}

void EnumFieldGenerator::GenerateCFunctionDeclarations(
Expand All @@ -67,7 +66,7 @@ void EnumFieldGenerator::GenerateCFunctionDeclarations(
return;
}

auto vars = printer->WithVars(variables_);
auto vars = variables_.Install(printer);
printer->Emit(R"objc(
/**
* Fetches the raw value of a @c $owning_message_class$'s @c $name$ property, even
Expand All @@ -90,7 +89,7 @@ void EnumFieldGenerator::GenerateCFunctionImplementations(
return;
}

auto vars = printer->WithVars(variables_);
auto vars = variables_.Install(printer);
printer->Emit(R"objc(
int32_t $owning_message_class$_$capitalized_name$_RawValue($owning_message_class$ *message) {
GPBDescriptor *descriptor = [$owning_message_class$ descriptor];
Expand Down Expand Up @@ -134,11 +133,11 @@ RepeatedEnumFieldGenerator::RepeatedEnumFieldGenerator(
const FieldDescriptor* descriptor,
const GenerationOptions& generation_options)
: RepeatedFieldGenerator(descriptor, generation_options) {
SetEnumVariables(descriptor, generation_options, &variables_);
SetEnumVariables(descriptor, generation_options, variables_);
}

void RepeatedEnumFieldGenerator::EmitArrayComment(io::Printer* printer) const {
auto vars = printer->WithVars(variables_);
auto vars = variables_.Install(printer);
printer->Emit(R"objc(
// |$name$| contains |$enum_name$|
)objc");
Expand Down
119 changes: 65 additions & 54 deletions src/google/protobuf/compiler/objectivec/field.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
#include <vector>

#include "absl/container/btree_set.h"
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/log/absl_check.h"
#include "absl/log/absl_log.h"
Expand All @@ -34,10 +33,10 @@ namespace compiler {
namespace objectivec {

namespace {
using Sub = ::google::protobuf::io::Printer::Sub;

void SetCommonFieldVariables(
const FieldDescriptor* descriptor,
absl::flat_hash_map<absl::string_view, std::string>* variables) {
void SetCommonFieldVariables(const FieldDescriptor* descriptor,
SubstitutionMap& variables) {
std::string camel_case_name = FieldName(descriptor);
std::string raw_field_name;
if (internal::cpp::IsGroupLike(*descriptor)) {
Expand All @@ -51,26 +50,35 @@ void SetCommonFieldVariables(
const bool needs_custom_name = (raw_field_name != un_camel_case_name);

const std::string& classname = ClassName(descriptor->containing_type());
(*variables)["classname"] = classname;
(*variables)["name"] = camel_case_name;
variables.Set(Sub("classname", classname).AnnotatedAs(descriptor));
variables.Set(Sub("name", camel_case_name).AnnotatedAs(descriptor));

const std::string& capitalized_name = FieldNameCapitalized(descriptor);
(*variables)["capitalized_name"] = capitalized_name;
(*variables)["raw_field_name"] = raw_field_name;
(*variables)["field_number_name"] =
absl::StrCat(classname, "_FieldNumber_", capitalized_name);
(*variables)["field_number"] = absl::StrCat(descriptor->number());
(*variables)["property_type"] = FieldObjCType(
descriptor, static_cast<FieldObjCTypeOptions>(
kFieldObjCTypeOptions_IncludeSpaceAfterBasicTypes |
kFieldObjCTypeOptions_IncludeSpaceBeforeStar));
(*variables)["storage_type"] = FieldObjCType(
descriptor, static_cast<FieldObjCTypeOptions>(
kFieldObjCTypeOptions_IncludeSpaceAfterBasicTypes |
kFieldObjCTypeOptions_OmitLightweightGenerics |
kFieldObjCTypeOptions_IncludeSpaceBeforeStar));
(*variables)["field_type"] = GetCapitalizedType(descriptor);
(*variables)["deprecated_attribute"] =
GetOptionalDeprecatedAttribute(descriptor);
variables.Set(
Sub("hazzer_name", "has" + capitalized_name).AnnotatedAs(descriptor));
variables.Set(
Sub("capitalized_name", capitalized_name).AnnotatedAs(descriptor));
variables.Set("raw_field_name", raw_field_name);
variables.Set(Sub("field_number_name",
absl::StrCat(classname, "_FieldNumber_", capitalized_name))
.AnnotatedAs(descriptor));
variables.Set("field_number", absl::StrCat(descriptor->number()));
variables.Set(
"property_type",
FieldObjCType(descriptor,
static_cast<FieldObjCTypeOptions>(
kFieldObjCTypeOptions_IncludeSpaceAfterBasicTypes |
kFieldObjCTypeOptions_IncludeSpaceBeforeStar)));
variables.Set(
"storage_type",
FieldObjCType(descriptor,
static_cast<FieldObjCTypeOptions>(
kFieldObjCTypeOptions_IncludeSpaceAfterBasicTypes |
kFieldObjCTypeOptions_OmitLightweightGenerics |
kFieldObjCTypeOptions_IncludeSpaceBeforeStar)));
variables.Set("field_type", GetCapitalizedType(descriptor));
variables.Set("deprecated_attribute",
GetOptionalDeprecatedAttribute(descriptor));
std::vector<std::string> field_flags;
if (descriptor->is_repeated()) field_flags.push_back("GPBFieldRepeated");
if (descriptor->is_required()) field_flags.push_back("GPBFieldRequired");
Expand All @@ -96,20 +104,21 @@ void SetCommonFieldVariables(
field_flags.push_back("GPBFieldClearHasIvarOnZero");
}

(*variables)["fieldflags"] = BuildFlagsString(FLAGTYPE_FIELD, field_flags);
variables.Set("fieldflags", BuildFlagsString(FLAGTYPE_FIELD, field_flags));

(*variables)["default"] = DefaultValue(descriptor);
(*variables)["default_name"] = GPBGenericValueFieldName(descriptor);
variables.Set("default", DefaultValue(descriptor));
variables.Set("default_name", GPBGenericValueFieldName(descriptor));

(*variables)["dataTypeSpecific_name"] = "clazz";
(*variables)["dataTypeSpecific_value"] = "Nil";
variables.Set("dataTypeSpecific_name", "clazz");
variables.Set("dataTypeSpecific_value", "Nil");

(*variables)["storage_offset_value"] = absl::StrCat(
"(uint32_t)offsetof(", classname, "__storage_, ", camel_case_name, ")");
(*variables)["storage_offset_comment"] = "";
variables.Set("storage_offset_value",
absl::StrCat("(uint32_t)offsetof(", classname, "__storage_, ",
camel_case_name, ")"));
variables.Set("storage_offset_comment", "");

// Clear some common things so they can be set just when needed.
(*variables)["storage_attribute"] = "";
variables.Set("storage_attribute", "");
}

bool HasNonZeroDefaultValue(const FieldDescriptor* field) {
Expand Down Expand Up @@ -192,11 +201,11 @@ FieldGenerator* FieldGenerator::Make(
FieldGenerator::FieldGenerator(const FieldDescriptor* descriptor,
const GenerationOptions& generation_options)
: descriptor_(descriptor), generation_options_(generation_options) {
SetCommonFieldVariables(descriptor, &variables_);
SetCommonFieldVariables(descriptor, variables_);
}

void FieldGenerator::GenerateFieldNumberConstant(io::Printer* printer) const {
auto vars = printer->WithVars(variables_);
auto vars = variables_.Install(printer);
printer->Emit("$field_number_name$ = $field_number$,\n");
}

Expand Down Expand Up @@ -228,7 +237,7 @@ void FieldGenerator::DetermineNeededFiles(
void FieldGenerator::GenerateFieldDescription(io::Printer* printer,
bool include_default) const {
// Printed in the same order as the structure decl.
auto vars = printer->WithVars(variables_);
auto vars = variables_.Install(printer);
printer->Emit(
{{"prefix", include_default ? ".core" : ""},
{"maybe_default",
Expand All @@ -252,10 +261,12 @@ void FieldGenerator::GenerateFieldDescription(io::Printer* printer,
}

void FieldGenerator::SetRuntimeHasBit(int has_index) {
variables_["has_index"] = absl::StrCat(has_index);
variables_.Set("has_index", has_index);
}

void FieldGenerator::SetNoHasBit() { variables_["has_index"] = "GPBNoHasBit"; }
void FieldGenerator::SetNoHasBit() {
variables_.Set("has_index", "GPBNoHasBit");
}

int FieldGenerator::ExtraRuntimeHasBitsNeeded() const { return 0; }

Expand All @@ -269,7 +280,7 @@ void FieldGenerator::SetOneofIndexBase(int index_base) {
if (oneof != nullptr) {
int index = oneof->index() + index_base;
// Flip the sign to mark it as a oneof.
variables_["has_index"] = absl::StrCat(-index);
variables_.Set("has_index", -index);
}
}

Expand All @@ -286,13 +297,13 @@ SingleFieldGenerator::SingleFieldGenerator(

void SingleFieldGenerator::GenerateFieldStorageDeclaration(
io::Printer* printer) const {
auto vars = printer->WithVars(variables_);
auto vars = variables_.Install(printer);
printer->Emit("$storage_type$$name$;\n");
}

void SingleFieldGenerator::GeneratePropertyDeclaration(
io::Printer* printer) const {
auto vars = printer->WithVars(variables_);
auto vars = variables_.Install(printer);
printer->Emit({{"comments",
[&] {
EmitCommentsString(printer, generation_options_,
Expand All @@ -304,17 +315,17 @@ void SingleFieldGenerator::GeneratePropertyDeclaration(
)objc");
if (WantsHasProperty()) {
printer->Emit(R"objc(
@property(nonatomic, readwrite) BOOL has$capitalized_name$$ deprecated_attribute$;
@property(nonatomic, readwrite) BOOL $hazzer_name$$ deprecated_attribute$;
)objc");
}
printer->Emit("\n");
}

void SingleFieldGenerator::GeneratePropertyImplementation(
io::Printer* printer) const {
auto vars = printer->WithVars(variables_);
auto vars = variables_.Install(printer);
if (WantsHasProperty()) {
printer->Emit("@dynamic has$capitalized_name$, $name$;\n");
printer->Emit("@dynamic $hazzer_name$, $name$;\n");
} else {
printer->Emit("@dynamic $name$;\n");
}
Expand All @@ -332,15 +343,15 @@ ObjCObjFieldGenerator::ObjCObjFieldGenerator(
const FieldDescriptor* descriptor,
const GenerationOptions& generation_options)
: SingleFieldGenerator(descriptor, generation_options) {
variables_["property_storage_attribute"] = "strong";
if (IsRetainedName(variables_["name"])) {
variables_["storage_attribute"] = " NS_RETURNS_NOT_RETAINED";
variables_.Set("property_storage_attribute", "strong");
if (IsRetainedName(variable("name"))) {
variables_.Set("storage_attribute", " NS_RETURNS_NOT_RETAINED");
}
}

void ObjCObjFieldGenerator::GenerateFieldStorageDeclaration(
io::Printer* printer) const {
auto vars = printer->WithVars(variables_);
auto vars = variables_.Install(printer);
printer->Emit("$storage_type$$name$;\n");
}

Expand All @@ -350,7 +361,7 @@ void ObjCObjFieldGenerator::GeneratePropertyDeclaration(
// it uses pointers and deals with Objective-C's rules around storage name
// conventions (init*, new*, etc.)

auto vars = printer->WithVars(variables_);
auto vars = variables_.Install(printer);
printer->Emit({{"comments",
[&] {
EmitCommentsString(printer, generation_options_,
Expand All @@ -363,10 +374,10 @@ void ObjCObjFieldGenerator::GeneratePropertyDeclaration(
if (WantsHasProperty()) {
printer->Emit(R"objc(
/** Test to see if @c $name$ has been set. */
@property(nonatomic, readwrite) BOOL has$capitalized_name$$ deprecated_attribute$;
@property(nonatomic, readwrite) BOOL $hazzer_name$$ deprecated_attribute$;
)objc");
}
if (IsInitName(variables_.find("name")->second)) {
if (IsInitName(variable("name"))) {
// If property name starts with init we need to annotate it to get past ARC.
// http://stackoverflow.com/questions/18723226/how-do-i-annotate-an-objective-c-property-with-an-objc-method-family/18723227#18723227
printer->Emit(R"objc(
Expand All @@ -383,13 +394,13 @@ RepeatedFieldGenerator::RepeatedFieldGenerator(

void RepeatedFieldGenerator::GenerateFieldStorageDeclaration(
io::Printer* printer) const {
auto vars = printer->WithVars(variables_);
auto vars = variables_.Install(printer);
printer->Emit("$storage_type$$name$;\n");
}

void RepeatedFieldGenerator::GeneratePropertyImplementation(
io::Printer* printer) const {
auto vars = printer->WithVars(variables_);
auto vars = variables_.Install(printer);
printer->Emit("@dynamic $name$, $name$_Count;\n");
}

Expand All @@ -401,7 +412,7 @@ void RepeatedFieldGenerator::GeneratePropertyDeclaration(
// dealing with needing Objective-C's rules around storage name conventions
// (init*, new*, etc.)

auto vars = printer->WithVars(variables_);
auto vars = variables_.Install(printer);
printer->Emit(
{{"comments",
[&] { EmitCommentsString(printer, generation_options_, descriptor_); }},
Expand All @@ -413,7 +424,7 @@ void RepeatedFieldGenerator::GeneratePropertyDeclaration(
/** The number of items in @c $name$ without causing the container to be created. */
@property(nonatomic, readonly) NSUInteger $name$_Count$ deprecated_attribute$;
)objc");
if (IsInitName(variables_.find("name")->second)) {
if (IsInitName(variable("name"))) {
// If property name starts with init we need to annotate it to get past ARC.
// http://stackoverflow.com/questions/18723226/how-do-i-annotate-an-objective-c-property-with-an-objc-method-family/18723227#18723227
printer->Emit(R"objc(
Expand Down
8 changes: 4 additions & 4 deletions src/google/protobuf/compiler/objectivec/field.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@
#include <vector>

#include "absl/container/btree_set.h"
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/strings/match.h"
#include "absl/strings/string_view.h"
#include "google/protobuf/compiler/objectivec/helpers.h"
#include "google/protobuf/compiler/objectivec/options.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/io/printer.h"
Expand Down Expand Up @@ -70,8 +70,8 @@ class FieldGenerator {
virtual void SetExtraRuntimeHasBitsBase(int index_base);
void SetOneofIndexBase(int index_base);

std::string variable(const char* key) const {
return variables_.find(key)->second;
std::string variable(absl::string_view key) const {
return variables_.Value(key);
}

bool needs_textformat_name_support() const {
Expand All @@ -89,7 +89,7 @@ class FieldGenerator {

const FieldDescriptor* descriptor_;
const GenerationOptions& generation_options_;
absl::flat_hash_map<absl::string_view, std::string> variables_;
SubstitutionMap variables_;
};

class SingleFieldGenerator : public FieldGenerator {
Expand Down
Loading

0 comments on commit 3cf7321

Please sign in to comment.