Integrate shared type analysis of switch statements with analyzer.

This change replaces ResolverVisitor.visitSwitchStatement
with a call to the new shared type analyzer, and implements the
necessary callbacks to allow analysis to work end-to-end.

Additionally, it changes the convention context types in resolver
methods so that UnknownInferredType is now consistently used to
represent the unknown type (`?`).  Previously `null` was sometimes
used instead.

Change-Id: Idb81a7dc888dfbf460c78b93a99367c0c9263610
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/256960
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Paul Berry 2022-09-21 23:00:20 +00:00 committed by Commit Bot
parent 18a73f7591
commit 3fdc3c2588
23 changed files with 593 additions and 208 deletions

View file

@ -160,6 +160,7 @@ class LibraryAnalyzer {
var canResolveNode = resolverVisitor.prepareForResolving(nodeToResolve);
if (canResolveNode) {
nodeToResolve.accept(resolverVisitor);
resolverVisitor.checkIdle();
return AnalysisForCompletionResult(
parsedUnit: parsedUnit,
resolvedNodes: [nodeToResolve],

View file

@ -61,7 +61,7 @@ class AdjacentStringsImpl extends StringLiteralImpl implements AdjacentStrings {
E? accept<E>(AstVisitor<E> visitor) => visitor.visitAdjacentStrings(this);
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitAdjacentStrings(this, contextType: contextType);
}
@ -483,7 +483,7 @@ class AsExpressionImpl extends ExpressionImpl implements AsExpression {
E? accept<E>(AstVisitor<E> visitor) => visitor.visitAsExpression(this);
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitAsExpression(this, contextType: contextType);
}
@ -752,7 +752,7 @@ class AssignmentExpressionImpl extends ExpressionImpl
visitor.visitAssignmentExpression(this);
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitAssignmentExpression(this, contextType: contextType);
}
@ -984,7 +984,7 @@ class AwaitExpressionImpl extends ExpressionImpl implements AwaitExpression {
E? accept<E>(AstVisitor<E> visitor) => visitor.visitAwaitExpression(this);
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitAwaitExpression(this, contextType: contextType);
}
@ -1062,7 +1062,7 @@ class BinaryExpressionImpl extends ExpressionImpl implements BinaryExpression {
E? accept<E>(AstVisitor<E> visitor) => visitor.visitBinaryExpression(this);
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitBinaryExpression(this, contextType: contextType);
}
@ -1280,7 +1280,7 @@ class BooleanLiteralImpl extends LiteralImpl implements BooleanLiteral {
E? accept<E>(AstVisitor<E> visitor) => visitor.visitBooleanLiteral(this);
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitBooleanLiteral(this, contextType: contextType);
}
@ -1423,7 +1423,7 @@ class CascadeExpressionImpl extends ExpressionImpl
E? accept<E>(AstVisitor<E> visitor) => visitor.visitCascadeExpression(this);
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitCascadeExpression(this, contextType: contextType);
}
@ -2572,7 +2572,7 @@ class ConditionalExpressionImpl extends ExpressionImpl
visitor.visitConditionalExpression(this);
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitConditionalExpression(this, contextType: contextType);
}
@ -3135,7 +3135,7 @@ class ConstructorReferenceImpl extends CommentReferableExpressionImpl
visitor.visitConstructorReference(this);
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitConstructorReference(this, contextType: contextType);
}
@ -3666,7 +3666,7 @@ class DoubleLiteralImpl extends LiteralImpl implements DoubleLiteral {
E? accept<E>(AstVisitor<E> visitor) => visitor.visitDoubleLiteral(this);
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitDoubleLiteral(this, contextType: contextType);
}
@ -4236,7 +4236,7 @@ abstract class ExpressionImpl extends AstNodeImpl
/// Note: most code shouldn't call this method directly, but should instead
/// call [ResolverVisitor.analyzeExpression], which has some special logic for
/// handling dynamic contexts.
void resolveExpression(ResolverVisitor resolver, DartType? contextType);
void resolveExpression(ResolverVisitor resolver, DartType contextType);
}
/// An expression used as a statement.
@ -4561,7 +4561,7 @@ class ExtensionOverrideImpl extends ExpressionImpl
}
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitExtensionOverride(this, contextType: contextType);
}
@ -5091,6 +5091,7 @@ class ForElementImpl extends CollectionElementImpl implements ForElement {
void resolveElement(
ResolverVisitor resolver, CollectionLiteralContext? context) {
resolver.visitForElement(this, context: context);
resolver.pushRewrite(null);
}
@override
@ -5814,7 +5815,7 @@ class FunctionExpressionImpl extends ExpressionImpl
E? accept<E>(AstVisitor<E> visitor) => visitor.visitFunctionExpression(this);
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitFunctionExpression(this, contextType: contextType);
}
@ -5883,7 +5884,7 @@ class FunctionExpressionInvocationImpl extends InvocationExpressionImpl
visitor.visitFunctionExpressionInvocation(this);
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitFunctionExpressionInvocation(this, contextType: contextType);
}
@ -5952,7 +5953,7 @@ class FunctionReferenceImpl extends CommentReferableExpressionImpl
E? accept<E>(AstVisitor<E> visitor) => visitor.visitFunctionReference(this);
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitFunctionReference(this, contextType: contextType);
}
@ -6554,6 +6555,7 @@ class IfElementImpl extends CollectionElementImpl implements IfElement {
void resolveElement(
ResolverVisitor resolver, CollectionLiteralContext? context) {
resolver.visitIfElement(this, context: context);
resolver.pushRewrite(null);
}
@override
@ -6775,7 +6777,7 @@ class ImplicitCallReferenceImpl extends ExpressionImpl
}
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitImplicitCallReference(this, contextType: contextType);
}
@ -7098,7 +7100,7 @@ class IndexExpressionImpl extends ExpressionImpl
}
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitIndexExpression(this, contextType: contextType);
}
@ -7212,7 +7214,7 @@ class InstanceCreationExpressionImpl extends ExpressionImpl
visitor.visitInstanceCreationExpression(this);
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitInstanceCreationExpression(this, contextType: contextType);
}
@ -7277,7 +7279,7 @@ class IntegerLiteralImpl extends LiteralImpl implements IntegerLiteral {
E? accept<E>(AstVisitor<E> visitor) => visitor.visitIntegerLiteral(this);
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitIntegerLiteral(this, contextType: contextType);
}
@ -7560,7 +7562,7 @@ class IsExpressionImpl extends ExpressionImpl implements IsExpression {
E? accept<E>(AstVisitor<E> visitor) => visitor.visitIsExpression(this);
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitIsExpression(this, contextType: contextType);
}
@ -7831,7 +7833,7 @@ class LibraryIdentifierImpl extends IdentifierImpl
E? accept<E>(AstVisitor<E> visitor) => visitor.visitLibraryIdentifier(this);
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitLibraryIdentifier(this, contextType: contextType);
}
@ -7901,7 +7903,7 @@ class ListLiteralImpl extends TypedLiteralImpl implements ListLiteral {
E? accept<E>(AstVisitor<E> visitor) => visitor.visitListLiteral(this);
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitListLiteral(this, contextType: contextType);
}
@ -8052,6 +8054,7 @@ class MapLiteralEntryImpl extends CollectionElementImpl
void resolveElement(
ResolverVisitor resolver, CollectionLiteralContext? context) {
resolver.visitMapLiteralEntry(this, context: context);
resolver.pushRewrite(null);
}
@override
@ -8472,7 +8475,7 @@ class MethodInvocationImpl extends InvocationExpressionImpl
E? accept<E>(AstVisitor<E> visitor) => visitor.visitMethodInvocation(this);
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitMethodInvocation(this, contextType: contextType);
}
@ -8693,7 +8696,7 @@ class NamedExpressionImpl extends ExpressionImpl implements NamedExpression {
E? accept<E>(AstVisitor<E> visitor) => visitor.visitNamedExpression(this);
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitNamedExpression(this, contextType: contextType);
}
@ -9170,7 +9173,7 @@ class NullLiteralImpl extends LiteralImpl implements NullLiteral {
E? accept<E>(AstVisitor<E> visitor) => visitor.visitNullLiteral(this);
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitNullLiteral(this, contextType: contextType);
}
@ -9311,7 +9314,7 @@ class ParenthesizedExpressionImpl extends ExpressionImpl
visitor.visitParenthesizedExpression(this);
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitParenthesizedExpression(this, contextType: contextType);
}
@ -9747,7 +9750,7 @@ class PostfixExpressionImpl extends ExpressionImpl
E? accept<E>(AstVisitor<E> visitor) => visitor.visitPostfixExpression(this);
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitPostfixExpression(this, contextType: contextType);
}
@ -9874,7 +9877,7 @@ class PrefixedIdentifierImpl extends IdentifierImpl
E? accept<E>(AstVisitor<E> visitor) => visitor.visitPrefixedIdentifier(this);
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitPrefixedIdentifier(this, contextType: contextType);
}
@ -9953,7 +9956,7 @@ class PrefixExpressionImpl extends ExpressionImpl
E? accept<E>(AstVisitor<E> visitor) => visitor.visitPrefixExpression(this);
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitPrefixExpression(this, contextType: contextType);
}
@ -10072,7 +10075,7 @@ class PropertyAccessImpl extends CommentReferableExpressionImpl
E? accept<E>(AstVisitor<E> visitor) => visitor.visitPropertyAccess(this);
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitPropertyAccess(this, contextType: contextType);
}
@ -10133,7 +10136,7 @@ class RecordLiteralImpl extends LiteralImpl implements RecordLiteral {
E? accept<E>(AstVisitor<E> visitor) => visitor.visitRecordLiteral(this);
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitRecordLiteral(this, contextType: contextType);
}
@ -10575,7 +10578,7 @@ class RethrowExpressionImpl extends ExpressionImpl
E? accept<E>(AstVisitor<E> visitor) => visitor.visitRethrowExpression(this);
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitRethrowExpression(this, contextType: contextType);
}
@ -10755,7 +10758,7 @@ class SetOrMapLiteralImpl extends TypedLiteralImpl implements SetOrMapLiteral {
}
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitSetOrMapLiteral(this, contextType: contextType);
}
@ -11171,7 +11174,7 @@ class SimpleIdentifierImpl extends IdentifierImpl implements SimpleIdentifier {
}
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitSimpleIdentifier(this, contextType: contextType);
}
@ -11250,7 +11253,7 @@ class SimpleStringLiteralImpl extends SingleStringLiteralImpl
E? accept<E>(AstVisitor<E> visitor) => visitor.visitSimpleStringLiteral(this);
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitSimpleStringLiteral(this, contextType: contextType);
}
@ -11315,6 +11318,7 @@ class SpreadElementImpl extends AstNodeImpl
void resolveElement(
ResolverVisitor resolver, CollectionLiteralContext? context) {
resolver.visitSpreadElement(this, context: context);
resolver.pushRewrite(null);
}
@override
@ -11427,7 +11431,7 @@ class StringInterpolationImpl extends SingleStringLiteralImpl
E? accept<E>(AstVisitor<E> visitor) => visitor.visitStringInterpolation(this);
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitStringInterpolation(this, contextType: contextType);
}
@ -11661,7 +11665,7 @@ class SuperExpressionImpl extends ExpressionImpl implements SuperExpression {
E? accept<E>(AstVisitor<E> visitor) => visitor.visitSuperExpression(this);
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitSuperExpression(this, contextType: contextType);
}
@ -12273,7 +12277,7 @@ class SymbolLiteralImpl extends LiteralImpl implements SymbolLiteral {
E? accept<E>(AstVisitor<E> visitor) => visitor.visitSymbolLiteral(this);
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitSymbolLiteral(this, contextType: contextType);
}
@ -12312,7 +12316,7 @@ class ThisExpressionImpl extends ExpressionImpl implements ThisExpression {
E? accept<E>(AstVisitor<E> visitor) => visitor.visitThisExpression(this);
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitThisExpression(this, contextType: contextType);
}
@ -12366,7 +12370,7 @@ class ThrowExpressionImpl extends ExpressionImpl implements ThrowExpression {
E? accept<E>(AstVisitor<E> visitor) => visitor.visitThrowExpression(this);
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitThrowExpression(this, contextType: contextType);
}
@ -12706,7 +12710,7 @@ class TypeLiteralImpl extends CommentReferableExpressionImpl
E? accept<E>(AstVisitor<E> visitor) => visitor.visitTypeLiteral(this);
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
void resolveExpression(ResolverVisitor resolver, DartType contextType) {
resolver.visitTypeLiteral(this, contextType: contextType);
}

