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 const 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
107 namespace {
108
109 struct DescribeWorker {
DescribeWorkerstg::__anona1af26720111::DescribeWorker110 DescribeWorker(const Graph& graph, NameCache& names)
111 : graph(graph), names(names) {}
112
operator ()stg::__anona1af26720111::DescribeWorker113 Name operator()(Id id) {
114 // infinite recursion prevention - insert at most once
115 static const Name black_hole{"#"};
116 auto insertion = names.insert({id, black_hole});
117 Name& cached = insertion.first->second;
118 if (insertion.second) {
119 cached = graph.Apply(*this, id);
120 }
121 return cached;
122 }
123
operator ()stg::__anona1af26720111::DescribeWorker124 Name operator()(const Special& x) {
125 switch (x.kind) {
126 case Special::Kind::VOID:
127 return Name{"void"};
128 case Special::Kind::VARIADIC:
129 return Name{"..."};
130 case Special::Kind::NULLPTR:
131 return Name{"decltype(nullptr)"};
132 }
133 }
134
operator ()stg::__anona1af26720111::DescribeWorker135 Name operator()(const PointerReference& x) {
136 std::string sign;
137 switch (x.kind) {
138 case PointerReference::Kind::POINTER:
139 sign = "*";
140 break;
141 case PointerReference::Kind::LVALUE_REFERENCE:
142 sign = "&";
143 break;
144 case PointerReference::Kind::RVALUE_REFERENCE:
145 sign = "&&";
146 break;
147 }
148 return (*this)(x.pointee_type_id)
149 .Add(Side::LEFT, Precedence::POINTER, sign);
150 }
151
operator ()stg::__anona1af26720111::DescribeWorker152 Name operator()(const PointerToMember& x) {
153 std::ostringstream os;
154 os << (*this)(x.containing_type_id) << "::*";
155 return (*this)(x.pointee_type_id).Add(Side::LEFT, Precedence::POINTER,
156 os.str());
157 }
158
operator ()stg::__anona1af26720111::DescribeWorker159 Name operator()(const Typedef& x) {
160 return Name{x.name};
161 }
162
operator ()stg::__anona1af26720111::DescribeWorker163 Name operator()(const Qualified& x) {
164 return (*this)(x.qualified_type_id).Qualify(x.qualifier);
165 }
166
operator ()stg::__anona1af26720111::DescribeWorker167 Name operator()(const Primitive& x) {
168 return Name{x.name};
169 }
170
operator ()stg::__anona1af26720111::DescribeWorker171 Name operator()(const Array& x) {
172 std::ostringstream os;
173 os << '[' << x.number_of_elements << ']';
174 return (*this)(x.element_type_id)
175 .Add(Side::RIGHT, Precedence::ARRAY_FUNCTION, os.str());
176 }
177
operator ()stg::__anona1af26720111::DescribeWorker178 Name operator()(const BaseClass& x) {
179 return (*this)(x.type_id);
180 }
181
operator ()stg::__anona1af26720111::DescribeWorker182 Name operator()(const Method& x) {
183 return (*this)(x.type_id).Add(Side::LEFT, Precedence::ATOMIC, x.name);
184 }
185
operator ()stg::__anona1af26720111::DescribeWorker186 Name operator()(const Member& x) {
187 auto description = (*this)(x.type_id);
188 if (!x.name.empty()) {
189 description = description.Add(Side::LEFT, Precedence::ATOMIC, x.name);
190 }
191 if (x.bitsize) {
192 description = description.Add(
193 Side::RIGHT, Precedence::ATOMIC, ':' + std::to_string(x.bitsize));
194 }
195 return description;
196 }
197
operator ()stg::__anona1af26720111::DescribeWorker198 Name operator()(const VariantMember& x) {
199 auto description = (*this)(x.type_id);
200 description = description.Add(Side::LEFT, Precedence::ATOMIC, x.name);
201 return description;
202 }
203
operator ()stg::__anona1af26720111::DescribeWorker204 Name operator()(const StructUnion& x) {
205 std::ostringstream os;
206 os << x.kind;
207 if (!x.name.empty()) {
208 os << ' ' << x.name;
209 } else if (x.definition) {
210 os << " { ";
211 for (const auto& member : x.definition->members) {
212 os << (*this)(member) << "; ";
213 }
214 os << '}';
215 }
216 return Name{os.str()};
217 }
218
operator ()stg::__anona1af26720111::DescribeWorker219 Name operator()(const Enumeration& x) {
220 std::ostringstream os;
221 os << "enum";
222 if (!x.name.empty()) {
223 os << ' ' << x.name;
224 } else if (x.definition) {
225 os << " { ";
226 for (const auto& e : x.definition->enumerators) {
227 os << e.first << " = " << e.second << ", ";
228 }
229 os << '}';
230 }
231 return Name{os.str()};
232 }
233
operator ()stg::__anona1af26720111::DescribeWorker234 Name operator()(const Variant& x) {
235 std::ostringstream os;
236 os << "variant " << x.name;
237 return Name{os.str()};
238 }
239
operator ()stg::__anona1af26720111::DescribeWorker240 Name operator()(const Function& x) {
241 std::ostringstream os;
242 os << '(';
243 bool sep = false;
244 for (const Id p : x.parameters) {
245 if (sep) {
246 os << ", ";
247 } else {
248 sep = true;
249 }
250 os << (*this)(p);
251 }
252 os << ')';
253 return (*this)(x.return_type_id)
254 .Add(Side::RIGHT, Precedence::ARRAY_FUNCTION, os.str());
255 }
256
operator ()stg::__anona1af26720111::DescribeWorker257 Name operator()(const ElfSymbol& x) {
258 const auto& name = x.full_name ? *x.full_name : x.symbol_name;
259 return x.type_id
260 ? (*this)(*x.type_id).Add(Side::LEFT, Precedence::ATOMIC, name)
261 : Name{name};
262 }
263
operator ()stg::__anona1af26720111::DescribeWorker264 Name operator()(const Interface&) {
265 return Name{"interface"};
266 }
267
268 const Graph& graph;
269 NameCache& names;
270 };
271
272 struct DescribeKindWorker {
DescribeKindWorkerstg::__anona1af26720111::DescribeKindWorker273 explicit DescribeKindWorker(const Graph& graph) : graph(graph) {}
274
operator ()stg::__anona1af26720111::DescribeKindWorker275 std::string operator()(Id id) {
276 return graph.Apply(*this, id);
277 }
278
operator ()stg::__anona1af26720111::DescribeKindWorker279 std::string operator()(const BaseClass&) {
280 return "base class";
281 }
282
operator ()stg::__anona1af26720111::DescribeKindWorker283 std::string operator()(const Method&) {
284 return "method";
285 }
286
operator ()stg::__anona1af26720111::DescribeKindWorker287 std::string operator()(const Member&) {
288 return "member";
289 }
290
operator ()stg::__anona1af26720111::DescribeKindWorker291 std::string operator()(const ElfSymbol& x) {
292 std::ostringstream os;
293 os << x.symbol_type << " symbol";
294 return os.str();
295 }
296
operator ()stg::__anona1af26720111::DescribeKindWorker297 std::string operator()(const Interface&) {
298 return "interface";
299 }
300
301 template <typename Node>
operator ()stg::__anona1af26720111::DescribeKindWorker302 std::string operator()(const Node&) {
303 return "type";
304 }
305
306 const Graph& graph;
307 };
308
309 struct DescribeExtraWorker {
DescribeExtraWorkerstg::__anona1af26720111::DescribeExtraWorker310 explicit DescribeExtraWorker(const Graph& graph) : graph(graph) {}
311
operator ()stg::__anona1af26720111::DescribeExtraWorker312 std::string operator()(Id id) {
313 return graph.Apply(*this, id);
314 }
315
operator ()stg::__anona1af26720111::DescribeExtraWorker316 std::string operator()(const ElfSymbol& x) {
317 const auto& name = x.full_name ? *x.full_name : x.symbol_name;
318 auto versioned = VersionedSymbolName(x);
319 return name == versioned ? std::string() : " {" + versioned + '}';
320 }
321
322 template <typename Node>
operator ()stg::__anona1af26720111::DescribeExtraWorker323 std::string operator()(const Node&) {
324 return {};
325 }
326
327 const Graph& graph;
328 };
329
330 } // namespace
331
operator ()(Id id)332 Name Describe::operator()(Id id) {
333 return DescribeWorker(graph, names)(id);
334 }
335
operator ()(Id id)336 std::string DescribeKind::operator()(Id id) {
337 return DescribeKindWorker(graph)(id);
338 }
339
operator ()(Id id)340 std::string DescribeExtra::operator()(Id id) {
341 return DescribeExtraWorker(graph)(id);
342 }
343
344 } // namespace stg
345