diff --git a/pkg/front_end/lib/src/fasta/builder/field_builder.dart b/pkg/front_end/lib/src/fasta/builder/field_builder.dart index a98bd3f68f1..6ef5385b902 100644 --- a/pkg/front_end/lib/src/fasta/builder/field_builder.dart +++ b/pkg/front_end/lib/src/fasta/builder/field_builder.dart @@ -165,7 +165,7 @@ class FieldBuilder extends MemberBuilder { .createBodyBuilderForOutlineExpression( library, classBuilder, this, scope, fileUri); bodyBuilder.constantContext = - isConst ? ConstantContext.inferred : ConstantContext.none; + isConst ? ConstantContext.inferred : ConstantContext.required; initializer = bodyBuilder.parseFieldInitializer(constInitializerToken) ..parent = field; bodyBuilder.typeInferrer diff --git a/pkg/front_end/lib/src/fasta/builder/procedure_builder.dart b/pkg/front_end/lib/src/fasta/builder/procedure_builder.dart index 391a32ed69f..bf45b086ed7 100644 --- a/pkg/front_end/lib/src/fasta/builder/procedure_builder.dart +++ b/pkg/front_end/lib/src/fasta/builder/procedure_builder.dart @@ -905,7 +905,7 @@ class ConstructorBuilder extends FunctionBuilder { BodyBuilder bodyBuilder = library.loader .createBodyBuilderForOutlineExpression( library, classBuilder, this, classBuilder.scope, fileUri); - bodyBuilder.constantContext = ConstantContext.inferred; + bodyBuilder.constantContext = ConstantContext.required; bodyBuilder.parseInitializers(beginInitializers); bodyBuilder.resolveRedirectingFactoryTargets(); } diff --git a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart index 9e76db91098..a2a26226e11 100644 --- a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart +++ b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart @@ -6925,6 +6925,16 @@ const MessageCode messageMissingConstFinalVarOrType = const MessageCode( tip: r"""Try adding the name of the type of the variable or the keyword 'var'."""); +// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. +const Code codeMissingExplicitConst = messageMissingExplicitConst; + +// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. +const MessageCode messageMissingExplicitConst = const MessageCode( + "MissingExplicitConst", + analyzerCodes: ["NOT_CONSTANT_EXPRESSION"], + message: r"""Constant expression expected.""", + tip: r"""Try inserting 'const'."""); + // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. const Template templateMissingExplicitTypeArguments = diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart index 1ab3588e8d9..281970dd689 100644 --- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart +++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart @@ -2491,6 +2491,11 @@ class BodyBuilder extends ScopeListener int count, Token leftBracket, Token constKeyword, Token rightBracket) { debugEvent("LiteralList"); + if (constantContext == ConstantContext.required && constKeyword == null) { + addProblem(fasta.messageMissingExplicitConst, offsetForToken(leftBracket), + noLength); + } + // TODO(danrubel): Replace this with popListForValue // when control flow and spread collections have been enabled by default List expressions = @@ -2583,6 +2588,11 @@ class BodyBuilder extends ScopeListener ) { debugEvent("LiteralSetOrMap"); + if (constantContext == ConstantContext.required && constKeyword == null) { + addProblem(fasta.messageMissingExplicitConst, offsetForToken(leftBrace), + noLength); + } + List setOrMapEntries = new List.filled(count, null, growable: true); for (int i = count - 1; i >= 0; i--) { @@ -3445,12 +3455,14 @@ class BodyBuilder extends ScopeListener charOffset); } - bool isConst = constness == Constness.explicitConst; + bool isConst = constness == Constness.explicitConst || + constantContext != ConstantContext.none; if (target is Constructor) { - isConst = - isConst || constantContext != ConstantContext.none && target.isConst; - if ((isConst || constantContext == ConstantContext.inferred) && - !target.isConst) { + if (constantContext == ConstantContext.required && + constness == Constness.implicit) { + addProblem(fasta.messageMissingExplicitConst, charOffset, charLength); + } + if (isConst && !target.isConst) { return wrapInvalidConstructorInvocation( desugarSyntheticExpression(buildProblem( fasta.messageNonConstConstructor, charOffset, charLength)), @@ -3467,10 +3479,11 @@ class BodyBuilder extends ScopeListener } else { Procedure procedure = target; if (procedure.isFactory) { - isConst = isConst || - constantContext != ConstantContext.none && procedure.isConst; - if ((isConst || constantContext == ConstantContext.inferred) && - !procedure.isConst) { + if (constantContext == ConstantContext.required && + constness == Constness.implicit) { + addProblem(fasta.messageMissingExplicitConst, charOffset, charLength); + } + if (isConst && !procedure.isConst) { return wrapInvalidConstructorInvocation( desugarSyntheticExpression(buildProblem( fasta.messageNonConstFactory, charOffset, charLength)), @@ -3486,8 +3499,9 @@ class BodyBuilder extends ScopeListener node, typeEnvironment, uri); return node; } else { + assert(constness == Constness.implicit); StaticInvocation node = - new StaticInvocation(target, arguments, isConst: isConst) + new StaticInvocation(target, arguments, isConst: false) ..fileOffset = charOffset; libraryBuilder.checkBoundsInStaticInvocation( node, typeEnvironment, uri); @@ -4906,6 +4920,7 @@ class BodyBuilder extends ScopeListener forest.createIntLiteral(location?.line ?? 0, null) ..fileOffset = charOffset, ]), + constness: Constness.explicitNew, charOffset: charOffset)) ..fileOffset = charOffset; } @@ -4922,6 +4937,7 @@ class BodyBuilder extends ScopeListener forest.createArguments(charOffset, [ forest.createStringLiteral(className, null)..fileOffset = charOffset ]), + constness: Constness.explicitNew, charOffset: charOffset); if (invocation is shadow.SyntheticExpressionJudgment) { invocation = desugarSyntheticExpression(invocation); @@ -5025,6 +5041,7 @@ class BodyBuilder extends ScopeListener forest.createStringLiteral(name, null) ..fileOffset = assignmentOffset ]), + constness: Constness.explicitNew, charOffset: assignmentOffset); if (invocation is shadow.SyntheticExpressionJudgment) { invocation = desugarSyntheticExpression(invocation); diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml index 056dbc84f47..0436850ca5f 100644 --- a/pkg/front_end/messages.yaml +++ b/pkg/front_end/messages.yaml @@ -195,6 +195,16 @@ NotAConstantExpression: template: "Not a constant expression." analyzerCode: NOT_CONSTANT_EXPRESSION +MissingExplicitConst: + template: "Constant expression expected." + tip: "Try inserting 'const'." + analyzerCode: NOT_CONSTANT_EXPRESSION + script: > + class A { + final x; + const A(): x = []; + } + NonAsciiIdentifier: template: "The non-ASCII character '#character' (#unicode) can't be used in identifiers, only in strings and comments." tip: "Try using an US-ASCII letter, a digit, '_' (an underscore), or '$' (a dollar sign)." diff --git a/pkg/front_end/testcases/general/magic_const.dart.strong.expect b/pkg/front_end/testcases/general/magic_const.dart.strong.expect index 6d9ece411b0..de746477e57 100644 --- a/pkg/front_end/testcases/general/magic_const.dart.strong.expect +++ b/pkg/front_end/testcases/general/magic_const.dart.strong.expect @@ -15,6 +15,21 @@ library; // // Problems in library: // +// pkg/front_end/testcases/general/magic_const.dart:15:9: Error: Constant expression expected. +// Try inserting 'const'. +// foo({a: Constant(), b: Constant(), c: []}) {} +// ^^^^^^^^ +// +// pkg/front_end/testcases/general/magic_const.dart:15:24: Error: Constant expression expected. +// Try inserting 'const'. +// foo({a: Constant(), b: Constant(), c: []}) {} +// ^^^^^^^^ +// +// pkg/front_end/testcases/general/magic_const.dart:15:39: Error: Constant expression expected. +// Try inserting 'const'. +// foo({a: Constant(), b: Constant(), c: []}) {} +// ^ +// // pkg/front_end/testcases/general/magic_const.dart:18:9: Error: Cannot invoke a non-'const' constructor where a const expression is expected. // Try using a constructor or factory that is 'const'. // const NotConstant(); diff --git a/pkg/front_end/testcases/general/magic_const.dart.strong.transformed.expect b/pkg/front_end/testcases/general/magic_const.dart.strong.transformed.expect index 6d9ece411b0..de746477e57 100644 --- a/pkg/front_end/testcases/general/magic_const.dart.strong.transformed.expect +++ b/pkg/front_end/testcases/general/magic_const.dart.strong.transformed.expect @@ -15,6 +15,21 @@ library; // // Problems in library: // +// pkg/front_end/testcases/general/magic_const.dart:15:9: Error: Constant expression expected. +// Try inserting 'const'. +// foo({a: Constant(), b: Constant(), c: []}) {} +// ^^^^^^^^ +// +// pkg/front_end/testcases/general/magic_const.dart:15:24: Error: Constant expression expected. +// Try inserting 'const'. +// foo({a: Constant(), b: Constant(), c: []}) {} +// ^^^^^^^^ +// +// pkg/front_end/testcases/general/magic_const.dart:15:39: Error: Constant expression expected. +// Try inserting 'const'. +// foo({a: Constant(), b: Constant(), c: []}) {} +// ^ +// // pkg/front_end/testcases/general/magic_const.dart:18:9: Error: Cannot invoke a non-'const' constructor where a const expression is expected. // Try using a constructor or factory that is 'const'. // const NotConstant(); diff --git a/pkg/front_end/testcases/rasta/mandatory_parameter_initializer.dart.strong.expect b/pkg/front_end/testcases/rasta/mandatory_parameter_initializer.dart.strong.expect index 391c47700ad..9096c74c01a 100644 --- a/pkg/front_end/testcases/rasta/mandatory_parameter_initializer.dart.strong.expect +++ b/pkg/front_end/testcases/rasta/mandatory_parameter_initializer.dart.strong.expect @@ -20,6 +20,11 @@ library; // main(arguments = [x]) { // ^ // +// pkg/front_end/testcases/rasta/mandatory_parameter_initializer.dart:5:18: Error: Constant expression expected. +// Try inserting 'const'. +// main(arguments = [x]) { +// ^ +// // pkg/front_end/testcases/rasta/mandatory_parameter_initializer.dart:5:19: Error: Getter not found: 'x'. // main(arguments = [x]) { // ^ diff --git a/pkg/front_end/testcases/rasta/mandatory_parameter_initializer.dart.strong.transformed.expect b/pkg/front_end/testcases/rasta/mandatory_parameter_initializer.dart.strong.transformed.expect index 391c47700ad..9096c74c01a 100644 --- a/pkg/front_end/testcases/rasta/mandatory_parameter_initializer.dart.strong.transformed.expect +++ b/pkg/front_end/testcases/rasta/mandatory_parameter_initializer.dart.strong.transformed.expect @@ -20,6 +20,11 @@ library; // main(arguments = [x]) { // ^ // +// pkg/front_end/testcases/rasta/mandatory_parameter_initializer.dart:5:18: Error: Constant expression expected. +// Try inserting 'const'. +// main(arguments = [x]) { +// ^ +// // pkg/front_end/testcases/rasta/mandatory_parameter_initializer.dart:5:19: Error: Getter not found: 'x'. // main(arguments = [x]) { // ^