mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 21:10:12 +01:00
I was wondering, instead of treating `misc` differently and enabling specific checks, maybe we can flip that since we actually seem okay with most of the checks? The main check I'm enabling, with significant edits here, is `misc-no-recursion`. But maybe this is helpful to enable, even with the necessary NOLINTs, since we want to avoid recursion in the toolchain? This PR shows some example fixes in subst.cpp (which are more stylistic, since the code shouldn't actually have recursed due to its structure; I think we could remove the warning on TryResolveInst the same way). Some also just don't seem worth fixing, like those in tests files (I didn't see a way to exclude files in .clang-tidy, so instead I'm using NOLINTBEGIN). But I think we might actually want to fix inst_namer, and there's enough in convert that I didn't look closely. Also, I made some protected -> private style fixes based on `misc-non-private-member-variables-in-classes` (this is also how I noticed `class Real` versus `struct Real`). With node_stack, it looks like the `protected` wasn't even used. [Per style](https://google.github.io/styleguide/cppguide.html#Access_Control), data members should be private outside tests. But since we can't trivially exclude `protected` members in tests, I'm turning it off -- I don't view it as offering enough benefit on the whole. migrate_cpp issues are preexisting (I believe we just aren't monitoring it), but changes there make `bazel build --config=clang-tidy -k //...` work cleanly. --------- Co-authored-by: Richard Smith <richard@metafoo.co.uk>
1031 lines
30 KiB
C++
1031 lines
30 KiB
C++
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
|
// Exceptions. See /LICENSE for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
#include "testing/fuzzing/proto_to_carbon.h"
|
|
|
|
#include <google/protobuf/text_format.h>
|
|
|
|
#include <string_view>
|
|
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include "testing/fuzzing/carbon.pb.h"
|
|
|
|
// This is a test file, so the recursion is okay.
|
|
// NOLINTBEGIN(misc-no-recursion)
|
|
|
|
namespace Carbon {
|
|
|
|
static auto ExpressionToCarbon(const Fuzzing::Expression& expression,
|
|
llvm::raw_ostream& out) -> void;
|
|
static auto PatternToCarbon(const Fuzzing::Pattern& pattern,
|
|
llvm::raw_ostream& out) -> void;
|
|
static auto StatementToCarbon(const Fuzzing::Statement& statement,
|
|
llvm::raw_ostream& out) -> void;
|
|
static auto DeclarationToCarbon(const Fuzzing::Declaration& declaration,
|
|
llvm::raw_ostream& out) -> void;
|
|
|
|
// Produces a valid Carbon identifier, which must match the regex
|
|
// `[A-Za-z_][A-Za-z0-9_]*`. In the case when `s` is generated by the
|
|
// fuzzing framework, it might contain invalid/non-printable characters.
|
|
static auto IdentifierToCarbon(std::string_view s, llvm::raw_ostream& out)
|
|
-> void {
|
|
if (s.empty()) {
|
|
out << "EmptyIdentifier";
|
|
} else {
|
|
if (!llvm::isAlpha(s[0]) && s[0] != '_') {
|
|
// Ensures that identifier starts with a valid character.
|
|
out << 'x';
|
|
}
|
|
for (const char c : s) {
|
|
if (llvm::isAlnum(c) || c == '_') {
|
|
out << c;
|
|
} else {
|
|
out << llvm::toHex(c);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static auto StringLiteralToCarbon(std::string_view s, llvm::raw_ostream& out) {
|
|
out << '"';
|
|
out.write_escaped(s, /*UseHexEscapes=*/true);
|
|
out << '"';
|
|
}
|
|
|
|
static auto LibraryNameToCarbon(const Fuzzing::LibraryName& library,
|
|
llvm::raw_ostream& out) -> void {
|
|
IdentifierToCarbon(library.package_name(), out);
|
|
|
|
// Path is optional.
|
|
if (library.has_path()) {
|
|
out << " library ";
|
|
// library.path() is a string literal.
|
|
StringLiteralToCarbon(library.path(), out);
|
|
}
|
|
}
|
|
|
|
static auto PrefixUnaryOperatorToCarbon(std::string_view op,
|
|
const Fuzzing::Expression& arg,
|
|
llvm::raw_ostream& out) -> void {
|
|
out << op;
|
|
ExpressionToCarbon(arg, out);
|
|
}
|
|
|
|
static auto PostfixUnaryOperatorToCarbon(const Fuzzing::Expression& arg,
|
|
std::string_view op,
|
|
llvm::raw_ostream& out) -> void {
|
|
ExpressionToCarbon(arg, out);
|
|
out << op;
|
|
}
|
|
|
|
static auto BinaryOperatorToCarbon(const Fuzzing::Expression& lhs,
|
|
std::string_view op,
|
|
const Fuzzing::Expression& rhs,
|
|
llvm::raw_ostream& out) -> void {
|
|
ExpressionToCarbon(lhs, out);
|
|
out << op;
|
|
ExpressionToCarbon(rhs, out);
|
|
}
|
|
|
|
static auto OperatorToCarbon(const Fuzzing::OperatorExpression& operator_expr,
|
|
llvm::raw_ostream& out) -> void {
|
|
const Fuzzing::Expression& arg0 =
|
|
!operator_expr.arguments().empty()
|
|
? operator_expr.arguments(0)
|
|
: Fuzzing::Expression::default_instance();
|
|
const Fuzzing::Expression& arg1 =
|
|
operator_expr.arguments().size() > 1
|
|
? operator_expr.arguments(1)
|
|
: Fuzzing::Expression::default_instance();
|
|
out << "(";
|
|
switch (operator_expr.op()) {
|
|
case Fuzzing::OperatorExpression::UnknownOperator:
|
|
// `-` is an arbitrary default to avoid getting invalid syntax.
|
|
PrefixUnaryOperatorToCarbon("-", arg0, out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::AddressOf:
|
|
PrefixUnaryOperatorToCarbon("&", arg0, out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::As:
|
|
BinaryOperatorToCarbon(arg0, " as ", arg1, out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::Deref:
|
|
PrefixUnaryOperatorToCarbon("*", arg0, out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::Mul:
|
|
BinaryOperatorToCarbon(arg0, " * ", arg1, out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::Div:
|
|
BinaryOperatorToCarbon(arg0, " / ", arg1, out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::Mod:
|
|
BinaryOperatorToCarbon(arg0, " % ", arg1, out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::Ptr:
|
|
PostfixUnaryOperatorToCarbon(arg0, "*", out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::Neg:
|
|
PrefixUnaryOperatorToCarbon("-", arg0, out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::Sub:
|
|
BinaryOperatorToCarbon(arg0, " - ", arg1, out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::Not:
|
|
// Needs a space to 'unglue' from the operand.
|
|
PrefixUnaryOperatorToCarbon("not ", arg0, out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::Add:
|
|
BinaryOperatorToCarbon(arg0, " + ", arg1, out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::And:
|
|
BinaryOperatorToCarbon(arg0, " and ", arg1, out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::Eq:
|
|
BinaryOperatorToCarbon(arg0, " == ", arg1, out);
|
|
break;
|
|
case Fuzzing::OperatorExpression::Less:
|
|
BinaryOperatorToCarbon(arg0, " < ", arg1, out);
|
|
break;
|
|
case Fuzzing::OperatorExpression::LessEq:
|
|
BinaryOperatorToCarbon(arg0, " <= ", arg1, out);
|
|
break;
|
|
case Fuzzing::OperatorExpression::GreaterEq:
|
|
BinaryOperatorToCarbon(arg0, " >= ", arg1, out);
|
|
break;
|
|
case Fuzzing::OperatorExpression::Greater:
|
|
BinaryOperatorToCarbon(arg0, " > ", arg1, out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::Or:
|
|
BinaryOperatorToCarbon(arg0, " or ", arg1, out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::Complement:
|
|
PrefixUnaryOperatorToCarbon("^", arg0, out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::BitwiseAnd:
|
|
BinaryOperatorToCarbon(arg0, " & ", arg1, out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::BitwiseOr:
|
|
BinaryOperatorToCarbon(arg0, " | ", arg1, out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::BitwiseXor:
|
|
BinaryOperatorToCarbon(arg0, " ^ ", arg1, out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::BitShiftLeft:
|
|
BinaryOperatorToCarbon(arg0, " << ", arg1, out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::BitShiftRight:
|
|
BinaryOperatorToCarbon(arg0, " >> ", arg1, out);
|
|
break;
|
|
case Fuzzing::OperatorExpression::NotEq:
|
|
BinaryOperatorToCarbon(arg0, " != ", arg1, out);
|
|
break;
|
|
}
|
|
out << ")";
|
|
}
|
|
|
|
static auto FieldInitializerToCarbon(const Fuzzing::FieldInitializer& field,
|
|
std::string_view separator,
|
|
llvm::raw_ostream& out) -> void {
|
|
out << ".";
|
|
IdentifierToCarbon(field.name(), out);
|
|
out << " " << separator << " ";
|
|
ExpressionToCarbon(field.expression(), out);
|
|
}
|
|
|
|
static auto TupleLiteralExpressionToCarbon(
|
|
const Fuzzing::TupleLiteralExpression& tuple_literal,
|
|
llvm::raw_ostream& out) -> void {
|
|
out << "(";
|
|
llvm::ListSeparator sep;
|
|
for (const auto& field : tuple_literal.fields()) {
|
|
out << sep;
|
|
ExpressionToCarbon(field, out);
|
|
}
|
|
if (tuple_literal.fields_size() == 1) {
|
|
// Adding a trailing comma so that generated source will be parsed as a
|
|
// tuple expression. See docs/design/tuples.md.
|
|
out << ", ";
|
|
}
|
|
out << ")";
|
|
}
|
|
|
|
static auto ExpressionToCarbon(const Fuzzing::Expression& expression,
|
|
llvm::raw_ostream& out) -> void {
|
|
switch (expression.kind_case()) {
|
|
case Fuzzing::Expression::KIND_NOT_SET:
|
|
// Arbitrary default for missing expressions to avoid invalid syntax.
|
|
out << "true";
|
|
break;
|
|
|
|
case Fuzzing::Expression::kCall: {
|
|
const auto& call = expression.call();
|
|
ExpressionToCarbon(call.function(), out);
|
|
if (call.argument().kind_case() == Fuzzing::Expression::kTupleLiteral) {
|
|
TupleLiteralExpressionToCarbon(call.argument().tuple_literal(), out);
|
|
} else {
|
|
out << "(";
|
|
ExpressionToCarbon(call.argument(), out);
|
|
out << ")";
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Expression::kFunctionType: {
|
|
const auto& fun_type = expression.function_type();
|
|
out << "__Fn";
|
|
TupleLiteralExpressionToCarbon(fun_type.parameter(), out);
|
|
out << " -> ";
|
|
ExpressionToCarbon(fun_type.return_type(), out);
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Expression::kSimpleMemberAccess: {
|
|
const auto& simple_member_access = expression.simple_member_access();
|
|
ExpressionToCarbon(simple_member_access.object(), out);
|
|
out << ".";
|
|
IdentifierToCarbon(simple_member_access.field(), out);
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Expression::kCompoundMemberAccess: {
|
|
const auto& simple_member_access = expression.compound_member_access();
|
|
ExpressionToCarbon(simple_member_access.object(), out);
|
|
out << ".(";
|
|
ExpressionToCarbon(simple_member_access.path(), out);
|
|
out << ")";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Expression::kIndex: {
|
|
const auto& index = expression.index();
|
|
ExpressionToCarbon(index.object(), out);
|
|
out << "[";
|
|
ExpressionToCarbon(index.offset(), out);
|
|
out << "]";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Expression::kOperator:
|
|
OperatorToCarbon(expression.operator_(), out);
|
|
break;
|
|
|
|
case Fuzzing::Expression::kTupleLiteral: {
|
|
TupleLiteralExpressionToCarbon(expression.tuple_literal(), out);
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Expression::kStructLiteral: {
|
|
const auto& struct_literal = expression.struct_literal();
|
|
out << "{";
|
|
llvm::ListSeparator sep;
|
|
for (const auto& field : struct_literal.fields()) {
|
|
out << sep;
|
|
FieldInitializerToCarbon(field, "=", out);
|
|
}
|
|
out << "}";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Expression::kStructTypeLiteral: {
|
|
const auto& struct_type_literal = expression.struct_type_literal();
|
|
out << "{";
|
|
llvm::ListSeparator sep;
|
|
for (const auto& field : struct_type_literal.fields()) {
|
|
out << sep;
|
|
FieldInitializerToCarbon(field, ":", out);
|
|
}
|
|
out << "}";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Expression::kIdentifier: {
|
|
const auto& identifier = expression.identifier();
|
|
IdentifierToCarbon(identifier.name(), out);
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Expression::kDesignator: {
|
|
const auto& designator = expression.designator();
|
|
out << ".";
|
|
IdentifierToCarbon(designator.name(), out);
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Expression::kIfExpression: {
|
|
const auto& if_expression = expression.if_expression();
|
|
out << "if ";
|
|
ExpressionToCarbon(if_expression.condition(), out);
|
|
out << " then ";
|
|
ExpressionToCarbon(if_expression.then_expression(), out);
|
|
out << " else ";
|
|
ExpressionToCarbon(if_expression.else_expression(), out);
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Expression::kBoolTypeLiteral:
|
|
out << "bool";
|
|
break;
|
|
|
|
case Fuzzing::Expression::kBoolLiteral: {
|
|
const auto& bool_literal = expression.bool_literal();
|
|
out << (bool_literal.value() ? "true" : "false");
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Expression::kIntTypeLiteral:
|
|
out << "i32";
|
|
break;
|
|
|
|
case Fuzzing::Expression::kIntLiteral: {
|
|
out << expression.int_literal().value();
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Expression::kStringLiteral:
|
|
StringLiteralToCarbon(expression.string_literal().value(), out);
|
|
break;
|
|
|
|
case Fuzzing::Expression::kStringTypeLiteral:
|
|
out << "String";
|
|
break;
|
|
|
|
case Fuzzing::Expression::kTypeTypeLiteral:
|
|
out << "type";
|
|
break;
|
|
|
|
case Fuzzing::Expression::kUnimplementedExpression:
|
|
// Not really supported.
|
|
// This is an arbitrary default to avoid getting invalid syntax.
|
|
out << "1 __unimplemented_example_infix 2";
|
|
break;
|
|
|
|
case Fuzzing::Expression::kArrayTypeLiteral: {
|
|
const Fuzzing::ArrayTypeLiteral& array_literal =
|
|
expression.array_type_literal();
|
|
out << "[";
|
|
ExpressionToCarbon(array_literal.element_type(), out);
|
|
out << ";";
|
|
if (array_literal.has_size()) {
|
|
out << " ";
|
|
ExpressionToCarbon(array_literal.size(), out);
|
|
}
|
|
out << "]";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Expression::kWhere: {
|
|
const Fuzzing::WhereExpression& where = expression.where();
|
|
ExpressionToCarbon(where.base(), out);
|
|
out << " where ";
|
|
llvm::ListSeparator sep(" and ");
|
|
for (const auto& clause : where.clauses()) {
|
|
out << sep;
|
|
switch (clause.kind_case()) {
|
|
case Fuzzing::WhereClause::kImpls:
|
|
ExpressionToCarbon(clause.impls().type(), out);
|
|
out << " impls ";
|
|
ExpressionToCarbon(clause.impls().constraint(), out);
|
|
break;
|
|
case Fuzzing::WhereClause::kEquals:
|
|
ExpressionToCarbon(clause.equals().lhs(), out);
|
|
out << " == ";
|
|
ExpressionToCarbon(clause.equals().rhs(), out);
|
|
break;
|
|
case Fuzzing::WhereClause::kRewrite:
|
|
out << "." << clause.rewrite().member_name() << " = ";
|
|
ExpressionToCarbon(clause.rewrite().replacement(), out);
|
|
break;
|
|
case Fuzzing::WhereClause::KIND_NOT_SET:
|
|
// Arbitrary default to avoid invalid syntax.
|
|
out << ".Self == .Self";
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static auto BindingPatternToCarbon(const Fuzzing::BindingPattern& pattern,
|
|
llvm::raw_ostream& out) -> void {
|
|
IdentifierToCarbon(pattern.name(), out);
|
|
out << ": ";
|
|
PatternToCarbon(pattern.type(), out);
|
|
}
|
|
|
|
static auto GenericBindingToCarbon(
|
|
const Fuzzing::GenericBinding& generic_binding, llvm::raw_ostream& out) {
|
|
switch (generic_binding.kind()) {
|
|
case Fuzzing::GenericBinding::Checked:
|
|
break;
|
|
case Fuzzing::GenericBinding::Template:
|
|
out << "template ";
|
|
break;
|
|
}
|
|
IdentifierToCarbon(generic_binding.name(), out);
|
|
out << ":! ";
|
|
ExpressionToCarbon(generic_binding.type(), out);
|
|
}
|
|
|
|
static auto TuplePatternToCarbon(const Fuzzing::TuplePattern& tuple_pattern,
|
|
llvm::raw_ostream& out) -> void {
|
|
out << "(";
|
|
llvm::ListSeparator sep;
|
|
for (const auto& field : tuple_pattern.fields()) {
|
|
out << sep;
|
|
PatternToCarbon(field, out);
|
|
}
|
|
if (tuple_pattern.fields_size() == 1) {
|
|
// Adding a trailing comma so that generated source will be parsed as a
|
|
// tuple pattern expression. See docs/design/tuples.md.
|
|
out << ", ";
|
|
}
|
|
out << ")";
|
|
}
|
|
|
|
static auto PatternToCarbon(const Fuzzing::Pattern& pattern,
|
|
llvm::raw_ostream& out) -> void {
|
|
switch (pattern.kind_case()) {
|
|
case Fuzzing::Pattern::KIND_NOT_SET:
|
|
// Arbitrary default to avoid getting invalid syntax.
|
|
out << "auto";
|
|
break;
|
|
|
|
case Fuzzing::Pattern::kBindingPattern:
|
|
BindingPatternToCarbon(pattern.binding_pattern(), out);
|
|
break;
|
|
|
|
case Fuzzing::Pattern::kTuplePattern:
|
|
TuplePatternToCarbon(pattern.tuple_pattern(), out);
|
|
break;
|
|
|
|
case Fuzzing::Pattern::kAlternativePattern: {
|
|
const auto& alternative_pattern = pattern.alternative_pattern();
|
|
ExpressionToCarbon(alternative_pattern.choice_type(), out);
|
|
out << ".";
|
|
IdentifierToCarbon(alternative_pattern.alternative_name(), out);
|
|
TuplePatternToCarbon(alternative_pattern.arguments(), out);
|
|
break;
|
|
}
|
|
|
|
// Arbitrary expression.
|
|
case Fuzzing::Pattern::kExpressionPattern: {
|
|
const auto& expression_pattern = pattern.expression_pattern();
|
|
ExpressionToCarbon(expression_pattern.expression(), out);
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Pattern::kAutoPattern:
|
|
out << "auto";
|
|
break;
|
|
|
|
case Fuzzing::Pattern::kVarPattern:
|
|
out << "var ";
|
|
PatternToCarbon(pattern.var_pattern().pattern(), out);
|
|
break;
|
|
|
|
case Fuzzing::Pattern::kGenericBinding:
|
|
GenericBindingToCarbon(pattern.generic_binding(), out);
|
|
break;
|
|
|
|
case Fuzzing::Pattern::kAddrPattern:
|
|
out << "addr ";
|
|
BindingPatternToCarbon(pattern.addr_pattern().binding_pattern(), out);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static auto BlockStatementToCarbon(const Fuzzing::BlockStatement& block,
|
|
llvm::raw_ostream& out) -> void {
|
|
out << "{\n";
|
|
for (const auto& statement : block.statements()) {
|
|
StatementToCarbon(statement, out);
|
|
out << "\n";
|
|
}
|
|
out << "}\n";
|
|
}
|
|
|
|
static auto StatementToCarbon(const Fuzzing::Statement& statement,
|
|
llvm::raw_ostream& out) -> void {
|
|
switch (statement.kind_case()) {
|
|
case Fuzzing::Statement::KIND_NOT_SET:
|
|
// Arbitrary default to avoid getting invalid syntax.
|
|
out << "true;\n";
|
|
break;
|
|
|
|
case Fuzzing::Statement::kExpressionStatement: {
|
|
const auto& expression_statement = statement.expression_statement();
|
|
ExpressionToCarbon(expression_statement.expression(), out);
|
|
out << ";";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Statement::kAssign: {
|
|
const auto& assign_statement = statement.assign();
|
|
ExpressionToCarbon(assign_statement.lhs(), out);
|
|
switch (assign_statement.op()) {
|
|
case Fuzzing::AssignStatement::Plain:
|
|
out << " = ";
|
|
break;
|
|
case Fuzzing::AssignStatement::Add:
|
|
out << " += ";
|
|
break;
|
|
case Fuzzing::AssignStatement::And:
|
|
out << " &= ";
|
|
break;
|
|
case Fuzzing::AssignStatement::Div:
|
|
out << " /= ";
|
|
break;
|
|
case Fuzzing::AssignStatement::Mod:
|
|
out << " %= ";
|
|
break;
|
|
case Fuzzing::AssignStatement::Mul:
|
|
out << " *= ";
|
|
break;
|
|
case Fuzzing::AssignStatement::Or:
|
|
out << " |= ";
|
|
break;
|
|
case Fuzzing::AssignStatement::ShiftLeft:
|
|
out << " <<= ";
|
|
break;
|
|
case Fuzzing::AssignStatement::ShiftRight:
|
|
out << " >>= ";
|
|
break;
|
|
case Fuzzing::AssignStatement::Sub:
|
|
out << " -= ";
|
|
break;
|
|
case Fuzzing::AssignStatement::Xor:
|
|
out << " ^= ";
|
|
break;
|
|
}
|
|
ExpressionToCarbon(assign_statement.rhs(), out);
|
|
out << ";";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Statement::kIncDec: {
|
|
const auto& inc_dec_statement = statement.inc_dec();
|
|
out << (inc_dec_statement.is_increment() ? "++" : "--");
|
|
ExpressionToCarbon(inc_dec_statement.operand(), out);
|
|
out << ";";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Statement::kVariableDefinition: {
|
|
const auto& def = statement.variable_definition();
|
|
if (def.is_returned()) {
|
|
out << "returned ";
|
|
}
|
|
out << "var ";
|
|
PatternToCarbon(def.pattern(), out);
|
|
if (def.has_init()) {
|
|
out << " = ";
|
|
ExpressionToCarbon(def.init(), out);
|
|
}
|
|
out << ";";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Statement::kIfStatement: {
|
|
const auto& if_statement = statement.if_statement();
|
|
out << "if (";
|
|
ExpressionToCarbon(if_statement.condition(), out);
|
|
out << ") ";
|
|
BlockStatementToCarbon(if_statement.then_block(), out);
|
|
// `else` is optional.
|
|
if (if_statement.has_else_block()) {
|
|
out << " else ";
|
|
BlockStatementToCarbon(if_statement.else_block(), out);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Statement::kReturnVarStatement: {
|
|
out << "return var;";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Statement::kReturnExpressionStatement: {
|
|
const auto& ret = statement.return_expression_statement();
|
|
out << "return";
|
|
if (!ret.is_omitted_expression()) {
|
|
out << " ";
|
|
ExpressionToCarbon(ret.expression(), out);
|
|
}
|
|
out << ";";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Statement::kBlock:
|
|
BlockStatementToCarbon(statement.block(), out);
|
|
break;
|
|
|
|
case Fuzzing::Statement::kWhileStatement: {
|
|
const auto& while_statement = statement.while_statement();
|
|
out << "while (";
|
|
ExpressionToCarbon(while_statement.condition(), out);
|
|
out << ") ";
|
|
BlockStatementToCarbon(while_statement.body(), out);
|
|
break;
|
|
}
|
|
case Fuzzing::Statement::kForStatement: {
|
|
const auto& for_statement = statement.for_statement();
|
|
out << "for (";
|
|
BindingPatternToCarbon(for_statement.var_decl(), out);
|
|
out << " in ";
|
|
ExpressionToCarbon(for_statement.target(), out);
|
|
out << ") ";
|
|
BlockStatementToCarbon(for_statement.body(), out);
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Statement::kMatch: {
|
|
const auto& match = statement.match();
|
|
out << "match (";
|
|
ExpressionToCarbon(match.expression(), out);
|
|
out << ") {";
|
|
for (const auto& clause : match.clauses()) {
|
|
if (clause.is_default()) {
|
|
out << "default";
|
|
} else {
|
|
out << "case ";
|
|
PatternToCarbon(clause.pattern(), out);
|
|
}
|
|
out << " => ";
|
|
StatementToCarbon(clause.statement(), out);
|
|
}
|
|
out << "}";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Statement::kBreakStatement:
|
|
out << "break;";
|
|
break;
|
|
|
|
case Fuzzing::Statement::kContinueStatement:
|
|
out << "continue;";
|
|
break;
|
|
}
|
|
}
|
|
|
|
static auto ReturnTermToCarbon(const Fuzzing::ReturnTerm& return_term,
|
|
llvm::raw_ostream& out) -> void {
|
|
switch (return_term.kind()) {
|
|
case Fuzzing::ReturnTerm::UnknownReturnKind:
|
|
case Fuzzing::ReturnTerm::Omitted:
|
|
break;
|
|
case Fuzzing::ReturnTerm::Auto:
|
|
out << " -> auto";
|
|
break;
|
|
case Fuzzing::ReturnTerm::Expression:
|
|
out << " -> ";
|
|
ExpressionToCarbon(return_term.type(), out);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static auto DeclaredNameToCarbon(const Fuzzing::DeclaredName& name,
|
|
llvm::raw_ostream& out) -> void {
|
|
for (const std::string& qual : name.qualifiers()) {
|
|
IdentifierToCarbon(qual, out);
|
|
out << ".";
|
|
}
|
|
IdentifierToCarbon(name.name(), out);
|
|
}
|
|
|
|
static auto DeclarationToCarbon(const Fuzzing::Declaration& declaration,
|
|
llvm::raw_ostream& out) -> void {
|
|
switch (declaration.kind_case()) {
|
|
case Fuzzing::Declaration::KIND_NOT_SET: {
|
|
// Arbitrary default to avoid getting invalid syntax.
|
|
out << "var x: i32;";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Declaration::kNamespace: {
|
|
out << "namespace ";
|
|
DeclaredNameToCarbon(declaration.namespace_().name(), out);
|
|
out << ";";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Declaration::kDestructor: {
|
|
const auto& function = declaration.destructor();
|
|
out << "destructor";
|
|
llvm::ListSeparator sep;
|
|
out << "[";
|
|
if (function.has_self_pattern()) {
|
|
// This is a class method.
|
|
out << sep;
|
|
PatternToCarbon(function.self_pattern(), out);
|
|
}
|
|
out << "]";
|
|
|
|
// Body is optional.
|
|
if (function.has_body()) {
|
|
out << "\n";
|
|
BlockStatementToCarbon(function.body(), out);
|
|
} else {
|
|
out << ";";
|
|
}
|
|
break;
|
|
}
|
|
case Fuzzing::Declaration::kFunction: {
|
|
const auto& function = declaration.function();
|
|
out << "fn ";
|
|
DeclaredNameToCarbon(function.name(), out);
|
|
|
|
if (!function.deduced_parameters().empty() ||
|
|
function.has_self_pattern()) {
|
|
out << "[";
|
|
llvm::ListSeparator sep;
|
|
for (const Fuzzing::GenericBinding& p : function.deduced_parameters()) {
|
|
out << sep;
|
|
GenericBindingToCarbon(p, out);
|
|
}
|
|
if (function.has_self_pattern()) {
|
|
// This is a class method.
|
|
out << sep;
|
|
PatternToCarbon(function.self_pattern(), out);
|
|
}
|
|
out << "]";
|
|
}
|
|
TuplePatternToCarbon(function.param_pattern(), out);
|
|
ReturnTermToCarbon(function.return_term(), out);
|
|
|
|
// Body is optional.
|
|
if (function.has_body()) {
|
|
out << "\n";
|
|
BlockStatementToCarbon(function.body(), out);
|
|
} else {
|
|
out << ";";
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Declaration::kClassDeclaration: {
|
|
const auto& class_declaration = declaration.class_declaration();
|
|
out << "class ";
|
|
DeclaredNameToCarbon(class_declaration.name(), out);
|
|
|
|
// type_params is optional.
|
|
if (class_declaration.has_type_params()) {
|
|
TuplePatternToCarbon(class_declaration.type_params(), out);
|
|
}
|
|
|
|
out << "{\n";
|
|
for (const auto& member : class_declaration.members()) {
|
|
DeclarationToCarbon(member, out);
|
|
out << "\n";
|
|
}
|
|
out << "}";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Declaration::kExtendBase: {
|
|
const auto& extend_base_declaration = declaration.extend_base();
|
|
out << " extend base: ";
|
|
ExpressionToCarbon(extend_base_declaration.base_class(), out);
|
|
out << ";\n";
|
|
break;
|
|
}
|
|
|
|
// EXPERIMENTAL MIXIN FEATURE
|
|
case Fuzzing::Declaration::kMixin: {
|
|
const auto& mixin_declaration = declaration.mixin();
|
|
out << "__mixin ";
|
|
DeclaredNameToCarbon(mixin_declaration.name(), out);
|
|
|
|
// type params are not implemented yet
|
|
// if (mixin_declaration.has_params()) {
|
|
// TuplePatternToCarbon(mixin_declaration.params(), out);
|
|
//}
|
|
|
|
out << "{\n";
|
|
for (const auto& member : mixin_declaration.members()) {
|
|
DeclarationToCarbon(member, out);
|
|
out << "\n";
|
|
}
|
|
out << "}";
|
|
// TODO: need to handle interface.self()?
|
|
break;
|
|
}
|
|
|
|
// EXPERIMENTAL MIXIN FEATURE
|
|
case Fuzzing::Declaration::kMix: {
|
|
const auto& mix_declaration = declaration.mix();
|
|
out << "__mix ";
|
|
ExpressionToCarbon(mix_declaration.mixin(), out);
|
|
out << ";";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Declaration::kChoice: {
|
|
const auto& choice = declaration.choice();
|
|
out << "choice ";
|
|
DeclaredNameToCarbon(choice.name(), out);
|
|
|
|
out << "{";
|
|
llvm::ListSeparator sep;
|
|
for (const auto& alternative : choice.alternatives()) {
|
|
out << sep;
|
|
IdentifierToCarbon(alternative.name(), out);
|
|
if (alternative.has_signature()) {
|
|
TupleLiteralExpressionToCarbon(alternative.signature(), out);
|
|
}
|
|
}
|
|
out << "}";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Declaration::kVariable: {
|
|
const auto& var = declaration.variable();
|
|
out << "var ";
|
|
BindingPatternToCarbon(var.binding(), out);
|
|
|
|
// Initializer is optional.
|
|
if (var.has_initializer()) {
|
|
out << " = ";
|
|
ExpressionToCarbon(var.initializer(), out);
|
|
}
|
|
out << ";";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Declaration::kLet: {
|
|
const auto& let = declaration.let();
|
|
out << "let ";
|
|
PatternToCarbon(let.pattern(), out);
|
|
|
|
// TODO: Print out the initializer once it's supported.
|
|
// if (let.has_initializer()) {
|
|
// out << " = ";
|
|
// ExpressionToCarbon(let.initializer(), out);
|
|
// }
|
|
out << ";";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Declaration::kInterfaceExtend: {
|
|
const auto& extend = declaration.interface_extend();
|
|
out << "extend ";
|
|
ExpressionToCarbon(extend.base(), out);
|
|
out << ";";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Declaration::kInterfaceRequire: {
|
|
const auto& impl = declaration.interface_require();
|
|
out << "require ";
|
|
ExpressionToCarbon(impl.impl_type(), out);
|
|
out << " impls ";
|
|
ExpressionToCarbon(impl.constraint(), out);
|
|
out << ";";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Declaration::kInterface: {
|
|
const auto& interface = declaration.interface();
|
|
out << "interface ";
|
|
DeclaredNameToCarbon(interface.name(), out);
|
|
out << " {\n";
|
|
for (const auto& member : interface.members()) {
|
|
DeclarationToCarbon(member, out);
|
|
out << "\n";
|
|
}
|
|
out << "}";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Declaration::kConstraint: {
|
|
const auto& constraint = declaration.constraint();
|
|
out << "constraint ";
|
|
DeclaredNameToCarbon(constraint.name(), out);
|
|
out << " {\n";
|
|
for (const auto& member : constraint.members()) {
|
|
DeclarationToCarbon(member, out);
|
|
out << "\n";
|
|
}
|
|
out << "}";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Declaration::kImpl: {
|
|
const auto& impl = declaration.impl();
|
|
if (impl.kind() == Fuzzing::ImplDeclaration::InternalImpl) {
|
|
out << "extend ";
|
|
}
|
|
out << "impl ";
|
|
if (!impl.deduced_parameters().empty()) {
|
|
out << "forall [";
|
|
llvm::ListSeparator sep;
|
|
for (const Fuzzing::GenericBinding& p : impl.deduced_parameters()) {
|
|
out << sep;
|
|
GenericBindingToCarbon(p, out);
|
|
}
|
|
out << "] ";
|
|
}
|
|
if (impl.kind() != Fuzzing::ImplDeclaration::InternalImpl) {
|
|
ExpressionToCarbon(impl.impl_type(), out);
|
|
out << " ";
|
|
}
|
|
out << "as ";
|
|
ExpressionToCarbon(impl.interface(), out);
|
|
out << " {\n";
|
|
for (const auto& member : impl.members()) {
|
|
DeclarationToCarbon(member, out);
|
|
out << "\n";
|
|
}
|
|
out << "}";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Declaration::kMatchFirst: {
|
|
const auto& match_first = declaration.match_first();
|
|
out << "__match_first {\n";
|
|
for (const auto& impl : match_first.impl_declarations()) {
|
|
DeclarationToCarbon(impl, out);
|
|
out << "\n";
|
|
}
|
|
out << "}";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Declaration::kAlias: {
|
|
const auto& alias = declaration.alias();
|
|
out << "alias ";
|
|
DeclaredNameToCarbon(alias.name(), out);
|
|
out << " = ";
|
|
ExpressionToCarbon(alias.target(), out);
|
|
out << ";";
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
auto ProtoToCarbon(const Fuzzing::Carbon& proto, bool maybe_add_main)
|
|
-> std::string {
|
|
std::string source;
|
|
llvm::raw_string_ostream out(source);
|
|
|
|
out << "// Generated by proto_to_carbon.\n"
|
|
"package ";
|
|
const auto& unit = proto.compilation_unit();
|
|
LibraryNameToCarbon(unit.package_statement(), out);
|
|
out << (unit.is_api() ? " api" : " impl") << ";\n\n";
|
|
|
|
bool has_main = false;
|
|
if (!unit.declarations().empty()) {
|
|
for (const auto& declaration : unit.declarations()) {
|
|
DeclarationToCarbon(declaration, out);
|
|
if (declaration.kind_case() == Fuzzing::Declaration::kFunction &&
|
|
declaration.function().name().name() == "Main") {
|
|
has_main = true;
|
|
}
|
|
out << "\n";
|
|
}
|
|
}
|
|
|
|
if (maybe_add_main && !has_main) {
|
|
out << "fn Main() -> i32 { return 0; }\n";
|
|
}
|
|
|
|
return source;
|
|
}
|
|
|
|
auto ParseCarbonTextProto(const std::string& contents)
|
|
-> ErrorOr<Fuzzing::Carbon> {
|
|
google::protobuf::TextFormat::Parser parser;
|
|
Fuzzing::Carbon carbon_proto;
|
|
if (!parser.ParseFromString(contents, &carbon_proto)) {
|
|
return ErrorBuilder() << "Couldn't parse Carbon text proto";
|
|
}
|
|
return carbon_proto;
|
|
}
|
|
|
|
} // namespace Carbon
|
|
|
|
// NOLINTEND(misc-no-recursion)
|