Skip to content

Commit 3936b8b

Browse files
abradymeta-codesync[bot]
authored andcommitted
Add constant code generation to C# generator
Summary: Add constant support to the C# code generator: - make_prototype_for_const with csharp_const_name, csharp_type, csharp_value - const.mustache template generating a static Constants class - Update module.mustache to conditionally include constants block Update golden fixture output for basic test. Reviewed By: vitaut Differential Revision: D94439323 fbshipit-source-id: db11f444d280ec58301334e82c02b30146f25743
1 parent b88f506 commit 3936b8b

22 files changed

Lines changed: 1512 additions & 509 deletions

File tree

third-party/thrift/src/thrift/compiler/generate/t_mstch_csharp_generator.cc

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,61 @@ class t_csharp_EXPERIMENTAL_generator : public t_whisker_generator {
106106

107107
return std::move(def).make();
108108
}
109+
110+
prototype<t_const>::ptr make_prototype_for_const(
111+
const prototype_database& proto) const override {
112+
auto base = t_whisker_generator::make_prototype_for_const(proto);
113+
auto def = whisker::dsl::prototype_builder<h_const>::extends(base);
114+
return std::move(def).make();
115+
}
116+
117+
prototype<t_const_value>::ptr make_prototype_for_const_value(
118+
const prototype_database& proto) const override {
119+
auto base = t_whisker_generator::make_prototype_for_const_value(proto);
120+
auto def = whisker::dsl::prototype_builder<h_const_value>::extends(base);
121+
122+
// Override string_value to use C#-specific escaping
123+
// The base generator uses get_escaped_string() which outputs octal escapes
124+
// that C# doesn't understand. We need \t, \n, \r, etc.
125+
def.property("string_value", [](const t_const_value& self) {
126+
return self.kind() == t_const_value::CV_STRING
127+
? w::string(csharp::escape_csharp_string(self.get_string()))
128+
: w::null;
129+
});
130+
131+
// Returns the string value as a comma-separated list of byte values.
132+
// For binary default values like "7", returns "55" (ASCII code for '7').
133+
// Used in templates for generating C# byte array initializers.
134+
def.property("byte_array_elements", [](const t_const_value& self) {
135+
if (self.kind() != t_const_value::CV_STRING) {
136+
return w::null;
137+
}
138+
const std::string& str = self.get_string();
139+
whisker::array::raw result;
140+
result.reserve(str.size());
141+
for (unsigned char c : str) {
142+
result.emplace_back(w::i64(static_cast<int64_t>(c)));
143+
}
144+
return w::array(std::move(result));
145+
});
146+
147+
// Whisker's strict_printable_types only allows i64 and string to be
148+
// printed via {{...}} interpolation. f64, bool, and null require
149+
// explicit string conversion with language-specific formatting.
150+
def.property("bool_string", [](const t_const_value& self) {
151+
return self.kind() == t_const_value::CV_BOOL
152+
? w::string(self.get_bool() ? "true" : "false")
153+
: w::null;
154+
});
155+
156+
def.property("double_string", [](const t_const_value& self) {
157+
return self.kind() == t_const_value::CV_DOUBLE
158+
? w::string(fmt::format("{}", self.get_double()))
159+
: w::null;
160+
});
161+
162+
return std::move(def).make();
163+
}
109164
};
110165

111166
} // namespace

