// 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 #include #include "llvm/ADT/StringExtras.h" #include "llvm/Support/raw_ostream.h" #include "testing/fuzzing/carbon.pb.h" 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 { 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