| // Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| import 'package:_fe_analyzer_shared/src/messages/codes.dart'; | |
| import 'package:_fe_analyzer_shared/src/parser/parser.dart'; | |
| import 'package:_fe_analyzer_shared/src/parser/quote.dart'; | |
| import 'package:_fe_analyzer_shared/src/parser/stack_listener.dart'; | |
| import 'package:_fe_analyzer_shared/src/scanner/token.dart'; | |
| import 'package:_fe_analyzer_shared/src/util/null_value.dart'; | |
| import 'package:_fe_analyzer_shared/src/util/value_kind.dart'; | |
| import 'arguments.dart'; | |
| import 'elements.dart'; | |
| import 'expressions.dart'; | |
| import 'formal_parameters.dart'; | |
| import 'proto.dart'; | |
| import 'record_fields.dart'; | |
| import 'references.dart'; | |
| import 'scope.dart'; | |
| import 'string_literal_parts.dart'; | |
| import 'type_annotations.dart'; | |
| /// Parser listener that can create [Expression] node for metadata annotations | |
| /// and constant expressions. | |
| class AnnotationsListener extends StackListener { | |
| @override | |
| final bool isDartLibrary; | |
| @override | |
| final Uri uri; | |
| final Scope _initialScope; | |
| final References _references; | |
| final bool delayLookup; | |
| AnnotationsListener( | |
| this.uri, | |
| this._initialScope, | |
| this._references, { | |
| required this.delayLookup, | |
| required this.isDartLibrary, | |
| }); | |
| final List<FunctionTypeParameterScope> _typeParameterScopes = []; | |
| Scope get _scope => | |
| _typeParameterScopes.isEmpty ? _initialScope : _typeParameterScopes.last; | |
| @override | |
| void beginMetadata(Token token) {} | |
| @override | |
| void endMetadata(Token beginToken, Token? periodBeforeName, Token endToken) { | |
| assert( | |
| checkState(beginToken, [ | |
| /*arguments*/ _ValueKinds._ArgumentsOrNull, | |
| /*suffix*/ if (periodBeforeName != null) _ValueKinds._IdentifierProto, | |
| /*type arguments*/ _ValueKinds._TypeAnnotationsOrNull, | |
| /*type*/ _ValueKinds._Proto, | |
| ]), | |
| ); | |
| List<Argument>? arguments = pop(_NullValues.Arguments) as List<Argument>?; | |
| IdentifierProto? identifier = periodBeforeName != null | |
| ? pop() as IdentifierProto | |
| : null; | |
| List<TypeAnnotation>? typeArguments = | |
| pop(_NullValues.TypeAnnotations) as List<TypeAnnotation>?; | |
| Proto proto = pop() as Proto; | |
| push( | |
| proto | |
| .instantiate(typeArguments) | |
| .apply(identifier) | |
| .invoke(arguments) | |
| .toExpression(), | |
| ); | |
| } | |
| @override | |
| void endMetadataStar(int count) { | |
| assert(checkState(null, repeatedKind(_ValueKinds._Expression, count))); | |
| List<Expression> expressions = new List.filled(count, _dummyExpression); | |
| while (--count >= 0) { | |
| expressions[count] = pop() as Expression; | |
| } | |
| push(expressions); | |
| } | |
| @override | |
| void handleIdentifier(Token token, IdentifierContext context) { | |
| switch (context) { | |
| case IdentifierContext.metadataReference: | |
| case IdentifierContext.typeReference: | |
| case IdentifierContext.prefixedTypeReference: | |
| case IdentifierContext.expression: | |
| case IdentifierContext.constructorReference: | |
| String name = token.lexeme; | |
| if (delayLookup) { | |
| push(new UnresolvedIdentifier(_scope, name)); | |
| } else { | |
| push(_scope.lookup(name)); | |
| } | |
| case IdentifierContext.typeVariableDeclaration: | |
| String name = token.lexeme; | |
| push(_typeParameterScopes.last.declareTypeParameter(name)); | |
| case IdentifierContext.metadataContinuation: | |
| case IdentifierContext.metadataContinuationAfterTypeArguments: | |
| case IdentifierContext.typeReferenceContinuation: | |
| case IdentifierContext.literalSymbol: | |
| case IdentifierContext.expressionContinuation: | |
| case IdentifierContext.constructorReferenceContinuation: | |
| case IdentifierContext.constructorReferenceContinuationAfterTypeArguments: | |
| case IdentifierContext.namedRecordFieldReference: | |
| case IdentifierContext.namedArgumentReference: | |
| case IdentifierContext.formalParameterDeclaration: | |
| case IdentifierContext.recordFieldDeclaration: | |
| push(new IdentifierProto(token.lexeme)); | |
| default: | |
| throw new UnsupportedError("Unsupported context $context"); | |
| } | |
| } | |
| @override | |
| void endConstructorReference( | |
| Token start, | |
| Token? periodBeforeName, | |
| Token endToken, | |
| ConstructorReferenceContext constructorReferenceContext, | |
| ) { | |
| assert( | |
| checkState(start, [ | |
| if (periodBeforeName != null) | |
| /* constructor name */ _ValueKinds._IdentifierProto, | |
| /* type arguments */ _ValueKinds._TypeAnnotationsOrNull, | |
| /* (qualified) name before type arguments */ _ValueKinds._Proto, | |
| ]), | |
| ); | |
| IdentifierProto? constructorName = periodBeforeName != null | |
| ? pop() as IdentifierProto | |
| : null; | |
| List<TypeAnnotation>? typeArguments = | |
| pop(_NullValues.TypeAnnotations) as List<TypeAnnotation>?; | |
| Proto className = pop() as Proto; | |
| push(className.instantiate(typeArguments).apply(constructorName)); | |
| } | |
| @override | |
| void endConstExpression(Token token) { | |
| assert( | |
| checkState(token, [ | |
| /* arguments */ _ValueKinds._Arguments, | |
| /* constructor reference */ _ValueKinds._Proto, | |
| ]), | |
| ); | |
| List<Argument> arguments = pop() as List<Argument>; | |
| Proto constructorReference = pop() as Proto; | |
| push(constructorReference.invoke(arguments)); | |
| } | |
| @override | |
| void handleLiteralList( | |
| int count, | |
| Token leftBracket, | |
| Token? constKeyword, | |
| Token rightBracket, | |
| ) { | |
| assert( | |
| checkState(leftBracket, [ | |
| ...repeatedKind(_ValueKinds._ElementOrProto, count), | |
| _ValueKinds._TypeAnnotationsOrNull, | |
| ]), | |
| ); | |
| List<Element> elements = new List.filled(count, _dummyElement); | |
| while (--count >= 0) { | |
| elements[count] = _popElementOrProto(); | |
| } | |
| List<TypeAnnotation>? typeArguments = | |
| pop(_NullValues.TypeAnnotations) as List<TypeAnnotation>?; | |
| push( | |
| new ExpressionProto(new ListLiteral(typeArguments ?? const [], elements)), | |
| ); | |
| } | |
| Element _popElementOrProto() { | |
| Object? element = pop(); | |
| if (element is Element) { | |
| return element; | |
| } else { | |
| return new ExpressionElement( | |
| (element as Proto).toExpression(), | |
| isNullAware: false, | |
| ); | |
| } | |
| } | |
| Expression _popExpression() { | |
| return (pop() as Proto).toExpression(); | |
| } | |
| Argument _popArgument() { | |
| Object? argument = pop(); | |
| if (argument is Argument) { | |
| return argument; | |
| } else { | |
| return new PositionalArgument((argument as Proto).toExpression()); | |
| } | |
| } | |
| RecordField _popRecordField() { | |
| Object? field = pop(); | |
| if (field is RecordField) { | |
| return field; | |
| } else { | |
| return new RecordPositionalField((field as Proto).toExpression()); | |
| } | |
| } | |
| @override | |
| void handleLiteralSetOrMap( | |
| int count, | |
| Token leftBrace, | |
| Token? constKeyword, | |
| Token rightBrace, | |
| bool hasSetEntry, | |
| ) { | |
| assert( | |
| checkState(leftBrace, [ | |
| ...repeatedKind(_ValueKinds._ElementOrProto, count), | |
| _ValueKinds._TypeAnnotationsOrNull, | |
| ]), | |
| ); | |
| List<Element> elements = new List.filled(count, _dummyElement); | |
| while (--count >= 0) { | |
| elements[count] = _popElementOrProto(); | |
| } | |
| List<TypeAnnotation>? typeArguments = | |
| pop(_NullValues.TypeAnnotations) as List<TypeAnnotation>?; | |
| push( | |
| new ExpressionProto( | |
| new SetOrMapLiteral(typeArguments ?? const [], elements), | |
| ), | |
| ); | |
| } | |
| @override | |
| void handleLiteralMapEntry( | |
| Token colon, | |
| Token endToken, { | |
| Token? nullAwareKeyToken, | |
| Token? nullAwareValueToken, | |
| }) { | |
| assert( | |
| checkState(colon, [ | |
| /* value */ _ValueKinds._Proto, | |
| /* key */ _ValueKinds._Proto, | |
| ]), | |
| ); | |
| Expression value = _popExpression(); | |
| Expression key = _popExpression(); | |
| push( | |
| new MapEntryElement( | |
| key, | |
| value, | |
| isNullAwareKey: nullAwareKeyToken != null, | |
| isNullAwareValue: nullAwareValueToken != null, | |
| ), | |
| ); | |
| } | |
| @override | |
| void handleSpreadExpression(Token spreadToken) { | |
| assert(checkState(spreadToken, [/* expression */ _ValueKinds._Proto])); | |
| Proto expression = pop() as Proto; | |
| push( | |
| new SpreadElement( | |
| expression.toExpression(), | |
| isNullAware: spreadToken.lexeme == '...?', | |
| ), | |
| ); | |
| } | |
| @override | |
| void handleNullAwareElement(Token nullAwareToken) { | |
| assert(checkState(nullAwareToken, [/* expression */ _ValueKinds._Proto])); | |
| Proto expression = pop() as Proto; | |
| push(new ExpressionElement(expression.toExpression(), isNullAware: true)); | |
| } | |
| @override | |
| void handleParenthesizedCondition(Token token, Token? case_, Token? when) { | |
| if (case_ != null) { | |
| throw new UnsupportedError( | |
| "handleParenthesizedCondition($token,$case_,$when", | |
| ); | |
| } else { | |
| assert(checkState(token, [_ValueKinds._Proto])); | |
| } | |
| } | |
| @override | |
| void endIfControlFlow(Token token) { | |
| assert( | |
| checkState(token, [ | |
| /* then */ _ValueKinds._ElementOrProto, | |
| /* condition */ _ValueKinds._Proto, | |
| ]), | |
| ); | |
| Element then = _popElementOrProto(); | |
| Expression condition = _popExpression(); | |
| push(new IfElement(condition, then)); | |
| } | |
| @override | |
| void handleElseControlFlow(Token elseToken) { | |
| assert( | |
| checkState(elseToken, [ | |
| /* otherwise */ unionOfKinds([ | |
| _ValueKinds._Element, | |
| _ValueKinds._Proto, | |
| ]), | |
| ]), | |
| ); | |
| } | |
| @override | |
| void endIfElseControlFlow(Token token) { | |
| assert( | |
| checkState(token, [ | |
| /* otherwise */ unionOfKinds([ | |
| _ValueKinds._Element, | |
| _ValueKinds._Proto, | |
| ]), | |
| /* then */ unionOfKinds([_ValueKinds._Element, _ValueKinds._Proto]), | |
| /* condition */ _ValueKinds._Proto, | |
| ]), | |
| ); | |
| Element otherwise = _popElementOrProto(); | |
| Element then = _popElementOrProto(); | |
| Expression condition = _popExpression(); | |
| push(new IfElement(condition, then, otherwise)); | |
| } | |
| @override | |
| void endConstLiteral(Token token) { | |
| assert(checkState(token, [_ValueKinds._Proto])); | |
| } | |
| @override | |
| void handleNamedRecordField(Token colon) { | |
| assert( | |
| checkState(colon, [ | |
| /* expression */ _ValueKinds._Proto, | |
| /* name */ _ValueKinds._IdentifierProto, | |
| ]), | |
| ); | |
| Expression expression = _popExpression(); | |
| IdentifierProto name = pop() as IdentifierProto; | |
| push(new RecordNamedField(name.text, expression)); | |
| } | |
| @override | |
| void endRecordLiteral(Token token, int count, Token? constKeyword) { | |
| assert( | |
| checkState( | |
| token, | |
| /* fields */ repeatedKind(_ValueKinds._RecordFieldOrProto, count), | |
| ), | |
| ); | |
| List<RecordField> fields = new List.filled(count, _dummyRecordField); | |
| while (--count >= 0) { | |
| fields[count] = _popRecordField(); | |
| } | |
| push(new ExpressionProto(new RecordLiteral(fields))); | |
| } | |
| @override | |
| void handleDotAccess(Token token, Token endToken, bool isNullAware) { | |
| assert( | |
| checkState(token, [ | |
| /* right */ _ValueKinds._Proto, | |
| /* left */ _ValueKinds._Proto, | |
| ]), | |
| ); | |
| Proto right = pop() as Proto; | |
| Proto left = pop() as Proto; | |
| IdentifierProto identifierProto = right as IdentifierProto; | |
| push(left.apply(identifierProto, isNullAware: isNullAware)); | |
| } | |
| @override | |
| void endBinaryExpression(Token token, Token endToken) { | |
| assert( | |
| checkState(token, [ | |
| /* right */ _ValueKinds._Proto, | |
| /* left */ _ValueKinds._Proto, | |
| ]), | |
| ); | |
| Proto right = pop() as Proto; | |
| Proto left = pop() as Proto; | |
| switch (token.lexeme) { | |
| case '??': | |
| push( | |
| new ExpressionProto( | |
| new IfNull(left.toExpression(), right.toExpression()), | |
| ), | |
| ); | |
| case '||': | |
| push( | |
| new ExpressionProto( | |
| new LogicalExpression( | |
| left.toExpression(), | |
| LogicalOperator.or, | |
| right.toExpression(), | |
| ), | |
| ), | |
| ); | |
| case '&&': | |
| push( | |
| new ExpressionProto( | |
| new LogicalExpression( | |
| left.toExpression(), | |
| LogicalOperator.and, | |
| right.toExpression(), | |
| ), | |
| ), | |
| ); | |
| case '==': | |
| push( | |
| new ExpressionProto( | |
| new EqualityExpression( | |
| left.toExpression(), | |
| right.toExpression(), | |
| isNotEquals: false, | |
| ), | |
| ), | |
| ); | |
| case '!=': | |
| push( | |
| new ExpressionProto( | |
| new EqualityExpression( | |
| left.toExpression(), | |
| right.toExpression(), | |
| isNotEquals: true, | |
| ), | |
| ), | |
| ); | |
| case '>': | |
| push( | |
| new ExpressionProto( | |
| new BinaryExpression( | |
| left.toExpression(), | |
| BinaryOperator.greaterThan, | |
| right.toExpression(), | |
| ), | |
| ), | |
| ); | |
| case '>=': | |
| push( | |
| new ExpressionProto( | |
| new BinaryExpression( | |
| left.toExpression(), | |
| BinaryOperator.greaterThanOrEqual, | |
| right.toExpression(), | |
| ), | |
| ), | |
| ); | |
| case '<': | |
| push( | |
| new ExpressionProto( | |
| new BinaryExpression( | |
| left.toExpression(), | |
| BinaryOperator.lessThan, | |
| right.toExpression(), | |
| ), | |
| ), | |
| ); | |
| case '<=': | |
| push( | |
| new ExpressionProto( | |
| new BinaryExpression( | |
| left.toExpression(), | |
| BinaryOperator.lessThanOrEqual, | |
| right.toExpression(), | |
| ), | |
| ), | |
| ); | |
| case '<<': | |
| push( | |
| new ExpressionProto( | |
| new BinaryExpression( | |
| left.toExpression(), | |
| BinaryOperator.shiftLeft, | |
| right.toExpression(), | |
| ), | |
| ), | |
| ); | |
| case '>>': | |
| push( | |
| new ExpressionProto( | |
| new BinaryExpression( | |
| left.toExpression(), | |
| BinaryOperator.signedShiftRight, | |
| right.toExpression(), | |
| ), | |
| ), | |
| ); | |
| case '>>>': | |
| push( | |
| new ExpressionProto( | |
| new BinaryExpression( | |
| left.toExpression(), | |
| BinaryOperator.unsignedShiftRight, | |
| right.toExpression(), | |
| ), | |
| ), | |
| ); | |
| case '+': | |
| push( | |
| new ExpressionProto( | |
| new BinaryExpression( | |
| left.toExpression(), | |
| BinaryOperator.plus, | |
| right.toExpression(), | |
| ), | |
| ), | |
| ); | |
| case '-': | |
| push( | |
| new ExpressionProto( | |
| new BinaryExpression( | |
| left.toExpression(), | |
| BinaryOperator.minus, | |
| right.toExpression(), | |
| ), | |
| ), | |
| ); | |
| case '*': | |
| push( | |
| new ExpressionProto( | |
| new BinaryExpression( | |
| left.toExpression(), | |
| BinaryOperator.times, | |
| right.toExpression(), | |
| ), | |
| ), | |
| ); | |
| case '/': | |
| push( | |
| new ExpressionProto( | |
| new BinaryExpression( | |
| left.toExpression(), | |
| BinaryOperator.divide, | |
| right.toExpression(), | |
| ), | |
| ), | |
| ); | |
| case '~/': | |
| push( | |
| new ExpressionProto( | |
| new BinaryExpression( | |
| left.toExpression(), | |
| BinaryOperator.integerDivide, | |
| right.toExpression(), | |
| ), | |
| ), | |
| ); | |
| case '%': | |
| push( | |
| new ExpressionProto( | |
| new BinaryExpression( | |
| left.toExpression(), | |
| BinaryOperator.modulo, | |
| right.toExpression(), | |
| ), | |
| ), | |
| ); | |
| case '|': | |
| push( | |
| new ExpressionProto( | |
| new BinaryExpression( | |
| left.toExpression(), | |
| BinaryOperator.bitwiseOr, | |
| right.toExpression(), | |
| ), | |
| ), | |
| ); | |
| case '&': | |
| push( | |
| new ExpressionProto( | |
| new BinaryExpression( | |
| left.toExpression(), | |
| BinaryOperator.bitwiseAnd, | |
| right.toExpression(), | |
| ), | |
| ), | |
| ); | |
| case '^': | |
| push( | |
| new ExpressionProto( | |
| new BinaryExpression( | |
| left.toExpression(), | |
| BinaryOperator.bitwiseXor, | |
| right.toExpression(), | |
| ), | |
| ), | |
| ); | |
| default: | |
| throw new UnimplementedError("Binary operator '${token.lexeme}'."); | |
| } | |
| } | |
| @override | |
| void handleIsOperator(Token isOperator, Token? not) { | |
| assert( | |
| checkState(isOperator, [ | |
| /* type */ _ValueKinds._TypeAnnotation, | |
| /* expression */ _ValueKinds._Proto, | |
| ]), | |
| ); | |
| TypeAnnotation type = pop() as TypeAnnotation; | |
| Expression expression = _popExpression(); | |
| push(new ExpressionProto(new IsTest(expression, type, isNot: not != null))); | |
| } | |
| @override | |
| void handleAsOperator(Token operator) { | |
| assert( | |
| checkState(operator, [ | |
| /* type */ _ValueKinds._TypeAnnotation, | |
| /* expression */ _ValueKinds._Proto, | |
| ]), | |
| ); | |
| TypeAnnotation type = pop() as TypeAnnotation; | |
| Expression expression = _popExpression(); | |
| push(new ExpressionProto(new AsExpression(expression, type))); | |
| } | |
| @override | |
| void endIsOperatorType(Token operator) { | |
| // Do nothing. | |
| } | |
| @override | |
| void endAsOperatorType(Token operator) { | |
| // Do nothing. | |
| } | |
| @override | |
| void handleUnaryPrefixExpression(Token token) { | |
| assert(checkState(token, [/* expression */ _ValueKinds._Proto])); | |
| Expression expression = _popExpression(); | |
| switch (token.lexeme) { | |
| case '-': | |
| push( | |
| new ExpressionProto( | |
| new UnaryExpression(UnaryOperator.minus, expression), | |
| ), | |
| ); | |
| case '!': | |
| push( | |
| new ExpressionProto( | |
| new UnaryExpression(UnaryOperator.bang, expression), | |
| ), | |
| ); | |
| case '~': | |
| push( | |
| new ExpressionProto( | |
| new UnaryExpression(UnaryOperator.tilde, expression), | |
| ), | |
| ); | |
| default: | |
| throw new UnimplementedError("Unary operator '${token.lexeme}'."); | |
| } | |
| } | |
| @override | |
| void handleNonNullAssertExpression(Token bang) { | |
| assert(checkState(bang, [/* expression */ _ValueKinds._Proto])); | |
| Expression expression = _popExpression(); | |
| push(new ExpressionProto(new NullCheck(expression))); | |
| } | |
| @override | |
| void handleQualified(Token period) { | |
| assert( | |
| checkState(period, [ | |
| /* suffix */ _ValueKinds._IdentifierProto, | |
| /* prefix */ _ValueKinds._Proto, | |
| ]), | |
| ); | |
| IdentifierProto suffix = pop() as IdentifierProto; | |
| Proto prefix = pop() as Proto; | |
| push(prefix.apply(suffix)); | |
| } | |
| @override | |
| void handleSend(Token beginToken, Token endToken) { | |
| assert( | |
| checkState(beginToken, [ | |
| _ValueKinds._ArgumentsOrNull, | |
| _ValueKinds._TypeAnnotationsOrNull, | |
| _ValueKinds._Proto, | |
| ]), | |
| ); | |
| List<Argument>? arguments = pop(_NullValues.Arguments) as List<Argument>?; | |
| List<TypeAnnotation>? typeArguments = | |
| pop(_NullValues.TypeAnnotations) as List<TypeAnnotation>?; | |
| Proto proto = pop() as Proto; | |
| assert( | |
| typeArguments == null || arguments != null, | |
| 'Unexpected type argument application as send.', | |
| ); | |
| push(proto.instantiate(typeArguments).invoke(arguments)); | |
| } | |
| @override | |
| void handleSendWithoutArguments( | |
| Token beginToken, | |
| Token endToken, | |
| Token nextToken, | |
| ) { | |
| assert(checkState(beginToken, [/* receiver */ _ValueKinds._Proto])); | |
| Proto proto = pop() as Proto; | |
| push(proto.instantiate(null).invoke(null)); | |
| } | |
| @override | |
| void handleInvocationWithoutTypeArguments(Token beginToken, Token endToken) { | |
| assert( | |
| checkState(beginToken, [ | |
| /* arguments */ _ValueKinds._ArgumentsOrNull, | |
| /* receiver */ _ValueKinds._Proto, | |
| ]), | |
| ); | |
| List<Argument>? arguments = pop(_NullValues.Arguments) as List<Argument>?; | |
| Proto proto = pop() as Proto; | |
| push(proto.instantiate(null).invoke(arguments)); | |
| } | |
| @override | |
| void handleNoArguments(Token token) { | |
| push(_NullValues.Arguments); | |
| } | |
| @override | |
| void handleNamedArgument(Token colon) { | |
| assert( | |
| checkState(colon, [ | |
| /* expression */ _ValueKinds._Proto, | |
| /* name */ _ValueKinds._IdentifierProto, | |
| ]), | |
| ); | |
| Expression expression = _popExpression(); | |
| IdentifierProto name = pop() as IdentifierProto; | |
| push(new NamedArgument(name.text, expression)); | |
| } | |
| @override | |
| void endArguments(int count, Token beginToken, Token endToken) { | |
| assert( | |
| checkState( | |
| beginToken, | |
| /* arguments */ repeatedKind(_ValueKinds._ArgumentOrProto, count), | |
| ), | |
| ); | |
| List<Argument> arguments = new List.filled(count, _dummyArgument); | |
| while (--count >= 0) { | |
| arguments[count] = _popArgument(); | |
| } | |
| push(arguments); | |
| } | |
| @override | |
| void handleNoTypeArguments(Token token) { | |
| push(_NullValues.TypeAnnotations); | |
| } | |
| @override | |
| void endTypeArguments(int count, Token beginToken, Token endToken) { | |
| assert( | |
| checkState( | |
| beginToken, | |
| /* type arguments */ repeatedKind(_ValueKinds._TypeAnnotation, count), | |
| ), | |
| ); | |
| List<TypeAnnotation> typeArguments = new List.filled( | |
| count, | |
| _dummyTypeAnnotation, | |
| ); | |
| while (--count >= 0) { | |
| typeArguments[count] = pop() as TypeAnnotation; | |
| } | |
| push(typeArguments); | |
| } | |
| @override | |
| void handleType(Token beginToken, Token? questionMark) { | |
| assert( | |
| checkState(beginToken, [ | |
| _ValueKinds._TypeAnnotationsOrNull, | |
| _ValueKinds._Proto, | |
| ]), | |
| ); | |
| List<TypeAnnotation>? typeArguments = | |
| pop(_NullValues.TypeAnnotations) as List<TypeAnnotation>?; | |
| Proto type = pop() as Proto; | |
| TypeAnnotation typeAnnotation = type | |
| .instantiate(typeArguments) | |
| .toTypeAnnotation(); | |
| if (questionMark != null) { | |
| typeAnnotation = new NullableTypeAnnotation(typeAnnotation); | |
| } | |
| push(typeAnnotation); | |
| } | |
| @override | |
| void handleVoidKeywordWithTypeArguments(Token token) { | |
| assert(checkState(token, [_ValueKinds._TypeAnnotationsOrNull])); | |
| List<TypeAnnotation>? typeArguments = | |
| pop(_NullValues.TypeAnnotations) as List<TypeAnnotation>?; | |
| push( | |
| new VoidProto( | |
| _references.voidReference, | |
| ).instantiate(typeArguments).toTypeAnnotation(), | |
| ); | |
| } | |
| @override | |
| void handleVoidKeyword(Token token) { | |
| push(new VoidProto(_references.voidReference).toTypeAnnotation()); | |
| } | |
| @override | |
| void handleLiteralInt(Token token) { | |
| int? value = intFromToken(token, hasSeparators: false); | |
| push(new ExpressionProto(new IntegerLiteral.fromText(token.lexeme, value))); | |
| } | |
| @override | |
| void handleLiteralIntWithSeparators(Token token) { | |
| int? value = intFromToken(token, hasSeparators: true); | |
| push(new ExpressionProto(new IntegerLiteral.fromText(token.lexeme, value))); | |
| } | |
| @override | |
| void handleLiteralDouble(Token token) { | |
| push( | |
| new ExpressionProto( | |
| new DoubleLiteral( | |
| token.lexeme, | |
| doubleFromToken(token, hasSeparators: false), | |
| ), | |
| ), | |
| ); | |
| } | |
| @override | |
| void handleLiteralDoubleWithSeparators(Token token) { | |
| push( | |
| new ExpressionProto( | |
| new DoubleLiteral( | |
| token.lexeme, | |
| doubleFromToken(token, hasSeparators: true), | |
| ), | |
| ), | |
| ); | |
| } | |
| @override | |
| void handleLiteralBool(Token token) { | |
| push(new ExpressionProto(new BooleanLiteral(boolFromToken(token)))); | |
| } | |
| @override | |
| void handleLiteralNull(Token token) { | |
| push(new ExpressionProto(new NullLiteral())); | |
| } | |
| @override | |
| void beginLiteralString(Token token) { | |
| push(new StringPart(token.lexeme)); | |
| } | |
| @override | |
| void handleStringPart(Token token) { | |
| push(new StringPart(token.lexeme)); | |
| } | |
| @override | |
| void endLiteralString(int interpolationCount, Token endToken) { | |
| int count = 1 + interpolationCount * 2; | |
| assert( | |
| checkState( | |
| endToken, | |
| repeatedKind( | |
| unionOfKinds([_ValueKinds._StringPart, _ValueKinds._Proto]), | |
| count, | |
| ), | |
| ), | |
| ); | |
| if (interpolationCount == 0) { | |
| // TODO(johnniwinther): Use the token corresponding to [part]. | |
| Token token = endToken; | |
| StringPart part = pop() as StringPart; | |
| String value = unescapeString(part.text, token, this); | |
| push(new ExpressionProto(new StringLiteral([new StringPart(value)]))); | |
| } else { | |
| List<Object?> objects = new List.filled(count, /* dummyValue */ null); | |
| int index = count; | |
| while (--index >= 0) { | |
| objects[index] = pop(); | |
| } | |
| StringPart first = objects.first as StringPart; | |
| StringPart last = objects.last as StringPart; | |
| Quote quote = analyzeQuote(first.text); | |
| List<StringLiteralPart> parts = []; | |
| // Contains more than just \' or \". | |
| if (first.text.length > 1) { | |
| // TODO(johnniwinther): Use the token corresponding to [first]. | |
| Token token = endToken; | |
| String value = unescapeFirstStringPart(first.text, quote, token, this); | |
| if (value.isNotEmpty) { | |
| parts.add(new StringPart(value)); | |
| } | |
| } | |
| for (int i = 1; i < objects.length - 1; i++) { | |
| Object? object = objects[i]; | |
| if (object is StringPart) { | |
| if (object.text.length != 0) { | |
| // TODO(johnniwinther): Use the token corresponding to [object]. | |
| Token token = endToken; | |
| String value = unescapeMiddleStringPart( | |
| object.text, | |
| quote, | |
| token, | |
| this, | |
| ); | |
| parts.add(new StringPart(value)); | |
| } | |
| } else { | |
| parts.add(new InterpolationPart((object as Proto).toExpression())); | |
| } | |
| } | |
| // Contains more than just \' or \". | |
| if (last.text.length > 1) { | |
| // TODO(johnniwinther): Use the token corresponding to [last]. | |
| Token token = endToken; | |
| String value = unescapeLastStringPart( | |
| last.text, | |
| quote, | |
| token, | |
| token.isSynthetic, | |
| this, | |
| ); | |
| if (value.isNotEmpty) { | |
| parts.add(new StringPart(value)); | |
| } | |
| } | |
| push(new ExpressionProto(new StringLiteral(parts))); | |
| } | |
| } | |
| @override | |
| void handleAdjacentStringLiterals(Token startToken, int literalCount) { | |
| assert( | |
| checkState(startToken, repeatedKind(_ValueKinds._Proto, literalCount)), | |
| ); | |
| List<Expression> expressions = new List.filled( | |
| literalCount, | |
| _dummyExpression, | |
| ); | |
| while (--literalCount >= 0) { | |
| expressions[literalCount] = _popExpression(); | |
| } | |
| push(new ExpressionProto(new AdjacentStringLiterals(expressions))); | |
| } | |
| @override | |
| void endLiteralSymbol(Token hashToken, int identifierCount) { | |
| assert( | |
| checkState( | |
| hashToken, | |
| repeatedKind(_ValueKinds._IdentifierProto, identifierCount), | |
| ), | |
| ); | |
| List<String> parts = new List.filled(identifierCount, /* dummy value */ ''); | |
| while (--identifierCount >= 0) { | |
| parts[identifierCount] = (pop() as IdentifierProto).text; | |
| } | |
| push(new ExpressionProto(new SymbolLiteral(parts))); | |
| } | |
| @override | |
| void handleTypeArgumentApplication(Token openAngleBracket) { | |
| assert( | |
| checkState(openAngleBracket, [ | |
| _ValueKinds._TypeAnnotations, | |
| _ValueKinds._Proto, | |
| ]), | |
| ); | |
| List<TypeAnnotation> typeArguments = pop() as List<TypeAnnotation>; | |
| Proto receiver = pop() as Proto; | |
| push(receiver.instantiate(typeArguments)); | |
| } | |
| @override | |
| void endParenthesizedExpression(Token token) { | |
| assert(checkState(token, [_ValueKinds._Proto])); | |
| Expression expression = _popExpression(); | |
| push(new ExpressionProto(new ParenthesizedExpression(expression))); | |
| } | |
| @override | |
| void endConditionalExpression(Token question, Token colon, Token endToken) { | |
| assert( | |
| checkState(question, [ | |
| /* otherwise */ _ValueKinds._Proto, | |
| /* then */ _ValueKinds._Proto, | |
| /* condition */ _ValueKinds._Proto, | |
| ]), | |
| ); | |
| Expression otherwise = _popExpression(); | |
| Expression then = _popExpression(); | |
| Expression condition = _popExpression(); | |
| push( | |
| new ExpressionProto( | |
| new ConditionalExpression(condition, then, otherwise), | |
| ), | |
| ); | |
| } | |
| @override | |
| void handleValuedFormalParameter( | |
| Token equals, | |
| Token token, | |
| FormalParameterKind kind, | |
| ) { | |
| assert(checkState(token, [_ValueKinds._Proto])); | |
| push(_popExpression()); | |
| } | |
| @override | |
| void handleFormalParameterWithoutValue(Token token) { | |
| push(_NullValues.Expression); | |
| } | |
| @override | |
| void endFormalParameter( | |
| Token? varOrFinal, | |
| Token? thisKeyword, | |
| Token? superKeyword, | |
| Token? periodAfterThisOrSuper, | |
| Token nameToken, | |
| Token? initializerStart, | |
| Token? initializerEnd, | |
| FormalParameterKind kind, | |
| MemberKind memberKind, | |
| ) { | |
| assert( | |
| checkState(nameToken, [ | |
| _ValueKinds._ExpressionOrNull, | |
| _ValueKinds._IdentifierProtoOrNull, | |
| _ValueKinds._TypeAnnotationOrNull, | |
| _ValueKinds._Expressions, | |
| ]), | |
| ); | |
| Expression? defaultValue = pop() as Expression?; | |
| IdentifierProto? name = pop() as IdentifierProto?; | |
| TypeAnnotation? typeAnnotation = pop() as TypeAnnotation?; | |
| List<Expression> metadata = pop() as List<Expression>; | |
| push( | |
| new FormalParameter( | |
| metadata, | |
| typeAnnotation, | |
| name?.text, | |
| defaultValue, | |
| isNamed: kind.isNamed, | |
| isRequired: kind.isRequired, | |
| ), | |
| ); | |
| } | |
| @override | |
| void handleNoName(Token token) { | |
| push(_NullValues.Identifier); | |
| } | |
| @override | |
| void endOptionalFormalParameters( | |
| int count, | |
| Token beginToken, | |
| Token endToken, | |
| MemberKind kind, | |
| ) { | |
| assert( | |
| checkState(beginToken, repeatedKind(_ValueKinds._FormalParameter, count)), | |
| ); | |
| List<FormalParameter> formalParameters = new List.filled( | |
| count, | |
| _dummyFormalParameter, | |
| ); | |
| while (--count >= 0) { | |
| formalParameters[count] = pop() as FormalParameter; | |
| } | |
| push(new FormalParameterGroup(formalParameters)); | |
| } | |
| @override | |
| void endFormalParameters( | |
| int count, | |
| Token beginToken, | |
| Token endToken, | |
| MemberKind kind, | |
| ) { | |
| assert( | |
| checkState( | |
| beginToken, | |
| repeatedKind( | |
| unionOfKinds([ | |
| _ValueKinds._FormalParameter, | |
| _ValueKinds._FormalParameterGroup, | |
| ]), | |
| count, | |
| ), | |
| ), | |
| ); | |
| List<Object?> objects = new List.filled(count, /* dummy value */ null); | |
| while (--count >= 0) { | |
| objects[count] = pop(); | |
| } | |
| List<FormalParameter> formalParameters = []; | |
| for (Object? object in objects) { | |
| if (object is FormalParameter) { | |
| formalParameters.add(object); | |
| } else { | |
| formalParameters.addAll( | |
| (object as FormalParameterGroup).formalParameters, | |
| ); | |
| } | |
| } | |
| push(formalParameters); | |
| } | |
| @override | |
| void endTypeVariable( | |
| Token token, | |
| int index, | |
| Token? extendsOrSuper, | |
| Token? variance, | |
| ) { | |
| assert( | |
| checkState(token, [ | |
| _ValueKinds._TypeAnnotationOrNull, | |
| _ValueKinds._FunctionTypeParameters, | |
| ]), | |
| ); | |
| TypeAnnotation? bound = pop(_NullValues.TypeAnnotation) as TypeAnnotation?; | |
| List<FunctionTypeParameter> functionTypeParameters = | |
| pop() as List<FunctionTypeParameter>; | |
| FunctionTypeParameter functionTypeParameter = functionTypeParameters[index]; | |
| functionTypeParameter.bound = bound; | |
| push(functionTypeParameters); | |
| } | |
| @override | |
| void handleTypeVariablesDefined(Token token, int count) { | |
| assert( | |
| checkState( | |
| token, | |
| repeatedKinds([ | |
| _ValueKinds._FunctionTypeParameter, | |
| _ValueKinds._Expressions, | |
| ], count), | |
| ), | |
| ); | |
| List<FunctionTypeParameter> functionTypeParameters = new List.filled( | |
| count, | |
| _dummyFunctionTypeParameter, | |
| ); | |
| while (--count >= 0) { | |
| FunctionTypeParameter functionTypeParameter = | |
| functionTypeParameters[count] = pop() as FunctionTypeParameter; | |
| functionTypeParameter.metadata = pop() as List<Expression>; | |
| } | |
| push(functionTypeParameters); | |
| } | |
| @override | |
| void endTypeVariables(Token beginToken, Token endToken) { | |
| assert(checkState(beginToken, [_ValueKinds._FunctionTypeParameters])); | |
| } | |
| @override | |
| void beginFunctionType(Token beginToken) { | |
| _typeParameterScopes.add(new FunctionTypeParameterScope(_scope)); | |
| } | |
| @override | |
| void endFunctionType(Token functionToken, Token? questionMark) { | |
| assert( | |
| checkState(functionToken, [ | |
| _ValueKinds._FormalParameters, | |
| _ValueKinds._TypeAnnotationOrNull, | |
| _ValueKinds._FunctionTypeParametersOrNull, | |
| ]), | |
| ); | |
| _typeParameterScopes.removeLast(); | |
| List<FormalParameter> formalParameters = pop() as List<FormalParameter>; | |
| TypeAnnotation? returnType = | |
| pop(_NullValues.TypeAnnotation) as TypeAnnotation?; | |
| List<FunctionTypeParameter>? typeParameters = | |
| pop(_NullValues.FunctionTypeParameters) as List<FunctionTypeParameter>?; | |
| push( | |
| new FunctionTypeAnnotation( | |
| returnType, | |
| typeParameters ?? const [], | |
| formalParameters, | |
| ), | |
| ); | |
| } | |
| @override | |
| void endRecordType( | |
| Token leftBracket, | |
| Token? questionMark, | |
| int count, | |
| bool hasNamedFields, | |
| ) { | |
| assert( | |
| checkState( | |
| leftBracket, | |
| hasNamedFields | |
| ? [ | |
| _ValueKinds._RecordTypeEntries, | |
| ...repeatedKind(_ValueKinds._RecordTypeEntry, count - 1), | |
| ] | |
| : repeatedKind(_ValueKinds._RecordTypeEntry, count), | |
| ), | |
| ); | |
| List<RecordTypeEntry>? named; | |
| if (hasNamedFields) { | |
| named = pop() as List<RecordTypeEntry>; | |
| count--; | |
| } | |
| List<RecordTypeEntry> positional = new List.filled( | |
| count, | |
| _dummyRecordTypeEntry, | |
| ); | |
| while (--count >= 0) { | |
| positional[count] = pop() as RecordTypeEntry; | |
| } | |
| push(new RecordTypeAnnotation(positional, named ?? const [])); | |
| } | |
| @override | |
| void endRecordTypeEntry() { | |
| assert( | |
| checkState(null, [ | |
| _ValueKinds._IdentifierProtoOrNull, | |
| _ValueKinds._TypeAnnotation, | |
| _ValueKinds._Expressions, | |
| ]), | |
| ); | |
| IdentifierProto? name = pop() as IdentifierProto?; | |
| TypeAnnotation type = pop() as TypeAnnotation; | |
| List<Expression> metadata = pop() as List<Expression>; | |
| push(new RecordTypeEntry(metadata, type, name?.text)); | |
| } | |
| @override | |
| void endRecordTypeNamedFields(int count, Token leftBracket) { | |
| assert( | |
| checkState( | |
| leftBracket, | |
| repeatedKind(_ValueKinds._RecordTypeEntry, count), | |
| ), | |
| ); | |
| List<RecordTypeEntry> entries = new List.filled( | |
| count, | |
| _dummyRecordTypeEntry, | |
| ); | |
| while (--count >= 0) { | |
| entries[count] = pop() as RecordTypeEntry; | |
| } | |
| push(entries); | |
| } | |
| @override | |
| void handleNoType(Token lastConsumed) { | |
| push(_NullValues.TypeAnnotation); | |
| } | |
| @override | |
| void handleNoTypeVariables(Token token) { | |
| push(_NullValues.FunctionTypeParameters); | |
| } | |
| @override | |
| void addProblem( | |
| Message message, | |
| int charOffset, | |
| int length, { | |
| bool wasHandled = false, | |
| List<LocatedMessage> context = const [], | |
| }) { | |
| // Don't report errors. | |
| } | |
| @override | |
| Never internalProblem(Message message, int charOffset, Uri uri) { | |
| throw new UnimplementedError(message.problemMessage); | |
| } | |
| } | |
| class _NullValues { | |
| static const NullValue Arguments = const NullValue("Argument"); | |
| static const NullValue Expression = const NullValue("Expression"); | |
| static const NullValue FunctionTypeParameters = const NullValue( | |
| "FunctionTypeParameter", | |
| ); | |
| static const NullValue Identifier = const NullValue("Identifier"); | |
| static const NullValue TypeAnnotation = const NullValue("TypeAnnotation"); | |
| static const NullValue TypeAnnotations = const NullValue("TypeAnnotations"); | |
| } | |
| final Argument _dummyArgument = new PositionalArgument( | |
| new IntegerLiteral.fromText('0'), | |
| ); | |
| final RecordField _dummyRecordField = new RecordPositionalField( | |
| _dummyExpression, | |
| ); | |
| final TypeAnnotation _dummyTypeAnnotation = new InvalidTypeAnnotation(); | |
| final Expression _dummyExpression = new NullLiteral(); | |
| final Element _dummyElement = new ExpressionElement( | |
| _dummyExpression, | |
| isNullAware: false, | |
| ); | |
| final FormalParameter _dummyFormalParameter = new FormalParameter( | |
| const [], | |
| null, | |
| null, | |
| null, | |
| isNamed: false, | |
| isRequired: false, | |
| ); | |
| final FunctionTypeParameter _dummyFunctionTypeParameter = | |
| new FunctionTypeParameter(''); | |
| final RecordTypeEntry _dummyRecordTypeEntry = new RecordTypeEntry( | |
| const [], | |
| _dummyTypeAnnotation, | |
| null, | |
| ); | |
| class _ValueKinds { | |
| static const ValueKind _Proto = const SingleValueKind<Proto>(); | |
| static const ValueKind _IdentifierProto = | |
| const SingleValueKind<IdentifierProto>(); | |
| static const ValueKind _IdentifierProtoOrNull = | |
| const SingleValueKind<IdentifierProto>(_NullValues.Identifier); | |
| static const ValueKind _Expression = const SingleValueKind<Expression>(); | |
| static const ValueKind _ExpressionOrNull = const SingleValueKind<Expression>( | |
| _NullValues.Expression, | |
| ); | |
| static const ValueKind _Expressions = | |
| const SingleValueKind<List<Expression>>(); | |
| static const ValueKind _Element = const SingleValueKind<Element>(); | |
| static final ValueKind _ElementOrProto = unionOfKinds([ | |
| _ValueKinds._Element, | |
| _ValueKinds._Proto, | |
| ]); | |
| static const ValueKind _Argument = const SingleValueKind<Argument>(); | |
| static final ValueKind _ArgumentOrProto = unionOfKinds([ | |
| _ValueKinds._Argument, | |
| _ValueKinds._Proto, | |
| ]); | |
| static const ValueKind _RecordField = const SingleValueKind<RecordField>(); | |
| static final ValueKind _RecordFieldOrProto = unionOfKinds([ | |
| _ValueKinds._RecordField, | |
| _ValueKinds._Proto, | |
| ]); | |
| static final ValueKind _RecordTypeEntry = | |
| const SingleValueKind<RecordTypeEntry>(); | |
| static final ValueKind _RecordTypeEntries = | |
| const SingleValueKind<List<RecordTypeEntry>>(); | |
| static const ValueKind _Arguments = const SingleValueKind<List<Argument>>(); | |
| static const ValueKind _ArgumentsOrNull = | |
| const SingleValueKind<List<Argument>>(_NullValues.Arguments); | |
| static const ValueKind _TypeAnnotation = | |
| const SingleValueKind<TypeAnnotation>(); | |
| static const ValueKind _TypeAnnotationOrNull = | |
| const SingleValueKind<TypeAnnotation>(_NullValues.TypeAnnotation); | |
| static const ValueKind _TypeAnnotations = | |
| const SingleValueKind<List<TypeAnnotation>>(); | |
| static const ValueKind _TypeAnnotationsOrNull = | |
| const SingleValueKind<List<TypeAnnotation>>(_NullValues.TypeAnnotations); | |
| static const ValueKind _StringPart = const SingleValueKind<StringPart>(); | |
| static const ValueKind _FormalParameter = | |
| const SingleValueKind<FormalParameter>(); | |
| static const ValueKind _FormalParameters = | |
| const SingleValueKind<List<FormalParameter>>(); | |
| static const ValueKind _FormalParameterGroup = | |
| const SingleValueKind<FormalParameterGroup>(); | |
| static const ValueKind _FunctionTypeParameter = | |
| const SingleValueKind<FunctionTypeParameter>(); | |
| static const ValueKind _FunctionTypeParameters = | |
| const SingleValueKind<List<FunctionTypeParameter>>(); | |
| static const ValueKind _FunctionTypeParametersOrNull = | |
| const SingleValueKind<List<FunctionTypeParameter>>( | |
| _NullValues.FunctionTypeParameters, | |
| ); | |
| } | |
| /// Parses the metadata annotation beginning at [atToken]. | |
| Expression parseAnnotation( | |
| Token atToken, | |
| Uri fileUri, | |
| Scope scope, | |
| References references, | |
| ExperimentalFeatures experimentalFeatures, { | |
| required bool isDartLibrary, | |
| bool delayLookupForTesting = false, | |
| }) { | |
| AnnotationsListener listener = new AnnotationsListener( | |
| fileUri, | |
| scope, | |
| references, | |
| delayLookup: delayLookupForTesting, | |
| isDartLibrary: isDartLibrary, | |
| ); | |
| Parser parser = new Parser( | |
| listener, | |
| useImplicitCreationExpression: false, | |
| experimentalFeatures: experimentalFeatures, | |
| ); | |
| parser.parseMetadata(parser.syntheticPreviousToken(atToken)); | |
| return listener.pop() as Expression; | |
| } | |
| /// Parses the expression beginning at [initializerToken]. | |
| Expression parseExpression( | |
| Token initializerToken, | |
| Uri fileUri, | |
| Scope scope, | |
| References references, | |
| ExperimentalFeatures experimentalFeatures, { | |
| required bool isDartLibrary, | |
| bool delayLookupForTesting = false, | |
| }) { | |
| AnnotationsListener listener = new AnnotationsListener( | |
| fileUri, | |
| scope, | |
| references, | |
| delayLookup: delayLookupForTesting, | |
| isDartLibrary: isDartLibrary, | |
| ); | |
| Parser parser = new Parser( | |
| listener, | |
| useImplicitCreationExpression: false, | |
| experimentalFeatures: experimentalFeatures, | |
| ); | |
| parser.parseExpression(parser.syntheticPreviousToken(initializerToken)); | |
| return listener._popExpression(); | |
| } | |
| /// A [Scope] extended to include function type parameters. | |
| class FunctionTypeParameterScope implements Scope { | |
| final Scope parentScope; | |
| final Map<String, FunctionTypeParameter> functionTypeParameterMap = {}; | |
| FunctionTypeParameterScope(this.parentScope); | |
| FunctionTypeParameter declareTypeParameter(String name) { | |
| return functionTypeParameterMap[name] = new FunctionTypeParameter(name); | |
| } | |
| @override | |
| Proto lookup(String name) { | |
| FunctionTypeParameter? functionTypeParameter = | |
| functionTypeParameterMap[name]; | |
| if (functionTypeParameter != null) { | |
| return new FunctionTypeParameterProto(functionTypeParameter); | |
| } | |
| return parentScope.lookup(name); | |
| } | |
| } |