xref: /aosp_15_r20/external/stg/naming.cc (revision 9e3b08ae94a55201065475453d799e8b1378bea6)
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