[cfe] Verify new variables have contexts

Part of https://github.com/dart-lang/sdk/issues/61572

Change-Id: I01295904c8fd5f76574e8979eb40e4cd174f9e09
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/504202
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Chloe Stefantsova <cstefantsova@google.com>
This commit is contained in:
Chloe Stefantsova
2026-05-19 04:29:29 -07:00
committed by dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent 52cfd29cbb
commit 10dd51ee4c
20 changed files with 242 additions and 42 deletions
+3 -1
View File
@@ -1900,11 +1900,13 @@ class LegacyVariable extends TreeNode
}
@override
VariableContext get context {
// TODO(62620): Conforming to [VariableDeclaration] interface. Remove this.
VariableContext? get context {
throw new UnsupportedError("${this.runtimeType}.context");
}
@override
// TODO(62620): Conforming to [VariableDeclaration] interface. Remove this.
void set context(VariableContext value) {
throw new UnsupportedError("${this.runtimeType}.context=");
}
+17 -3
View File
@@ -2446,17 +2446,31 @@ class TypeVariable extends VariableBase {
@override
String? cosmeticName;
@override
late VariableContext context;
/// Function type parameter this [TypeVariable] is associated with.
final TypeParameter parameter;
@override
List<Expression> annotations = const <Expression>[];
VariableContext? _context;
TypeVariable({this.cosmeticName, required this.parameter});
@override
VariableContext? get context {
assert(
_context != null,
"The context of a '${runtimeType}' variable with cosmetic name "
"'${cosmeticName}' is accessed, but hasn't been set yet.",
);
return _context;
}
@override
void set context(VariableContext value) {
_context = value;
}
@override
void addAnnotation(Expression annotation) {
if (annotations.isEmpty) {
+102 -14
View File
@@ -6,7 +6,9 @@ part of '../../ast.dart';
/// Generalized notion of a variable.
sealed class VariableBase extends TreeNode implements Annotatable {
abstract VariableContext context;
VariableContext? get context;
void set context(VariableContext value);
/// The cosmetic name of the variable from the source code, if exists.
String? get cosmeticName;
@@ -30,7 +32,6 @@ abstract interface class IVariable implements TreeNode {
abstract String? cosmeticName;
abstract VariableInitialization? variableInitialization;
abstract Expression? initializer;
abstract VariableContext context;
abstract bool isFinal;
abstract bool isConst;
abstract bool isLate;
@@ -54,6 +55,9 @@ abstract interface class IVariable implements TreeNode {
abstract VariableDeclaration variable;
void clearAnnotations();
VariableContext? get context;
void set context(VariableContext value);
bool get isAssignable;
bool get hasIsFinal;
bool get hasIsConst;
@@ -213,8 +217,7 @@ class LocalVariable extends VariableDeclaration {
@override
List<Expression> annotations = const <Expression>[];
@override
late VariableContext context;
VariableContext? _context;
LocalVariable({
this.cosmeticName,
@@ -231,6 +234,21 @@ class LocalVariable extends VariableDeclaration {
this.isWildcard = isWildcard;
}
@override
VariableContext? get context {
assert(
_context != null,
"The context of a '${runtimeType}' variable with cosmetic name "
"'${cosmeticName}' is accessed, but hasn't been set yet.",
);
return _context;
}
@override
void set context(VariableContext value) {
_context = value;
}
@override
void addAnnotation(Expression annotation) {
if (annotations.isEmpty) {
@@ -521,8 +539,7 @@ class CatchVariable extends VariableDeclaration {
@override
List<Expression> annotations = const <Expression>[];
@override
late VariableContext context;
VariableContext? _context;
CatchVariable({
required String name,
@@ -534,6 +551,21 @@ class CatchVariable extends VariableDeclaration {
this.isWildcard = isWildcard;
}
@override
VariableContext? get context {
assert(
_context != null,
"The context of a '${runtimeType}' variable with cosmetic name "
"'${cosmeticName}' is accessed, but hasn't been set yet.",
);
return _context;
}
@override
void set context(VariableContext value) {
_context = value;
}
@override
String? get cosmeticName => catchVariableName;
@@ -1022,8 +1054,7 @@ class PositionalParameter extends FunctionParameter {
@override
List<Expression> annotations = const <Expression>[];
@override
late VariableContext context;
VariableContext? _context;
PositionalParameter({
this.cosmeticName,
@@ -1040,6 +1071,21 @@ class PositionalParameter extends FunctionParameter {
super.isWildcard = false,
});
@override
VariableContext? get context {
assert(
_context != null,
"The context of a '${runtimeType}' variable with cosmetic name "
"'${cosmeticName}' is accessed, but hasn't been set yet.",
);
return _context;
}
@override
void set context(VariableContext value) {
_context = value;
}
@override
// TODO(62620): Conforming to [VariableDeclaration] interface. Remove this.
List<VariableContext>? get capturedContexts {
@@ -1169,8 +1215,7 @@ class NamedParameter extends FunctionParameter {
@override
List<Expression> annotations = const <Expression>[];
@override
late VariableContext context;
VariableContext? _context;
NamedParameter({
required this.parameterName,
@@ -1187,6 +1232,21 @@ class NamedParameter extends FunctionParameter {
super.isWildcard = false,
});
@override
VariableContext? get context {
assert(
_context != null,
"The context of a '${runtimeType}' variable with cosmetic name "
"'${cosmeticName}' is accessed, but hasn't been set yet.",
);
return _context;
}
@override
void set context(VariableContext value) {
_context = value;
}
@override
// TODO(62620): Conforming to [VariableDeclaration] interface. Remove this.
List<VariableContext>? get capturedContexts {
@@ -1319,11 +1379,25 @@ class ThisVariable extends VariableDeclaration {
@override
List<Expression> annotations = const <Expression>[];
@override
late VariableContext context;
VariableContext? _context;
ThisVariable({required this.type}) : super.empty();
@override
VariableContext? get context {
assert(
_context != null,
"The context of a '${runtimeType}' variable with cosmetic name "
"'${cosmeticName}' is accessed, but hasn't been set yet.",
);
return _context;
}
@override
void set context(VariableContext value) {
_context = value;
}
// TODO(cstefantsova): Consider a throwing implementation instead.
@override
void addAnnotation(Expression annotation) {
@@ -1597,11 +1671,25 @@ class SyntheticVariable extends VariableDeclaration {
@override
List<Expression> annotations = const <Expression>[];
@override
late VariableContext context;
VariableContext? _context;
SyntheticVariable({this.cosmeticName, required this.type}) : super.empty();
@override
VariableContext? get context {
assert(
_context != null,
"The context of a '${runtimeType}' variable with cosmetic name "
"'${cosmeticName}' is accessed, but hasn't been set yet.",
);
return _context;
}
@override
void set context(VariableContext value) {
_context = value;
}
// TODO(cstefantsova): Consider a throwing implementation instead.
@override
void addAnnotation(Expression annotation) {
+12 -12
View File
@@ -6326,15 +6326,15 @@ class EquivalenceStrategy {
if (!checkTypeVariable_cosmeticName(visitor, node, other)) {
result = visitor.resultOnInequivalence;
}
if (!checkTypeVariable_context(visitor, node, other)) {
result = visitor.resultOnInequivalence;
}
if (!checkTypeVariable_parameter(visitor, node, other)) {
result = visitor.resultOnInequivalence;
}
if (!checkTypeVariable_annotations(visitor, node, other)) {
result = visitor.resultOnInequivalence;
}
if (!checkTypeVariable_context(visitor, node, other)) {
result = visitor.resultOnInequivalence;
}
if (!checkTypeVariable_flags(visitor, node, other)) {
result = visitor.resultOnInequivalence;
}
@@ -14120,15 +14120,6 @@ class EquivalenceStrategy {
);
}
bool checkTypeVariable_context(
EquivalenceVisitor visitor,
TypeVariable node,
TypeVariable other,
) {
'context';
return checkVariableContext(visitor, node.context, other.context);
}
bool checkTypeVariable_parameter(
EquivalenceVisitor visitor,
TypeVariable node,
@@ -14154,6 +14145,15 @@ class EquivalenceStrategy {
);
}
bool checkTypeVariable_context(
EquivalenceVisitor visitor,
TypeVariable node,
TypeVariable other,
) {
'context';
return checkVariableContext(visitor, node.context, other.context);
}
bool checkTypeVariable_flags(
EquivalenceVisitor visitor,
TypeVariable node,
+47
View File
@@ -1179,6 +1179,53 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
exitTreeNode(node);
}
@override
void visitCatchVariable(CatchVariable node) {
_verifyVariable(node);
}
@override
void visitLocalVariable(LocalVariable node) {
_verifyVariable(node);
}
@override
void visitPositionalParameter(PositionalParameter node) {
_verifyVariable(node);
}
@override
void visitNamedParameter(NamedParameter node) {
_verifyVariable(node);
}
@override
void visitSyntheticVariable(SyntheticVariable node) {
_verifyVariable(node);
}
@override
void visitThisVariable(ThisVariable node) {
_verifyVariable(node);
}
void _verifyVariable(VariableDeclaration node) {
enterTreeNode(node);
TreeNode? oldParent = enterParent(node);
_visitAnnotations(node.annotations);
exitParent(oldParent);
declareVariable(node);
exitTreeNode(node);
if (!isOutline && node.context == null) {
problem(
node,
"A '${node.runtimeType}' variable with cosmetic name "
"'${node.cosmeticName}' doesn't have its context set.",
);
}
}
@override
void visitVariableGet(VariableGet node) {
// TODO(cstefantsova): Support new variable model.