|  | // Copyright (c) 2020, 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/types/shared_type.dart'; | 
|  | import 'package:analyzer/dart/ast/token.dart'; | 
|  | import 'package:analyzer/dart/element/element.dart'; | 
|  | import 'package:analyzer/dart/element/type.dart'; | 
|  | import 'package:analyzer/dart/element/type_provider.dart'; | 
|  | import 'package:analyzer/error/listener.dart'; | 
|  | import 'package:analyzer/src/dart/ast/ast.dart'; | 
|  | import 'package:analyzer/src/dart/ast/extensions.dart'; | 
|  | import 'package:analyzer/src/dart/element/type.dart'; | 
|  | import 'package:analyzer/src/dart/element/type_schema.dart'; | 
|  | import 'package:analyzer/src/dart/element/type_system.dart'; | 
|  | import 'package:analyzer/src/dart/resolver/assignment_expression_resolver.dart'; | 
|  | import 'package:analyzer/src/dart/resolver/invocation_inferrer.dart'; | 
|  | import 'package:analyzer/src/dart/resolver/type_property_resolver.dart'; | 
|  | import 'package:analyzer/src/error/codes.dart'; | 
|  | import 'package:analyzer/src/generated/resolver.dart'; | 
|  |  | 
|  | /// Helper for resolving [PrefixExpression]s. | 
|  | class PrefixExpressionResolver { | 
|  | final ResolverVisitor _resolver; | 
|  | final TypePropertyResolver _typePropertyResolver; | 
|  | final AssignmentExpressionShared _assignmentShared; | 
|  |  | 
|  | PrefixExpressionResolver({ | 
|  | required ResolverVisitor resolver, | 
|  | })  : _resolver = resolver, | 
|  | _typePropertyResolver = resolver.typePropertyResolver, | 
|  | _assignmentShared = AssignmentExpressionShared( | 
|  | resolver: resolver, | 
|  | ); | 
|  |  | 
|  | ErrorReporter get _errorReporter => _resolver.errorReporter; | 
|  |  | 
|  | TypeProvider get _typeProvider => _resolver.typeProvider; | 
|  |  | 
|  | TypeSystemImpl get _typeSystem => _resolver.typeSystem; | 
|  |  | 
|  | void resolve(PrefixExpressionImpl node, {required DartType contextType}) { | 
|  | var operator = node.operator.type; | 
|  |  | 
|  | if (operator == TokenType.BANG) { | 
|  | _resolveNegation(node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (node.operand case AugmentedExpressionImpl operand) { | 
|  | _resolveAugmented(node, operand); | 
|  | return; | 
|  | } | 
|  |  | 
|  | var operand = node.operand; | 
|  | if (operator.isIncrementOperator) { | 
|  | var operandResolution = _resolver.resolveForWrite( | 
|  | node: node.operand, | 
|  | hasRead: true, | 
|  | ); | 
|  |  | 
|  | var readElement = operandResolution.readElement; | 
|  | var writeElement = operandResolution.writeElement; | 
|  |  | 
|  | _resolver.setReadElement( | 
|  | operand, | 
|  | readElement, | 
|  | atDynamicTarget: operandResolution.atDynamicTarget, | 
|  | ); | 
|  | _resolver.setWriteElement( | 
|  | operand, | 
|  | writeElement, | 
|  | atDynamicTarget: operandResolution.atDynamicTarget, | 
|  | ); | 
|  |  | 
|  | _assignmentShared.checkFinalAlreadyAssigned(node.operand); | 
|  | } else { | 
|  | DartType innerContextType; | 
|  | if (operator == TokenType.MINUS && operand is IntegerLiteralImpl) { | 
|  | // Negated integer literals should undergo int->double conversion in the | 
|  | // same circumstances as non-negated integer literals, so pass the | 
|  | // context type through. | 
|  | innerContextType = contextType; | 
|  | } else { | 
|  | innerContextType = UnknownInferredType.instance; | 
|  | } | 
|  | _resolver.analyzeExpression( | 
|  | operand, SharedTypeSchemaView(innerContextType)); | 
|  | _resolver.popRewrite(); | 
|  | } | 
|  |  | 
|  | _resolve1(node); | 
|  | _resolve2(node); | 
|  | } | 
|  |  | 
|  | /// Check that the result [type] of a prefix or postfix `++` or `--` | 
|  | /// expression is assignable to the write type of the operand. | 
|  | /// | 
|  | // TODO(scheglov): this is duplicate | 
|  | void _checkForInvalidAssignmentIncDec( | 
|  | PrefixExpressionImpl node, DartType type) { | 
|  | var operandWriteType = node.writeType!; | 
|  | if (!_typeSystem.isAssignableTo(type, operandWriteType, | 
|  | strictCasts: _resolver.analysisOptions.strictCasts)) { | 
|  | _resolver.errorReporter.atNode( | 
|  | node, | 
|  | CompileTimeErrorCode.INVALID_ASSIGNMENT, | 
|  | arguments: [type, operandWriteType], | 
|  | ); | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Compute the static return type of the method or function represented by the given element. | 
|  | /// | 
|  | /// @param element the element representing the method or function invoked by the given node | 
|  | /// @return the static return type that was computed | 
|  | /// | 
|  | // TODO(scheglov): this is duplicate | 
|  | DartType _computeStaticReturnType(Element? element) { | 
|  | if (element is PropertyAccessorElement) { | 
|  | // | 
|  | // This is a function invocation expression disguised as something else. | 
|  | // We are invoking a getter and then invoking the returned function. | 
|  | // | 
|  | var propertyType = element.type; | 
|  | return InvocationInferrer.computeInvokeReturnType( | 
|  | propertyType.returnType, | 
|  | ); | 
|  | } else if (element is ExecutableElement) { | 
|  | return InvocationInferrer.computeInvokeReturnType(element.type); | 
|  | } | 
|  | return InvalidTypeImpl.instance; | 
|  | } | 
|  |  | 
|  | /// Return the name of the method invoked by the given postfix [expression]. | 
|  | String _getPrefixOperator(PrefixExpression expression) { | 
|  | var operator = expression.operator; | 
|  | var operatorType = operator.type; | 
|  | if (operatorType == TokenType.PLUS_PLUS) { | 
|  | return TokenType.PLUS.lexeme; | 
|  | } else if (operatorType == TokenType.MINUS_MINUS) { | 
|  | return TokenType.MINUS.lexeme; | 
|  | } else if (operatorType == TokenType.MINUS) { | 
|  | return "unary-"; | 
|  | } else { | 
|  | return operator.lexeme; | 
|  | } | 
|  | } | 
|  |  | 
|  | void _resolve1(PrefixExpressionImpl node) { | 
|  | Token operator = node.operator; | 
|  | TokenType operatorType = operator.type; | 
|  | if (operatorType.isUserDefinableOperator || | 
|  | operatorType.isIncrementOperator) { | 
|  | Expression operand = node.operand; | 
|  | String methodName = _getPrefixOperator(node); | 
|  | if (operand is ExtensionOverride) { | 
|  | var element = operand.element; | 
|  | var member = element.getMethod(methodName); | 
|  | if (member == null) { | 
|  | // Extension overrides always refer to named extensions, so we can | 
|  | // safely assume `element.name` is non-`null`. | 
|  | _errorReporter.atToken( | 
|  | node.operator, | 
|  | CompileTimeErrorCode.UNDEFINED_EXTENSION_OPERATOR, | 
|  | arguments: [methodName, element.name!], | 
|  | ); | 
|  | } | 
|  | node.staticElement = member; | 
|  | return; | 
|  | } | 
|  |  | 
|  | var readType = node.readType ?? operand.typeOrThrow; | 
|  | if (readType is InvalidType) { | 
|  | return; | 
|  | } | 
|  | if (identical(readType, NeverTypeImpl.instance)) { | 
|  | _resolver.errorReporter.atNode( | 
|  | operand, | 
|  | WarningCode.RECEIVER_OF_TYPE_NEVER, | 
|  | ); | 
|  | return; | 
|  | } | 
|  |  | 
|  | var result = _typePropertyResolver.resolve( | 
|  | receiver: operand, | 
|  | receiverType: readType, | 
|  | name: methodName, | 
|  | propertyErrorEntity: node.operator, | 
|  | nameErrorEntity: operand, | 
|  | ); | 
|  | node.staticElement = result.getter as MethodElement?; | 
|  | if (result.needsGetterError) { | 
|  | if (operand is SuperExpression) { | 
|  | _errorReporter.atToken( | 
|  | operator, | 
|  | CompileTimeErrorCode.UNDEFINED_SUPER_OPERATOR, | 
|  | arguments: [methodName, readType], | 
|  | ); | 
|  | } else { | 
|  | _errorReporter.atToken( | 
|  | operator, | 
|  | CompileTimeErrorCode.UNDEFINED_OPERATOR, | 
|  | arguments: [methodName, readType], | 
|  | ); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void _resolve2(PrefixExpressionImpl node) { | 
|  | TokenType operator = node.operator.type; | 
|  | var readType = node.readType ?? node.operand.staticType; | 
|  | if (identical(readType, NeverTypeImpl.instance)) { | 
|  | node.recordStaticType(NeverTypeImpl.instance, resolver: _resolver); | 
|  | } else { | 
|  | // The other cases are equivalent to invoking a method. | 
|  | DartType staticType; | 
|  | if (readType is DynamicType) { | 
|  | staticType = DynamicTypeImpl.instance; | 
|  | } else if (readType is InvalidType) { | 
|  | staticType = InvalidTypeImpl.instance; | 
|  | } else { | 
|  | var staticMethodElement = node.staticElement; | 
|  | staticType = _computeStaticReturnType(staticMethodElement); | 
|  | } | 
|  | Expression operand = node.operand; | 
|  | if (operand is ExtensionOverride) { | 
|  | // No special handling for incremental operators. | 
|  | } else if (operator.isIncrementOperator) { | 
|  | if (readType!.isDartCoreInt) { | 
|  | staticType = _typeProvider.intType; | 
|  | } else { | 
|  | _checkForInvalidAssignmentIncDec(node, staticType); | 
|  | } | 
|  | if (operand is SimpleIdentifier) { | 
|  | var element = operand.staticElement; | 
|  | if (element is PromotableElement) { | 
|  | _resolver.flowAnalysis.flow | 
|  | ?.write(node, element, SharedTypeView(staticType), null); | 
|  | } | 
|  | } | 
|  | } | 
|  | node.recordStaticType(staticType, resolver: _resolver); | 
|  | } | 
|  | _resolver.nullShortingTermination(node); | 
|  | } | 
|  |  | 
|  | void _resolveAugmented( | 
|  | PrefixExpressionImpl node, | 
|  | AugmentedExpressionImpl operand, | 
|  | ) { | 
|  | var methodName = _getPrefixOperator(node); | 
|  | var augmentation = _resolver.enclosingAugmentation!; | 
|  | var augmentationTarget = augmentation.augmentationTarget; | 
|  |  | 
|  | // Unresolved by default. | 
|  | operand.setPseudoExpressionStaticType(InvalidTypeImpl.instance); | 
|  |  | 
|  | switch (augmentationTarget) { | 
|  | case MethodElement operatorElement: | 
|  | operand.element = operatorElement; | 
|  | operand.setPseudoExpressionStaticType( | 
|  | _resolver.thisType ?? InvalidTypeImpl.instance); | 
|  | if (operatorElement.name == methodName) { | 
|  | node.staticElement = operatorElement; | 
|  | node.recordStaticType(operatorElement.returnType, | 
|  | resolver: _resolver); | 
|  | } else { | 
|  | _errorReporter.atToken( | 
|  | operand.augmentedKeyword, | 
|  | CompileTimeErrorCode.AUGMENTED_EXPRESSION_NOT_OPERATOR, | 
|  | arguments: [ | 
|  | methodName, | 
|  | ], | 
|  | ); | 
|  | node.recordStaticType(InvalidTypeImpl.instance, resolver: _resolver); | 
|  | } | 
|  | case PropertyAccessorElement accessor: | 
|  | operand.element = accessor; | 
|  | if (accessor.isGetter) { | 
|  | operand.setPseudoExpressionStaticType(accessor.returnType); | 
|  | _resolve1(node); | 
|  | _resolve2(node); | 
|  | } else { | 
|  | _errorReporter.atToken( | 
|  | operand.augmentedKeyword, | 
|  | CompileTimeErrorCode.AUGMENTED_EXPRESSION_IS_SETTER, | 
|  | ); | 
|  | node.recordStaticType(InvalidTypeImpl.instance, resolver: _resolver); | 
|  | } | 
|  | case PropertyInducingElement property: | 
|  | operand.element = property; | 
|  | operand.setPseudoExpressionStaticType(property.type); | 
|  | _resolve1(node); | 
|  | _resolve2(node); | 
|  | } | 
|  | } | 
|  |  | 
|  | void _resolveNegation(PrefixExpressionImpl node) { | 
|  | var operand = node.operand; | 
|  |  | 
|  | _resolver.analyzeExpression( | 
|  | operand, SharedTypeSchemaView(_typeProvider.boolType)); | 
|  | operand = _resolver.popRewrite()!; | 
|  | var whyNotPromoted = _resolver.flowAnalysis.flow?.whyNotPromoted(operand); | 
|  |  | 
|  | _resolver.boolExpressionVerifier.checkForNonBoolNegationExpression(operand, | 
|  | whyNotPromoted: whyNotPromoted); | 
|  |  | 
|  | node.recordStaticType(_typeProvider.boolType, resolver: _resolver); | 
|  |  | 
|  | _resolver.flowAnalysis.flow?.logicalNot_end(node, operand); | 
|  | } | 
|  | } |