View file

@ -1876,7 +1876,7 @@ class NodeReplacer extends ThrowingAstVisitor<bool> {
/// Initialize a newly created node locator to replace the [_oldNode] with the
/// [_newNode].
NodeReplacer(this._oldNode, this._newNode);
NodeReplacer._(this._oldNode, this._newNode);
@override
bool visitAdjacentStrings(covariant AdjacentStringsImpl node) {
@ -3393,7 +3393,7 @@ class NodeReplacer extends ThrowingAstVisitor<bool> {
if (parent == null) {
throw ArgumentError("The old node is not a child of another node");
}
NodeReplacer replacer = NodeReplacer(oldNode, newNode);
NodeReplacer replacer = NodeReplacer._(oldNode, newNode);
return parent.accept(replacer)!;
}
}

View file

@ -664,7 +664,6 @@ class ConstantVerifier extends RecursiveAstVisitor<void> {
}
void _validateSwitchStatement_nullSafety(SwitchStatement node) {
var switchType = node.expression.typeOrThrow;
for (var switchMember in node.members) {
if (switchMember is SwitchCase) {
Expression expression = switchMember.expression;
@ -692,15 +691,6 @@ class ConstantVerifier extends RecursiveAstVisitor<void> {
[expressionType],
);
}
if (!_typeSystem.isSubtypeOf(expressionType, switchType)) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode
.CASE_EXPRESSION_TYPE_IS_NOT_SWITCH_EXPRESSION_SUBTYPE,
expression,
[expressionType, switchType],
);
}
}
}
}

View file

