| // 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:analyzer/dart/ast/token.dart'; | 
 | import 'package:analyzer/dart/element/element.dart'; | 
 | import 'package:analyzer/dart/element/type.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_system.dart'; | 
 | import 'package:analyzer/src/dart/error/syntactic_errors.dart'; | 
 | import 'package:analyzer/src/dart/resolver/assignment_expression_resolver.dart'; | 
 | import 'package:analyzer/src/dart/resolver/invocation_inference_helper.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 [PostfixExpression]s. | 
 | class PostfixExpressionResolver { | 
 |   final ResolverVisitor _resolver; | 
 |   final TypePropertyResolver _typePropertyResolver; | 
 |   final InvocationInferenceHelper _inferenceHelper; | 
 |   final AssignmentExpressionShared _assignmentShared; | 
 |  | 
 |   PostfixExpressionResolver({ | 
 |     required ResolverVisitor resolver, | 
 |   })  : _resolver = resolver, | 
 |         _typePropertyResolver = resolver.typePropertyResolver, | 
 |         _inferenceHelper = resolver.inferenceHelper, | 
 |         _assignmentShared = AssignmentExpressionShared( | 
 |           resolver: resolver, | 
 |         ); | 
 |  | 
 |   ErrorReporter get _errorReporter => _resolver.errorReporter; | 
 |  | 
 |   bool get _isNonNullableByDefault => _typeSystem.isNonNullableByDefault; | 
 |  | 
 |   TypeSystemImpl get _typeSystem => _resolver.typeSystem; | 
 |  | 
 |   void resolve(PostfixExpressionImpl node, {required DartType? contextType}) { | 
 |     if (node.operator.type == TokenType.BANG) { | 
 |       _resolveNullCheck(node, contextType: contextType); | 
 |       return; | 
 |     } | 
 |  | 
 |     var operandResolution = _resolver.resolveForWrite( | 
 |       node: node.operand, | 
 |       hasRead: true, | 
 |     ); | 
 |  | 
 |     var readElement = operandResolution.readElement; | 
 |     var writeElement = operandResolution.writeElement; | 
 |  | 
 |     var operand = node.operand; | 
 |     _resolver.setReadElement( | 
 |       operand, | 
 |       readElement, | 
 |       atDynamicTarget: operandResolution.atDynamicTarget, | 
 |     ); | 
 |     _resolver.setWriteElement( | 
 |       operand, | 
 |       writeElement, | 
 |       atDynamicTarget: operandResolution.atDynamicTarget, | 
 |     ); | 
 |     _resolver.migrationResolutionHooks | 
 |         ?.setCompoundAssignmentExpressionTypes(node); | 
 |  | 
 |     // TODO(scheglov) Use VariableElement and do in resolveForWrite() ? | 
 |     _assignmentShared.checkFinalAlreadyAssigned(operand); | 
 |  | 
 |     var receiverType = node.readType!; | 
 |     _resolve1(node, receiverType); | 
 |     _resolve2(node, receiverType, contextType: contextType); | 
 |   } | 
 |  | 
 |   /// 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( | 
 |       PostfixExpression node, Expression operand, DartType type) { | 
 |     var operandWriteType = node.writeType!; | 
 |     if (!_typeSystem.isAssignableTo(type, operandWriteType)) { | 
 |       _resolver.errorReporter.reportErrorForNode( | 
 |         CompileTimeErrorCode.INVALID_ASSIGNMENT, | 
 |         node, | 
 |         [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. | 
 |       // | 
 |       FunctionType propertyType = element.type; | 
 |       return InvocationInferrer.computeInvokeReturnType( | 
 |         propertyType.returnType, | 
 |       ); | 
 |     } else if (element is ExecutableElement) { | 
 |       return InvocationInferrer.computeInvokeReturnType(element.type); | 
 |     } | 
 |     return DynamicTypeImpl.instance; | 
 |   } | 
 |  | 
 |   /// Return the name of the method invoked by the given postfix [expression]. | 
 |   String _getPostfixOperator(PostfixExpression expression) { | 
 |     if (expression.operator.type == TokenType.PLUS_PLUS) { | 
 |       return TokenType.PLUS.lexeme; | 
 |     } else if (expression.operator.type == TokenType.MINUS_MINUS) { | 
 |       return TokenType.MINUS.lexeme; | 
 |     } else { | 
 |       throw UnsupportedError( | 
 |           'Unsupported postfix operator ${expression.operator.lexeme}'); | 
 |     } | 
 |   } | 
 |  | 
 |   void _resolve1(PostfixExpressionImpl node, DartType receiverType) { | 
 |     Expression operand = node.operand; | 
 |  | 
 |     if (identical(receiverType, NeverTypeImpl.instance)) { | 
 |       _resolver.errorReporter.reportErrorForNode( | 
 |         WarningCode.RECEIVER_OF_TYPE_NEVER, | 
 |         operand, | 
 |       ); | 
 |       return; | 
 |     } | 
 |  | 
 |     String methodName = _getPostfixOperator(node); | 
 |     var result = _typePropertyResolver.resolve( | 
 |       receiver: operand, | 
 |       receiverType: receiverType, | 
 |       name: methodName, | 
 |       propertyErrorEntity: node.operator, | 
 |       nameErrorEntity: operand, | 
 |     ); | 
 |     node.staticElement = result.getter as MethodElement?; | 
 |     if (result.needsGetterError) { | 
 |       if (operand is SuperExpression) { | 
 |         _errorReporter.reportErrorForToken( | 
 |           CompileTimeErrorCode.UNDEFINED_SUPER_OPERATOR, | 
 |           node.operator, | 
 |           [methodName, receiverType], | 
 |         ); | 
 |       } else { | 
 |         _errorReporter.reportErrorForToken( | 
 |           CompileTimeErrorCode.UNDEFINED_OPERATOR, | 
 |           node.operator, | 
 |           [methodName, receiverType], | 
 |         ); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   void _resolve2(PostfixExpressionImpl node, DartType receiverType, | 
 |       {required DartType? contextType}) { | 
 |     Expression operand = node.operand; | 
 |  | 
 |     if (identical(receiverType, NeverTypeImpl.instance)) { | 
 |       _inferenceHelper.recordStaticType(node, NeverTypeImpl.instance, | 
 |           contextType: contextType); | 
 |     } else { | 
 |       DartType operatorReturnType; | 
 |       if (receiverType.isDartCoreInt) { | 
 |         // No need to check for `intVar++`, the result is `int`. | 
 |         operatorReturnType = receiverType; | 
 |       } else { | 
 |         var operatorElement = node.staticElement; | 
 |         operatorReturnType = _computeStaticReturnType(operatorElement); | 
 |         _checkForInvalidAssignmentIncDec(node, operand, operatorReturnType); | 
 |       } | 
 |       if (operand is SimpleIdentifier) { | 
 |         var element = operand.staticElement; | 
 |         if (element is PromotableElement) { | 
 |           _resolver.flowAnalysis.flow | 
 |               ?.write(node, element, operatorReturnType, null); | 
 |         } | 
 |       } | 
 |     } | 
 |  | 
 |     _inferenceHelper.recordStaticType(node, receiverType, | 
 |         contextType: contextType); | 
 |     _resolver.nullShortingTermination(node); | 
 |   } | 
 |  | 
 |   void _resolveNullCheck(PostfixExpressionImpl node, | 
 |       {required DartType? contextType}) { | 
 |     var operand = node.operand; | 
 |  | 
 |     if (operand is SuperExpression) { | 
 |       _resolver.errorReporter.reportErrorForNode( | 
 |         ParserErrorCode.MISSING_ASSIGNABLE_SELECTOR, | 
 |         node, | 
 |       ); | 
 |       _inferenceHelper.recordStaticType(operand, DynamicTypeImpl.instance, | 
 |           contextType: contextType); | 
 |       _inferenceHelper.recordStaticType(node, DynamicTypeImpl.instance, | 
 |           contextType: contextType); | 
 |       return; | 
 |     } | 
 |  | 
 |     if (contextType != null && _isNonNullableByDefault) { | 
 |       contextType = _typeSystem.makeNullable(contextType); | 
 |     } | 
 |  | 
 |     _resolver.analyzeExpression(operand, contextType); | 
 |     operand = _resolver.popRewrite()!; | 
 |  | 
 |     var operandType = operand.typeOrThrow; | 
 |  | 
 |     var type = _typeSystem.promoteToNonNull(operandType); | 
 |     _inferenceHelper.recordStaticType(node, type, contextType: contextType); | 
 |  | 
 |     _resolver.nullShortingTermination(node); | 
 |     _resolver.flowAnalysis.flow?.nonNullAssert_end(operand); | 
 |   } | 
 | } |