Skip to content

Commit 080af85

Browse files
jensjohaCommit Queue
authored andcommitted
[CFE] Specific error message when using unavailable variables in expression evaluation
So instead of either being told that a variable doesn't exist, or using a field or something instead of a local, you'll now get a message saying "<variable> is unavailable in this expression evaluation". Bug: #53996 Bug: #53087 Bug: #45913 (sort of?) Bug: #53688 Change-Id: I6726209584e414ddc40f3e7ff6bffa9e7283e9d0 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/445701 Reviewed-by: Alexander Markov <alexmarkov@google.com> Reviewed-by: Johnni Winther <johnniwinther@google.com> Reviewed-by: Ben Konyi <bkonyi@google.com> Commit-Queue: Jens Johansen <jensj@google.com>
1 parent b39d019 commit 080af85

27 files changed

+639
-43
lines changed

pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3732,6 +3732,31 @@ const MessageCode codeExportedMain = const MessageCode(
37323732
problemMessage: r"""This is exported 'main' declaration.""",
37333733
);
37343734

3735+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
3736+
const Template<Message Function(String name)>
3737+
codeExpressionEvaluationKnownVariableUnavailable = const Template<
3738+
Message Function(String name)
3739+
>(
3740+
"ExpressionEvaluationKnownVariableUnavailable",
3741+
problemMessageTemplate:
3742+
r"""The variable '#name' is unavailable in this expression evaluation.""",
3743+
withArguments: _withArgumentsExpressionEvaluationKnownVariableUnavailable,
3744+
);
3745+
3746+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
3747+
Message _withArgumentsExpressionEvaluationKnownVariableUnavailable(
3748+
String name,
3749+
) {
3750+
if (name.isEmpty) throw 'No name provided';
3751+
name = demangleMixinApplicationName(name);
3752+
return new Message(
3753+
codeExpressionEvaluationKnownVariableUnavailable,
3754+
problemMessage:
3755+
"""The variable '${name}' is unavailable in this expression evaluation.""",
3756+
arguments: {'name': name},
3757+
);
3758+
}
3759+
37353760
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
37363761
const MessageCode codeExpressionNotMetadata = const MessageCode(
37373762
"ExpressionNotMetadata",

pkg/front_end/lib/src/base/incremental_compiler.dart

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import 'dart:typed_data';
99
import 'package:_fe_analyzer_shared/src/scanner/abstract_scanner.dart'
1010
show ScannerConfiguration;
1111
import 'package:front_end/src/base/name_space.dart';
12+
import 'package:front_end/src/type_inference/inference_results.dart';
1213
import 'package:kernel/binary/ast_from_binary.dart'
1314
show
1415
BinaryBuilderWithMetadata,
@@ -50,7 +51,9 @@ import 'package:kernel/kernel.dart'
5051
TypeParameter,
5152
VariableDeclaration,
5253
VisitorDefault,
53-
VisitorVoidMixin;
54+
VisitorVoidMixin,
55+
VariableGet,
56+
VariableSet;
5457
import 'package:kernel/kernel.dart' as kernel show Combinator;
5558
import 'package:kernel/reference_from_index.dart';
5659
import 'package:kernel/target/changed_structure_notifier.dart'
@@ -87,6 +90,9 @@ import '../source/source_compilation_unit.dart' show SourceCompilationUnitImpl;
8790
import '../source/source_library_builder.dart'
8891
show ImplicitLanguageVersion, SourceLibraryBuilder;
8992
import '../source/source_loader.dart';
93+
import '../type_inference/inference_helper.dart' show InferenceHelper;
94+
import '../type_inference/inference_visitor.dart'
95+
show ExpressionEvaluationHelper;
9096
import '../util/error_reporter_file_copier.dart' show saveAsGzip;
9197
import '../util/experiment_environment_getter.dart'
9298
show enableIncrementalCompilerBenchmarking, getExperimentEnvironment;
@@ -1693,6 +1699,21 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
16931699
hasDeclaredInitializer: true,
16941700
initializer: def.value.initializer)
16951701
..fileOffset = def.value.fileOffset);
1702+
} else if (def.value.isInitializingFormal ||
1703+
def.value.isSuperInitializingFormal) {
1704+
// An (super) initializing formal parameter of a constructor
1705+
// should not shadow the field it was used to initialize,
1706+
// so we'll ignore it.
1707+
} else {
1708+
// Non-const variable we should know about but wasn't told
1709+
// about. Maybe the variable was optimized out? Maybe it wasn't
1710+
// captured? Either way there's something shadowing any fields
1711+
// etc.
1712+
extraKnownVariables.add(new VariableDeclarationImpl(
1713+
def.key,
1714+
type: def.value.type,
1715+
isConst: false,
1716+
)..fileOffset = def.value.fileOffset);
16961717
}
16971718
} else if (existingType is DynamicType ||
16981719
_ExtensionTypeFinder.isOrContainsExtensionType(
@@ -1932,14 +1953,18 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
19321953
new Name(syntheticProcedureName), ProcedureKind.Method, parameters,
19331954
isStatic: isStatic, fileUri: debugLibrary.fileUri);
19341955

1956+
ExpressionEvaluationHelper expressionEvaluationHelper =
1957+
new ExpressionEvaluationHelperImpl(extraKnownVariables);
1958+
19351959
Expression compiledExpression = await lastGoodKernelTarget.loader
19361960
.buildExpression(
19371961
debugLibrary,
19381962
className ?? extensionName,
19391963
(className != null && !isStatic) || extensionThis != null,
19401964
procedure,
19411965
extensionThis,
1942-
extraKnownVariables);
1966+
extraKnownVariables,
1967+
expressionEvaluationHelper);
19431968

