blob: c55dd3c75b8b06010b50b4151b5391fbd5e65a96 [file] [log] [blame]
// 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 'type_analyzer.dart';
/// Container for the result of running type analysis on an expression.
///
/// This class keeps track of a provisional type of the expression (prior to
/// resolving null shorting) as well as the information necessary to resolve
/// null shorting.
abstract class ExpressionTypeAnalysisResult<Type extends Object> {
/// Type of the expression before resolving null shorting.
///
/// For example, if `this` is the result of analyzing `(... as int?)?.isEven`,
/// [provisionalType] will be `bool`, because the `isEven` getter returns
/// `bool`, and it is not yet known (until looking at the surrounding code)
/// whether there will be additional selectors after `isEven` that should act
/// on the `bool` type.
Type get provisionalType;
/// Resolves any pending null shorting. For example, if `this` is the result
/// of analyzing `(... as int?)?.isEven`, then calling [resolveShorting] will
/// cause the `?.` to be desugared (if code generation is occurring) and will
/// return the type `bool?`.
///
/// TODO(paulberry): document what calls back to the client might be made by
/// invoking this method.
Type resolveShorting();
}
/// Container for the result of running type analysis on an integer literal.
class IntTypeAnalysisResult<Type extends Object>
extends SimpleTypeAnalysisResult<Type> {
/// Whether the integer literal was converted to a double.
final bool convertedToDouble;
IntTypeAnalysisResult({required super.type, required this.convertedToDouble});
}
/// Information about the code context surrounding a pattern match.
class MatchContext<Node extends Object, Expression extends Node,
Pattern extends Node, Type extends Object, Variable extends Object> {
/// If non-`null`, the match is being done in an irrefutable context, and this
/// is the surrounding AST node that establishes the irrefutable context.
final Node? irrefutableContext;
/// Indicates whether variables declared in the pattern should be `final`.
final bool isFinal;
/// Indicates whether variables declared in the pattern should be `late`.
final bool isLate;
/// The initializer being assigned to this pattern via a variable declaration
/// statement, or `null` if this pattern does not occur in a variable
/// declaration statement, or this pattern is not the top-level pattern in
/// the declaration.
final Expression? initializer;
/// The switch scrutinee, or `null` if this pattern does not occur in a switch
/// statement or switch expression, or this pattern is not the top-level
/// pattern.
final Expression? switchScrutinee;
/// If the match is being done in a pattern assignment, the set of variables
/// assigned so far.
final Map<Variable, Pattern>? assignedVariables;
/// For each variable name in the pattern, a list of the variables which might
/// capture that variable's value, depending upon which alternative is taken
/// in a logical-or pattern.
final Map<String, List<Variable>> componentVariables;
/// For each variable name in the pattern, the promotion key holding the value
/// captured by that variable.
final Map<String, int> patternVariablePromotionKeys;
/// If non-null, the warning that should be issued if the pattern is `_`
final UnnecessaryWildcardKind? unnecessaryWildcardKind;
MatchContext({
this.initializer,
this.irrefutableContext,
required this.isFinal,
this.isLate = false,
this.switchScrutinee,
this.assignedVariables,
required this.componentVariables,
required this.patternVariablePromotionKeys,
this.unnecessaryWildcardKind,
});
/// Returns a modified version of `this`, with [irrefutableContext] set to
/// `null`. This is used to suppress cascading errors after reporting
/// [TypeAnalyzerErrors.refutablePatternInIrrefutableContext].
MatchContext<Node, Expression, Pattern, Type, Variable> makeRefutable() =>
irrefutableContext == null
? this
: new MatchContext(
initializer: initializer,
isFinal: isFinal,
isLate: isLate,
switchScrutinee: switchScrutinee,
assignedVariables: assignedVariables,
componentVariables: componentVariables,
patternVariablePromotionKeys: patternVariablePromotionKeys,
);
/// Returns a modified version of `this`, with a new value of
/// [patternVariablePromotionKeys].
MatchContext<Node, Expression, Pattern, Type, Variable> withPromotionKeys(
Map<String, int> patternVariablePromotionKeys) =>
new MatchContext(
initializer: null,
irrefutableContext: irrefutableContext,
isFinal: isFinal,
isLate: isLate,
switchScrutinee: null,
assignedVariables: assignedVariables,
componentVariables: componentVariables,
patternVariablePromotionKeys: patternVariablePromotionKeys,
unnecessaryWildcardKind: unnecessaryWildcardKind,
);
/// Returns a modified version of `this`, with both [initializer] and
/// [switchScrutinee] set to `null` (because this context is not for a
/// top-level pattern anymore).
MatchContext<Node, Expression, Pattern, Type, Variable>
withUnnecessaryWildcardKind(
UnnecessaryWildcardKind? unnecessaryWildcardKind) {
return new MatchContext(
initializer: null,
irrefutableContext: irrefutableContext,
isFinal: isFinal,
isLate: isLate,
assignedVariables: assignedVariables,
switchScrutinee: null,
componentVariables: componentVariables,
patternVariablePromotionKeys: patternVariablePromotionKeys,
unnecessaryWildcardKind: unnecessaryWildcardKind,
);
}
}
/// Container for the result of running type analysis on a pattern assignment.
class PatternAssignmentAnalysisResult<Type extends Object>
extends SimpleTypeAnalysisResult<Type> {
/// The type schema of the pattern on the left hand size of the assignment.
final Type patternSchema;
PatternAssignmentAnalysisResult({
required this.patternSchema,
required super.type,
});
}
/// Container for the result of running type analysis on an expression that does
/// not contain any null shorting.
class SimpleTypeAnalysisResult<Type extends Object>
implements ExpressionTypeAnalysisResult<Type> {
/// The static type of the expression.
final Type type;
SimpleTypeAnalysisResult({required this.type});
@override
Type get provisionalType => type;
@override
Type resolveShorting() => type;
}
/// Container for the result of running type analysis on an integer literal.
class SwitchStatementTypeAnalysisResult<Type> {
/// Whether the switch statement had a `default` clause.
final bool hasDefault;
/// Whether the switch statement was exhaustive.
final bool isExhaustive;
/// Whether the last case body in the switch statement terminated.
final bool lastCaseTerminates;
/// If `true`, patterns support is enabled, there is no default clause, and
/// the static type of the scrutinee expression is an "always exhaustive"
/// type. Therefore, flow analysis has assumed (without checking) that the
/// switch statement is exhaustive. So at a later stage of compilation, the
/// exhaustiveness checking algorithm should check whether this switch
/// statement was exhaustive, and report a compile-time error if it wasn't.
final bool requiresExhaustivenessValidation;
/// The static type of the scrutinee expression.
final Type scrutineeType;
SwitchStatementTypeAnalysisResult({
required this.hasDefault,
required this.isExhaustive,
required this.lastCaseTerminates,
required this.requiresExhaustivenessValidation,
required this.scrutineeType,
});
}
/// The location of a wildcard pattern that was found unnecessary.
///
/// When a wildcard pattern always matches, and is not required by the
/// by the location, we can report it as unnecessary. The locations where it
/// is necessary include list patterns, record patterns, cast patterns, etc.
enum UnnecessaryWildcardKind {
/// The wildcard pattern is the left or the right side of a logical-and
/// pattern. Because we found that is always matches, it has no effect,
/// and can be removed.
logicalAndPatternOperand,
}