• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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