Parsing support for if expressions. (#2883)

We model `if a then b else` as a prefix operator for parsing precedence purposes. The rule that a statement starting with `if` is never an `if` expression is handled implicitly because the statement parser never invokes the expression parser for a statement starting with `if`.

This exposed a bug in our diagnosis of the whitespace rule for prefix operators, which was incorrectly being applied to non-symbolic operators in some cases, and was producing a bogus second diagnostic in some cases, which is also fixed here.
This commit is contained in:
Richard Smith
2023-06-09 16:41:10 -07:00
committed by GitHub
parent 4e1adcf4c7
commit 2c45cb3be8
18 changed files with 400 additions and 22 deletions
@@ -79,6 +79,10 @@ CARBON_DIAGNOSTIC_KIND(ExpectedSemiToEndPackageDirective)
CARBON_DIAGNOSTIC_KIND(ExpectedIn)
CARBON_DIAGNOSTIC_KIND(ExpectedInNotColon)
// If-specific diagnostics.
CARBON_DIAGNOSTIC_KIND(ExpectedThenAfterIf)
CARBON_DIAGNOSTIC_KIND(ExpectedElseAfterIf)
// Declaration diagnostics.
CARBON_DIAGNOSTIC_KIND(ExpectedDeclarationName)
CARBON_DIAGNOSTIC_KIND(ExpectedDeclarationSemiOrDefinition)
+12 -1
View File
@@ -173,7 +173,7 @@ CARBON_PARSE_NODE_KIND_BRACKET(ForIn, VariableIntroducer)
CARBON_PARSE_NODE_KIND_BRACKET(ForHeader, ForHeaderStart)
CARBON_PARSE_NODE_KIND_CHILD_COUNT(ForStatement, 2)
// `if` + `else`:
// `if` statement + `else`:
// IfConditionStart
// _external_: expression
// IfCondition
@@ -259,6 +259,17 @@ CARBON_PARSE_NODE_KIND_CHILD_COUNT(InfixOperator, 2)
// PostfixOperator
CARBON_PARSE_NODE_KIND_CHILD_COUNT(PostfixOperator, 1)
// `if` expression + `then` + `else`:
// _external_: expression
// IfExpressionThen
// _external_: expression
// IfExpressionElse
// _external_: expression
// IfExpression
CARBON_PARSE_NODE_KIND_CHILD_COUNT(IfExpression, 5)
CARBON_PARSE_NODE_KIND_CHILD_COUNT(IfExpressionThen, 0)
CARBON_PARSE_NODE_KIND_CHILD_COUNT(IfExpressionElse, 0)
// Struct literals, such as `{.a = 0}`:
// StructLiteralOrStructTypeLiteralStart
// _external_: DesignatedName
+8 -5
View File
@@ -313,6 +313,11 @@ auto ParserContext::IsTrailingOperatorInfix() -> bool {
}
auto ParserContext::DiagnoseOperatorFixity(OperatorFixity fixity) -> void {
if (!PositionKind().is_symbol()) {
// Whitespace-based fixity rules only apply to symbolic operators.
return;
}
if (fixity == OperatorFixity::Infix) {
// Infix operators must satisfy the infix operator rules.
if (!IsLexicallyValidInfixOperator()) {
@@ -331,8 +336,7 @@ auto ParserContext::DiagnoseOperatorFixity(OperatorFixity fixity) -> void {
// Whitespace is not permitted between a symbolic pre/postfix operator and
// its operand.
if (PositionKind().is_symbol() &&
(prefix ? tokens().HasTrailingWhitespace(*position_)
if ((prefix ? tokens().HasTrailingWhitespace(*position_)
: tokens().HasLeadingWhitespace(*position_))) {
CARBON_DIAGNOSTIC(UnaryOperatorHasWhitespace, Error,
"Whitespace is not allowed {0} this unary operator.",
@@ -340,9 +344,8 @@ auto ParserContext::DiagnoseOperatorFixity(OperatorFixity fixity) -> void {
emitter_->Emit(
*position_, UnaryOperatorHasWhitespace,
prefix ? RelativeLocation::After : RelativeLocation::Before);
}
// Pre/postfix operators must not satisfy the infix operator rules.
if (IsLexicallyValidInfixOperator()) {
} else if (IsLexicallyValidInfixOperator()) {
// Pre/postfix operators must not satisfy the infix operator rules.
CARBON_DIAGNOSTIC(UnaryOperatorRequiresWhitespace, Error,
"Whitespace is required {0} this unary operator.",
RelativeLocation);
+61 -3
View File
@@ -27,9 +27,15 @@ auto ParserHandleExpression(ParserContext& context) -> void {
context.DiagnoseOperatorFixity(ParserContext::OperatorFixity::Prefix);
}
context.PushStateForExpressionLoop(ParserState::ExpressionLoopForPrefix,
state.ambient_precedence,
*operator_precedence);
if (context.PositionIs(TokenKind::If)) {
context.PushState(ParserState::ExpressionIfFinish);
context.PushState(ParserState::ExpressionThen);
} else {
context.PushStateForExpressionLoop(ParserState::ExpressionLoopForPrefix,
state.ambient_precedence,
*operator_precedence);
}
++context.position();
context.PushStateForExpression(*operator_precedence);
} else {
@@ -203,6 +209,58 @@ auto ParserHandleExpressionLoopForPrefix(ParserContext& context) -> void {
context.PushState(state);
}
auto ParserHandleExpressionThen(ParserContext& context) -> void {
auto state = context.PopState();
if (context.ConsumeAndAddLeafNodeIf(TokenKind::Then,
ParseNodeKind::IfExpressionThen)) {
context.PushState(ParserState::ExpressionElse);
context.PushStateForExpression(*PrecedenceGroup::ForLeading(TokenKind::If));
} else {
// TODO: Include the location of the `if` token.
CARBON_DIAGNOSTIC(ExpectedThenAfterIf, Error,
"Expected `then` after `if` condition.");
if (!state.has_error) {
context.emitter().Emit(*context.position(), ExpectedThenAfterIf);
}
// Add placeholders for `then expression else expression`.
for (int i = 0; i != 4; ++i) {
context.AddLeafNode(ParseNodeKind::InvalidParse, *context.position(),
/*has_error=*/true);
}
context.ReturnErrorOnState();
}
}
auto ParserHandleExpressionElse(ParserContext& context) -> void {
auto state = context.PopState();
if (context.ConsumeAndAddLeafNodeIf(TokenKind::Else,
ParseNodeKind::IfExpressionElse)) {
context.PushStateForExpression(*PrecedenceGroup::ForLeading(TokenKind::If));
} else {
// TODO: Include the location of the `if` token.
CARBON_DIAGNOSTIC(ExpectedElseAfterIf, Error,
"Expected `else` after `if ... then ...`.");
if (!state.has_error) {
context.emitter().Emit(*context.position(), ExpectedElseAfterIf);
}
// Add placeholders for `else expression`.
for (int i = 0; i != 2; ++i) {
context.AddLeafNode(ParseNodeKind::InvalidParse, *context.position(),
/*has_error=*/true);
}
context.ReturnErrorOnState();
}
}
auto ParserHandleExpressionIfFinish(ParserContext& context) -> void {
auto state = context.PopState();
context.AddNode(ParseNodeKind::IfExpression, state.token, state.subtree_start,
state.has_error);
}
auto ParserHandleExpressionStatementFinish(ParserContext& context) -> void {
auto state = context.PopState();
+28 -1
View File
@@ -181,7 +181,11 @@ CARBON_PARSER_STATE_VARIANTS2(Designator, Expression, Struct)
// Handles processing of an expression.
//
// If valid prefix operator:
// If `If`:
// 1. Expression
// 2. ExpressionThen
// 3. ExpressionIfFinish
// Else if valid prefix operator:
// 1. Expression
// 2. ExpressionLoopForPrefix
// Else:
@@ -242,6 +246,29 @@ CARBON_PARSER_STATE(ExpressionLoopForBinary)
// 1. ExpressionLoop
CARBON_PARSER_STATE(ExpressionLoopForPrefix)
// Completes an IfExpression.
//
// Always:
// (state done)
CARBON_PARSER_STATE(ExpressionIfFinish)
// Handles the `then` token in an `if` expression.
//
// If `Then`:
// 1. Expression
// 2. ExpressionElse
// Else:
// (state done)
CARBON_PARSER_STATE(ExpressionThen)
// Handles the `else` token in an `if` expression.
//
// If `Else`:
// 1. Expression
// Else:
// (state done)
CARBON_PARSER_STATE(ExpressionElse)
// Handles the `;` for an expression statement, which is different from most
// keyword statements.
//
+10 -5
View File
@@ -37,6 +37,8 @@ enum PrecedenceLevel : int8_t {
Relational,
LogicalAnd,
LogicalOr,
// Conditional.
If,
// Assignment.
SimpleAssignment,
CompoundAssignment,
@@ -62,11 +64,11 @@ struct OperatorPriorityTable {
MarkHigherThan({TypePostfix}, {Type});
MarkHigherThan(
{Modulo, Additive, BitwiseAnd, BitwiseOr, BitwiseXor, BitShift, Type},
{SimpleAssignment, CompoundAssignment, Relational});
{Relational});
MarkHigherThan({Relational, LogicalPrefix}, {LogicalAnd, LogicalOr});
MarkHigherThan(
{SimpleAssignment, CompoundAssignment, LogicalAnd, LogicalOr},
{Lowest});
MarkHigherThan({LogicalAnd, LogicalOr}, {If});
MarkHigherThan({If}, {SimpleAssignment, CompoundAssignment});
MarkHigherThan({SimpleAssignment, CompoundAssignment}, {Lowest});
// Compute the transitive closure of the above relationships: if we parse
// `a $ b @ c` as `(a $ b) @ c` and parse `b @ c % d` as `(b @ c) % d`,
@@ -137,7 +139,7 @@ struct OperatorPriorityTable {
// Ambiguous would mean it's an error. LeftFirst is meaningless. For now we
// allow all prefix operators to be repeated.
for (PrecedenceLevel prefix :
{TermPrefix, NumericPrefix, BitwisePrefix, LogicalPrefix}) {
{TermPrefix, NumericPrefix, BitwisePrefix, LogicalPrefix, If}) {
table[prefix][prefix] = OperatorPriority::RightFirst;
}
@@ -210,6 +212,9 @@ auto PrecedenceGroup::ForLeading(TokenKind kind)
case TokenKind::Tilde:
return PrecedenceGroup(BitwisePrefix);
case TokenKind::If:
return PrecedenceGroup(If);
default:
return std::nullopt;
}
+39
View File
@@ -0,0 +1,39 @@
// 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
//
// AUTOUPDATE
// CHECK:STDOUT: [
// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'},
// CHECK:STDOUT: {kind: 'DeclaredName', text: 'F'},
// CHECK:STDOUT: {kind: 'ParameterListStart', text: '('},
// CHECK:STDOUT: {kind: 'DeclaredName', text: 'b'},
// CHECK:STDOUT: {kind: 'NameReference', text: 'bool'},
// CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 3},
// CHECK:STDOUT: {kind: 'ParameterListComma', text: ','},
// CHECK:STDOUT: {kind: 'DeclaredName', text: 'x'},
// CHECK:STDOUT: {kind: 'Literal', text: 'i32'},
// CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 3},
// CHECK:STDOUT: {kind: 'ParameterListComma', text: ','},
// CHECK:STDOUT: {kind: 'DeclaredName', text: 'y'},
// CHECK:STDOUT: {kind: 'Literal', text: 'i32'},
// CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 3},
// CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 13},
// CHECK:STDOUT: {kind: 'NameReference', text: 'bool'},
// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 2},
// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 18},
// CHECK:STDOUT: {kind: 'ReturnStatementStart', text: 'return'},
// CHECK:STDOUT: {kind: 'NameReference', text: 'b'},
// CHECK:STDOUT: {kind: 'IfExpressionThen', text: 'then'},
// CHECK:STDOUT: {kind: 'NameReference', text: 'x'},
// CHECK:STDOUT: {kind: 'IfExpressionElse', text: 'else'},
// CHECK:STDOUT: {kind: 'NameReference', text: 'y'},
// CHECK:STDOUT: {kind: 'IfExpression', text: 'if', subtree_size: 6},
// CHECK:STDOUT: {kind: 'ReturnStatement', text: ';', subtree_size: 8},
// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 27},
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
// CHECK:STDOUT: ]
fn F(b: bool, x: i32, y: i32) -> bool {
return if b then x else y;
}
@@ -0,0 +1,31 @@
// 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
//
// AUTOUPDATE
// CHECK:STDOUT: [
// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'},
// CHECK:STDOUT: {kind: 'DeclaredName', text: 'F'},
// CHECK:STDOUT: {kind: 'ParameterListStart', text: '('},
// CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 2},
// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'},
// CHECK:STDOUT: {kind: 'DeclaredName', text: 'n'},
// CHECK:STDOUT: {kind: 'Literal', text: 'i32'},
// CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 3},
// CHECK:STDOUT: {kind: 'VariableInitializer', text: '='},
// CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes},
// CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes},
// CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes},
// CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes},
// CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes},
// CHECK:STDOUT: {kind: 'IfExpression', text: 'if', has_error: yes, subtree_size: 6},
// CHECK:STDOUT: {kind: 'VariableDeclaration', text: ';', subtree_size: 12},
// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 18},
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
// CHECK:STDOUT: ]
fn F() {
// CHECK:STDERR: fail_condition_missing.carbon:[[@LINE+1]]:18: Expected expression.
var n: i32 = if;
}
@@ -0,0 +1,31 @@
// 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
//
// AUTOUPDATE
// CHECK:STDOUT: [
// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'},
// CHECK:STDOUT: {kind: 'DeclaredName', text: 'F'},
// CHECK:STDOUT: {kind: 'ParameterListStart', text: '('},
// CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 2},
// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'},
// CHECK:STDOUT: {kind: 'DeclaredName', text: 'n'},
// CHECK:STDOUT: {kind: 'Literal', text: 'i32'},
// CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 3},
// CHECK:STDOUT: {kind: 'VariableInitializer', text: '='},
// CHECK:STDOUT: {kind: 'NameReference', text: 'true'},
// CHECK:STDOUT: {kind: 'IfExpressionThen', text: 'then'},
// CHECK:STDOUT: {kind: 'Literal', text: '1'},
// CHECK:STDOUT: {kind: 'IfExpressionElse', text: 'else'},
// CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes},
// CHECK:STDOUT: {kind: 'IfExpression', text: 'if', has_error: yes, subtree_size: 6},
// CHECK:STDOUT: {kind: 'VariableDeclaration', text: ';', subtree_size: 12},
// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 18},
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
// CHECK:STDOUT: ]
fn F() {
// CHECK:STDERR: fail_else_expr_missing.carbon:[[@LINE+1]]:35: Expected expression.
var n: i32 = if true then 1 else;
}
@@ -0,0 +1,31 @@
// 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
//
// AUTOUPDATE
// CHECK:STDOUT: [
// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'},
// CHECK:STDOUT: {kind: 'DeclaredName', text: 'F'},
// CHECK:STDOUT: {kind: 'ParameterListStart', text: '('},
// CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 2},
// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'},
// CHECK:STDOUT: {kind: 'DeclaredName', text: 'n'},
// CHECK:STDOUT: {kind: 'Literal', text: 'i32'},
// CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 3},
// CHECK:STDOUT: {kind: 'VariableInitializer', text: '='},
// CHECK:STDOUT: {kind: 'NameReference', text: 'true'},
// CHECK:STDOUT: {kind: 'IfExpressionThen', text: 'then'},
// CHECK:STDOUT: {kind: 'Literal', text: '1'},
// CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes},
// CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes},
// CHECK:STDOUT: {kind: 'IfExpression', text: 'if', has_error: yes, subtree_size: 6},
// CHECK:STDOUT: {kind: 'VariableDeclaration', text: ';', subtree_size: 12},
// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 18},
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
// CHECK:STDOUT: ]
fn F() {
// CHECK:STDERR: fail_else_missing.carbon:[[@LINE+1]]:30: Expected `else` after `if ... then ...`.
var n: i32 = if true then 1;
}
@@ -0,0 +1,31 @@
// 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
//
// AUTOUPDATE
// CHECK:STDOUT: [
// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'},
// CHECK:STDOUT: {kind: 'DeclaredName', text: 'F'},
// CHECK:STDOUT: {kind: 'ParameterListStart', text: '('},
// CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 2},
// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'},
// CHECK:STDOUT: {kind: 'DeclaredName', text: 'n'},
// CHECK:STDOUT: {kind: 'Literal', text: 'i32'},
// CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 3},
// CHECK:STDOUT: {kind: 'VariableInitializer', text: '='},
// CHECK:STDOUT: {kind: 'NameReference', text: 'true'},
// CHECK:STDOUT: {kind: 'IfExpressionThen', text: 'then'},
// CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes},
// CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes},
// CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes},
// CHECK:STDOUT: {kind: 'IfExpression', text: 'if', has_error: yes, subtree_size: 6},
// CHECK:STDOUT: {kind: 'VariableDeclaration', text: ';', subtree_size: 12},
// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 18},
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
// CHECK:STDOUT: ]
fn F() {
// CHECK:STDERR: fail_then_expr_missing.carbon:[[@LINE+1]]:28: Expected expression.
var n: i32 = if true then;
}
@@ -0,0 +1,31 @@
// 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
//
// AUTOUPDATE
// CHECK:STDOUT: [
// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'},
// CHECK:STDOUT: {kind: 'DeclaredName', text: 'F'},
// CHECK:STDOUT: {kind: 'ParameterListStart', text: '('},
// CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 2},
// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'},
// CHECK:STDOUT: {kind: 'DeclaredName', text: 'n'},
// CHECK:STDOUT: {kind: 'Literal', text: 'i32'},
// CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 3},
// CHECK:STDOUT: {kind: 'VariableInitializer', text: '='},
// CHECK:STDOUT: {kind: 'NameReference', text: 'true'},
// CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes},
// CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes},
// CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes},
// CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes},
// CHECK:STDOUT: {kind: 'IfExpression', text: 'if', has_error: yes, subtree_size: 6},
// CHECK:STDOUT: {kind: 'VariableDeclaration', text: ';', subtree_size: 12},
// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 18},
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
// CHECK:STDOUT: ]
fn F() {
// CHECK:STDERR: fail_then_missing.carbon:[[@LINE+1]]:23: Expected `then` after `if` condition.
var n: i32 = if true;
}
@@ -0,0 +1,29 @@
// 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
//
// AUTOUPDATE
// CHECK:STDOUT: [
// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'},
// CHECK:STDOUT: {kind: 'DeclaredName', text: 'F'},
// CHECK:STDOUT: {kind: 'ParameterListStart', text: '('},
// CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 2},
// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
// CHECK:STDOUT: {kind: 'IfConditionStart', text: 'if', has_error: yes},
// CHECK:STDOUT: {kind: 'NameReference', text: 'true'},
// CHECK:STDOUT: {kind: 'IfCondition', text: 'if', has_error: yes, subtree_size: 3},
// CHECK:STDOUT: {kind: 'CodeBlockStart', text: 'then', has_error: yes},
// CHECK:STDOUT: {kind: 'InvalidParse', text: 'then', has_error: yes},
// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', has_error: yes, subtree_size: 2},
// CHECK:STDOUT: {kind: 'CodeBlock', text: 'then', has_error: yes, subtree_size: 4},
// CHECK:STDOUT: {kind: 'IfStatement', text: 'if', subtree_size: 8},
// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 14},
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
// CHECK:STDOUT: ]
fn F() {
// CHECK:STDERR: fail_top_level_if.carbon:[[@LINE+3]]:6: Expected `(` after `if`.
// CHECK:STDERR: fail_top_level_if.carbon:[[@LINE+2]]:11: Expected braced code block.
// CHECK:STDERR: fail_top_level_if.carbon:[[@LINE+1]]:11: Expected expression.
if true then 1 else 2;
}
@@ -0,0 +1,37 @@
// 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
//
// AUTOUPDATE
// CHECK:STDOUT: [
// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'},
// CHECK:STDOUT: {kind: 'DeclaredName', text: 'F'},
// CHECK:STDOUT: {kind: 'ParameterListStart', text: '('},
// CHECK:STDOUT: {kind: 'DeclaredName', text: 'b'},
// CHECK:STDOUT: {kind: 'NameReference', text: 'bool'},
// CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 3},
// CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 5},
// CHECK:STDOUT: {kind: 'NameReference', text: 'bool'},
// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 2},
// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 10},
// CHECK:STDOUT: {kind: 'ReturnStatementStart', text: 'return'},
// CHECK:STDOUT: {kind: 'NameReference', text: 'b'},
// CHECK:STDOUT: {kind: 'NameReference', text: 'b'},
// CHECK:STDOUT: {kind: 'InfixOperator', text: 'and', subtree_size: 3},
// CHECK:STDOUT: {kind: 'IfExpressionThen', text: 'then'},
// CHECK:STDOUT: {kind: 'NameReference', text: 'b'},
// CHECK:STDOUT: {kind: 'NameReference', text: 'b'},
// CHECK:STDOUT: {kind: 'InfixOperator', text: 'and', subtree_size: 3},
// CHECK:STDOUT: {kind: 'IfExpressionElse', text: 'else'},
// CHECK:STDOUT: {kind: 'NameReference', text: 'b'},
// CHECK:STDOUT: {kind: 'NameReference', text: 'b'},
// CHECK:STDOUT: {kind: 'InfixOperator', text: 'or', subtree_size: 3},
// CHECK:STDOUT: {kind: 'IfExpression', text: 'if', subtree_size: 12},
// CHECK:STDOUT: {kind: 'ReturnStatement', text: ';', subtree_size: 14},
// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 25},
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
// CHECK:STDOUT: ]
fn F(b: bool) -> bool {
return if b and b then b and b else b or b;
}
@@ -23,8 +23,5 @@
// CHECK:STDOUT: ]
fn F() {
// CHECK:STDERR: missing_precedence_not.carbon:[[@LINE+3]]:3: Whitespace is required before this unary operator.
// CHECK:STDERR: missing_precedence_not.carbon:[[@LINE+2]]:13: Whitespace is required before this unary operator.
// CHECK:STDERR: missing_precedence_not.carbon:[[@LINE+1]]:23: Whitespace is required before this unary operator.
not a and not b and not c;
}
@@ -15,6 +15,5 @@
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
// CHECK:STDOUT: ]
// CHECK:STDERR: recover_postfix_space_surrounding.carbon:[[@LINE+2]]:18: Whitespace is not allowed before this unary operator.
// CHECK:STDERR: recover_postfix_space_surrounding.carbon:[[@LINE+1]]:18: Whitespace is required after this unary operator.
// CHECK:STDERR: recover_postfix_space_surrounding.carbon:[[@LINE+1]]:18: Whitespace is not allowed before this unary operator.
var v: type = i8 * ;
@@ -15,6 +15,5 @@
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
// CHECK:STDOUT: ]
// CHECK:STDERR: recover_prefix_space.carbon:[[@LINE+2]]:13: Whitespace is not allowed after this unary operator.
// CHECK:STDERR: recover_prefix_space.carbon:[[@LINE+1]]:13: Whitespace is required before this unary operator.
// CHECK:STDERR: recover_prefix_space.carbon:[[@LINE+1]]:13: Whitespace is not allowed after this unary operator.
var n: i8 = - n;
+15
View File
@@ -178,6 +178,21 @@ auto SemanticsHandleIfConditionStart(SemanticsContext& context,
return context.TODO(parse_node, "HandleIfConditionStart");
}
auto SemanticsHandleIfExpression(SemanticsContext& context,
ParseTree::Node parse_node) -> bool {
return context.TODO(parse_node, "HandleIfExpression");
}
auto SemanticsHandleIfExpressionElse(SemanticsContext& context,
ParseTree::Node parse_node) -> bool {
return context.TODO(parse_node, "HandleIfExpressionElse");
}
auto SemanticsHandleIfExpressionThen(SemanticsContext& context,
ParseTree::Node parse_node) -> bool {
return context.TODO(parse_node, "HandleIfExpressionThen");
}
auto SemanticsHandleIfStatement(SemanticsContext& context,
ParseTree::Node parse_node) -> bool {
return context.TODO(parse_node, "HandleIfStatement");