diff --git a/packages/pyright-internal/src/parser/parseNodes.ts b/packages/pyright-internal/src/parser/parseNodes.ts index 3faabec3c..4e70f4a5c 100644 --- a/packages/pyright-internal/src/parser/parseNodes.ts +++ b/packages/pyright-internal/src/parser/parseNodes.ts @@ -866,18 +866,22 @@ export namespace BinaryOperationNode { export interface AssignmentExpressionNode extends ParseNodeBase { readonly nodeType: ParseNodeType.AssignmentExpression; name: NameNode; + walrusToken: Token; rightExpression: ExpressionNode; + isParenthesized: boolean; } export namespace AssignmentExpressionNode { - export function create(name: NameNode, rightExpression: ExpressionNode) { + export function create(name: NameNode, walrusToken: Token, rightExpression: ExpressionNode) { const node: AssignmentExpressionNode = { start: name.start, length: name.length, nodeType: ParseNodeType.AssignmentExpression, id: _nextNodeId++, name, + walrusToken, rightExpression, + isParenthesized: false, }; name.parent = node; diff --git a/packages/pyright-internal/src/parser/parser.ts b/packages/pyright-internal/src/parser/parser.ts index 7e932c293..9a32a9781 100644 --- a/packages/pyright-internal/src/parser/parser.ts +++ b/packages/pyright-internal/src/parser/parser.ts @@ -3184,7 +3184,7 @@ export class Parser { const rightExpr = this._parseTestExpression(/* allowAssignmentExpression */ false); - return AssignmentExpressionNode.create(leftExpr, rightExpr); + return AssignmentExpressionNode.create(leftExpr, walrusToken, rightExpr); } // or_test: and_test ('or' and_test)* @@ -3910,11 +3910,11 @@ export class Parser { possibleTupleNode.parenthesized = true; } - if (possibleTupleNode.nodeType === ParseNodeType.StringList) { - possibleTupleNode.isParenthesized = true; - } - - if (possibleTupleNode.nodeType === ParseNodeType.Comprehension) { + if ( + possibleTupleNode.nodeType === ParseNodeType.StringList || + possibleTupleNode.nodeType === ParseNodeType.Comprehension || + possibleTupleNode.nodeType === ParseNodeType.AssignmentExpression + ) { possibleTupleNode.isParenthesized = true; } @@ -4140,10 +4140,23 @@ export class Parser { if (this._consumeTokenIfOperator(OperatorType.Power)) { doubleStarExpression = this._parseExpression(/* allowUnpack */ false); } else { - keyExpression = this._parseTestOrStarExpression(/* allowAssignmentExpression */ false); + keyExpression = this._parseTestOrStarExpression(/* allowAssignmentExpression */ true); + + // Allow walrus operators in this context only for Python 3.10 and newer. + // Older versions of Python generated a syntax error in this context. + let isWalrusAllowed = this._getLanguageVersion().isGreaterOrEqualTo(pythonVersion3_10); if (this._consumeTokenIfType(TokenType.Colon)) { valueExpression = this._parseTestExpression(/* allowAssignmentExpression */ false); + isWalrusAllowed = false; + } + + if ( + !isWalrusAllowed && + keyExpression.nodeType === ParseNodeType.AssignmentExpression && + !keyExpression.isParenthesized + ) { + this._addSyntaxError(LocMessage.walrusNotAllowed(), keyExpression.walrusToken); } }