Track a reason for a pattern variable inconsistency.

This allows us report more specific errors.

Bug: https://github.com/dart-lang/sdk/issues/51505
Change-Id: I6e40af1fedce55886a58b954721154db933ede17
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/286605
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Konstantin Shcheglov 2023-03-03 16:14:48 +00:00 committed by Commit Queue
parent 8b33eb6114
commit f67f7d11d7
15 changed files with 257 additions and 118 deletions

View file

@ -34,6 +34,47 @@ class CaseHeadOrDefaultInfo<Node extends Object, Expression extends Node,
});
}
/// The kind of inconsistency identified for a variable.
enum JoinedPatternVariableInconsistency {
/// No inconsistency.
none(0),
/// Only one branch of a logical-or pattern has the variable.
logicalOr(4),
/// Not every case of a shared case scope has the variable.
sharedCaseAbsent(3),
/// The shared case scope has a label or `default` case.
sharedCaseHasLabel(2),
/// The finality or type of the variable components is not the same.
/// This is reported for both logical-or and shared cases.
differentFinalityOrType(1);
final int _severity;
const JoinedPatternVariableInconsistency(this._severity);
/// Returns the most serious inconsistency for `this` or [other].
JoinedPatternVariableInconsistency maxWith(
JoinedPatternVariableInconsistency other,
) {
return _severity > other._severity ? this : other;
}
/// Returns the most serious inconsistency for `this` or [others].
JoinedPatternVariableInconsistency maxWithAll(
Iterable<JoinedPatternVariableInconsistency> others,
) {
JoinedPatternVariableInconsistency result = this;
for (JoinedPatternVariableInconsistency other in others) {
result = result.maxWith(other);
}
return result;
}
}
/// The location where the join of a pattern variable happens.
enum JoinedPatternVariableLocation {
/// A single pattern, from `logical-or` patterns.
@ -1867,7 +1908,7 @@ mixin TypeAnalyzer<
void finishJoinedPatternVariable(
Variable variable, {
required JoinedPatternVariableLocation location,
required bool isConsistent,
required JoinedPatternVariableInconsistency inconsistency,
required bool isFinal,
required Type type,
});
@ -2210,8 +2251,10 @@ mixin TypeAnalyzer<
if (!isIdenticalToComponent) {
finishJoinedPatternVariable(variable,
location: location,
isConsistent:
typeIfConsistent != null && isFinalIfConsistent != null,
inconsistency: typeIfConsistent != null &&
isFinalIfConsistent != null
? JoinedPatternVariableInconsistency.none
: JoinedPatternVariableInconsistency.differentFinalityOrType,
isFinal: isFinalIfConsistent ?? false,
type: typeIfConsistent ?? errorType);
flow.assignMatchedPatternVariable(variable, promotionKey);

View file

@ -90,7 +90,7 @@ abstract class VariableBinder<Node extends Object, Variable extends Object> {
Variable joinPatternVariables({
required Object key,
required List<Variable> components,
required bool isConsistent,
required JoinedPatternVariableInconsistency inconsistency,
});
/// Updates the binder after visiting a logical-or pattern, joins variables
@ -109,7 +109,7 @@ abstract class VariableBinder<Node extends Object, Variable extends Object> {
joinPatternVariables(
key: node,
components: [leftVariable, rightVariable],
isConsistent: true,
inconsistency: JoinedPatternVariableInconsistency.none,
),
);
} else {
@ -124,7 +124,7 @@ abstract class VariableBinder<Node extends Object, Variable extends Object> {
joinPatternVariables(
key: node,
components: [leftVariable],
isConsistent: false,
inconsistency: JoinedPatternVariableInconsistency.logicalOr,
),
);
}
@ -143,7 +143,7 @@ abstract class VariableBinder<Node extends Object, Variable extends Object> {
joinPatternVariables(
key: node,
components: [rightVariable],
isConsistent: false,
inconsistency: JoinedPatternVariableInconsistency.logicalOr,
),
);
}
@ -165,6 +165,7 @@ abstract class VariableBinder<Node extends Object, Variable extends Object> {
void switchStatementSharedCaseScopeEmpty(Object key) {
_SharedCaseScope<Variable> sharedScope = _sharedCaseScopes.last;
assert(sharedScope.key == key);
sharedScope.hasLabel = true;
sharedScope.addAll({});
}
@ -183,13 +184,17 @@ abstract class VariableBinder<Node extends Object, Variable extends Object> {
in sharedScope.variables.entries) {
_SharedCaseScopeVariable<Variable> sharedVariable = entry.value;
List<Variable> variables = sharedVariable.variables;
if (sharedVariable.isConsistent && variables.length == 1) {
if (sharedVariable.allCases && variables.length == 1) {
result[entry.key] = variables[0];
} else {
result[entry.key] = joinPatternVariables(
key: key,
components: variables,
isConsistent: sharedVariable.isConsistent,
inconsistency: sharedVariable.allCases
? JoinedPatternVariableInconsistency.none
: sharedScope.hasLabel
? JoinedPatternVariableInconsistency.sharedCaseHasLabel
: JoinedPatternVariableInconsistency.sharedCaseAbsent,
);
}
}
@ -230,6 +235,7 @@ abstract class VariableBinderErrors<Node extends Object,
class _SharedCaseScope<Variable extends Object> {
final Object key;
bool isEmpty = true;
bool hasLabel = false;
Map<String, _SharedCaseScopeVariable<Variable>> variables = {};
_SharedCaseScope(this.key);
@ -253,7 +259,7 @@ class _SharedCaseScope<Variable extends Object> {
if (newVariable != null) {
variable.variables.add(newVariable);
} else {
variable.isConsistent = false;
variable.allCases = false;
}
}
for (MapEntry<String, Variable> newEntry in newVariables.entries) {
@ -261,7 +267,7 @@ class _SharedCaseScope<Variable extends Object> {
Variable newVariable = newEntry.value;
if (!variables.containsKey(name)) {
_getVariable(name)
..isConsistent = false
..allCases = false
..variables.add(newVariable);
}
}
@ -274,6 +280,6 @@ class _SharedCaseScope<Variable extends Object> {
}
class _SharedCaseScopeVariable<Variable extends Object> {
bool isConsistent = true;
bool allCases = true;
final List<Variable> variables = [];
}

View file

@ -1299,7 +1299,8 @@ class PatternVariableJoin extends Var {
/// of `true` either means that the variable is consistent or that analysis
/// has not yet completed.
@override
bool isConsistent = true;
JoinedPatternVariableInconsistency inconsistency =
JoinedPatternVariableInconsistency.none;
/// Indicates whether [VariableBinder.joinPatternVariables] has been called
/// for this variable join yet.
@ -1321,10 +1322,10 @@ class PatternVariableJoin extends Var {
@override
String toString() {
var isConsistent = this.isConsistent;
var declarationStr = <String>[
if (_type != null) ...[
if (!isConsistent) 'notConsistent',
if (inconsistency != JoinedPatternVariableInconsistency.none)
'notConsistent:${inconsistency.name}',
if (isFinal) 'final',
type.type,
],
@ -1336,14 +1337,16 @@ class PatternVariableJoin extends Var {
}
/// Called by [VariableBinder.joinPatternVariables].
void _handleJoin(
{required List<Var> components, required bool isConsistent}) {
void _handleJoin({
required List<Var> components,
required JoinedPatternVariableInconsistency inconsistency,
}) {
expect(isJoined, false);
expect(components.map((c) => c.identity),
expectedComponents.map((c) => c.identity),
reason: 'at $location');
expect(components, expectedComponents, reason: 'at $location');
this.isConsistent = isConsistent;
this.inconsistency = inconsistency;
this.isJoined = true;
}
}
@ -1498,7 +1501,9 @@ class Var extends Node implements Promotable {
LValue get expr =>
new _VariableReference(this, null, location: computeLocation());
bool get isConsistent => true;
JoinedPatternVariableInconsistency get inconsistency {
return JoinedPatternVariableInconsistency.none;
}
/// The string that should be used to check variables in a set.
String get stringToCheckVariables => identity;
@ -3614,15 +3619,13 @@ class _MiniAstTypeAnalyzer
void finishJoinedPatternVariable(
covariant PatternVariableJoin variable, {
required JoinedPatternVariableLocation location,
required bool isConsistent,
required JoinedPatternVariableInconsistency inconsistency,
required bool isFinal,
required Type type,
}) {
variable.isFinal = isFinal;
variable.type = type;
if (!isConsistent) {
variable.isConsistent = false;
}
variable.inconsistency = variable.inconsistency.maxWith(inconsistency);
}
@override
@ -4862,14 +4865,16 @@ class _VariableBinder extends VariableBinder<Node, Var> {
Var joinPatternVariables({
required Object? key,
required List<Var> components,
required bool isConsistent,
required JoinedPatternVariableInconsistency inconsistency,
}) {
var joinedVariable = components[0]._joinedVar;
if (joinedVariable == null) {
fail('No joined variable for ${components[0].location}');
}
joinedVariable._handleJoin(
components: components, isConsistent: isConsistent);
components: components,
inconsistency: inconsistency,
);
return joinedVariable;
}
}

View file

@ -443,8 +443,8 @@ main() {
'varPattern(x, matchedType: double, staticType: double), '
'varPattern(x, matchedType: double, staticType: '
'double), matchedType: double), true, variables('
'notConsistent double x = [x1, x2])), expr(int)), '
'case(default, expr(int)))',
'notConsistent:differentFinalityOrType double x = '
'[x1, x2])), expr(int)), case(default, expr(int)))',
)
.stmt,
], expectedErrors: {
@ -470,8 +470,8 @@ main() {
'varPattern(x, matchedType: double, staticType: double), '
'varPattern(x, matchedType: double, staticType: '
'num), matchedType: double), true, variables('
'notConsistent error x = [x1, x2])), expr(int)), '
'case(default, expr(int)))',
'notConsistent:differentFinalityOrType error x = '
'[x1, x2])), expr(int)), case(default, expr(int)))',
)
.stmt,
], expectedErrors: {
@ -972,8 +972,8 @@ main() {
'matchedType: int, staticType: num), true, '
'variables(x1)), head(varPattern(x, matchedType: int, '
'staticType: int), true, variables(x2)), '
'variables(notConsistent error x = [x1, x2])), '
'block(break())))'),
'variables(notConsistent:differentFinalityOrType error '
'x = [x1, x2])), block(break())))'),
]);
});
test('explicit / implicit', () {
@ -995,8 +995,8 @@ main() {
'matchedType: int, staticType: num), true, variables('
'x1)), head(varPattern(x, matchedType: int, '
'staticType: int), true, variables(x2)), '
'variables(notConsistent error x = [x1, x2])), '
'block(break())))'),
'variables(notConsistent:differentFinalityOrType error '
'x = [x1, x2])), block(break())))'),
]);
});
test('implicit / implicit', () {
@ -1020,7 +1020,8 @@ main() {
'variables(x1)), head(listPattern(varPattern(x, '
'matchedType: int, staticType: int), matchedType: '
'List<int>, requiredType: List<int>), true, '
'variables(x2)), variables(notConsistent error '
'variables(x2)), variables('
'notConsistent:differentFinalityOrType error '
'x = [x1, x2])), block(break())))'),
]);
});
@ -1043,8 +1044,9 @@ main() {
).checkIr('switch(expr(int), case(heads(head(varPattern(x, '
'matchedType: int, staticType: int), true, variables(x1)), '
'head(varPattern(x, matchedType: int, staticType: int), '
'true, variables(x2)), variables(notConsistent '
'int x = [x1, x2])), block(break())))'),
'true, variables(x2)), variables('
'notConsistent:differentFinalityOrType int x = [x1, x2])), '
'block(break())))'),
]);
});
});
@ -1065,7 +1067,8 @@ main() {
).checkIr('switch(expr(int), case(heads(head(varPattern(x, '
'matchedType: int, staticType: int), true, variables(x1)), '
'head(const(0, matchedType: int), true, variables()), '
'variables(notConsistent int x = [x1])), block(break())))'),
'variables(notConsistent:sharedCaseAbsent int x = [x1])), '
'block(break())))'),
]);
});
test('case not, case has', () {
@ -1085,7 +1088,8 @@ main() {
).checkIr('switch(expr(int), case(heads(head(const(0, '
'matchedType: int), true, variables()), head(varPattern(x, '
'matchedType: int, staticType: int), true, variables(x1)), '
'variables(notConsistent int x = [x1])), block(break())))'),
'variables(notConsistent:sharedCaseAbsent int x = [x1])), '
'block(break())))'),
]);
});
test('case has, default', () {
@ -1104,8 +1108,8 @@ main() {
],
).checkIr('switch(expr(int), case(heads(head(varPattern(x, '
'matchedType: int, staticType: int), true, variables(x1)), '
'default, variables(notConsistent int x = [x1])), '
'block(break())))'),
'default, variables(notConsistent:sharedCaseHasLabel int x '
'= [x1])), block(break())))'),
]);
});
test('case has, with label', () {
@ -1123,7 +1127,8 @@ main() {
],
).checkIr('switch(expr(int), case(heads(head(varPattern(x, '
'matchedType: int, staticType: int), true, variables(x1)), '
'variables(notConsistent int x = [x1])), block(break())))'),
'variables(notConsistent:sharedCaseHasLabel int x = '
'[x1])), block(break())))'),
]);
});
});
@ -2829,8 +2834,8 @@ main() {
).checkIr('ifCase(expr(Object), logicalOrPattern(varPattern(x, '
'matchedType: Object, staticType: int), varPattern(x, '
'matchedType: Object, staticType: num), matchedType: '
'Object), variables(notConsistent error x = [x1, x2]), '
'true, block(), noop)'),
'Object), variables(notConsistent:differentFinalityOrType '
'error x = [x1, x2]), true, block(), noop)'),
], expectedErrors: {
'inconsistentJoinedPatternVariable(variable: x = [x1, x2], '
'component: x2)',
@ -2848,8 +2853,8 @@ main() {
).checkIr('ifCase(expr(num), logicalOrPattern(varPattern(x, '
'matchedType: num, staticType: int), varPattern(x, '
'matchedType: num, staticType: num), matchedType: num), '
'variables(notConsistent error x = [x1, x2]), true, '
'block(), noop)'),
'variables(notConsistent:differentFinalityOrType error x = '
'[x1, x2]), true, block(), noop)'),
], expectedErrors: {
'inconsistentJoinedPatternVariable(variable: x = [x1, x2], '
'component: x2)',
@ -2869,8 +2874,8 @@ main() {
).checkIr('ifCase(expr(int), logicalOrPattern(varPattern(x, '
'matchedType: int, staticType: int), varPattern(x, '
'matchedType: int, staticType: int), matchedType: int), '
'variables(notConsistent int x = [x1, x2]), true, '
'block(), noop)'),
'variables(notConsistent:differentFinalityOrType int x = '
'[x1, x2]), true, block(), noop)'),
], expectedErrors: {
'inconsistentJoinedPatternVariable(variable: x = [x1, x2], '
'component: x2)',
@ -2903,7 +2908,8 @@ main() {
).checkIr('ifCase(expr(int), logicalOrPattern(varPattern(x, '
'matchedType: int, staticType: int), wildcardPattern('
'matchedType: int), matchedType: int), variables('
'notConsistent int x = [x1]), true, block(), noop)'),
'notConsistent:logicalOr int x = [x1]), true, block(), '
'noop)'),
], expectedErrors: {
'logicalOrPatternBranchMissingVariable(node: PATTERN, '
'hasInLeft: true, name: x, variable: x1)',
@ -2920,7 +2926,8 @@ main() {
).checkIr('ifCase(expr(int), logicalOrPattern(wildcardPattern('
'matchedType: int), varPattern(x, matchedType: int, '
'staticType: int), matchedType: int), variables('
'notConsistent int x = [x1]), true, block(), noop)'),
'notConsistent:logicalOr int x = [x1]), true, block(), '
'noop)'),
], expectedErrors: {
'logicalOrPatternBranchMissingVariable(node: PATTERN, '
'hasInLeft: false, name: x, variable: x1)',

View file

@ -2,6 +2,7 @@
// 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:_fe_analyzer_shared/src/type_inference/variable_bindings.dart';
import 'package:test/test.dart';
@ -45,7 +46,7 @@ main() {
_Empty(),
id: 2,
),
expectedVariables: {'x: notConsistent [1]'},
expectedVariables: {'x: notConsistent:logicalOr [1]'},
expectErrors: [
'logicalOrPatternBranchMissingVariable(node: 2, '
'hasInLeft: true, name: x, variable: 1)'
@ -59,7 +60,7 @@ main() {
_VarPattern('x', 1),
id: 2,
),
expectedVariables: {'x: notConsistent [1]'},
expectedVariables: {'x: notConsistent:logicalOr [1]'},
expectErrors: [
'logicalOrPatternBranchMissingVariable(node: 2, '
'hasInLeft: false, name: x, variable: 1)'
@ -87,7 +88,7 @@ main() {
_VarPattern('x', 1),
_Empty(),
],
expectedVariables: {'x: notConsistent [1]'},
expectedVariables: {'x: notConsistent:sharedCaseAbsent [1]'},
);
});
test('Second has', () {
@ -97,7 +98,7 @@ main() {
_Empty(),
_VarPattern('x', 1),
],
expectedVariables: {'x: notConsistent [1]'},
expectedVariables: {'x: notConsistent:sharedCaseAbsent [1]'},
);
});
test('Partial intersection', () {
@ -110,7 +111,10 @@ main() {
),
_VarPattern('x', 3),
],
expectedVariables: {'x: [1, 3]', 'y: notConsistent [2]'},
expectedVariables: {
'x: [1, 3]',
'y: notConsistent:sharedCaseAbsent [2]',
},
);
});
group('Has default', () {
@ -121,7 +125,7 @@ main() {
_VarPattern('x', 1),
],
hasDefaultFirst: true, // does not happen normally
expectedVariables: {'x: notConsistent [1]'},
expectedVariables: {'x: notConsistent:sharedCaseHasLabel [1]'},
);
});
test('Last', () {
@ -131,7 +135,7 @@ main() {
_VarPattern('x', 1),
],
hasDefaultLast: true,
expectedVariables: {'x: notConsistent [1]'},
expectedVariables: {'x: notConsistent:sharedCaseHasLabel [1]'},
);
});
});
@ -159,7 +163,9 @@ main() {
),
_VarPattern('x', 2),
],
expectedVariables: {'x: notConsistent [notConsistent [1], 2]'},
expectedVariables: {
'x: notConsistent:logicalOr [notConsistent:logicalOr [1], 2]',
},
expectErrors: [
'logicalOrPatternBranchMissingVariable(node: null, '
'hasInLeft: true, name: x, variable: 1)',
@ -176,7 +182,7 @@ main() {
),
_Empty(),
],
expectedVariables: {'x: notConsistent [[1, 2]]'},
expectedVariables: {'x: notConsistent:sharedCaseAbsent [[1, 2]]'},
);
});
test('Second has', () {
@ -189,7 +195,7 @@ main() {
_VarPattern('x', 2),
),
],
expectedVariables: {'x: notConsistent [[1, 2]]'},
expectedVariables: {'x: notConsistent:sharedCaseAbsent [[1, 2]]'},
);
});
});
@ -349,7 +355,7 @@ class _VariableBinder extends VariableBinder<_Node, _VariableElement> {
_VariableElement joinPatternVariables({
required Object? key,
required List<_VariableElement> components,
required bool isConsistent,
required JoinedPatternVariableInconsistency inconsistency,
}) {
return _VariableJoinElement(
components: [
@ -359,30 +365,35 @@ class _VariableBinder extends VariableBinder<_Node, _VariableElement> {
else
variable
],
isConsistent: isConsistent && components.every((e) => e.isConsistent),
inconsistency: inconsistency.maxWithAll(
components.map((e) => e.inconsistency),
),
);
}
}
class _VariableElement {
bool get isConsistent => true;
JoinedPatternVariableInconsistency get inconsistency {
return JoinedPatternVariableInconsistency.none;
}
}
class _VariableJoinElement extends _VariableElement {
final List<_VariableElement> components;
@override
final bool isConsistent;
final JoinedPatternVariableInconsistency inconsistency;
_VariableJoinElement({
required this.components,
required this.isConsistent,
required this.inconsistency,
});
@override
String toString() {
return [
if (!isConsistent) 'notConsistent',
if (inconsistency != JoinedPatternVariableInconsistency.none)
'notConsistent:${inconsistency.name}',
components,
].join(' ');
}

View file

@ -612,8 +612,6 @@ CompileTimeErrorCode.INCONSISTENT_INHERITANCE_GETTER_AND_METHOD:
status: needsEvaluation
CompileTimeErrorCode.INCONSISTENT_LANGUAGE_VERSION_OVERRIDE:
status: needsEvaluation
CompileTimeErrorCode.INCONSISTENT_PATTERN_VARIABLE_SHARED_CASE_SCOPE:
status: noFix
CompileTimeErrorCode.INCONSISTENT_PATTERN_VARIABLE_LOGICAL_OR:
status: noFix
notes: |-
@ -958,6 +956,12 @@ CompileTimeErrorCode.PATTERN_VARIABLE_ASSIGNMENT_INSIDE_GUARD:
notes: |-
We do not have enough information to figure out the user's intent with the
use of the variable in the pattern.
CompileTimeErrorCode.PATTERN_VARIABLE_SHARED_CASE_SCOPE_DIFFERENT_FINALITY_OR_TYPE:
status: needsEvaluation
CompileTimeErrorCode.PATTERN_VARIABLE_SHARED_CASE_SCOPE_HAS_LABEL:
status: needsEvaluation
CompileTimeErrorCode.PATTERN_VARIABLE_SHARED_CASE_SCOPE_NOT_ALL_CASES:
status: needsEvaluation
CompileTimeErrorCode.POSITIONAL_SUPER_FORMAL_PARAMETER_WITH_POSITIONAL_ARGUMENT:
status: needsFix
since: 2.17

View file

@ -5,6 +5,8 @@
import 'dart:collection';
import 'package:_fe_analyzer_shared/src/scanner/string_canonicalizer.dart';
import 'package:_fe_analyzer_shared/src/type_inference/type_analyzer.dart'
as shared;
import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/analysis/session.dart';
import 'package:analyzer/dart/ast/ast.dart';
@ -3838,8 +3840,7 @@ class JoinPatternVariableElementImpl extends PatternVariableElementImpl
@override
final List<PatternVariableElementImpl> variables;
@override
bool isConsistent;
shared.JoinedPatternVariableInconsistency inconsistency;
/// The identifiers that reference this element.
final List<SimpleIdentifier> references = [];
@ -3848,7 +3849,7 @@ class JoinPatternVariableElementImpl extends PatternVariableElementImpl
super.name,
super.offset,
this.variables,
this.isConsistent,
this.inconsistency,
) {
for (var component in variables) {
component.join = this;
@ -3858,6 +3859,11 @@ class JoinPatternVariableElementImpl extends PatternVariableElementImpl
@override
int get hashCode => identityHashCode(this);
@override
bool get isConsistent {
return inconsistency == shared.JoinedPatternVariableInconsistency.none;
}
/// Returns this variable, and variables that join into it.
List<PatternVariableElementImpl> get transitiveVariables {
var result = <PatternVariableElementImpl>[];

View file

@ -2,6 +2,8 @@
// 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'
as shared;
import 'package:_fe_analyzer_shared/src/type_inference/variable_bindings.dart';
import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/ast/ast.dart';
@ -1699,7 +1701,7 @@ class _VariableBinder
JoinPatternVariableElementImpl joinPatternVariables({
required Object key,
required List<PromotableElement> components,
required bool isConsistent,
required shared.JoinedPatternVariableInconsistency inconsistency,
}) {
var first = components.first;
List<PatternVariableElementImpl> expandedVariables;
@ -1723,10 +1725,11 @@ class _VariableBinder
first.name,
-1,
expandedVariables,
isConsistent &&
components.every((element) =>
element is! JoinPatternVariableElementImpl ||
element.isConsistent),
inconsistency.maxWithAll(
components
.whereType<JoinPatternVariableElementImpl>()
.map((e) => e.inconsistency),
),
)..enclosingElement = first.enclosingElement;
}
}

View file

@ -2139,18 +2139,6 @@ class CompileTimeErrorCode extends AnalyzerErrorCode {
"both branches.",
);
/// Parameters:
/// 0: the name of the pattern variable
static const CompileTimeErrorCode
INCONSISTENT_PATTERN_VARIABLE_SHARED_CASE_SCOPE = CompileTimeErrorCode(
'INCONSISTENT_PATTERN_VARIABLE_SHARED_CASE_SCOPE',
"The variable '{0}' doesn't have the same type and/or finality in all "
"cases that share this body.",
correctionMessage:
"Try declaring the variable pattern with the same type and finality in "
"all cases.",
);
/// Parameters:
/// 0: the name of the initializing formal that is not an instance variable in
/// the immediately enclosing class
@ -3913,6 +3901,43 @@ class CompileTimeErrorCode extends AnalyzerErrorCode {
correctionMessage: "Try assigning to a different variable.",
);
/// Parameters:
/// 0: the name of the pattern variable
static const CompileTimeErrorCode
PATTERN_VARIABLE_SHARED_CASE_SCOPE_DIFFERENT_FINALITY_OR_TYPE =
CompileTimeErrorCode(
'PATTERN_VARIABLE_SHARED_CASE_SCOPE_DIFFERENT_FINALITY_OR_TYPE',
"The variable '{0}' doesn't have the same type and/or finality in all "
"cases that share this body.",
correctionMessage:
"Try declaring the variable pattern with the same type and finality in "
"all cases.",
);
/// Parameters:
/// 0: the name of the pattern variable
static const CompileTimeErrorCode
PATTERN_VARIABLE_SHARED_CASE_SCOPE_HAS_LABEL = CompileTimeErrorCode(
'PATTERN_VARIABLE_SHARED_CASE_SCOPE_HAS_LABEL',
"The variable '{0}' is not available because there is a label or 'default' "
"case.",
correctionMessage:
"Try removing the label, or providing the 'default' case with its own "
"body.",
);
/// Parameters:
/// 0: the name of the pattern variable
static const CompileTimeErrorCode
PATTERN_VARIABLE_SHARED_CASE_SCOPE_NOT_ALL_CASES = CompileTimeErrorCode(
'PATTERN_VARIABLE_SHARED_CASE_SCOPE_NOT_ALL_CASES',
"The variable '{0}' is available in some, but not all cases that share "
"this body.",
correctionMessage:
"Try declaring the variable pattern with the same type and finality in "
"all cases.",
);
/// No parameters.
static const CompileTimeErrorCode
POSITIONAL_SUPER_FORMAL_PARAMETER_WITH_POSITIONAL_ARGUMENT =

View file

@ -235,7 +235,6 @@ const List<ErrorCode> errorCodeValues = [
CompileTimeErrorCode.INCONSISTENT_INHERITANCE_GETTER_AND_METHOD,
CompileTimeErrorCode.INCONSISTENT_LANGUAGE_VERSION_OVERRIDE,
CompileTimeErrorCode.INCONSISTENT_PATTERN_VARIABLE_LOGICAL_OR,
CompileTimeErrorCode.INCONSISTENT_PATTERN_VARIABLE_SHARED_CASE_SCOPE,
CompileTimeErrorCode.INITIALIZER_FOR_NON_EXISTENT_FIELD,
CompileTimeErrorCode.INITIALIZER_FOR_STATIC_FIELD,
CompileTimeErrorCode.INITIALIZING_FORMAL_FOR_NON_EXISTENT_FIELD,
@ -401,6 +400,10 @@ const List<ErrorCode> errorCodeValues = [
CompileTimeErrorCode.PATTERN_CONSTANT_FROM_DEFERRED_LIBRARY,
CompileTimeErrorCode.PATTERN_TYPE_MISMATCH_IN_IRREFUTABLE_CONTEXT,
CompileTimeErrorCode.PATTERN_VARIABLE_ASSIGNMENT_INSIDE_GUARD,
CompileTimeErrorCode
.PATTERN_VARIABLE_SHARED_CASE_SCOPE_DIFFERENT_FINALITY_OR_TYPE,
CompileTimeErrorCode.PATTERN_VARIABLE_SHARED_CASE_SCOPE_HAS_LABEL,
CompileTimeErrorCode.PATTERN_VARIABLE_SHARED_CASE_SCOPE_NOT_ALL_CASES,
CompileTimeErrorCode
.POSITIONAL_SUPER_FORMAL_PARAMETER_WITH_POSITIONAL_ARGUMENT,
CompileTimeErrorCode.PREFIX_COLLIDES_WITH_TOP_LEVEL_MEMBER,

View file

@ -887,20 +887,36 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
void finishJoinedPatternVariable(
covariant JoinPatternVariableElementImpl variable, {
required JoinedPatternVariableLocation location,
required bool isConsistent,
required shared.JoinedPatternVariableInconsistency inconsistency,
required bool isFinal,
required DartType type,
}) {
variable.isConsistent &= isConsistent;
variable.inconsistency = variable.inconsistency.maxWith(inconsistency);
variable.isFinal = isFinal;
variable.type = type;
if (location == JoinedPatternVariableLocation.sharedCaseScope) {
for (var reference in variable.references) {
if (!variable.isConsistent) {
if (variable.inconsistency ==
shared.JoinedPatternVariableInconsistency.sharedCaseAbsent) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode
.INCONSISTENT_PATTERN_VARIABLE_SHARED_CASE_SCOPE,
.PATTERN_VARIABLE_SHARED_CASE_SCOPE_NOT_ALL_CASES,
reference,
[variable.name],
);
} else if (variable.inconsistency ==
shared.JoinedPatternVariableInconsistency.sharedCaseHasLabel) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.PATTERN_VARIABLE_SHARED_CASE_SCOPE_HAS_LABEL,
reference,
[variable.name],
);
} else if (variable.inconsistency ==
shared.JoinedPatternVariableInconsistency.differentFinalityOrType) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode
.PATTERN_VARIABLE_SHARED_CASE_SCOPE_DIFFERENT_FINALITY_OR_TYPE,
reference,
[variable.name],
);

View file

@ -6205,12 +6205,6 @@ CompileTimeErrorCode:
comment: |-
Parameters:
0: the name of the pattern variable
INCONSISTENT_PATTERN_VARIABLE_SHARED_CASE_SCOPE:
problemMessage: "The variable '{0}' doesn't have the same type and/or finality in all cases that share this body."
correctionMessage: Try declaring the variable pattern with the same type and finality in all cases.
comment: |-
Parameters:
0: the name of the pattern variable
INITIALIZER_FOR_NON_EXISTENT_FIELD:
problemMessage: "'{0}' isn't a field in the enclosing class."
correctionMessage: "Try correcting the name to match an existing field, or defining a field named '{0}'."
@ -11353,6 +11347,24 @@ CompileTimeErrorCode:
PATTERN_VARIABLE_ASSIGNMENT_INSIDE_GUARD:
problemMessage: Pattern variables can't be assigned inside the guard of the enclosing guarded pattern.
correctionMessage: Try assigning to a different variable.
PATTERN_VARIABLE_SHARED_CASE_SCOPE_DIFFERENT_FINALITY_OR_TYPE:
problemMessage: "The variable '{0}' doesn't have the same type and/or finality in all cases that share this body."
correctionMessage: Try declaring the variable pattern with the same type and finality in all cases.
comment: |-
Parameters:
0: the name of the pattern variable
PATTERN_VARIABLE_SHARED_CASE_SCOPE_HAS_LABEL:
problemMessage: "The variable '{0}' is not available because there is a label or 'default' case."
correctionMessage: Try removing the label, or providing the 'default' case with its own body.
comment: |-
Parameters:
0: the name of the pattern variable
PATTERN_VARIABLE_SHARED_CASE_SCOPE_NOT_ALL_CASES:
problemMessage: "The variable '{0}' is available in some, but not all cases that share this body."
correctionMessage: Try declaring the variable pattern with the same type and finality in all cases.
comment: |-
Parameters:
0: the name of the pattern variable
POSITIONAL_SUPER_FORMAL_PARAMETER_WITH_POSITIONAL_ARGUMENT:
problemMessage: Positional super parameters can't be used when the super constructor invocation has a positional argument.
correctionMessage: Try making all the positional parameters passed to the super constructor be either all super parameters or all normal parameters.

View file

@ -582,7 +582,8 @@ void f(Object? x) {
}
''', [
error(
CompileTimeErrorCode.INCONSISTENT_PATTERN_VARIABLE_SHARED_CASE_SCOPE,
CompileTimeErrorCode
.PATTERN_VARIABLE_SHARED_CASE_SCOPE_DIFFERENT_FINALITY_OR_TYPE,
101,
1),
]);
@ -682,7 +683,8 @@ void f(Object? x) {
}
''', [
error(
CompileTimeErrorCode.INCONSISTENT_PATTERN_VARIABLE_SHARED_CASE_SCOPE,
CompileTimeErrorCode
.PATTERN_VARIABLE_SHARED_CASE_SCOPE_DIFFERENT_FINALITY_OR_TYPE,
101,
1),
]);
@ -782,7 +784,8 @@ void f(Object? x) {
}
''', [
error(
CompileTimeErrorCode.INCONSISTENT_PATTERN_VARIABLE_SHARED_CASE_SCOPE,
CompileTimeErrorCode
.PATTERN_VARIABLE_SHARED_CASE_SCOPE_DIFFERENT_FINALITY_OR_TYPE,
95,
1),
]);
@ -881,7 +884,7 @@ void f(Object? x) {
}
''', [
error(
CompileTimeErrorCode.INCONSISTENT_PATTERN_VARIABLE_SHARED_CASE_SCOPE,
CompileTimeErrorCode.PATTERN_VARIABLE_SHARED_CASE_SCOPE_NOT_ALL_CASES,
80,
1),
]);
@ -959,7 +962,7 @@ void f(Object? x) {
}
''', [
error(
CompileTimeErrorCode.INCONSISTENT_PATTERN_VARIABLE_SHARED_CASE_SCOPE,
CompileTimeErrorCode.PATTERN_VARIABLE_SHARED_CASE_SCOPE_NOT_ALL_CASES,
80,
1),
]);
@ -1036,10 +1039,8 @@ void f(Object? x) {
}
}
''', [
error(
CompileTimeErrorCode.INCONSISTENT_PATTERN_VARIABLE_SHARED_CASE_SCOPE,
81,
1),
error(CompileTimeErrorCode.PATTERN_VARIABLE_SHARED_CASE_SCOPE_HAS_LABEL,
81, 1),
]);
final node = findNode.switchStatement('switch');
@ -1113,8 +1114,7 @@ void f(Object? x) {
error(HintCode.UNREACHABLE_SWITCH_CASE, 55, 4),
error(HintCode.DEAD_CODE, 71, 7),
error(
CompileTimeErrorCode
.INCONSISTENT_PATTERN_VARIABLE_SHARED_CASE_SCOPE,
CompileTimeErrorCode.PATTERN_VARIABLE_SHARED_CASE_SCOPE_HAS_LABEL,
86,
1),
]));
@ -1176,10 +1176,8 @@ void f(Object? x) {
}
''', [
error(WarningCode.UNUSED_LABEL, 39, 8),
error(
CompileTimeErrorCode.INCONSISTENT_PATTERN_VARIABLE_SHARED_CASE_SCOPE,
81,
1),
error(CompileTimeErrorCode.PATTERN_VARIABLE_SHARED_CASE_SCOPE_HAS_LABEL,
81, 1),
]);
final node = findNode.switchStatement('switch');
@ -1256,15 +1254,15 @@ void f(Object? x) {
}
''', [
error(
CompileTimeErrorCode.INCONSISTENT_PATTERN_VARIABLE_SHARED_CASE_SCOPE,
CompileTimeErrorCode.PATTERN_VARIABLE_SHARED_CASE_SCOPE_NOT_ALL_CASES,
95,
1),
error(
CompileTimeErrorCode.INCONSISTENT_PATTERN_VARIABLE_SHARED_CASE_SCOPE,
CompileTimeErrorCode.PATTERN_VARIABLE_SHARED_CASE_SCOPE_NOT_ALL_CASES,
104,
1),
error(
CompileTimeErrorCode.INCONSISTENT_PATTERN_VARIABLE_SHARED_CASE_SCOPE,
CompileTimeErrorCode.PATTERN_VARIABLE_SHARED_CASE_SCOPE_NOT_ALL_CASES,
113,
1),
]);

View file

@ -290,7 +290,7 @@ void f(Object? x) {
}
''', [
error(
CompileTimeErrorCode.INCONSISTENT_PATTERN_VARIABLE_SHARED_CASE_SCOPE,
CompileTimeErrorCode.PATTERN_VARIABLE_SHARED_CASE_SCOPE_NOT_ALL_CASES,
71,
1),
]);

View file

@ -11081,7 +11081,7 @@ class InferenceVisitorImpl extends InferenceVisitorBase
void finishJoinedPatternVariable(
VariableDeclaration variable, {
required JoinedPatternVariableLocation location,
required bool isConsistent,
required JoinedPatternVariableInconsistency inconsistency,
required bool isFinal,
required DartType type,
}) {