third-party/thrift/src/thrift/compiler/generate/templates/csharp/common/constants.mustache

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,11 @@
3535
{{#else if value.type.double?}}
3636
{{#if value.double?}}{{value.double_string}}{{#else}}{{value.integer_value}}.0{{/if}}
3737
{{#else if value.type.string?}}
38-
{{value.csharp_quoted_value}}
38+
"{{value.string_value}}"
3939
{{#else if value.type.binary?}}
4040
new byte[] { {{#each (array.enumerate value.byte_array_elements with_first=true) as |i elem first?|}}{{#if (not first?)}} {{/if}}{{elem}},{{/each}} }
4141
{{#else if value.type.enum?}}
42-
({{#partial types.csharp_type type=value.type}}){{value.integer_value}}
42+
{{#if value.enum_value?}}{{#partial types.csharp_type type=value.type}}.{{value.enum_value.csharp_name}}{{#else}}({{#partial types.csharp_type type=value.type}}){{value.integer_value}}{{/if value.enum_value?}}
4343
{{/if value.type.bool?}}
4444
{{/let partial}}
4545

@@ -65,11 +65,11 @@ new byte[] { {{#each (array.enumerate value.byte_array_elements with_first=true)
6565
}}{{#else if value.type.structured?}}
6666
new {{#partial types.csharp_type type=value.type}}()
6767
{
68-
{{#if (object.notnull? value.structured_elements)}}
69-
{{#each value.structured_elements as |elem|}}
70-
{{elem.field.csharp_property_name}} = {{#partial csharp_const_value value=elem.value}},
68+
{{#if (object.notnull? value.map_elements)}}
69+
{{#each value.map_elements as |entry|}}
70+
{{entry.key.string_value}} = {{#partial csharp_const_value value=entry.value}},
7171
{{/each}}
72-
{{/if (object.notnull? value.structured_elements)}}
72+
{{/if (object.notnull? value.map_elements)}}
7373
}{{!
7474
}}{{#else if value.type.map?}}
7575
new {{#partial types.csharp_type type=value.type}}()

third-party/thrift/src/thrift/compiler/generate/templates/csharp/common/file_header.mustache

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,5 @@
3030
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3131
// See the License for the specific language governing permissions and
3232
// limitations under the License.
33+
3334
using System.Collections.Generic;

third-party/thrift/src/thrift/compiler/generate/templates/csharp/common/typedef_types.mustache

Lines changed: 0 additions & 49 deletions
This file was deleted.

third-party/thrift/src/thrift/compiler/generate/templates/csharp/common/types.mustache

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,19 @@
1818

1919
{{! Renders a C# type name for a given Thrift type.
2020
Uses short/keyword type names (e.g. "int", "string", "bool").
21-
Container types recurse to build generic syntax. }}
21+
Container types recurse to build generic syntax.
22+
For typedefs resolving to binary/primitive, we use the resolved type directly
23+
because C# using aliases don't support these (byte[], keyword types). }}
2224
{{#let export partial csharp_type |type|}}
2325
{{#pragma ignore-newlines}}
24-
{{#if type.typedef?}}{{type.csharp_qualified_name}}
26+
{{#if type.typedef?}}
27+
{{! If typedef resolves to binary or primitive, use the resolved type instead of the typedef name
28+
because C# can't create using aliases for byte[] or keyword types like float/double }}
29+
{{#if (or type.resolved.binary? type.resolved.primitive?)}}
30+
{{#partial csharp_type type=type.resolved}} /* {{type.name}} */
31+
{{#else}}
32+
{{type.csharp_qualified_name}}
33+
{{/if (or type.resolved.binary? type.resolved.primitive?)}}
2534
{{#else if type.void?}}void
2635
{{#else if type.bool?}}bool
2736
{{#else if type.byte?}}sbyte

third-party/thrift/src/thrift/compiler/generate/templates/csharp/module.mustache

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,34 @@
1414
limitations under the License.
1515
1616
}}
17+
{{#import "common/types" as types}}
1718
{{#import "types/enum" as enum_tmpl}}
1819
{{> common/file_header}}
20+
1921
{{#each root_program.includes_for_codegen as |inc|}}
2022
{{#if inc.is_useful_alias?}}
2123
using {{inc.alias}} = {{inc.csharp_namespace}};
2224
{{/if inc.is_useful_alias?}}
2325
{{/each}}
26+
{{! Generate typedef aliases - maps Thrift typedefs to C# using aliases.
27+
NOTE: Skip typedefs that resolve to types incompatible with C# using aliases:
28+
- binary -> byte[] (arrays not allowed in using aliases until C# 12 / .NET 8+)
29+
- float/double -> C# keywords can't be used directly in using aliases
30+
- other primitives -> same issue with keyword types (int, bool, etc.)
31+
Once we upgrade to .NET 8+, we can use C# 12's "alias any type" feature. }}
32+
{{#each root_program.typedefs as |td|}}
33+
{{#if (and (not td.resolved.binary?) (not td.resolved.primitive?))}}
34+
using {{td.csharp_name}} = {{#partial types.csharp_type type=td.resolved}};
35+
{{/if (and (not td.resolved.binary?) (not td.resolved.primitive?))}}
36+
{{/each}}
2437

2538
namespace {{root_program.csharp_namespace}}
2639
{
2740
{{#each root_program.enums as |enum|}}
2841
{{#partial enum_tmpl.enum_type enum=enum}}
2942

3043
{{/each}}
44+
{{#if root_program.constants?}}
45+
{{> types/const}}
46+
{{/if root_program.constants?}}
3147
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{{!
2+
Copyright (c) Meta Platforms, Inc. and affiliates.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
16+
}}
17+
{{#import "common/types" as types}}
18+
{{#import "common/constants" as constants}}
19+
20+
/// <summary>
21+
/// Auto-generated constants
22+
/// </summary>
23+
public static class Constants
24+
{
25+
{{#each root_program.consts as |constant|}}
26+
public static readonly {{#partial types.csharp_type type=constant.type}} {{constant.csharp_name}} = {{#partial constants.csharp_const_value value=constant.value}};
27+
{{/each}}
28+
}

third-party/thrift/src/thrift/compiler/test/fixtures/constants/cmd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
android: android src/module.thrift
2+
csharp: csharp_EXPERIMENTAL src/module.thrift
23
py: py src/module.thrift
34
hack: hack:typedef,hack_collections=1 src/module.thrift
45
cpp2: mstch_cpp2 src/module.thrift

third-party/thrift/src/thrift/compiler/test/fixtures/constants/out/cpp2/gen-cpp2/module_constants.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -338,13 +338,13 @@ ::std::set<::std::int32_t> const& set_map_initializer() {
338338
}
339339

340340

341-
::std::string_view _fbthrift_schema_e1bb5b11caac6d65() {
341+
::std::string_view _fbthrift_schema_11d69176884858b4() {
342342
return "";
343343
}
344-
::folly::Range<const ::std::string_view*> _fbthrift_schema_e1bb5b11caac6d65_includes() {
344+
::folly::Range<const ::std::string_view*> _fbthrift_schema_11d69176884858b4_includes() {
345345
return {};
346346
}
347-
::folly::Range<const ::std::string_view*> _fbthrift_schema_e1bb5b11caac6d65_uris() {
347+
::folly::Range<const ::std::string_view*> _fbthrift_schema_11d69176884858b4_uris() {
348348
return {};
349349
}
350350

third-party/thrift/src/thrift/compiler/test/fixtures/constants/out/cpp2/gen-cpp2/module_constants.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -471,9 +471,9 @@ namespace module_constants {
471471
/** Glean {"constant": "set_map_initializer"} */
472472
::std::set<::std::int32_t> const& set_map_initializer();
473473

474-
FOLLY_EXPORT ::std::string_view _fbthrift_schema_e1bb5b11caac6d65();
475-
FOLLY_EXPORT ::folly::Range<const ::std::string_view*> _fbthrift_schema_e1bb5b11caac6d65_includes();
476-
FOLLY_EXPORT ::folly::Range<const ::std::string_view*> _fbthrift_schema_e1bb5b11caac6d65_uris();
474+
FOLLY_EXPORT ::std::string_view _fbthrift_schema_11d69176884858b4();
475+
FOLLY_EXPORT ::folly::Range<const ::std::string_view*> _fbthrift_schema_11d69176884858b4_includes();
476+
FOLLY_EXPORT ::folly::Range<const ::std::string_view*> _fbthrift_schema_11d69176884858b4_uris();
477477

478478
} // namespace module_constants
479479
} // namespace cpp2

0 commit comments

Comments
 (0)