1 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2 // -*- mode: C++ -*-
3 //
4 // Copyright 2020-2024 Google LLC
5 //
6 // Licensed under the Apache License v2.0 with LLVM Exceptions (the
7 // "License"); you may not use this file except in compliance with the
8 // License. You may obtain a copy of the License at
9 //
10 // https://llvm.org/LICENSE.txt
11 //
12 // Unless required by applicable law or agreed to in writing, software
13 // distributed under the License is distributed on an "AS IS" BASIS,
14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 // See the License for the specific language governing permissions and
16 // limitations under the License.
17 //
18 // Author: Giuliano Procida
19 // Author: Ignes Simeonova
20
21 #include "naming.h"
22
23 #include <ostream>
24 #include <sstream>
25 #include <string>
26
27 #include "graph.h"
28
29 namespace stg {
30
Add(Side side,Precedence precedence,const std::string & text) const31 Name Name::Add(Side side, Precedence precedence,
32 const std::string& text) const {
33 bool bracket = precedence < precedence_;
34 std::ostringstream left;
35 std::ostringstream right;
36
37 // Bits on the left require whitespace separation when an identifier is being
38 // added. While it would be simpler to unconditionally add a space, we choose
39 // to only do this for identifiers and not for pointer and reference tokens,
40 // except for the longer pointer-to-member syntax.
41 //
42 // For illegal types containing && & or & && this could result in &&&.
43 left << left_;
44 if (bracket) {
45 left << '(';
46 } else if (side == Side::LEFT
47 && (precedence == Precedence::ATOMIC || text.size() > 2)) {
48 left << ' ';
49 }
50
51 (side == Side::LEFT ? left : right) << text;
52
53 // Bits on the right are arrays [] and functions () and need no whitespace.
54 if (bracket) {
55 right << ')';
56 }
57 right << right_;
58
59 return Name{left.str(), precedence, right.str()};
60 }
61
Qualify(Qualifier qualifier) const62 Name Name::Qualify(Qualifier qualifier) const {
63 std::ostringstream os;
64 // Qualifiers attach without affecting precedence but the precedence
65 // determines the relative position of the qualifier.
66 switch (precedence_) {
67 case Precedence::NIL: {
68 // Add qualifier to the left of the type stem.
69 //
70 // This gives the more popular format (const int rather than int const)
71 // and is safe because NIL precedence types are always leaf syntax.
72 os << qualifier << ' ' << left_;
73 return Name{os.str(), precedence_, right_};
74 }
75 case Precedence::POINTER: {
76 // Add qualifier to the right of the sigil.
77 //
78 // TODO: consider dropping ' ' here.
79 os << left_ << ' ' << qualifier;
80 return Name{os.str(), precedence_, right_};
81 }
82 case Precedence::ARRAY_FUNCTION: {
83 // Qualifiers should not normally apply to arrays or functions.
84 os << '{' << qualifier << ">}" << right_;
85 return Name{left_, precedence_, os.str()};
86 }
87 case Precedence::ATOMIC: {
88 // Qualifiers should not normally apply to names.
89 os << left_ << "{<" << qualifier << '}';
90 return Name{os.str(), precedence_, right_};
91 }
92 }
93 }
94
Print(std::ostream & os) const95 std::ostream& Name::Print(std::ostream& os) const {
96 return os << left_ << right_;
97 }
98
ToString() const99 std::string Name::ToString() const {
100 return left_ + right_;
101 }
102
operator <<(std::ostream & os,const Name & name)103 std::ostream& operator<<(std::ostream& os, const Name& name) {
104 return name.Print(os);
105 }
106
operator ()(Id id)107 Name Describe::operator()(Id id) {
108 // infinite recursion prevention - insert at most once
109 static const Name black_hole{"#"};
110 auto insertion = names.insert({id, black_hole});
111 Name& cached = insertion.first->second;
112 if (insertion.second) {
113 cached = graph.Apply<Name>(*this, id);
114 }
115 return cached;
116 }
117
operator ()(const Special & x)118 Name Describe::operator()(const Special& x) {
119 switch (x.kind) {
120 case Special::Kind::VOID:
121 return Name{"void"};
122 case Special::Kind::VARIADIC:
123 return Name{"..."};
124 case Special::Kind::NULLPTR:
125 return Name{"decltype(nullptr)"};
126 }
127 }
128
operator ()(const PointerReference & x)129 Name Describe::operator()(const PointerReference& x) {
130 std::string sign;
131 switch (x.kind) {
132 case PointerReference::Kind::POINTER:
133 sign = "*";
134 break;
135 case PointerReference::Kind::LVALUE_REFERENCE:
136 sign = "&";
137 break;
138 case PointerReference::Kind::RVALUE_REFERENCE:
139 sign = "&&";
140 break;
141 }
142 return (*this)(x.pointee_type_id)
143 .Add(Side::LEFT, Precedence::POINTER, sign);
144 }
145
operator ()(const PointerToMember & x)146 Name Describe::operator()(const PointerToMember& x) {
147 std::ostringstream os;
148 os << (*this)(x.containing_type_id) << "::*";
149 return (*this)(x.pointee_type_id).Add(Side::LEFT, Precedence::POINTER,
150 os.str());
151 }
152
operator ()(const Typedef & x)153 Name Describe::operator()(const Typedef& x) {
154 return Name{x.name};
155 }
156
operator ()(const Qualified & x)157 Name Describe::operator()(const Qualified& x) {
158 return (*this)(x.qualified_type_id).Qualify(x.qualifier);
159 }
160
operator ()(const Primitive & x)161 Name Describe::operator()(const Primitive& x) {
162 return Name{x.name};
163 }
164
operator ()(const Array & x)165 Name Describe::operator()(const Array& x) {
166 std::ostringstream os;
167 os << '[' << x.number_of_elements << ']';
168 return (*this)(x.element_type_id)
169 .Add(Side::RIGHT, Precedence::ARRAY_FUNCTION, os.str());
170 }
171
operator ()(const BaseClass & x)172 Name Describe::operator()(const BaseClass& x) {
173 return (*this)(x.type_id);
174 }
175
operator ()(const Method & x)176 Name Describe::operator()(const Method& x) {
177 return (*this)(x.type_id).Add(Side::LEFT, Precedence::ATOMIC, x.name);
178 }
179
operator ()(const Member & x)180 Name Describe::operator()(const Member& x) {
181 auto description = (*this)(x.type_id);
182 if (!x.name.empty()) {
183 description = description.Add(Side::LEFT, Precedence::ATOMIC, x.name);
184 }
185 if (x.bitsize) {
186 description = description.Add(
187 Side::RIGHT, Precedence::ATOMIC, ':' + std::to_string(x.bitsize));
188 }
189 return description;
190 }
191
operator ()(const VariantMember & x)192 Name Describe::operator()(const VariantMember& x) {
193 auto description = (*this)(x.type_id);
194 description = description.Add(Side::LEFT, Precedence::ATOMIC, x.name);
195 return description;
196 }
197
operator ()(const StructUnion & x)198 Name Describe::operator()(const StructUnion& x) {
199 std::ostringstream os;
200 os << x.kind << ' ';
201 if (!x.name.empty()) {
202 os << x.name;
203 } else if (x.definition) {
204 os << "{ ";
205 for (const auto& member : x.definition->members) {
206 os << (*this)(member) << "; ";
207 }
208 os << '}';
209 }
210 return Name{os.str()};
211 }
212
operator ()(const Enumeration & x)213 Name Describe::operator()(const Enumeration& x) {
214 std::ostringstream os;
215 os << "enum ";
216 if (!x.name.empty()) {
217 os << x.name;
218 } else if (x.definition) {
219 os << "{ ";
220 for (const auto& e : x.definition->enumerators) {
221 os << e.first << " = " << e.second << ", ";
222 }
223 os << '}';
224 }
225 return Name{os.str()};
226 }
227
operator ()(const Variant & x)228 Name Describe::operator()(const Variant& x) {
229 std::ostringstream os;
230 os << "variant " << x.name;
231 return Name{os.str()};
232 }
233
operator ()(const Function & x)234 Name Describe::operator()(const Function& x) {
235 std::ostringstream os;
236 os << '(';
237 bool sep = false;
238 for (const Id p : x.parameters) {
239 if (sep) {
240 os << ", ";
241 } else {
242 sep = true;
243 }
244 os << (*this)(p);
245 }
246 os << ')';
247 return (*this)(x.return_type_id)
248 .Add(Side::RIGHT, Precedence::ARRAY_FUNCTION, os.str());
249 }
250
operator ()(const ElfSymbol & x)251 Name Describe::operator()(const ElfSymbol& x) {
252 const auto& name = x.full_name ? *x.full_name : x.symbol_name;
253 return x.type_id
254 ? (*this)(*x.type_id).Add(Side::LEFT, Precedence::ATOMIC, name)
255 : Name{name};
256 }
257
operator ()(const Interface &)258 Name Describe::operator()(const Interface&) {
259 return Name{"interface"};
260 }
261
operator ()(Id id)262 std::string DescribeKind::operator()(Id id) {
263 return graph.Apply<std::string>(*this, id);
264 }
265
operator ()(const BaseClass &)266 std::string DescribeKind::operator()(const BaseClass&) {
267 return "base class";
268 }
269
operator ()(const Method &)270 std::string DescribeKind::operator()(const Method&) {
271 return "method";
272 }
273
operator ()(const Member &)274 std::string DescribeKind::operator()(const Member&) {
275 return "member";
276 }
277
operator ()(const ElfSymbol & x)278 std::string DescribeKind::operator()(const ElfSymbol& x) {
279 std::ostringstream os;
280 os << x.symbol_type << " symbol";
281 return os.str();
282 }
283
operator ()(const Interface &)284 std::string DescribeKind::operator()(const Interface&) {
285 return "interface";
286 }
287
288 template <typename Node>
operator ()(const Node &)289 std::string DescribeKind::operator()(const Node&) {
290 return "type";
291 }
292
operator ()(Id id)293 std::string DescribeExtra::operator()(Id id) {
294 return graph.Apply<std::string>(*this, id);
295 }
296
operator ()(const ElfSymbol & x)297 std::string DescribeExtra::operator()(const ElfSymbol& x) {
298 const auto& name = x.full_name ? *x.full_name : x.symbol_name;
299 auto versioned = VersionedSymbolName(x);
300 return name == versioned ? std::string() : " {" + versioned + '}';
301 }
302
303 template <typename Node>
operator ()(const Node &)304 std::string DescribeExtra::operator()(const Node&) {
305 return {};
306 }
307
308 } // namespace stg
309