diff --git a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart index bb3b0ab8794..1c77f0214b5 100644 --- a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart +++ b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart @@ -3732,6 +3732,31 @@ const MessageCode codeExportedMain = const MessageCode( problemMessage: r"""This is exported 'main' declaration.""", ); +// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. +const Template +codeExpressionEvaluationKnownVariableUnavailable = const Template< + Message Function(String name) +>( + "ExpressionEvaluationKnownVariableUnavailable", + problemMessageTemplate: + r"""The variable '#name' is unavailable in this expression evaluation.""", + withArguments: _withArgumentsExpressionEvaluationKnownVariableUnavailable, +); + +// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. +Message _withArgumentsExpressionEvaluationKnownVariableUnavailable( + String name, +) { + if (name.isEmpty) throw 'No name provided'; + name = demangleMixinApplicationName(name); + return new Message( + codeExpressionEvaluationKnownVariableUnavailable, + problemMessage: + """The variable '${name}' is unavailable in this expression evaluation.""", + arguments: {'name': name}, + ); +} + // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. const MessageCode codeExpressionNotMetadata = const MessageCode( "ExpressionNotMetadata", diff --git a/pkg/front_end/lib/src/base/incremental_compiler.dart b/pkg/front_end/lib/src/base/incremental_compiler.dart index 77ecce4c8c7..9e08bd4ee8a 100644 --- a/pkg/front_end/lib/src/base/incremental_compiler.dart +++ b/pkg/front_end/lib/src/base/incremental_compiler.dart @@ -9,6 +9,7 @@ import 'dart:typed_data'; import 'package:_fe_analyzer_shared/src/scanner/abstract_scanner.dart' show ScannerConfiguration; import 'package:front_end/src/base/name_space.dart'; +import 'package:front_end/src/type_inference/inference_results.dart'; import 'package:kernel/binary/ast_from_binary.dart' show BinaryBuilderWithMetadata, @@ -50,7 +51,9 @@ import 'package:kernel/kernel.dart' TypeParameter, VariableDeclaration, VisitorDefault, - VisitorVoidMixin; + VisitorVoidMixin, + VariableGet, + VariableSet; import 'package:kernel/kernel.dart' as kernel show Combinator; import 'package:kernel/reference_from_index.dart'; import 'package:kernel/target/changed_structure_notifier.dart' @@ -87,6 +90,9 @@ import '../source/source_compilation_unit.dart' show SourceCompilationUnitImpl; import '../source/source_library_builder.dart' show ImplicitLanguageVersion, SourceLibraryBuilder; import '../source/source_loader.dart'; +import '../type_inference/inference_helper.dart' show InferenceHelper; +import '../type_inference/inference_visitor.dart' + show ExpressionEvaluationHelper; import '../util/error_reporter_file_copier.dart' show saveAsGzip; import '../util/experiment_environment_getter.dart' show enableIncrementalCompilerBenchmarking, getExperimentEnvironment; @@ -1693,6 +1699,21 @@ class IncrementalCompiler implements IncrementalKernelGenerator { hasDeclaredInitializer: true, initializer: def.value.initializer) ..fileOffset = def.value.fileOffset); + } else if (def.value.isInitializingFormal || + def.value.isSuperInitializingFormal) { + // An (super) initializing formal parameter of a constructor + // should not shadow the field it was used to initialize, + // so we'll ignore it. + } else { + // Non-const variable we should know about but wasn't told + // about. Maybe the variable was optimized out? Maybe it wasn't + // captured? Either way there's something shadowing any fields + // etc. + extraKnownVariables.add(new VariableDeclarationImpl( + def.key, + type: def.value.type, + isConst: false, + )..fileOffset = def.value.fileOffset); } } else if (existingType is DynamicType || _ExtensionTypeFinder.isOrContainsExtensionType( @@ -1932,6 +1953,9 @@ class IncrementalCompiler implements IncrementalKernelGenerator { new Name(syntheticProcedureName), ProcedureKind.Method, parameters, isStatic: isStatic, fileUri: debugLibrary.fileUri); + ExpressionEvaluationHelper expressionEvaluationHelper = + new ExpressionEvaluationHelperImpl(extraKnownVariables); + Expression compiledExpression = await lastGoodKernelTarget.loader .buildExpression( debugLibrary, @@ -1939,7 +1963,8 @@ class IncrementalCompiler implements IncrementalKernelGenerator { (className != null && !isStatic) || extensionThis != null, procedure, extensionThis, - extraKnownVariables); + extraKnownVariables, + expressionEvaluationHelper); parameters.body = new ReturnStatement(compiledExpression) ..parent = parameters; @@ -2160,6 +2185,55 @@ class IncrementalCompiler implements IncrementalKernelGenerator { } } +// Coverage-ignore(suite): Not run. +class ExpressionEvaluationHelperImpl implements ExpressionEvaluationHelper { + final Set knownButUnavailable = {}; + + ExpressionEvaluationHelperImpl(List extraKnown) { + for (VariableDeclarationImpl variable in extraKnown) { + if (variable.isConst) { + // We allow const variables - these are inlined (we check + // `alwaysInlineConstants` in `compileExpression`). + continue; + } + knownButUnavailable.add(variable); + } + } + + @override + ExpressionInferenceResult? visitVariableGet( + VariableGet node, DartType typeContext, InferenceHelper helper) { + if (knownButUnavailable.contains(node.variable)) { + return _returnKnownVariableUnavailable(node, node.variable, helper); + } + return null; + } + + @override + ExpressionInferenceResult? visitVariableSet( + VariableSet node, DartType typeContext, InferenceHelper helper) { + if (knownButUnavailable.contains(node.variable)) { + return _returnKnownVariableUnavailable(node, node.variable, helper); + } + return null; + } + + ExpressionInferenceResult _returnKnownVariableUnavailable( + Expression node, VariableDeclaration variable, InferenceHelper helper) { + return new ExpressionInferenceResult( + variable.type, + helper.wrapInProblem( + node, + codeExpressionEvaluationKnownVariableUnavailable + .withArguments(variable.name!), + node.fileOffset, + variable.name!.length, + errorHasBeenReported: false, + includeExpression: false, + )); + } +} + // Coverage-ignore(suite): Not run. class _ExtensionTypeFinder extends VisitorDefault with VisitorVoidMixin { static bool isOrContainsExtensionType(DartType type) { diff --git a/pkg/front_end/lib/src/kernel/body_builder.dart b/pkg/front_end/lib/src/kernel/body_builder.dart index 44aa7b2bed2..342484791ab 100644 --- a/pkg/front_end/lib/src/kernel/body_builder.dart +++ b/pkg/front_end/lib/src/kernel/body_builder.dart @@ -112,6 +112,8 @@ import '../source/type_parameter_factory.dart'; import '../source/value_kinds.dart'; import '../type_inference/inference_results.dart' show InitializerInferenceResult; +import '../type_inference/inference_visitor.dart' + show ExpressionEvaluationHelper; import '../type_inference/type_inferrer.dart' show TypeInferrer, InferredFunctionBody; import '../type_inference/type_schema.dart' show UnknownType; @@ -1253,7 +1255,8 @@ class BodyBuilder extends StackListenerImpl _context.memberNameOffset, _context.returnTypeContext, asyncModifier, - body); + body, + null); body = inferredFunctionBody.body; function.emittedValueType = inferredFunctionBody.emittedValueType; assert(function.asyncMarker == AsyncMarker.Sync || @@ -1590,7 +1593,8 @@ class BodyBuilder extends StackListenerImpl Parser parser, Token token, FunctionNode parameters, - List extraKnownVariables) { + List extraKnownVariables, + ExpressionEvaluationHelper expressionEvaluationHelper) { int fileOffset = offsetForToken(token); List? typeParameterBuilders; for (TypeParameter typeParameter in parameters.typeParameters) { @@ -1678,7 +1682,12 @@ class BodyBuilder extends StackListenerImpl } InferredFunctionBody inferredFunctionBody = typeInferrer.inferFunctionBody( - this, fileOffset, const DynamicType(), AsyncMarker.Sync, fakeReturn); + this, + fileOffset, + const DynamicType(), + AsyncMarker.Sync, + fakeReturn, + expressionEvaluationHelper); assert( fakeReturn == inferredFunctionBody.body, "Previously implicit assumption about inferFunctionBody " @@ -9085,13 +9094,17 @@ class BodyBuilder extends StackListenerImpl @override Expression wrapInProblem( Expression expression, Message message, int fileOffset, int length, - {List? context}) { + {List? context, + bool? errorHasBeenReported, + bool includeExpression = true}) { CfeSeverity severity = message.code.severity; if (severity == CfeSeverity.error) { return wrapInLocatedProblem( expression, message.withLocation(uri, fileOffset, length), context: context, - errorHasBeenReported: expression is InvalidExpression); + errorHasBeenReported: + errorHasBeenReported ?? expression is InvalidExpression, + includeExpression: includeExpression); } else { // Coverage-ignore-block(suite): Not run. if (expression is! InvalidExpression) { @@ -9103,7 +9116,9 @@ class BodyBuilder extends StackListenerImpl @override Expression wrapInLocatedProblem(Expression expression, LocatedMessage message, - {List? context, bool errorHasBeenReported = false}) { + {List? context, + bool errorHasBeenReported = false, + bool includeExpression = true}) { // TODO(askesc): Produce explicit error expression wrapping the original. // See [issue 29717](https://github.com/dart-lang/sdk/issues/29717) int offset = expression.fileOffset; @@ -9113,7 +9128,7 @@ class BodyBuilder extends StackListenerImpl return buildProblem( message.messageObject, message.charOffset, message.length, context: context, - expression: expression, + expression: includeExpression ? expression : null, errorHasBeenReported: errorHasBeenReported); } diff --git a/pkg/front_end/lib/src/kernel/constant_evaluator.dart b/pkg/front_end/lib/src/kernel/constant_evaluator.dart index 5f44a8c68c8..69090eaaf0d 100644 --- a/pkg/front_end/lib/src/kernel/constant_evaluator.dart +++ b/pkg/front_end/lib/src/kernel/constant_evaluator.dart @@ -407,10 +407,7 @@ class ConstantsTransformer extends RemovingTransformer { makeConstantExpression(constant, initializer)..parent = node; // If this constant is inlined, remove it. - if (!keepLocals && - // Coverage-ignore(suite): Not run. - shouldInline(initializer)) { - // Coverage-ignore-block(suite): Not run. + if (!keepLocals && shouldInline(initializer)) { if (constant is! UnevaluatedConstant) { // If the constant is unevaluated we need to keep the expression, // so that, in the case the constant contains error but the local @@ -593,7 +590,6 @@ class ConstantsTransformer extends RemovingTransformer { } } if (storeIndex < node.statements.length) { - // Coverage-ignore-block(suite): Not run. node.statements.length = storeIndex; } return node; diff --git a/pkg/front_end/lib/src/kernel/expression_generator_helper.dart b/pkg/front_end/lib/src/kernel/expression_generator_helper.dart index d3d928ad8af..4c920a379da 100644 --- a/pkg/front_end/lib/src/kernel/expression_generator_helper.dart +++ b/pkg/front_end/lib/src/kernel/expression_generator_helper.dart @@ -149,7 +149,7 @@ abstract class ExpressionGeneratorHelper implements InferenceHelper { Message message, int charOffset, int length); Expression wrapInLocatedProblem(Expression expression, LocatedMessage message, - {List? context}); + {List? context, bool includeExpression = true}); Expression evaluateArgumentsBefore( Arguments arguments, Expression expression); diff --git a/pkg/front_end/lib/src/source/source_loader.dart b/pkg/front_end/lib/src/source/source_loader.dart index b050d88ef87..056b533eb40 100644 --- a/pkg/front_end/lib/src/source/source_loader.dart +++ b/pkg/front_end/lib/src/source/source_loader.dart @@ -70,6 +70,8 @@ import '../kernel/kernel_helper.dart' show DelayedDefaultValueCloner, TypeDependency; import '../kernel/kernel_target.dart' show KernelTarget; import '../kernel/type_builder_computer.dart' show TypeBuilderComputer; +import '../type_inference/inference_visitor.dart' + show ExpressionEvaluationHelper; import '../type_inference/type_inference_engine.dart'; import '../type_inference/type_inferrer.dart'; import '../util/reference_map.dart'; @@ -1249,7 +1251,8 @@ severity: $severity bool isClassInstanceMember, Procedure procedure, VariableDeclaration? extensionThis, - List extraKnownVariables) async { + List extraKnownVariables, + ExpressionEvaluationHelper expressionEvaluationHelper) async { // TODO(johnniwinther): Support expression compilation in a specific // compilation unit. LookupScope memberScope = @@ -1308,14 +1311,16 @@ severity: $severity } return listener.parseSingleExpression( - new Parser(listener, - useImplicitCreationExpression: useImplicitCreationExpressionInCfe, - allowPatterns: libraryBuilder.libraryFeatures.patterns.isEnabled, - enableFeatureEnhancedParts: - libraryBuilder.libraryFeatures.enhancedParts.isEnabled), - token, - procedure.function, - extraKnownVariables); + new Parser(listener, + useImplicitCreationExpression: useImplicitCreationExpressionInCfe, + allowPatterns: libraryBuilder.libraryFeatures.patterns.isEnabled, + enableFeatureEnhancedParts: + libraryBuilder.libraryFeatures.enhancedParts.isEnabled), + token, + procedure.function, + extraKnownVariables, + expressionEvaluationHelper, + ); } DietListener createDietListener(SourceLibraryBuilder library, diff --git a/pkg/front_end/lib/src/type_inference/inference_helper.dart b/pkg/front_end/lib/src/type_inference/inference_helper.dart index 68bf3314d45..5745e456af2 100644 --- a/pkg/front_end/lib/src/type_inference/inference_helper.dart +++ b/pkg/front_end/lib/src/type_inference/inference_helper.dart @@ -24,7 +24,9 @@ abstract class InferenceHelper { Expression wrapInProblem( Expression expression, Message message, int fileOffset, int length, - {List? context}); + {List? context, + bool? errorHasBeenReported, + bool includeExpression = true}); String superConstructorNameForDiagnostics(String name); diff --git a/pkg/front_end/lib/src/type_inference/inference_visitor.dart b/pkg/front_end/lib/src/type_inference/inference_visitor.dart index 643c22b0d40..334a2fffd4b 100644 --- a/pkg/front_end/lib/src/type_inference/inference_visitor.dart +++ b/pkg/front_end/lib/src/type_inference/inference_visitor.dart @@ -174,8 +174,17 @@ class InferenceVisitorImpl extends InferenceVisitorBase // variable was declared outside the try statement or local function. bool _inTryOrLocalFunction = false; - InferenceVisitorImpl(TypeInferrerImpl inferrer, InferenceHelper helper, - this._constructorBuilder, this.operations, this.typeAnalyzerOptions) + /// Helper used to issue correct error messages and avoid access to + /// unavailable variables upon expression evaluation. + final ExpressionEvaluationHelper? expressionEvaluationHelper; + + InferenceVisitorImpl( + TypeInferrerImpl inferrer, + InferenceHelper helper, + this._constructorBuilder, + this.operations, + this.typeAnalyzerOptions, + this.expressionEvaluationHelper) : super(inferrer, helper); @override @@ -9252,6 +9261,14 @@ class InferenceVisitorImpl extends InferenceVisitorBase @override ExpressionInferenceResult visitVariableSet( VariableSet node, DartType typeContext) { + if (expressionEvaluationHelper != null) { + // Coverage-ignore-block(suite): Not run. + ExpressionInferenceResult? result = expressionEvaluationHelper + ?.visitVariableSet(node, typeContext, helper); + if (result != null) { + return result; + } + } VariableDeclarationImpl variable = node.variable as VariableDeclarationImpl; bool isDefinitelyAssigned = flowAnalysis.isAssigned(variable); bool isDefinitelyUnassigned = flowAnalysis.isUnassigned(variable); @@ -9551,6 +9568,14 @@ class InferenceVisitorImpl extends InferenceVisitorBase @override ExpressionInferenceResult visitVariableGet( VariableGet node, DartType typeContext) { + if (expressionEvaluationHelper != null) { + // Coverage-ignore-block(suite): Not run. + ExpressionInferenceResult? result = expressionEvaluationHelper + ?.visitVariableGet(node, typeContext, helper); + if (result != null) { + return result; + } + } if (node is! VariableGetImpl) { // Coverage-ignore-block(suite): Not run. // This node is created as part of a lowering and doesn't need inference. @@ -12375,3 +12400,11 @@ class MapEntryInferenceContext extends CollectionElementInferenceContext { inferredSpreadTypes: inferredSpreadTypes, inferredConditionTypes: inferredConditionTypes); } + +abstract class ExpressionEvaluationHelper { + ExpressionInferenceResult? visitVariableGet( + VariableGet node, DartType typeContext, InferenceHelper helper); + + ExpressionInferenceResult? visitVariableSet( + VariableSet node, DartType typeContext, InferenceHelper helper); +} diff --git a/pkg/front_end/lib/src/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/type_inference/type_inferrer.dart index 10416c253ff..a987e1087bf 100644 --- a/pkg/front_end/lib/src/type_inference/type_inferrer.dart +++ b/pkg/front_end/lib/src/type_inference/type_inferrer.dart @@ -51,8 +51,13 @@ abstract class TypeInferrer { InferenceHelper helper, DartType declaredType, Expression initializer); /// Performs type inference on the given function body. - InferredFunctionBody inferFunctionBody(InferenceHelper helper, int fileOffset, - DartType returnType, AsyncMarker asyncMarker, Statement body); + InferredFunctionBody inferFunctionBody( + InferenceHelper helper, + int fileOffset, + DartType returnType, + AsyncMarker asyncMarker, + Statement body, + ExpressionEvaluationHelper? expressionEvaluationHelper); /// Performs type inference on the given constructor initializer. InitializerInferenceResult inferInitializer(InferenceHelper helper, @@ -155,11 +160,12 @@ class TypeInferrerImpl implements TypeInferrer { libraryBuilder.libraryFeatures.soundFlowAnalysis.isEnabled); InferenceVisitorBase _createInferenceVisitor(InferenceHelper helper, - [SourceConstructorBuilder? constructorBuilder]) { + {SourceConstructorBuilder? constructorBuilder, + ExpressionEvaluationHelper? expressionEvaluationHelper}) { // For full (non-top level) inference, we need access to the // InferenceHelper so that we can perform error reporting. - return new InferenceVisitorImpl( - this, helper, constructorBuilder, operations, typeAnalyzerOptions); + return new InferenceVisitorImpl(this, helper, constructorBuilder, + operations, typeAnalyzerOptions, expressionEvaluationHelper); } @override @@ -188,9 +194,16 @@ class TypeInferrerImpl implements TypeInferrer { } @override - InferredFunctionBody inferFunctionBody(InferenceHelper helper, int fileOffset, - DartType returnType, AsyncMarker asyncMarker, Statement body) { - InferenceVisitorBase visitor = _createInferenceVisitor(helper); + InferredFunctionBody inferFunctionBody( + InferenceHelper helper, + int fileOffset, + DartType returnType, + AsyncMarker asyncMarker, + Statement body, + ExpressionEvaluationHelper? expressionEvaluationHelper, + ) { + InferenceVisitorBase visitor = _createInferenceVisitor(helper, + expressionEvaluationHelper: expressionEvaluationHelper); ClosureContext closureContext = new ClosureContext(visitor, asyncMarker, returnType, false); StatementInferenceResult result = @@ -268,7 +281,7 @@ class TypeInferrerImpl implements TypeInferrer { // so that the type hierarchy will be simpler (which may speed up "is" // checks). InferenceVisitorBase visitor = - _createInferenceVisitor(helper, constructorBuilder); + _createInferenceVisitor(helper, constructorBuilder: constructorBuilder); InitializerInferenceResult result = visitor.inferInitializer(initializer); visitor.checkCleanState(); return result; @@ -361,11 +374,17 @@ class TypeInferrerImplBenchmarked implements TypeInferrer { } @override - InferredFunctionBody inferFunctionBody(InferenceHelper helper, int fileOffset, - DartType returnType, AsyncMarker asyncMarker, Statement body) { + InferredFunctionBody inferFunctionBody( + InferenceHelper helper, + int fileOffset, + DartType returnType, + AsyncMarker asyncMarker, + Statement body, + ExpressionEvaluationHelper? expressionEvaluationHelper, + ) { benchmarker.beginSubdivide(BenchmarkSubdivides.inferFunctionBody); - InferredFunctionBody result = impl.inferFunctionBody( - helper, fileOffset, returnType, asyncMarker, body); + InferredFunctionBody result = impl.inferFunctionBody(helper, fileOffset, + returnType, asyncMarker, body, expressionEvaluationHelper); benchmarker.endSubdivide(); return result; } diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status index 0a06a366e51..ba4f8f68e74 100644 --- a/pkg/front_end/messages.status +++ b/pkg/front_end/messages.status @@ -41,6 +41,7 @@ ExperimentExpiredDisabled/example: missingExample # Uncovered. ExperimentExpiredEnabled/example: missingExample # Uncovered. ExperimentNotEnabled/example: missingExample # issued via the parser, but overridden by StackListenerImpl --- so likely not possible via CFE outside of special tests? ExplicitExtensionAsLvalue/example: missingExample # Uncovered. +ExpressionEvaluationKnownVariableUnavailable/example: missingExample # Expression compilation. ExtensionAugmentationHasOnClause/part_wrapped_script: hasOnlyUnrelatedMessages # Doesn't seem to be any (direct?) way to get this for now ExtensionAugmentationHasOnClause/script: hasOnlyUnrelatedMessages # Doesn't seem to be any (direct?) way to get this for now ExtensionTypeShouldBeListedAsCallableInDynamicInterface/example: missingExample # Can't do dynamic modules stuff in messages suite. @@ -249,6 +250,7 @@ ExplicitExtensionArgumentMismatch/analyzerCode: missingAnalyzerCode ExplicitExtensionAsExpression/analyzerCode: missingAnalyzerCode ExplicitExtensionAsLvalue/analyzerCode: missingAnalyzerCode ExplicitExtensionTypeArgumentMismatch/analyzerCode: missingAnalyzerCode +ExpressionEvaluationKnownVariableUnavailable/analyzerCode: missingAnalyzerCode ExpressionNotMetadata/analyzerCode: missingAnalyzerCode ExtendsNever/analyzerCode: missingAnalyzerCode # Feature not yet in analyzer. ExtensionMemberConflictsWithObjectMember/analyzerCode: missingAnalyzerCode @@ -515,4 +517,4 @@ WeakReferenceNotStatic/analyzerCode: missingAnalyzerCode WeakReferenceReturnTypeNotNullable/analyzerCode: missingAnalyzerCode WeakReferenceTargetHasParameters/analyzerCode: missingAnalyzerCode WeakReferenceTargetNotStaticTearoff/analyzerCode: missingAnalyzerCode -WebLiteralCannotBeRepresentedExactly/analyzerCode: missingAnalyzerCode \ No newline at end of file +WebLiteralCannotBeRepresentedExactly/analyzerCode: missingAnalyzerCode diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml index 48866ab5e05..3eca544c52d 100644 --- a/pkg/front_end/messages.yaml +++ b/pkg/front_end/messages.yaml @@ -8346,3 +8346,6 @@ DotShorthandsUndefinedInvocation: void main() { C c = .foo(); } + +ExpressionEvaluationKnownVariableUnavailable: + problemMessage: "The variable '#name' is unavailable in this expression evaluation." diff --git a/pkg/front_end/testcases/expression/issue_53996_01.expression.yaml b/pkg/front_end/testcases/expression/issue_53996_01.expression.yaml new file mode 100644 index 00000000000..dbc5a3917e4 --- /dev/null +++ b/pkg/front_end/testcases/expression/issue_53996_01.expression.yaml @@ -0,0 +1,59 @@ +# Copyright (c) 2025, 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. + +# https://github.com/dart-lang/sdk/issues/53996 + +# Definition, offset, method etc extracted by starting the VM with +# `-DDFE_VERBOSE=true`, e.g. +# ``` +# out/ReleaseX64/dart -DDFE_VERBOSE=true --enable-vm-service \ +# --disable-service-auth-codes --pause_isolates_on_start inputFile.dart +# ``` +# and then issuing the expression compilation. + +sources: | + import "dart:developer"; + + bool debug = false; + + main() { + for(int i = 0; i < 50000; i++) { + foo(); + } + debug = true; + foo(); + } + + foo() { + List data = [1, 2, 3]; + for(int i in data) { + if (bar(i)) { + break; + } + } + } + + bool bar(int i) { + if (i == 2) { + if (debug) { + debugger(); + print("set a breakpoint here!"); + print("woke up"); + } + return true; + } + return false; + } + +definitions: [] +definition_types: [] +type_definitions: [] +type_bounds: [] +type_defaults: [] +method: "foo" +static: true +offset: 219 +scriptUri: main.dart +expression: | + data.length diff --git a/pkg/front_end/testcases/expression/issue_53996_01.expression.yaml.expect b/pkg/front_end/testcases/expression/issue_53996_01.expression.yaml.expect new file mode 100644 index 00000000000..5262cbb1b61 --- /dev/null +++ b/pkg/front_end/testcases/expression/issue_53996_01.expression.yaml.expect @@ -0,0 +1,7 @@ +Errors: { + org-dartlang-debug:synthetic_debug_expression:1:1: Error: The variable 'data' is unavailable in this expression evaluation. + data.length + ^^^^ +} +static method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr() → dynamic + return invalid-expression "org-dartlang-debug:synthetic_debug_expression:1:1: Error: The variable 'data' is unavailable in this expression evaluation.\ndata.length\n^^^^".{dart.core::List::length}{dart.core::int}; diff --git a/pkg/front_end/testcases/expression/issue_53996_02.expression.yaml b/pkg/front_end/testcases/expression/issue_53996_02.expression.yaml new file mode 100644 index 00000000000..b63f68d65f8 --- /dev/null +++ b/pkg/front_end/testcases/expression/issue_53996_02.expression.yaml @@ -0,0 +1,62 @@ +# Copyright (c) 2025, 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. + +# https://github.com/dart-lang/sdk/issues/53996 + +# Definition, offset, method etc extracted by starting the VM with +# `-DDFE_VERBOSE=true`, e.g. +# ``` +# out/ReleaseX64/dart -DDFE_VERBOSE=true --enable-vm-service \ +# --disable-service-auth-codes --pause_isolates_on_start inputFile.dart +# ``` +# and then issuing the expression compilation. + +sources: | + import "dart:developer"; + + bool debug = false; + + String data = "hello from top level field"; + + main() { + for(int i = 0; i < 50000; i++) { + foo(); + } + debug = true; + foo(); + print(data); + } + + foo() { + List data = [1, 2, 3]; + for(int i in data) { + if (bar(i)) { + break; + } + } + } + + bool bar(int i) { + if (i == 2) { + if (debug) { + debugger(); + print("set a breakpoint here!"); + print("woke up"); + } + return true; + } + return false; + } + +definitions: [] +definition_types: [] +type_definitions: [] +type_bounds: [] +type_defaults: [] +method: "foo" +static: true +offset: 279 +scriptUri: main.dart +expression: | + data.length diff --git a/pkg/front_end/testcases/expression/issue_53996_02.expression.yaml.expect b/pkg/front_end/testcases/expression/issue_53996_02.expression.yaml.expect new file mode 100644 index 00000000000..5262cbb1b61 --- /dev/null +++ b/pkg/front_end/testcases/expression/issue_53996_02.expression.yaml.expect @@ -0,0 +1,7 @@ +Errors: { + org-dartlang-debug:synthetic_debug_expression:1:1: Error: The variable 'data' is unavailable in this expression evaluation. + data.length + ^^^^ +} +static method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr() → dynamic + return invalid-expression "org-dartlang-debug:synthetic_debug_expression:1:1: Error: The variable 'data' is unavailable in this expression evaluation.\ndata.length\n^^^^".{dart.core::List::length}{dart.core::int}; diff --git a/pkg/front_end/testcases/expression/issue_53996_03.expression.yaml b/pkg/front_end/testcases/expression/issue_53996_03.expression.yaml new file mode 100644 index 00000000000..5fa22f556cf --- /dev/null +++ b/pkg/front_end/testcases/expression/issue_53996_03.expression.yaml @@ -0,0 +1,59 @@ +# Copyright (c) 2025, 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. + +# https://github.com/dart-lang/sdk/issues/53996 + +# Definition, offset, method etc extracted by starting the VM with +# `-DDFE_VERBOSE=true`, e.g. +# ``` +# out/ReleaseX64/dart -DDFE_VERBOSE=true --enable-vm-service \ +# --disable-service-auth-codes --pause_isolates_on_start inputFile.dart +# ``` +# and then issuing the expression compilation. + +sources: | + import "dart:developer"; + + bool debug = false; + + main() { + for(int i = 0; i < 50000; i++) { + foo(); + } + debug = true; + foo(); + } + + foo() { + List data = [1, 2, 3]; + for(int i in data) { + if (bar(i)) { + break; + } + } + } + + bool bar(int i) { + if (i == 2) { + if (debug) { + debugger(); + print("set a breakpoint here!"); + print("woke up"); + } + return true; + } + return false; + } + +definitions: [] +definition_types: [] +type_definitions: [] +type_bounds: [] +type_defaults: [] +method: "foo" +static: true +offset: 219 +scriptUri: main.dart +expression: | + data = 42 diff --git a/pkg/front_end/testcases/expression/issue_53996_03.expression.yaml.expect b/pkg/front_end/testcases/expression/issue_53996_03.expression.yaml.expect new file mode 100644 index 00000000000..0960c174309 --- /dev/null +++ b/pkg/front_end/testcases/expression/issue_53996_03.expression.yaml.expect @@ -0,0 +1,7 @@ +Errors: { + org-dartlang-debug:synthetic_debug_expression:1:1: Error: The variable 'data' is unavailable in this expression evaluation. + data = 42 + ^^^^ +} +static method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr() → dynamic + return invalid-expression "org-dartlang-debug:synthetic_debug_expression:1:1: Error: The variable 'data' is unavailable in this expression evaluation.\ndata = 42\n^^^^"; diff --git a/pkg/front_end/testcases/expression/issue_53996_04.expression.yaml b/pkg/front_end/testcases/expression/issue_53996_04.expression.yaml new file mode 100644 index 00000000000..0dab58fde93 --- /dev/null +++ b/pkg/front_end/testcases/expression/issue_53996_04.expression.yaml @@ -0,0 +1,62 @@ +# Copyright (c) 2025, 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. + +# https://github.com/dart-lang/sdk/issues/53996 + +# Definition, offset, method etc extracted by starting the VM with +# `-DDFE_VERBOSE=true`, e.g. +# ``` +# out/ReleaseX64/dart -DDFE_VERBOSE=true --enable-vm-service \ +# --disable-service-auth-codes --pause_isolates_on_start inputFile.dart +# ``` +# and then issuing the expression compilation. + +sources: | + import "dart:developer"; + + bool debug = false; + + String data = "hello from top level field"; + + main() { + for(int i = 0; i < 50000; i++) { + foo(); + } + debug = true; + foo(); + print(data); + } + + foo() { + List data = [1, 2, 3]; + for(int i in data) { + if (bar(i)) { + break; + } + } + } + + bool bar(int i) { + if (i == 2) { + if (debug) { + debugger(); + print("set a breakpoint here!"); + print("woke up"); + } + return true; + } + return false; + } + +definitions: [] +definition_types: [] +type_definitions: [] +type_bounds: [] +type_defaults: [] +method: "foo" +static: true +offset: 279 +scriptUri: main.dart +expression: | + data = 42 diff --git a/pkg/front_end/testcases/expression/issue_53996_04.expression.yaml.expect b/pkg/front_end/testcases/expression/issue_53996_04.expression.yaml.expect new file mode 100644 index 00000000000..0960c174309 --- /dev/null +++ b/pkg/front_end/testcases/expression/issue_53996_04.expression.yaml.expect @@ -0,0 +1,7 @@ +Errors: { + org-dartlang-debug:synthetic_debug_expression:1:1: Error: The variable 'data' is unavailable in this expression evaluation. + data = 42 + ^^^^ +} +static method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr() → dynamic + return invalid-expression "org-dartlang-debug:synthetic_debug_expression:1:1: Error: The variable 'data' is unavailable in this expression evaluation.\ndata = 42\n^^^^"; diff --git a/pkg/front_end/testcases/expression/issue_53996_05.expression.yaml b/pkg/front_end/testcases/expression/issue_53996_05.expression.yaml new file mode 100644 index 00000000000..c145cdb52b3 --- /dev/null +++ b/pkg/front_end/testcases/expression/issue_53996_05.expression.yaml @@ -0,0 +1,42 @@ +# Copyright (c) 2025, 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. + +# https://github.com/dart-lang/sdk/issues/53996 + +# Definition, offset, method etc extracted by starting the VM with +# `-DDFE_VERBOSE=true`, e.g. +# ``` +# out/ReleaseX64/dart -DDFE_VERBOSE=true --enable-vm-service \ +# --disable-service-auth-codes --pause_isolates_on_start inputFile.dart +# ``` +# and then issuing the expression compilation. + +sources: | + import "dart:developer"; + + class A { + List list; + A(this.list) { + debugger(); + list = [3]; + print(list); + } + } + + void main() { + new A([1, 2]); + } + +definitions: [] +definition_types: [] +type_definitions: [] +type_bounds: [] +type_defaults: [] +method: "A" +position: "#A" +static: false +offset: 70 +scriptUri: main.dart +expression: | + list diff --git a/pkg/front_end/testcases/expression/issue_53996_05.expression.yaml.expect b/pkg/front_end/testcases/expression/issue_53996_05.expression.yaml.expect new file mode 100644 index 00000000000..f0822b352b3 --- /dev/null +++ b/pkg/front_end/testcases/expression/issue_53996_05.expression.yaml.expect @@ -0,0 +1,4 @@ +Errors: { +} +method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr() → dynamic + return this.{#lib1::A::list}{dart.core::List}; diff --git a/pkg/front_end/testcases/expression/issue_53996_06.expression.yaml b/pkg/front_end/testcases/expression/issue_53996_06.expression.yaml new file mode 100644 index 00000000000..8ada9e35926 --- /dev/null +++ b/pkg/front_end/testcases/expression/issue_53996_06.expression.yaml @@ -0,0 +1,42 @@ +# Copyright (c) 2025, 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. + +# https://github.com/dart-lang/sdk/issues/53996 + +# Definition, offset, method etc extracted by starting the VM with +# `-DDFE_VERBOSE=true`, e.g. +# ``` +# out/ReleaseX64/dart -DDFE_VERBOSE=true --enable-vm-service \ +# --disable-service-auth-codes --pause_isolates_on_start inputFile.dart +# ``` +# and then issuing the expression compilation. + +sources: | + import "dart:developer"; + + class A { + List list; + A.named(this.list) { + debugger(); + list = [3]; + print(list); + } + } + + void main() { + new A.named([1, 2]); + } + +definitions: [] +definition_types: [] +type_definitions: [] +type_bounds: [] +type_defaults: [] +method: "A.named" +position: "#A" +static: false +offset: 76 +scriptUri: main.dart +expression: | + list diff --git a/pkg/front_end/testcases/expression/issue_53996_06.expression.yaml.expect b/pkg/front_end/testcases/expression/issue_53996_06.expression.yaml.expect new file mode 100644 index 00000000000..f0822b352b3 --- /dev/null +++ b/pkg/front_end/testcases/expression/issue_53996_06.expression.yaml.expect @@ -0,0 +1,4 @@ +Errors: { +} +method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr() → dynamic + return this.{#lib1::A::list}{dart.core::List}; diff --git a/pkg/front_end/testcases/expression/issue_53996_07.expression.yaml b/pkg/front_end/testcases/expression/issue_53996_07.expression.yaml new file mode 100644 index 00000000000..49e5b97bf6e --- /dev/null +++ b/pkg/front_end/testcases/expression/issue_53996_07.expression.yaml @@ -0,0 +1,48 @@ +# Copyright (c) 2025, 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. + +# https://github.com/dart-lang/sdk/issues/53996 + +# Definition, offset, method etc extracted by starting the VM with +# `-DDFE_VERBOSE=true`, e.g. +# ``` +# out/ReleaseX64/dart -DDFE_VERBOSE=true --enable-vm-service \ +# --disable-service-auth-codes --pause_isolates_on_start inputFile.dart +# ``` +# and then issuing the expression compilation. + +sources: | + import "dart:developer"; + + class A { + List list; + A(this.list) { + list = [3]; + } + } + + class B extends A { + B(super.list) { + debugger(); + list = [7]; + print(list); + } + } + + void main() { + new B([1, 2]); + } + +definitions: [] +definition_types: [] +type_definitions: [] +type_bounds: [] +type_defaults: [] +method: "B" +position: "#B" +static: false +offset: 131 +scriptUri: main.dart +expression: | + list diff --git a/pkg/front_end/testcases/expression/issue_53996_07.expression.yaml.expect b/pkg/front_end/testcases/expression/issue_53996_07.expression.yaml.expect new file mode 100644 index 00000000000..f0822b352b3 --- /dev/null +++ b/pkg/front_end/testcases/expression/issue_53996_07.expression.yaml.expect @@ -0,0 +1,4 @@ +Errors: { +} +method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr() → dynamic + return this.{#lib1::A::list}{dart.core::List}; diff --git a/pkg/vm_service/test/evaluate_optimized_out_variable_test.dart b/pkg/vm_service/test/evaluate_optimized_out_variable_test.dart index 10d87c32103..0f96c061267 100644 --- a/pkg/vm_service/test/evaluate_optimized_out_variable_test.dart +++ b/pkg/vm_service/test/evaluate_optimized_out_variable_test.dart @@ -67,7 +67,11 @@ final tests = [ } on RPCError catch (e) { expect(e.code, RPCErrorKind.kExpressionCompilationError.code); expect(e.message, 'Expression compilation error'); - expect(e.details, contains("Error: Undefined name 'data'.")); + expect( + e.details, + contains("Error: The variable 'data' " + 'is unavailable in this expression evaluation.'), + ); } }, ]; diff --git a/pkg/vm_service/test/evaluate_uninitialized_late_variable_test.dart b/pkg/vm_service/test/evaluate_uninitialized_late_variable_test.dart index 7f111b1a555..1c724073015 100644 --- a/pkg/vm_service/test/evaluate_uninitialized_late_variable_test.dart +++ b/pkg/vm_service/test/evaluate_uninitialized_late_variable_test.dart @@ -45,7 +45,11 @@ final tests = [ // solution. expect(e.code, RPCErrorKind.kExpressionCompilationError.code); expect(e.message, 'Expression compilation error'); - expect(e.details, contains("Error: Undefined name 'x'.")); + expect( + e.details, + contains("Error: The variable 'x' " + 'is unavailable in this expression evaluation.'), + ); } }, ];