@ -94,7 +94,7 @@ class AssignmentExpressionResolver {
}
_resolver.analyzeExpression(right, rhsContext);
right = node.rightHandSide;
right = _resolver.popRewrite()!;
var whyNotPromoted = flow?.whyNotPromoted(right);
_resolveTypes(node,

View file

@ -150,7 +150,7 @@ class BinaryExpressionResolver {
}
_resolver.analyzeExpression(left, leftContextType);
left = node.leftOperand;
left = _resolver.popRewrite()!;
var leftType = left.typeOrThrow;
var rightContextType = contextType;
@ -160,7 +160,7 @@ class BinaryExpressionResolver {
flow?.ifNullExpression_rightBegin(left, leftType);
_resolver.analyzeExpression(right, rightContextType);
right = node.rightOperand;
right = _resolver.popRewrite()!;
flow?.ifNullExpression_end();
var rightType = right.typeOrThrow;
@ -183,14 +183,14 @@ class BinaryExpressionResolver {
flow?.logicalBinaryOp_begin();
_resolver.analyzeExpression(left, _typeProvider.boolType);
left = node.leftOperand;
left = _resolver.popRewrite()!;
var leftWhyNotPromoted = _resolver.flowAnalysis.flow?.whyNotPromoted(left);
flow?.logicalBinaryOp_rightBegin(left, node, isAnd: true);
_resolver.checkUnreachableNode(right);
_resolver.analyzeExpression(right, _typeProvider.boolType);
right = node.rightOperand;
right = _resolver.popRewrite()!;
var rightWhyNotPromoted =
_resolver.flowAnalysis.flow?.whyNotPromoted(right);
@ -212,14 +212,14 @@ class BinaryExpressionResolver {
flow?.logicalBinaryOp_begin();
_resolver.analyzeExpression(left, _typeProvider.boolType);
left = node.leftOperand;
left = _resolver.popRewrite()!;
var leftWhyNotPromoted = _resolver.flowAnalysis.flow?.whyNotPromoted(left);
flow?.logicalBinaryOp_rightBegin(left, node, isAnd: false);
_resolver.checkUnreachableNode(right);
_resolver.analyzeExpression(right, _typeProvider.boolType);
right = node.rightOperand;
right = _resolver.popRewrite()!;
var rightWhyNotPromoted =
_resolver.flowAnalysis.flow?.whyNotPromoted(right);
@ -263,7 +263,7 @@ class BinaryExpressionResolver {
}
_resolver.analyzeExpression(right, rightContextType);
right = node.rightOperand;
right = _resolver.popRewrite()!;
var whyNotPromoted = _resolver.flowAnalysis.flow?.whyNotPromoted(right);
_resolveUserDefinableType(node, contextType: contextType);

View file

@ -61,7 +61,7 @@ class FlowAnalysisDataForTesting {
/// be extracted.
class FlowAnalysisHelper {
/// The reused instance for creating new [FlowAnalysis] instances.
final TypeSystemOperations _typeOperations;
final TypeSystemOperations typeOperations;
/// Precomputed sets of potentially assigned variables.
AssignedVariables<AstNode, PromotableElement>? assignedVariables;
@ -90,7 +90,7 @@ class FlowAnalysisHelper {
respectImplicitlyTypedVarInitializers:
featureSet.isEnabled(Feature.constructor_tearoffs));
FlowAnalysisHelper._(this._typeOperations, this.dataForTesting,
FlowAnalysisHelper._(this.typeOperations, this.dataForTesting,
{required this.isNonNullableByDefault,
required this.respectImplicitlyTypedVarInitializers});
@ -246,11 +246,11 @@ class FlowAnalysisHelper {
}
flow = isNonNullableByDefault
? FlowAnalysis<AstNode, Statement, Expression, PromotableElement,
DartType>(_typeOperations, assignedVariables!,
DartType>(typeOperations, assignedVariables!,
respectImplicitlyTypedVarInitializers:
respectImplicitlyTypedVarInitializers)
: FlowAnalysis<AstNode, Statement, Expression, PromotableElement,
DartType>.legacy(_typeOperations, assignedVariables!);
DartType>.legacy(typeOperations, assignedVariables!);
}
void topLevelDeclaration_exit() {
@ -378,7 +378,7 @@ class FlowAnalysisHelperForMigration extends FlowAnalysisHelper {
}
class TypeSystemOperations
with TypeOperations<DartType>
with TypeOperations<DartType>, TypeOperations2<DartType>
implements Operations<PromotableElement, DartType> {
final TypeSystemImpl typeSystem;
@ -400,6 +400,19 @@ class TypeSystemOperations
return typeSystem.factor(from, what);
}
@override
DartType glb(DartType type1, DartType type2) {
throw UnimplementedError('TODO(paulberry)');
}
@override
bool isAssignableTo(DartType fromType, DartType toType) {
return typeSystem.isAssignableTo(fromType, toType);
}
@override
bool isDynamic(DartType type) => type.isDynamic;
@override
bool isNever(DartType type) {
return typeSystem.isBottom(type);
@ -418,6 +431,21 @@ class TypeSystemOperations
@override
bool isTypeParameterType(DartType type) => type is TypeParameterType;
@override
DartType lub(DartType type1, DartType type2) {
throw UnimplementedError('TODO(paulberry)');
}
@override
DartType makeNullable(DartType type) {
throw UnimplementedError('TODO(paulberry)');
}
@override
DartType? matchListType(DartType type) {
throw UnimplementedError('TODO(paulberry)');
}
@override
DartType promoteToNonNull(DartType type) {
return typeSystem.promoteToNonNull(type);

View file

@ -29,6 +29,7 @@ class ForResolver {
var forLoopParts = node.forLoopParts;
void visitBody() {
node.body.resolveElement(_resolver, context);
_resolver.popRewrite();
}
if (forLoopParts is ForPartsImpl) {
@ -119,7 +120,7 @@ class ForResolver {
}
_resolver.analyzeExpression(iterable, targetType);
iterable = forEachParts.iterable;
iterable = _resolver.popRewrite()!;
_resolver.nullableDereferenceVerifier.expression(
CompileTimeErrorCode.UNCHECKED_USE_OF_NULLABLE_VALUE_AS_ITERATOR,
@ -165,7 +166,7 @@ class ForResolver {
var condition = forParts.condition;
if (condition != null) {
_resolver.analyzeExpression(condition, _resolver.typeProvider.boolType);
condition = forParts.condition!;
condition = _resolver.popRewrite()!;
var whyNotPromoted =
_resolver.flowAnalysis.flow?.whyNotPromoted(condition);
_resolver.boolExpressionVerifier

View file

@ -11,7 +11,6 @@ import 'package:analyzer/error/listener.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/ast/ast_factory.dart';
import 'package:analyzer/src/dart/ast/extensions.dart';
import 'package:analyzer/src/dart/ast/utilities.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/resolver/extension_member_resolver.dart';
import 'package:analyzer/src/error/codes.dart';
@ -291,7 +290,7 @@ class FunctionReferenceResolver {
typeArguments: node.typeArguments,
typeArgumentTypes: typeArgumentTypes,
);
NodeReplacer.replace(node, callReference);
_resolver.replaceExpression(node, callReference);
var instantiatedType = callMethodType.instantiate(typeArgumentTypes);
callReference.staticType = instantiatedType;
}
@ -826,7 +825,7 @@ class FunctionReferenceResolver {
typeName.name.staticType = instantiatedType;
var typeLiteral = astFactory.typeLiteral(typeName: typeName);
typeLiteral.staticType = _typeType;
NodeReplacer.replace(node, typeLiteral);
_resolver.replaceExpression(node, typeLiteral);
}
/// Resolves [name] as a property on [receiver].

View file

@ -461,8 +461,7 @@ class InvocationInferrer<Node extends AstNodeImpl> {
}
var argument = arguments[deferredArgument.index];
resolver.analyzeExpression(argument, parameterContextType);
// In case of rewrites, we need to grab the argument again.
argument = arguments[deferredArgument.index];
argument = resolver.popRewrite()!;
if (flow != null) {
identicalInfo?[deferredArgument.index] =
flow.equalityOperand_end(argument, argument.typeOrThrow);
@ -521,8 +520,7 @@ class InvocationInferrer<Node extends AstNodeImpl> {
parameterContextType = _computeContextForArgument(parameterType);
}
resolver.analyzeExpression(argument, parameterContextType);
// In case of rewrites, we need to grab the argument again.
argument = arguments[i];
argument = resolver.popRewrite()!;
if (flow != null) {
identicalInfo
?.add(flow.equalityOperand_end(argument, argument.typeOrThrow));

View file

@ -9,7 +9,6 @@ import 'package:analyzer/error/listener.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/ast/ast_factory.dart';
import 'package:analyzer/src/dart/ast/extensions.dart';
import 'package:analyzer/src/dart/ast/utilities.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
import 'package:analyzer/src/dart/element/type.dart';
@ -906,7 +905,7 @@ class MethodInvocationResolver with ScopeHelpers {
typeArguments: node.typeArguments,
argumentList: node.argumentList,
);
NodeReplacer.replace(node, invocation);
_resolver.replaceExpression(node, invocation);
node.setProperty(_rewriteResultKey, invocation);
_resolver.flowAnalysis.transferTestData(node, invocation);
}

View file

@ -208,7 +208,7 @@ class PostfixExpressionResolver {
}
_resolver.analyzeExpression(operand, contextType);
operand = node.operand;
operand = _resolver.popRewrite()!;
var operandType = operand.typeOrThrow;

View file

@ -74,6 +74,7 @@ class PrefixExpressionResolver {
innerContextType = contextType;
}
_resolver.analyzeExpression(operand, innerContextType);
_resolver.popRewrite();
}
_resolve1(node);
@ -226,7 +227,7 @@ class PrefixExpressionResolver {
var operand = node.operand;
_resolver.analyzeExpression(operand, _typeProvider.boolType);
operand = node.operand;
operand = _resolver.popRewrite()!;
var whyNotPromoted = _resolver.flowAnalysis.flow?.whyNotPromoted(operand);
_resolver.boolExpressionVerifier.checkForNonBoolNegationExpression(operand,

View file

@ -6,7 +6,6 @@ import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/ast/utilities.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type_provider.dart';
@ -43,7 +42,7 @@ class PrefixedIdentifierResolver {
node.period,
node.identifier,
);
NodeReplacer.replace(node, propertyAccess);
_resolver.replaceExpression(node, propertyAccess);
return propertyAccess;
}
}

View file

@ -116,6 +116,7 @@ class RecordLiteralResolver {
void _resolveField(Expression field, DartType? contextType) {
_resolver.analyzeExpression(field, contextType);
_resolver.popRewrite();
}
void _resolveFields(RecordLiteralImpl node, DartType? contextType) {

View file

@ -0,0 +1,105 @@
// Copyright (c) 2022, 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/type_inference/type_analyzer.dart';
import 'package:analyzer/dart/ast/ast.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/error/codes.dart';
import 'package:collection/collection.dart';
/// Implementation of [TypeAnalyzerErrors] that reports errors using the
/// analyzer's [ErrorReporter] class.
class SharedTypeAnalyzerErrors
implements
TypeAnalyzerErrors<AstNode, Statement, Expression, PromotableElement,
DartType> {
final ErrorReporter _errorReporter;
SharedTypeAnalyzerErrors(this._errorReporter);
@override
void assertInErrorRecovery() {
throw UnimplementedError('TODO(paulberry)');
}
@override
void caseExpressionTypeMismatch(
{required Expression scrutinee,
required Expression caseExpression,
required scrutineeType,
required caseExpressionType,
required bool nullSafetyEnabled}) {
if (nullSafetyEnabled) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode
.CASE_EXPRESSION_TYPE_IS_NOT_SWITCH_EXPRESSION_SUBTYPE,
caseExpression,
[caseExpressionType, scrutineeType]);
} else {
// We only report the error if it occurs on the first case; otherwise
// separate logic will report that different cases have different types.
var switchStatement = scrutinee.parent as SwitchStatement;
if (identical(
switchStatement.members
.whereType<SwitchCase>()
.firstOrNull
?.expression,
caseExpression)) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.SWITCH_EXPRESSION_NOT_ASSIGNABLE,
scrutinee,
[scrutineeType, caseExpressionType]);
}
}
}
@override
void inconsistentMatchVar(
{required AstNode pattern,
required DartType type,
required AstNode previousPattern,
required DartType previousType}) {
throw UnimplementedError('TODO(paulberry)');
}
@override
void inconsistentMatchVarExplicitness(
{required AstNode pattern, required AstNode previousPattern}) {
throw UnimplementedError('TODO(paulberry)');
}
@override
void nonBooleanCondition(Expression node) {
throw UnimplementedError('TODO(paulberry)');
}
@override
void patternDoesNotAllowLate(AstNode pattern) {
throw UnimplementedError('TODO(paulberry)');
}
@override
void patternTypeMismatchInIrrefutableContext(
{required AstNode pattern,
required AstNode context,
required DartType matchedType,
required DartType requiredType}) {
throw UnimplementedError('TODO(paulberry)');
}
@override
void refutablePatternInIrrefutableContext(AstNode pattern, AstNode context) {
throw UnimplementedError('TODO(paulberry)');
}
@override
void switchCaseCompletesNormally(
covariant SwitchStatement node, int caseIndex, int numHeads) {
_errorReporter.reportErrorForToken(
CompileTimeErrorCode.SWITCH_CASE_COMPLETES_NORMALLY,
node.members[caseIndex + numHeads - 1].keyword);
}
}

View file

@ -14,6 +14,7 @@ import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/generic_inferrer.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type_provider.dart';
import 'package:analyzer/src/dart/element/type_schema.dart';
import 'package:analyzer/src/dart/element/type_system.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/engine.dart';
@ -92,7 +93,7 @@ class TypedLiteralResolver {
}
void resolveListLiteral(ListLiteralImpl node,
{required DartType? contextType}) {
{required DartType contextType}) {
InterfaceType? listType;
GenericInferrer? inferrer;
@ -106,7 +107,7 @@ class TypedLiteralResolver {
}
} else {
inferrer = _inferListTypeDownwards(node, contextType: contextType);
if (contextType != null) {
if (contextType is! UnknownInferredType) {
var typeArguments = inferrer.partialInfer();
listType = _typeProvider.listElement.instantiate(
typeArguments: typeArguments, nullabilitySuffix: _noneOrStarSuffix);
@ -126,7 +127,7 @@ class TypedLiteralResolver {
}
void resolveSetOrMapLiteral(SetOrMapLiteral node,
{required DartType? contextType}) {
{required DartType contextType}) {
(node as SetOrMapLiteralImpl).becomeUnresolved();
var typeArguments = node.typeArguments?.arguments;
@ -472,7 +473,7 @@ class TypedLiteralResolver {
InterfaceType? _inferListTypeUpwards(
GenericInferrer inferrer, ListLiteral node,
{required DartType? contextType}) {
{required DartType contextType}) {
var element = _typeProvider.listElement;
var typeParameters = element.typeParameters;
var genericElementType = typeParameters[0].instantiate(
@ -486,7 +487,9 @@ class TypedLiteralResolver {
'element', genericElementType, ParameterKind.POSITIONAL);
List<ParameterElement> parameters =
List.filled(elementTypes.length, syntheticParameter);
if (_strictInference && parameters.isEmpty && contextType == null) {
if (_strictInference &&
parameters.isEmpty &&
contextType is UnknownInferredType) {
// We cannot infer the type of a collection literal with no elements, and
// no context type. If there are any elements, inference has not failed,
// as the types of those elements are considered resolved.
@ -606,11 +609,12 @@ class TypedLiteralResolver {
List<CollectionElement> elements, CollectionLiteralContext? context) {
for (var element in elements) {
(element as CollectionElementImpl).resolveElement(_resolver, context);
_resolver.popRewrite();
}
}
void _resolveListLiteral2(GenericInferrer? inferrer, ListLiteralImpl node,
{required DartType? contextType}) {
{required DartType contextType}) {
var typeArguments = node.typeArguments?.arguments;
// If we have explicit arguments, use them.
@ -645,7 +649,7 @@ class TypedLiteralResolver {
void _resolveSetOrMapLiteral2(GenericInferrer? inferrer,
_LiteralResolution literalResolution, SetOrMapLiteralImpl node,
{required DartType? contextType}) {
{required DartType contextType}) {
var typeArguments = node.typeArguments?.arguments;
// If we have type arguments, use them.
@ -688,7 +692,7 @@ class TypedLiteralResolver {
}
if (_strictInference &&
_getSetOrMapElements(node).isEmpty &&
contextType == null) {
contextType is UnknownInferredType) {
// We cannot infer the type of a collection literal with no elements, and
// no context type. If there are any elements, inference has not failed,
// as the types of those elements are considered resolved.

View file

@ -50,7 +50,7 @@ class VariableDeclarationResolver {
}
_resolver.analyzeExpression(initializer, element.type);
initializer = node.initializer!;
initializer = _resolver.popRewrite()!;
var whyNotPromoted =
_resolver.flowAnalysis.flow?.whyNotPromoted(initializer);

View file

@ -157,6 +157,7 @@ class YieldStatementResolver {
) {
_resolver.analyzeExpression(
node.expression, _computeContextType(bodyContext, node));
_resolver.popRewrite();
if (node.star != null) {
_resolver.nullableDereferenceVerifier.expression(

View file

@ -48,7 +48,6 @@ import 'package:analyzer/src/generated/this_access_tracker.dart';
import 'package:analyzer/src/summary2/macro_application_error.dart';
import 'package:analyzer/src/utilities/extensions/object.dart';
import 'package:analyzer/src/utilities/extensions/string.dart';
import 'package:collection/collection.dart';
class EnclosingExecutableContext {
final ExecutableElement? element;
@ -4243,30 +4242,7 @@ class ErrorVerifier extends RecursiveAstVisitor<void>
return;
}
Expression expression = statement.expression;
if (checkForUseOfVoidResult(expression)) {
return;
}
// prepare 'switch' expression type
DartType expressionType = expression.typeOrThrow;
// compare with type of the first non-default 'case'
var switchCase = statement.members.whereType<SwitchCase>().firstOrNull;
if (switchCase == null) {
return;
}
Expression caseExpression = switchCase.expression;
DartType caseType = caseExpression.typeOrThrow;
// check types
if (!typeSystem.isAssignableTo(expressionType, caseType)) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.SWITCH_EXPRESSION_NOT_ASSIGNABLE,
expression,
[expressionType, caseType]);
}
checkForUseOfVoidResult(statement.expression);
}
void _checkForThrowOfInvalidType(ThrowExpression node) {

View file

@ -5,6 +5,9 @@
import 'dart:collection';
import 'package:_fe_analyzer_shared/src/flow_analysis/flow_analysis.dart';
import 'package:_fe_analyzer_shared/src/type_inference/type_analysis_result.dart';
import 'package:_fe_analyzer_shared/src/type_inference/type_analyzer.dart';
import 'package:_fe_analyzer_shared/src/type_inference/type_operations.dart';
import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/syntactic_entity.dart';
@ -29,6 +32,7 @@ import 'package:analyzer/src/dart/element/nullability_eliminator.dart';
import 'package:analyzer/src/dart/element/scope.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type_provider.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/annotation_resolver.dart';
import 'package:analyzer/src/dart/resolver/assignment_expression_resolver.dart';
@ -52,6 +56,7 @@ import 'package:analyzer/src/dart/resolver/prefixed_identifier_resolver.dart';
import 'package:analyzer/src/dart/resolver/property_element_resolver.dart';
import 'package:analyzer/src/dart/resolver/record_literal_resolver.dart';
import 'package:analyzer/src/dart/resolver/scope.dart';
import 'package:analyzer/src/dart/resolver/shared_type_analyzer.dart';
import 'package:analyzer/src/dart/resolver/simple_identifier_resolver.dart';
import 'package:analyzer/src/dart/resolver/this_lookup.dart';
import 'package:analyzer/src/dart/resolver/type_property_resolver.dart';
@ -129,10 +134,31 @@ class InferenceContext {
/// Instances of the class `ResolverVisitor` are used to resolve the nodes
/// within a single compilation unit.
class ResolverVisitor extends ThrowingAstVisitor<void>
with ErrorDetectionHelpers {
with
ErrorDetectionHelpers,
TypeAnalyzer<AstNode, Statement, Expression, PromotableElement,
DartType> {
/// Debug-only: if `true`, manipulations of [_rewriteStack] performed by
/// [popRewrite], [pushRewrite], and [replaceExpression] will be printed.
static const bool _debugRewriteStack = false;
/// The element for the library containing the compilation unit being visited.
final LibraryElementImpl definingLibrary;
/// If the resolver visitor is visiting a switch statement, the tracker that
/// determines whether the switch is exhaustive.
///
/// TODO(paulberry): move exhaustiveness computation into the shared
/// [TypeAnalyzer].
SwitchExhaustiveness? switchExhaustiveness;
@override
final TypeAnalyzerOptions options;
@override
late final SharedTypeAnalyzerErrors errors =
SharedTypeAnalyzerErrors(errorReporter);
/// The source representing the compilation unit being visited.
final Source source;
@ -226,10 +252,6 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
/// be built and the comment resolved.
bool resolveOnlyCommentInFunctionBody = false;
/// The type of the expression of the immediately enclosing [SwitchStatement],
/// or `null` if not in a [SwitchStatement].
DartType? _enclosingSwitchStatementExpressionType;
/// Stack of expressions which we have not yet finished visiting, that should
/// terminate a null-shorting expression.
///
@ -256,6 +278,22 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
final bool genericMetadataIsEnabled;
/// Stack for obtaining rewritten expressions. Prior to visiting an
/// expression, a caller may push the expression on this stack; if
/// [replaceExpression] is later called, it will update the top of the stack
/// to point to the rewritten expression.
///
/// The stack sometimes contains `null`s. These account for situations where
/// it's necessary to push a value onto the stack to balance a later pop, but
/// there is no suitable expression to push.
final List<ExpressionImpl?> _rewriteStack = [];
/// Debug-only expando mapping AST nodes to the nodes they were replaced with
/// by [replaceExpression]. This is used by [dispatchExpression] as a sanity
/// check to make sure the expression it pops off the [_rewriteStack] is
/// actually correct.
late final Expando<AstNode> _replacements = Expando();
/// Initialize a newly created visitor to resolve the nodes in an AST node.
///
/// The [definingLibrary] is the element for the library containing the node
@ -306,7 +344,12 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
),
_featureSet = featureSet,
genericMetadataIsEnabled =
definingLibrary.featureSet.isEnabled(Feature.generic_metadata) {
definingLibrary.featureSet.isEnabled(Feature.generic_metadata),
options = TypeAnalyzerOptions(
nullSafetyEnabled: definingLibrary.isNonNullableByDefault,
// TODO(paulberry): set `patternsEnabled` correctly once we have an
// experiment flag for patterns.
patternsEnabled: false) {
var analysisOptions =
definingLibrary.context.analysisOptions as AnalysisOptionsImpl;
@ -375,12 +418,28 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
FunctionReferenceResolver(this, _isNonNullableByDefault);
}
@override
DartType get boolType => throw UnimplementedError('TODO(paulberry)');
@override
DartType get doubleType => throw UnimplementedError('TODO(paulberry)');
@override
DartType get dynamicType => throw UnimplementedError('TODO(paulberry)');
/// Return the element representing the function containing the current node,
/// or `null` if the current node is not contained in a function.
///
/// @return the element representing the function containing the current node
ExecutableElement? get enclosingFunction => _enclosingFunction;
@override
FlowAnalysis<AstNode, Statement, Expression, PromotableElement, DartType>?
get flow => flowAnalysis.flow;
@override
DartType get intType => throw UnimplementedError('TODO(paulberry)');
bool get isConstructorTearoffsEnabled =>
_featureSet.isEnabled(Feature.constructor_tearoffs);
@ -398,6 +457,14 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
: NullabilitySuffix.star;
}
@override
DartType get objectQuestionType =>
throw UnimplementedError('TODO(paulberry)');
/// Gets the current depth of the [_rewriteStack]. This may be used in
/// assertions to verify that pushes and pops are properly balanced.
int get rewriteStackDepth => _rewriteStack.length;
/// If a class, or mixin, is being resolved, the type of the class.
///
/// If an extension is being resolved, the type of `this`, the declared
@ -408,17 +475,16 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
return _thisType;
}
@override
TypeOperations2<DartType> get typeOperations => flowAnalysis.typeOperations;
@override
DartType get unknownType => UnknownInferredType.instance;
/// Return `true` if NNBD is enabled for this compilation unit.
bool get _isNonNullableByDefault =>
_featureSet.isEnabled(Feature.non_nullable);
void analyzeExpression(Expression expression, DartType? contextType) {
if (contextType != null && contextType.isDynamic) {
contextType = null;
}
(expression as ExpressionImpl).resolveExpression(this, contextType);
}
/// Verify that the arguments in the given [argumentList] can be assigned to
/// their corresponding parameters.
///
@ -513,6 +579,13 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
}
}
/// The client of the resolver should call this method after asking the
/// resolver to visit an AST node. This performs assertions to make sure that
/// temporary resolver state has been properly cleaned up.
void checkIdle() {
assert(_rewriteStack.isEmpty);
}
void checkReadOfNotAssignedLocalVariable(
SimpleIdentifier node,
Element? element,
@ -609,6 +682,51 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
return messages;
}
@override
ExpressionTypeAnalysisResult<DartType> dispatchExpression(
covariant ExpressionImpl expression, DartType context) {
int? stackDepth;
assert(() {
stackDepth = rewriteStackDepth;
return true;
}());
// TODO(paulberry): implement null shorting
// Stack: ()
pushRewrite(expression);
// Stack: (Expression)
expression.resolveExpression(this, context);
assert(rewriteStackDepth == stackDepth! + 1);
var replacementExpression = peekRewrite()!;
assert(identical(
_replacements[expression] ?? expression, replacementExpression));
var staticType = replacementExpression.staticType;
if (staticType == null) {
assert(replacementExpression is ExtensionOverride);
staticType = unknownType;
}
return SimpleTypeAnalysisResult<DartType>(type: staticType);
}
@override
PatternDispatchResult<AstNode, Expression, PromotableElement, DartType>
dispatchPattern(AstNode pattern) {
if (pattern is Expression) {
return analyzeConstantPattern(pattern, pattern);
} else {
throw UnimplementedError('TODO(paulberry): ${pattern.runtimeType}');
}
}
@override
void dispatchStatement(Statement statement) {
statement.accept(this);
}
@override
void finishExpressionCase(Expression node, int caseIndex) {
throw UnimplementedError('TODO(paulberry)');
}
/// Return the static element associated with the given expression whose type
/// can be overridden, or `null` if there is no element whose type can be
/// overridden.
@ -630,6 +748,110 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
return null;
}
@override
SwitchExpressionMemberInfo<AstNode, Expression> getSwitchExpressionMemberInfo(
Expression node, int index) {
throw UnimplementedError('TODO(paulberry)');
}
@override
SwitchStatementMemberInfo<AstNode, Statement, Expression>
getSwitchStatementMemberInfo(covariant SwitchStatement node, int index) {
var member = node.members[index];
Expression? pattern;
if (member is SwitchCase) {
pattern = member.expression;
}
return SwitchStatementMemberInfo(
[CaseHeadOrDefaultInfo(pattern: pattern)], member.statements,
labels: member.labels);
}
@override
void handleCase_afterCaseHeads(AstNode node, int caseIndex, int numHeads) {}
@override
void handleCaseHead(
// TODO(paulberry): once we support switch expressions this type will
// need to change.
covariant SwitchStatement node,
{required int caseIndex,
required int subIndex}) {
// Stack: (Expression)
popRewrite(); // "when" expression
// Stack: ()
switchExhaustiveness!.visitSwitchMember(node.members[caseIndex]);
}
@override
void handleCastPattern(AstNode node,
{required DartType matchedType, DartType? staticType}) {
throw UnimplementedError('TODO(paulberry)');
}
@override
void handleConstantPattern(AstNode node, {required DartType matchedType}) {
// Stack: (Expression)
popRewrite();
// Stack: ()
}
@override
void handleDefault(covariant SwitchStatement node, int caseIndex) {
switchExhaustiveness!.visitSwitchMember(node.members[caseIndex]);
}
@override
void handleListPattern(AstNode node, int numElements,
{required DartType matchedType, required DartType requiredType}) {
throw UnimplementedError('TODO(paulberry)');
}
@override
void handleLogicalPattern(AstNode node,
{required bool isAnd, required DartType matchedType}) {
throw UnimplementedError('TODO(paulberry)');
}
@override
void handleMergedStatementCase(covariant SwitchStatement node,
{required int caseIndex,
required int executionPathIndex,
required int numStatements}) {
nullSafetyDeadCodeVerifier.flowEnd(node.members[caseIndex]);
}
@override
void handleNoGuard(AstNode node, int caseIndex) {
// Stack: ()
// We can push `null` here because there is no actual expression associated
// with the lack of a guard, so there's nothing that will need rewriting.
pushRewrite(null);
// Stack: (Expression)
}
@override
void handleNoStatement(Statement node) {
throw UnimplementedError('TODO(paulberry)');
}
@override
void handleNullCheckOrAssertPattern(AstNode node,
{required DartType matchedType, required bool isAssert}) {
throw UnimplementedError('TODO(paulberry)');
}
@override
void handleSwitchScrutinee(DartType type) {
switchExhaustiveness = SwitchExhaustiveness(type);
}
@override
void handleVariablePattern(AstNode node,
{required DartType matchedType, DartType? staticType}) {
throw UnimplementedError('TODO(paulberry)');
}
/// If generic function instantiation should be performed on `expression`,
/// inserts a [FunctionReference] node which wraps [expression].
///
@ -678,8 +900,7 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
function: expression,
typeArguments: null,
);
NodeReplacer.replace(expression, genericFunctionInstantiation,
parent: parent);
replaceExpression(expression, genericFunctionInstantiation, parent: parent);
genericFunctionInstantiation.typeArgumentTypes = typeArgumentTypes;
genericFunctionInstantiation.staticType = staticType;
@ -687,6 +908,15 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
return genericFunctionInstantiation;
}
@override
bool isSwitchExhaustive(AstNode node, DartType expressionType) =>
switchExhaustiveness!.isExhaustive;
@override
DartType listType(DartType elementType) {
throw UnimplementedError('TODO(paulberry)');
}
/// If we reached a null-shorting termination, and the [node] has null
/// shorting, make the type of the [node] nullable.
void nullShortingTermination(ExpressionImpl node,
@ -718,6 +948,18 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
// TODO(brianwilkerson) Remove this method.
}
/// Examines the top entry of [_rewriteStack] but does not pop it.
ExpressionImpl? peekRewrite() => _rewriteStack.last;
/// Pops the top entry off of [_rewriteStack].
ExpressionImpl? popRewrite() {
var expression = _rewriteStack.removeLast();
if (_debugRewriteStack) {
assert(_debugPrint('POP ${expression.runtimeType} $expression'));
}
return expression;
}
/// Set information about enclosing declarations.
void prepareEnclosingDeclarations({
InterfaceElement? enclosingClassElement,
@ -767,6 +1009,36 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
return false;
}
/// Pushes an entry onto [_rewriteStack].
void pushRewrite(ExpressionImpl? expression) {
if (_debugRewriteStack) {
assert(_debugPrint('PUSH ${expression.runtimeType} $expression'));
}
_rewriteStack.add(expression);
}
/// Replaces the expression [oldNode] with [newNode], updating the node's
/// parent as appropriate.
///
/// If [newNode] is the parent of [oldNode] already (because [newNode] became
/// the parent of [oldNode] in its constructor), this action will loop
/// infinitely; pass [oldNode]'s previous parent as [parent] to avoid this.
void replaceExpression(Expression oldNode, ExpressionImpl newNode,
{AstNode? parent}) {
assert(() {
assert(_replacements[oldNode] == null);
_replacements[oldNode] = newNode;
return true;
}());
if (_rewriteStack.isNotEmpty && identical(peekRewrite(), oldNode)) {
if (_debugRewriteStack) {
assert(_debugPrint('REPLACE ${newNode.runtimeType} $newNode'));
}
_rewriteStack.last = newNode;
}
NodeReplacer.replace(oldNode, newNode, parent: parent);
}
/// Resolve LHS [node] of an assignment, an explicit [AssignmentExpression],
/// or implicit [PrefixExpression] or [PostfixExpression].
PropertyElementResolverResult resolveForWrite({
@ -784,6 +1056,7 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
);
analyzeExpression(node.index, result.indexContextType);
popRewrite();
var whyNotPromoted = flowAnalysis.flow?.whyNotPromoted(node.index);
checkIndexExpressionIndex(
node.index,
@ -884,6 +1157,11 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
_thisType = thisType;
}
@override
void setVariableType(PromotableElement variable, DartType type) {
throw UnimplementedError('TODO(paulberry)');
}
void setWriteElement(Expression node, Element? element) {
DartType writeType = DynamicTypeImpl.instance;
if (node is IndexExpression) {
@ -980,6 +1258,11 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
return NullabilityEliminator.perform(typeProvider, type);
}
@override
DartType variableTypeFromInitializerType(DartType type) {
throw UnimplementedError('TODO(paulberry)');
}
@override
void visitAdjacentStrings(AdjacentStrings node, {DartType? contextType}) {
checkUnreachableNode(node);
@ -1014,6 +1297,7 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
void visitAssertInitializer(AssertInitializer node) {
flowAnalysis.flow?.assert_begin();
analyzeExpression(node.condition, typeProvider.boolType);
popRewrite();
boolExpressionVerifier.checkForNonBoolExpression(
node.condition,
errorCode: CompileTimeErrorCode.NON_BOOL_EXPRESSION,
@ -1028,6 +1312,7 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
void visitAssertStatement(AssertStatement node) {
flowAnalysis.flow?.assert_begin();
analyzeExpression(node.condition, typeProvider.boolType);
popRewrite();
boolExpressionVerifier.checkForNonBoolExpression(
node.condition,
errorCode: CompileTimeErrorCode.NON_BOOL_EXPRESSION,
@ -1064,6 +1349,7 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
}
checkUnreachableNode(node);
analyzeExpression(node.expression, futureUnion);
popRewrite();
typeAnalyzer.visitAwaitExpression(node as AwaitExpressionImpl,
contextType: contextType);
_insertImplicitCallReference(
@ -1128,6 +1414,7 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
void visitCascadeExpression(covariant CascadeExpressionImpl node,
{DartType? contextType}) {
analyzeExpression(node.target, contextType);
popRewrite();
if (node.isNullAware) {
flowAnalysis.flow!.nullAwareAccess_rightBegin(
@ -1204,6 +1491,7 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
for (int i = 0; i < declarationCount; i++) {
declarations[i].accept(this);
}
checkIdle();
}
@override
@ -1215,7 +1503,7 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
// TODO(scheglov) Do we need these checks for null?
analyzeExpression(node.condition, typeProvider.boolType);
condition = node.condition;
condition = popRewrite()!;
var whyNotPromoted = flowAnalysis.flow?.whyNotPromoted(condition);
boolExpressionVerifier.checkForNonBoolCondition(condition,
whyNotPromoted: whyNotPromoted);
@ -1225,6 +1513,7 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
checkUnreachableNode(node.thenExpression);
}
analyzeExpression(node.thenExpression, contextType);
popRewrite();
nullSafetyDeadCodeVerifier.flowEnd(node.thenExpression);
Expression elseExpression = node.elseExpression;
@ -1238,7 +1527,7 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
} else {
analyzeExpression(elseExpression, contextType);
}
elseExpression = node.elseExpression;
elseExpression = popRewrite()!;
typeAnalyzer.visitConditionalExpression(node as ConditionalExpressionImpl,
contextType: contextType);
@ -1299,7 +1588,7 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
var fieldType = fieldElement?.type;
var expression = node.expression;
analyzeExpression(expression, fieldType);
expression = node.expression;
expression = popRewrite()!;
var whyNotPromoted = flowAnalysis.flow?.whyNotPromoted(expression);
elementResolver.visitConstructorFieldInitializer(
node as ConstructorFieldInitializerImpl);
@ -1354,6 +1643,7 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
var defaultValue = node.defaultValue;
if (defaultValue != null) {
analyzeExpression(defaultValue, node.declaredElement?.type);
popRewrite();
}
ParameterElement element = node.declaredElement!;
@ -1373,7 +1663,7 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
flowAnalysis.flow?.doStatement_conditionBegin();
analyzeExpression(condition, typeProvider.boolType);
condition = node.condition;
condition = popRewrite()!;
var whyNotPromoted = flowAnalysis.flow?.whyNotPromoted(condition);
boolExpressionVerifier.checkForNonBoolCondition(condition,
whyNotPromoted: whyNotPromoted);
@ -1465,6 +1755,7 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
);
for (var argument in argumentList.arguments) {
analyzeExpression(argument, argument.staticParameterElement?.type);
popRewrite();
}
arguments.typeArguments?.accept(this);
@ -1530,6 +1821,7 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
node.expression,
inferenceContext.bodyContext!.contextType,
);
popRewrite();
flowAnalysis.flow?.handleExit();
@ -1661,6 +1953,7 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
node.metadata.accept(this);
node.returnType?.accept(this);
analyzeExpression(node.functionExpression, functionType);
popRewrite();
elementResolver.visitFunctionDeclaration(node);
} finally {
_enclosingFunction = outerFunction;
@ -1758,7 +2051,7 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
flowAnalysis.flow?.ifStatement_conditionBegin();
Expression condition = node.condition;
analyzeExpression(condition, typeProvider.boolType);
condition = node.condition;
condition = popRewrite()!;
var whyNotPromoted = flowAnalysis.flow?.whyNotPromoted(condition);
boolExpressionVerifier.checkForNonBoolCondition(condition,
@ -1766,12 +2059,14 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
flowAnalysis.flow?.ifStatement_thenBegin(condition, node);
(node.thenElement as CollectionElementImpl).resolveElement(this, context);
popRewrite();
nullSafetyDeadCodeVerifier.flowEnd(node.thenElement);
var elseElement = node.elseElement;
if (elseElement != null) {
flowAnalysis.flow?.ifStatement_elseBegin();
(elseElement as CollectionElementImpl).resolveElement(this, context);
popRewrite();
nullSafetyDeadCodeVerifier.flowEnd(elseElement);
}
@ -1786,7 +2081,7 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
Expression condition = node.condition;
analyzeExpression(condition, typeProvider.boolType);
condition = node.condition;
condition = popRewrite()!;
var whyNotPromoted = flowAnalysis.flow?.whyNotPromoted(condition);
boolExpressionVerifier.checkForNonBoolCondition(condition,
@ -1817,6 +2112,7 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
{DartType? contextType}) {
checkUnreachableNode(node);
analyzeExpression(node.expression, null);
popRewrite();
node.typeArguments?.accept(this);
}
@ -1843,6 +2139,7 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
node.staticElement = element as MethodElement?;
analyzeExpression(node.index, result.indexContextType);
popRewrite();
var whyNotPromoted = flowAnalysis.flow?.whyNotPromoted(node.index);
checkIndexExpressionIndex(
node.index,
@ -1937,7 +2234,8 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
void visitListLiteral(covariant ListLiteralImpl node,
{DartType? contextType}) {
checkUnreachableNode(node);
_typedLiteralResolver.resolveListLiteral(node, contextType: contextType);
_typedLiteralResolver.resolveListLiteral(node,
contextType: contextType ?? UnknownInferredType.instance);
}
@override
@ -1945,7 +2243,9 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
{CollectionLiteralContext? context}) {
checkUnreachableNode(node);
analyzeExpression(node.key, context?.keyType);
popRewrite();
analyzeExpression(node.value, context?.valueType);
popRewrite();
}
@override
@ -2048,6 +2348,7 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
checkUnreachableNode(node);
node.name.accept(this);
analyzeExpression(node.expression, contextType);
popRewrite();
typeAnalyzer.visitNamedExpression(node as NamedExpressionImpl,
contextType: contextType);
// Any "why not promoted" information that flow analysis had associated with
@ -2099,6 +2400,7 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
{DartType? contextType}) {
checkUnreachableNode(node);
analyzeExpression(node.expression, contextType);
popRewrite();
typeAnalyzer.visitParenthesizedExpression(
node as ParenthesizedExpressionImpl,
contextType: contextType);
@ -2281,7 +2583,7 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
inferenceContext.bodyContext?.contextType,
);
// Pick up the expression again in case it was rewritten.
expression = node.expression;
expression = popRewrite();
}
inferenceContext.bodyContext?.addReturnExpression(expression);
@ -2292,7 +2594,7 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
void visitSetOrMapLiteral(SetOrMapLiteral node, {DartType? contextType}) {
checkUnreachableNode(node);
_typedLiteralResolver.resolveSetOrMapLiteral(node,
contextType: contextType);
contextType: contextType ?? UnknownInferredType.instance);
}
@override
@ -2332,6 +2634,7 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
}
checkUnreachableNode(node);
analyzeExpression(node.expression, iterableType);
popRewrite();
if (!node.isNullAware) {
nullableDereferenceVerifier.expression(
@ -2386,72 +2689,17 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
node.visitChildren(this);
}
@override
void visitSwitchCase(SwitchCase node) {
checkUnreachableNode(node);
checkUnreachableNode(node);
node.labels.accept(this);
analyzeExpression(node.expression, _enclosingSwitchStatementExpressionType);
node.statements.accept(this);
var flow = flowAnalysis.flow;
if (flow != null && flow.isReachable && _isNonNullableByDefault) {
var switchStatement = node.parent as SwitchStatement;
if (switchStatement.members.last != node && node.statements.isNotEmpty) {
errorReporter.reportErrorForToken(
CompileTimeErrorCode.SWITCH_CASE_COMPLETES_NORMALLY,
node.keyword,
);
}
}
nullSafetyDeadCodeVerifier.flowEnd(node);
}
@override
void visitSwitchDefault(SwitchDefault node) {
checkUnreachableNode(node);
node.visitChildren(this);
nullSafetyDeadCodeVerifier.flowEnd(node);
}
@override
void visitSwitchStatement(SwitchStatement node) {
// Stack: ()
checkUnreachableNode(node);
var previousExpressionType = _enclosingSwitchStatementExpressionType;
try {
var expression = node.expression;
expression.accept(this);
expression = node.expression;
_enclosingSwitchStatementExpressionType = expression.typeOrThrow;
var flow = flowAnalysis.flow!;
flow.switchStatement_expressionEnd(node);
var exhaustiveness = _SwitchExhaustiveness(
_enclosingSwitchStatementExpressionType!,
);
var members = node.members;
for (var member in members) {
flow.switchStatement_beginCase();
flow.switchStatement_beginAlternatives();
flow.switchStatement_endAlternative();
flow.switchStatement_endAlternatives(node,
hasLabels: member.labels.isNotEmpty);
member.accept(this);
exhaustiveness.visitSwitchMember(member);
}
flow.switchStatement_end(exhaustiveness.isExhaustive);
} finally {
_enclosingSwitchStatementExpressionType = previousExpressionType;
}
var previousExhaustiveness = switchExhaustiveness;
analyzeSwitchStatement(node, node.expression, node.members.length);
// Stack: (Expression)
popRewrite();
// Stack: ()
switchExhaustiveness = previousExhaustiveness;
}
@override
@ -2601,7 +2849,7 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
flowAnalysis.flow?.whileStatement_conditionBegin(node);
analyzeExpression(condition, typeProvider.boolType);
condition = node.condition;
condition = popRewrite()!;
var whyNotPromoted = flowAnalysis.flow?.whyNotPromoted(condition);
boolExpressionVerifier.checkForNonBoolCondition(node.condition,
@ -2656,6 +2904,14 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
return typeProvider.futureOrType(type);
}
/// Helper function used to print information to the console in debug mode.
/// This method returns `true` so that it can be conveniently called inside of
/// an `assert` statement.
bool _debugPrint(String s) {
print(s);
return true;
}
/// If `expression` should be treated as `expression.call`, inserts an
/// [ImplicitCallReference] node which wraps [expression].
void _insertImplicitCallReference(ExpressionImpl expression,
@ -2708,7 +2964,7 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
typeArguments: null,
typeArgumentTypes: typeArgumentTypes,
);
NodeReplacer.replace(expression, callReference, parent: parent);
replaceExpression(expression, callReference, parent: parent);
callReference.staticType = callMethodType;
}
@ -2956,6 +3212,7 @@ class ResolverVisitorForMigration extends ResolverVisitor {
var element = conditionalKnownValue ? node.thenElement : node.elseElement;
if (element != null) {
(element as CollectionElementImpl).resolveElement(this, context);
popRewrite();
}
}
}
@ -3870,7 +4127,7 @@ class ScopeResolverVisitor extends UnifyingAstVisitor<void> {
/// Tracker for whether a `switch` statement has `default` or is on an
/// enumeration, and all the enum constants are covered.
class _SwitchExhaustiveness {
class SwitchExhaustiveness {
/// If the switch is on an enumeration, the set of enum constants to cover.
/// Otherwise `null`.
final Set<FieldElement>? _enumConstants;
@ -3882,20 +4139,20 @@ class _SwitchExhaustiveness {
bool isExhaustive = false;
factory _SwitchExhaustiveness(DartType expressionType) {
factory SwitchExhaustiveness(DartType expressionType) {
if (expressionType is InterfaceType) {
var enum_ = expressionType.element2;
if (enum_ is EnumElementImpl) {
return _SwitchExhaustiveness._(
return SwitchExhaustiveness._(
enum_.constants.toSet(),
expressionType.nullabilitySuffix == NullabilitySuffix.none,
);
}
}
return _SwitchExhaustiveness._(null, false);
return SwitchExhaustiveness._(null, false);
}
_SwitchExhaustiveness._(this._enumConstants, this._isNullEnumValueCovered);
SwitchExhaustiveness._(this._enumConstants, this._isNullEnumValueCovered);
void visitSwitchMember(SwitchMember node) {
if (_enumConstants != null && node is SwitchCase) {

View file

@ -64,6 +64,7 @@ class AstResolver {
_prepareEnclosingDeclarations();
_flowAnalysis.topLevelDeclaration_enter(node, null);
node.accept(_resolverVisitor);
_resolverVisitor.checkIdle();
_flowAnalysis.topLevelDeclaration_exit();
}
@ -83,6 +84,7 @@ class AstResolver {
_flowAnalysis.topLevelDeclaration_enter(node, node.parameters,
visit: visit);
visit(_resolverVisitor);
_resolverVisitor.checkIdle();
_flowAnalysis.topLevelDeclaration_exit();
}
@ -98,6 +100,8 @@ class AstResolver {
_prepareEnclosingDeclarations();
_flowAnalysis.topLevelDeclaration_enter(node.parent!, null);
_resolverVisitor.analyzeExpression(node, contextType);
_resolverVisitor.popRewrite();
_resolverVisitor.checkIdle();
_flowAnalysis.topLevelDeclaration_exit();
}

View file

@ -16,6 +16,23 @@ main() {
@reflectiveTest
class SwitchExpressionNotAssignableTest extends PubPackageResolutionTest
with WithoutNullSafetyMixin {
test_do_not_report_on_cases_after_the_first() async {
await assertErrorsInCode('''
f() {
var x = 1;
try {
switch (x) {
case 0:
case 2:
case "false":
}
} catch (e) {}
}
''', [
error(CompileTimeErrorCode.INCONSISTENT_CASE_EXPRESSION_TYPES, 83, 7),
]);
}
test_simple() async {
await assertErrorsInCode('''
f(int p) {