blob: ffaf6b4131180de2286038fa1a82df68150d8334 [file] [log] [blame]
// 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);
}
}