Skip to content
← Back

src/semantic/dotnet/tuple_element_names.ghul

1
namespace Semantic.DotNet is
2
use Collections
3
4
use ATTRIBUTE_DATA = System.Reflection.CustomAttributeData
5
use TYPED_ARGUMENT = System.Reflection.CustomAttributeTypedArgument
6
7
use Ghul.Pipes
8
9
// Reads `System.Runtime.CompilerServices.TupleElementNamesAttribute`
10
// — the standard .NET carrier for tuple element names — off the
11
// custom attributes of a reflected member, parameter, field or
12
// property. The `ValueTuple` type itself holds no names, so a
13
// reflected tuple needs this to recover the names a C# (or ghūl)
14
// producer recorded at the declaration site.
15
class TUPLE_ELEMENT_NAMES is
16
// The flattened element-name array declared by a
17
// TupleElementNamesAttribute among `attributes`, or null when
18
// there is none. A null entry marks an unnamed element.
19
read(attributes: Iterable[ATTRIBUTE_DATA]?) -> List[string?]? static is
20
if !attributes? then
21
return null
22
fi
23
24
try
25
for attribute in attributes do
26
let attribute_type = attribute.attribute_type
27
28
if
29
attribute_type.full_name =~ "System.Runtime.CompilerServices.TupleElementNamesAttribute"
30
then
31
for constructor_argument in attribute.constructor_arguments do
32
return _read_names(constructor_argument)
33
od
34
fi
35
od
36
catch ex: System.Exception
37
yrt
38
39
return null
40
si
41
42
// The single `string[]` constructor argument surfaces as a
43
// collection of CustomAttributeTypedArgument, one per element.
44
_read_names(argument: TYPED_ARGUMENT) -> List[string?]? static is
45
let elements = cast Iterable[TYPED_ARGUMENT]?(argument.value)
46
47
if !elements? then
48
return null
49
fi
50
51
let result = LIST[string?]()
52
53
for element in elements do
54
result.add(cast string?(element.value))
55
od
56
57
return result
58
si
59
60
// The element names a `TupleElementNamesAttribute` on a slot of
61
// `type` would carry, or null when no tuple anywhere in it is
62
// named. The array covers every tuple in the type, however deep:
63
// a pre-order walk that lists each tuple's own element names and
64
// then descends into its type's arguments, left to right, which
65
// is the layout the attribute is defined to have and C# writes.
66
// An unnamed element, or an unnamed tuple, takes nulls.
67
attribute_names_for_type(type: Types.Type) -> List[string?]? static is
68
let names = LIST[string?]()
69
70
_flatten(type, names)
71
72
if !(names |> any(name => name?)) then
73
return null
74
fi
75
76
return names
77
si
78
79
_flatten(type: Types.Type?, names: LIST[string?]) static is
80
if !type? then
81
return
82
fi
83
84
let arguments = type.arguments
85
86
if _is_value_tuple(type) then
87
let own = type.tuple_element_names
88
89
for index in 0..arguments.count do
90
names.add(if own? /\ index < own.count then own[index] else null fi)
91
od
92
fi
93
94
for argument in arguments do
95
_flatten(argument, names)
96
od
97
si
98
99
// `type` with the names from a flattened attribute array put back
100
// on every tuple in it, by the same walk that wrote them.
101
apply(type: Types.Type, names: List[string?]) -> Types.Type static is
102
let (result, _, _) = _apply_walk(type, names, 0)
103
104
return result
105
si
106
107
// Returns the rebuilt type, the next index into `names`, and
108
// whether anything in the type took a name, so that an enclosing
109
// generic is rebuilt only when one of its arguments changed.
110
_apply_walk(type: Types.Type, names: List[string?], start: int) -> (Types.Type, int, bool) static is
111
let arguments = type.arguments
112
let index mut = start
113
let own: LIST[string?]? mut = null
114
115
if _is_value_tuple(type) then
116
let taken = LIST[string?]()
117
118
for _ in 0..arguments.count do
119
taken.add(if index < names.count then names[index] else null fi)
120
121
index = index + 1
122
od
123
124
own = taken
125
fi
126
127
let rebuilt mut = type
128
let changed mut = false
129
130
if arguments.count > 0 then
131
let new_arguments = LIST[Types.Type]()
132
133
for argument in arguments do
134
let (new_argument, next, argument_changed) = _apply_walk(argument, names, index)
135
136
index = next
137
138
if argument_changed then
139
changed = true
140
fi
141
142
new_arguments.add(new_argument)
143
od
144
145
if changed then
146
if let generic: Types.GENERIC = type then
147
if let rebuilt_generic = NULLABILITY.rebuild_generic(generic, new_arguments) then
148
rebuilt = rebuilt_generic
149
fi
150
fi
151
fi
152
fi
153
154
if let named = own /\ named |> any(name => name?) then
155
rebuilt = rebuilt.apply_tuple_element_names(named)
156
changed = true
157
fi
158
159
return (rebuilt, index, changed)
160
si
161
162
_is_value_tuple(type: Types.Type) -> bool static =>
163
type.is_value_tuple \/ isa Types.TUPLE(type)
164
si
165
si