blob: 83952917d7a4475eeecb407dfad6cdbba9d7ac29 [file] [log] [blame]
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- mode: C++ -*-
//
// Copyright 2020-2022 Google LLC
//
// Licensed under the Apache License v2.0 with LLVM Exceptions (the
// "License"); you may not use this file except in compliance with the
// License. You may obtain a copy of the License at
//
// https://llvm.org/LICENSE.txt
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: Giuliano Procida
// Author: Ignes Simeonova
#include "naming.h"
#include <ostream>
#include <sstream>
#include <string>
#include "graph.h"
namespace stg {
Name Name::Add(Side side, Precedence precedence,
const std::string& text) const {
bool bracket = precedence < precedence_;
std::ostringstream left;
std::ostringstream right;
// Bits on the left require whitespace separation when an identifier is being
// added. While it would be simpler to unconditionally add a space, we choose
// to only do this for identifiers and not for pointer and reference tokens,
// except for the longer pointer-to-member syntax.
//
// For illegal types containing && & or & && this could result in &&&.
left << left_;
if (bracket) {
left << '(';
} else if (side == Side::LEFT
&& (precedence == Precedence::ATOMIC || text.size() > 2)) {
left << ' ';
}
(side == Side::LEFT ? left : right) << text;
// Bits on the right are arrays [] and functions () and need no whitespace.
if (bracket) {
right << ')';
}
right << right_;
return Name{left.str(), precedence, right.str()};
}
Name Name::Qualify(Qualifier qualifier) const {
std::ostringstream os;
// Qualifiers attach without affecting precedence but the precedence
// determines the relative position of the qualifier.
switch (precedence_) {
case Precedence::NIL: {
// Add qualifier to the left of the type stem.
//
// This gives the more popular format (const int rather than int const)
// and is safe because NIL precedence types are always leaf syntax.
os << qualifier << ' ' << left_;
return Name{os.str(), precedence_, right_};
}
case Precedence::POINTER: {
// Add qualifier to the right of the sigil.
//
// TODO: consider dropping ' ' here.
os << left_ << ' ' << qualifier;
return Name{os.str(), precedence_, right_};
}
case Precedence::ARRAY_FUNCTION: {
// Qualifiers should not normally apply to arrays or functions.
os << '{' << qualifier << ">}" << right_;
return Name{left_, precedence_, os.str()};
}
case Precedence::ATOMIC: {
// Qualifiers should not normally apply to names.
os << left_ << "{<" << qualifier << '}';
return Name{os.str(), precedence_, right_};
}
}
}
std::ostream& Name::Print(std::ostream& os) const {
return os << left_ << right_;
}
std::string Name::ToString() const {
return left_ + right_;
}
std::ostream& operator<<(std::ostream& os, const Name& name) {
return name.Print(os);
}
Name Describe::operator()(Id id) {
// infinite recursion prevention - insert at most once
static const Name black_hole{"#"};
auto insertion = names.insert({id, black_hole});
Name& cached = insertion.first->second;
if (insertion.second) {
cached = graph.Apply<Name>(*this, id);
}
return cached;
}
Name Describe::operator()(const Special& x) {
switch (x.kind) {
case Special::Kind::VOID:
return Name{"void"};
case Special::Kind::VARIADIC:
return Name{"..."};
case Special::Kind::NULLPTR:
return Name{"decltype(nullptr)"};
}
}
Name Describe::operator()(const PointerReference& x) {
std::string sign;
switch (x.kind) {
case PointerReference::Kind::POINTER:
sign = "*";
break;
case PointerReference::Kind::LVALUE_REFERENCE:
sign = "&";
break;
case PointerReference::Kind::RVALUE_REFERENCE:
sign = "&&";
break;
}
return (*this)(x.pointee_type_id)
.Add(Side::LEFT, Precedence::POINTER, sign);
}
Name Describe::operator()(const PointerToMember& x) {
std::ostringstream os;
os << (*this)(x.containing_type_id) << "::*";
return (*this)(x.pointee_type_id).Add(Side::LEFT, Precedence::POINTER,
os.str());
}
Name Describe::operator()(const Typedef& x) {
return Name{x.name};
}
Name Describe::operator()(const Qualified& x) {
return (*this)(x.qualified_type_id).Qualify(x.qualifier);
}
Name Describe::operator()(const Primitive& x) {
return Name{x.name};
}
Name Describe::operator()(const Array& x) {
std::ostringstream os;
os << '[' << x.number_of_elements << ']';
return (*this)(x.element_type_id)
.Add(Side::RIGHT, Precedence::ARRAY_FUNCTION, os.str());
}
Name Describe::operator()(const BaseClass& x) {
return (*this)(x.type_id);
}
Name Describe::operator()(const Member& x) {
auto description = (*this)(x.type_id);
if (!x.name.empty()) {
description = description.Add(Side::LEFT, Precedence::ATOMIC, x.name);
}
if (x.bitsize) {
description = description.Add(
Side::RIGHT, Precedence::ATOMIC, ':' + std::to_string(x.bitsize));
}
return description;
}
Name Describe::operator()(const Method& x) {
if (x.mangled_name == x.name) {
return Name{x.name};
}
return Name{x.name + " {" + x.mangled_name + "}"};
}
Name Describe::operator()(const StructUnion& x) {
std::ostringstream os;
os << x.kind << ' ';
if (!x.name.empty()) {
os << x.name;
} else if (x.definition) {
os << "{ ";
for (const auto& member : x.definition->members) {
os << (*this)(member) << "; ";
}
os << '}';
}
return Name{os.str()};
}
Name Describe::operator()(const Enumeration& x) {
std::ostringstream os;
os << "enum ";
if (!x.name.empty()) {
os << x.name;
} else if (x.definition) {
os << "{ ";
for (const auto& e : x.definition->enumerators) {
os << e.first << " = " << e.second << ", ";
}
os << '}';
}
return Name{os.str()};
}
Name Describe::operator()(const Function& x) {
std::ostringstream os;
os << '(';
bool sep = false;
for (const Id p : x.parameters) {
if (sep) {
os << ", ";
} else {
sep = true;
}
os << (*this)(p);
}
os << ')';
return (*this)(x.return_type_id)
.Add(Side::RIGHT, Precedence::ARRAY_FUNCTION, os.str());
}
Name Describe::operator()(const ElfSymbol& x) {
const auto& name = x.full_name ? *x.full_name : x.symbol_name;
return x.type_id
? (*this)(*x.type_id).Add(Side::LEFT, Precedence::ATOMIC, name)
: Name{name};
}
Name Describe::operator()(const Interface&) {
return Name{"interface"};
}
std::string DescribeKind::operator()(Id id) {
return graph.Apply<std::string>(*this, id);
}
std::string DescribeKind::operator()(const BaseClass&) {
return "base class";
}
std::string DescribeKind::operator()(const Member&) {
return "member";
}
std::string DescribeKind::operator()(const Method&) {
return "method";
}
std::string DescribeKind::operator()(const ElfSymbol& x) {
std::ostringstream os;
os << x.symbol_type << " symbol";
return os.str();
}
std::string DescribeKind::operator()(const Interface&) {
return "interface";
}
template <typename Node>
std::string DescribeKind::operator()(const Node&) {
return "type";
}
std::string DescribeExtra::operator()(Id id) {
return graph.Apply<std::string>(*this, id);
}
std::string DescribeExtra::operator()(const ElfSymbol& x) {
const auto& name = x.full_name ? *x.full_name : x.symbol_name;
auto versioned = VersionedSymbolName(x);
return name == versioned ? std::string() : " {" + versioned + '}';
}
template <typename Node>
std::string DescribeExtra::operator()(const Node&) {
return {};
}
} // namespace stg