19441969
parameters.body = new ReturnStatement(compiledExpression)
19451970
..parent = parameters;
@@ -2160,6 +2185,55 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
21602185
}
21612186
}
21622187

2188+
// Coverage-ignore(suite): Not run.
2189+
class ExpressionEvaluationHelperImpl implements ExpressionEvaluationHelper {
2190+
final Set<VariableDeclarationImpl> knownButUnavailable = {};
2191+
2192+
ExpressionEvaluationHelperImpl(List<VariableDeclarationImpl> extraKnown) {
2193+
for (VariableDeclarationImpl variable in extraKnown) {
2194+
if (variable.isConst) {
2195+
// We allow const variables - these are inlined (we check
2196+
// `alwaysInlineConstants` in `compileExpression`).
2197+
continue;
2198+
}
2199+
knownButUnavailable.add(variable);
2200+
}
2201+
}
2202+
2203+
@override
2204+
ExpressionInferenceResult? visitVariableGet(
2205+
VariableGet node, DartType typeContext, InferenceHelper helper) {
2206+
if (knownButUnavailable.contains(node.variable)) {
2207+
return _returnKnownVariableUnavailable(node, node.variable, helper);
2208+
}
2209+
return null;
2210+
}
2211+
2212+
@override
2213+
ExpressionInferenceResult? visitVariableSet(
2214+
VariableSet node, DartType typeContext, InferenceHelper helper) {
2215+
if (knownButUnavailable.contains(node.variable)) {
2216+
return _returnKnownVariableUnavailable(node, node.variable, helper);
2217+
}
2218+
return null;
2219+
}
2220+
2221+
ExpressionInferenceResult _returnKnownVariableUnavailable(
2222+
Expression node, VariableDeclaration variable, InferenceHelper helper) {
2223+
return new ExpressionInferenceResult(
2224+
variable.type,
2225+
helper.wrapInProblem(
2226+
node,
2227+
codeExpressionEvaluationKnownVariableUnavailable
2228+
.withArguments(variable.name!),
2229+
node.fileOffset,
2230+
variable.name!.length,
2231+
errorHasBeenReported: false,
2232+
includeExpression: false,
2233+
));
2234+
}
2235+
}
2236+
21632237
// Coverage-ignore(suite): Not run.
21642238
class _ExtensionTypeFinder extends VisitorDefault<void> with VisitorVoidMixin {
21652239
static bool isOrContainsExtensionType(DartType type) {

pkg/front_end/lib/src/kernel/body_builder.dart

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ import '../source/type_parameter_factory.dart';
112112
import '../source/value_kinds.dart';
113113
import '../type_inference/inference_results.dart'
114114
show InitializerInferenceResult;
115+
import '../type_inference/inference_visitor.dart'
116+
show ExpressionEvaluationHelper;
115117
import '../type_inference/type_inferrer.dart'
116118
show TypeInferrer, InferredFunctionBody;
117119
import '../type_inference/type_schema.dart' show UnknownType;
@@ -1253,7 +1255,8 @@ class BodyBuilder extends StackListenerImpl
12531255
_context.memberNameOffset,
12541256
_context.returnTypeContext,
12551257
asyncModifier,
1256-
body);
1258+
body,
1259+
null);
12571260
body = inferredFunctionBody.body;
12581261
function.emittedValueType = inferredFunctionBody.emittedValueType;
12591262
assert(function.asyncMarker == AsyncMarker.Sync ||
@@ -1590,7 +1593,8 @@ class BodyBuilder extends StackListenerImpl
15901593
Parser parser,
15911594
Token token,
15921595
FunctionNode parameters,
1593-
List<VariableDeclarationImpl> extraKnownVariables) {
1596+
List<VariableDeclarationImpl> extraKnownVariables,
1597+
ExpressionEvaluationHelper expressionEvaluationHelper) {
15941598
int fileOffset = offsetForToken(token);
15951599
List<NominalParameterBuilder>? typeParameterBuilders;
15961600
for (TypeParameter typeParameter in parameters.typeParameters) {
@@ -1678,7 +1682,12 @@ class BodyBuilder extends StackListenerImpl
16781682
}
16791683

16801684
InferredFunctionBody inferredFunctionBody = typeInferrer.inferFunctionBody(
1681-
this, fileOffset, const DynamicType(), AsyncMarker.Sync, fakeReturn);
1685+
this,
1686+
fileOffset,
1687+
const DynamicType(),
1688+
AsyncMarker.Sync,
1689+
fakeReturn,
1690+
expressionEvaluationHelper);
16821691
assert(
16831692
fakeReturn == inferredFunctionBody.body,
16841693
"Previously implicit assumption about inferFunctionBody "
@@ -9085,13 +9094,17 @@ class BodyBuilder extends StackListenerImpl
90859094
@override
90869095
Expression wrapInProblem(
90879096
Expression expression, Message message, int fileOffset, int length,
9088-
{List<LocatedMessage>? context}) {
9097+
{List<LocatedMessage>? context,
9098+
bool? errorHasBeenReported,
9099+
bool includeExpression = true}) {
90899100
CfeSeverity severity = message.code.severity;
90909101
if (severity == CfeSeverity.error) {
90919102
return wrapInLocatedProblem(
90929103
expression, message.withLocation(uri, fileOffset, length),
90939104
context: context,
9094-
errorHasBeenReported: expression is InvalidExpression);
9105+
errorHasBeenReported:
9106+
errorHasBeenReported ?? expression is InvalidExpression,
9107+
includeExpression: includeExpression);
90959108
} else {
90969109
// Coverage-ignore-block(suite): Not run.
90979110
if (expression is! InvalidExpression) {
@@ -9103,7 +9116,9 @@ class BodyBuilder extends StackListenerImpl
91039116

91049117
@override
91059118
Expression wrapInLocatedProblem(Expression expression, LocatedMessage message,
9106-
{List<LocatedMessage>? context, bool errorHasBeenReported = false}) {
9119+
{List<LocatedMessage>? context,
9120+
bool errorHasBeenReported = false,
9121+
bool includeExpression = true}) {
91079122
// TODO(askesc): Produce explicit error expression wrapping the original.
91089123
// See [issue 29717](https://github.com/dart-lang/sdk/issues/29717)
91099124
int offset = expression.fileOffset;
@@ -9113,7 +9128,7 @@ class BodyBuilder extends StackListenerImpl
91139128
return buildProblem(
91149129
message.messageObject, message.charOffset, message.length,
91159130
context: context,
9116-
expression: expression,
9131+
expression: includeExpression ? expression : null,
91179132
errorHasBeenReported: errorHasBeenReported);
91189133
}
91199134

pkg/front_end/lib/src/kernel/constant_evaluator.dart

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -407,10 +407,7 @@ class ConstantsTransformer extends RemovingTransformer {
407407
makeConstantExpression(constant, initializer)..parent = node;
408408

409409
// If this constant is inlined, remove it.
410-
if (!keepLocals &&
411-
// Coverage-ignore(suite): Not run.
412-
shouldInline(initializer)) {
413-
// Coverage-ignore-block(suite): Not run.
410+
if (!keepLocals && shouldInline(initializer)) {
414411
if (constant is! UnevaluatedConstant) {
415412
// If the constant is unevaluated we need to keep the expression,
416413
// so that, in the case the constant contains error but the local
@@ -593,7 +590,6 @@ class ConstantsTransformer extends RemovingTransformer {
593590
}
594591
}
595592
if (storeIndex < node.statements.length) {
596-
// Coverage-ignore-block(suite): Not run.
597593
node.statements.length = storeIndex;
598594
}
599595
return node;

pkg/front_end/lib/src/kernel/expression_generator_helper.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ abstract class ExpressionGeneratorHelper implements InferenceHelper {
149149
Message message, int charOffset, int length);
150150

151151
Expression wrapInLocatedProblem(Expression expression, LocatedMessage message,
152-
{List<LocatedMessage>? context});
152+
{List<LocatedMessage>? context, bool includeExpression = true});
153153

154154
Expression evaluateArgumentsBefore(
155155
Arguments arguments, Expression expression);

pkg/front_end/lib/src/source/source_loader.dart

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ import '../kernel/kernel_helper.dart'
7070
show DelayedDefaultValueCloner, TypeDependency;
7171
import '../kernel/kernel_target.dart' show KernelTarget;
7272
import '../kernel/type_builder_computer.dart' show TypeBuilderComputer;
73+
import '../type_inference/inference_visitor.dart'
74+
show ExpressionEvaluationHelper;
7375
import '../type_inference/type_inference_engine.dart';
7476
import '../type_inference/type_inferrer.dart';
7577
import '../util/reference_map.dart';
@@ -1249,7 +1251,8 @@ severity: $severity
12491251
bool isClassInstanceMember,
12501252
Procedure procedure,
12511253
VariableDeclaration? extensionThis,
1252-
List<VariableDeclarationImpl> extraKnownVariables) async {
1254+
List<VariableDeclarationImpl> extraKnownVariables,
1255+
ExpressionEvaluationHelper expressionEvaluationHelper) async {
12531256
// TODO(johnniwinther): Support expression compilation in a specific
12541257
// compilation unit.
12551258
LookupScope memberScope =
@@ -1308,14 +1311,16 @@ severity: $severity
13081311
}
13091312

13101313
return listener.parseSingleExpression(
1311-
new Parser(listener,
1312-
useImplicitCreationExpression: useImplicitCreationExpressionInCfe,
1313-
allowPatterns: libraryBuilder.libraryFeatures.patterns.isEnabled,
1314-
enableFeatureEnhancedParts:
1315-
libraryBuilder.libraryFeatures.enhancedParts.isEnabled),
1316-
token,
1317-
procedure.function,
1318-
extraKnownVariables);
1314+
new Parser(listener,
1315+
useImplicitCreationExpression: useImplicitCreationExpressionInCfe,
1316+
allowPatterns: libraryBuilder.libraryFeatures.patterns.isEnabled,
1317+
enableFeatureEnhancedParts:
1318+
libraryBuilder.libraryFeatures.enhancedParts.isEnabled),
1319+
token,
1320+
procedure.function,
1321+
extraKnownVariables,
1322+
expressionEvaluationHelper,
1323+
);
13191324
}
13201325

13211326
DietListener createDietListener(SourceLibraryBuilder library,

pkg/front_end/lib/src/type_inference/inference_helper.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ abstract class InferenceHelper {
2424

2525
Expression wrapInProblem(
2626
Expression expression, Message message, int fileOffset, int length,
27-
{List<LocatedMessage>? context});
27+
{List<LocatedMessage>? context,
28+
bool? errorHasBeenReported,
29+
bool includeExpression = true});
2830

2931
String superConstructorNameForDiagnostics(String name);
3032

pkg/front_end/lib/src/type_inference/inference_visitor.dart

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,17 @@ class InferenceVisitorImpl extends InferenceVisitorBase
174174
// variable was declared outside the try statement or local function.
175175
bool _inTryOrLocalFunction = false;
176176

177-
InferenceVisitorImpl(TypeInferrerImpl inferrer, InferenceHelper helper,
178-
this._constructorBuilder, this.operations, this.typeAnalyzerOptions)
177+
/// Helper used to issue correct error messages and avoid access to
178+
/// unavailable variables upon expression evaluation.
179+
final ExpressionEvaluationHelper? expressionEvaluationHelper;
180+
181+
InferenceVisitorImpl(
182+
TypeInferrerImpl inferrer,
183+
InferenceHelper helper,
184+
this._constructorBuilder,
185+
this.operations,
186+
this.typeAnalyzerOptions,
187+
this.expressionEvaluationHelper)
179188
: super(inferrer, helper);
180189

181190
@override
@@ -9252,6 +9261,14 @@ class InferenceVisitorImpl extends InferenceVisitorBase
92529261
@override
92539262
ExpressionInferenceResult visitVariableSet(
92549263
VariableSet node, DartType typeContext) {
9264+
if (expressionEvaluationHelper != null) {
9265+
// Coverage-ignore-block(suite): Not run.
9266+
ExpressionInferenceResult? result = expressionEvaluationHelper
9267+
?.visitVariableSet(node, typeContext, helper);
9268+
if (result != null) {
9269+
return result;
9270+
}
9271+
}
92559272
VariableDeclarationImpl variable = node.variable as VariableDeclarationImpl;
92569273
bool isDefinitelyAssigned = flowAnalysis.isAssigned(variable);
92579274
bool isDefinitelyUnassigned = flowAnalysis.isUnassigned(variable);
@@ -9551,6 +9568,14 @@ class InferenceVisitorImpl extends InferenceVisitorBase
95519568
@override
95529569
ExpressionInferenceResult visitVariableGet(
95539570
VariableGet node, DartType typeContext) {
9571+
if (expressionEvaluationHelper != null) {
9572+
// Coverage-ignore-block(suite): Not run.
9573+
ExpressionInferenceResult? result = expressionEvaluationHelper
9574+
?.visitVariableGet(node, typeContext, helper);
9575+
if (result != null) {
9576+
return result;
9577+
}
9578+
}
95549579
if (node is! VariableGetImpl) {
95559580
// Coverage-ignore-block(suite): Not run.
95569581
// This node is created as part of a lowering and doesn't need inference.
@@ -12375,3 +12400,11 @@ class MapEntryInferenceContext extends CollectionElementInferenceContext {
1237512400
inferredSpreadTypes: inferredSpreadTypes,
1237612401
inferredConditionTypes: inferredConditionTypes);
1237712402
}
12403+
12404+
abstract class ExpressionEvaluationHelper {
12405+
ExpressionInferenceResult? visitVariableGet(
12406+
VariableGet node, DartType typeContext, InferenceHelper helper);
12407+
12408+
ExpressionInferenceResult? visitVariableSet(
12409+
VariableSet node, DartType typeContext, InferenceHelper helper);
12410+
}

0 commit comments

Comments
 (0)