Report a few errors for super-formal parameters in explicit super-constructor invocation.
Change-Id: I75cdc08ab26fba01fb44f387737ee44d6b09eaa8
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/225980
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart
index 77326fb..a3ae779 100644
--- a/pkg/analyzer/lib/error/error.dart
+++ b/pkg/analyzer/lib/error/error.dart
@@ -365,6 +365,8 @@
CompileTimeErrorCode.PART_OF_DIFFERENT_LIBRARY,
CompileTimeErrorCode.PART_OF_NON_PART,
CompileTimeErrorCode.PART_OF_UNNAMED_LIBRARY,
+ CompileTimeErrorCode
+ .POSITIONAL_SUPER_FORMAL_PARAMETER_WITH_POSITIONAL_ARGUMENT,
CompileTimeErrorCode.PREFIX_COLLIDES_WITH_TOP_LEVEL_MEMBER,
CompileTimeErrorCode.PREFIX_IDENTIFIER_NOT_FOLLOWED_BY_DOT,
CompileTimeErrorCode.PREFIX_SHADOWED_BY_LOCAL_DECLARATION,
@@ -403,6 +405,8 @@
CompileTimeErrorCode.SHARED_DEFERRED_PREFIX,
CompileTimeErrorCode.SPREAD_EXPRESSION_FROM_DEFERRED_LIBRARY,
CompileTimeErrorCode.STATIC_ACCESS_TO_INSTANCE_MEMBER,
+ CompileTimeErrorCode.SUPER_FORMAL_PARAMETER_WITHOUT_ASSOCIATED_NAMED,
+ CompileTimeErrorCode.SUPER_FORMAL_PARAMETER_WITHOUT_ASSOCIATED_POSITIONAL,
CompileTimeErrorCode.SUPER_IN_EXTENSION,
CompileTimeErrorCode.SUPER_IN_INVALID_CONTEXT,
CompileTimeErrorCode.SUPER_IN_REDIRECTING_CONSTRUCTOR,
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index 0a2341a..eabe7ee 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -5482,14 +5482,17 @@
if (superConstructor != null) {
var superParameters = superConstructor.parameters;
if (isNamed) {
- return superParameters.firstWhereOrNull((e) => e.name == name);
+ return superParameters
+ .firstWhereOrNull((e) => e.isNamed && e.name == name);
} else {
+ var positionalSuperParameters =
+ superParameters.where((e) => e.isPositional).toList();
var index = enclosingElement.parameters
.whereType<SuperFormalParameterElementImpl>()
.toList()
.indexOf(this);
- if (index >= 0 && index < superParameters.length) {
- return superParameters[index];
+ if (index >= 0 && index < positionalSuperParameters.length) {
+ return positionalSuperParameters[index];
}
}
}
diff --git a/pkg/analyzer/lib/src/error/codes.g.dart b/pkg/analyzer/lib/src/error/codes.g.dart
index 0f00d43..5f42ff2 100644
--- a/pkg/analyzer/lib/src/error/codes.g.dart
+++ b/pkg/analyzer/lib/src/error/codes.g.dart
@@ -11628,6 +11628,20 @@
);
/**
+ * No parameters.
+ */
+ static const CompileTimeErrorCode
+ POSITIONAL_SUPER_FORMAL_PARAMETER_WITH_POSITIONAL_ARGUMENT =
+ CompileTimeErrorCode(
+ 'POSITIONAL_SUPER_FORMAL_PARAMETER_WITH_POSITIONAL_ARGUMENT',
+ "Positional super-formal parameters can't be used when the "
+ "super-constructor invocation has a positional argument.",
+ correctionMessage:
+ "Try removing the 'super' modifier, or changing the super-constructor "
+ "to use named parameters.",
+ );
+
+ /**
* Parameters:
* 0: the name of the prefix
*/
@@ -13078,6 +13092,30 @@
);
/**
+ * No parameters.
+ */
+ static const CompileTimeErrorCode
+ SUPER_FORMAL_PARAMETER_WITHOUT_ASSOCIATED_NAMED = CompileTimeErrorCode(
+ 'SUPER_FORMAL_PARAMETER_WITHOUT_ASSOCIATED_NAMED',
+ "No associated named super-constructor parameter.",
+ correctionMessage: "Try changing the name to the name of an existing named "
+ "super-constructor parameter, or creating such named parameter.",
+ );
+
+ /**
+ * No parameters.
+ */
+ static const CompileTimeErrorCode
+ SUPER_FORMAL_PARAMETER_WITHOUT_ASSOCIATED_POSITIONAL =
+ CompileTimeErrorCode(
+ 'SUPER_FORMAL_PARAMETER_WITHOUT_ASSOCIATED_POSITIONAL',
+ "No associated positional super-constructor parameter.",
+ correctionMessage:
+ "Try using named parameters instead, or adding more positional "
+ "parameters to the super-constructor.",
+ );
+
+ /**
* 7.6.1 Generative Constructors: Let <i>k</i> be a generative constructor. It
* is a compile-time error if a generative constructor of class Object
* includes a superinitializer.
diff --git a/pkg/analyzer/lib/src/generated/element_resolver.dart b/pkg/analyzer/lib/src/generated/element_resolver.dart
index c430007..344c5e7 100644
--- a/pkg/analyzer/lib/src/generated/element_resolver.dart
+++ b/pkg/analyzer/lib/src/generated/element_resolver.dart
@@ -406,7 +406,11 @@
return;
}
var argumentList = node.argumentList;
- var parameters = _resolveArgumentsToFunction(argumentList, element);
+ var parameters = _resolveArgumentsToFunction(
+ argumentList,
+ element,
+ enclosingConstructor: node.thisOrAncestorOfType<ConstructorDeclaration>(),
+ );
if (parameters != null) {
argumentList.correspondingStaticParameters = parameters;
}
@@ -446,12 +450,19 @@
/// cannot be matched to a parameter. Return the parameters that correspond to
/// the arguments, or `null` if no correspondence could be computed.
List<ParameterElement?>? _resolveArgumentsToFunction(
- ArgumentList argumentList, ExecutableElement? executableElement) {
+ ArgumentList argumentList,
+ ExecutableElement? executableElement, {
+ ConstructorDeclaration? enclosingConstructor,
+ }) {
if (executableElement == null) {
return null;
}
List<ParameterElement> parameters = executableElement.parameters;
- return _resolveArgumentsToParameters(argumentList, parameters);
+ return _resolveArgumentsToParameters(
+ argumentList,
+ parameters,
+ enclosingConstructor: enclosingConstructor,
+ );
}
/// Given an [argumentList] and the [parameters] related to the element that
@@ -460,9 +471,16 @@
/// the arguments cannot be matched to a parameter. Return the parameters that
/// correspond to the arguments.
List<ParameterElement?> _resolveArgumentsToParameters(
- ArgumentList argumentList, List<ParameterElement> parameters) {
+ ArgumentList argumentList,
+ List<ParameterElement> parameters, {
+ ConstructorDeclaration? enclosingConstructor,
+ }) {
return ResolverVisitor.resolveArgumentsToParameters(
- argumentList, parameters, _errorReporter.reportErrorForNode);
+ argumentList,
+ parameters,
+ _errorReporter.reportErrorForNode,
+ enclosingConstructor: enclosingConstructor,
+ );
}
/// Resolve the names in the given [combinators] in the scope of the given
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index ddcbc07..ad495d9 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -2331,11 +2331,12 @@
/// Returns the parameters that correspond to the arguments. If no parameter
/// matched an argument, that position will be `null` in the list.
static List<ParameterElement?> resolveArgumentsToParameters(
- ArgumentList argumentList,
- List<ParameterElement> parameters,
- void Function(ErrorCode errorCode, AstNode node,
- [List<Object> arguments])?
- onError) {
+ ArgumentList argumentList,
+ List<ParameterElement> parameters,
+ void Function(ErrorCode errorCode, AstNode node, [List<Object> arguments])?
+ onError, {
+ ConstructorDeclaration? enclosingConstructor,
+ }) {
if (parameters.isEmpty && argumentList.arguments.isEmpty) {
return const <ParameterElement>[];
}
@@ -2401,6 +2402,45 @@
}
}
}
+
+ if (enclosingConstructor != null) {
+ var hasExplicitPositionalArguments = positionalArgumentCount != 0;
+ for (var formalParameter in enclosingConstructor.parameters.parameters) {
+ formalParameter = formalParameter.notDefault;
+ if (formalParameter is SuperFormalParameter) {
+ var element = formalParameter.declaredElement
+ as SuperFormalParameterElementImpl;
+ if (formalParameter.isNamed) {
+ if (onError != null && element.superConstructorParameter == null) {
+ onError(
+ CompileTimeErrorCode
+ .SUPER_FORMAL_PARAMETER_WITHOUT_ASSOCIATED_NAMED,
+ formalParameter.identifier,
+ );
+ }
+ } else {
+ positionalArgumentCount++;
+ if (onError != null) {
+ if (hasExplicitPositionalArguments) {
+ onError(
+ CompileTimeErrorCode
+ .POSITIONAL_SUPER_FORMAL_PARAMETER_WITH_POSITIONAL_ARGUMENT,
+ formalParameter.identifier,
+ );
+ }
+ if (element.superConstructorParameter == null) {
+ onError(
+ CompileTimeErrorCode
+ .SUPER_FORMAL_PARAMETER_WITHOUT_ASSOCIATED_POSITIONAL,
+ formalParameter.identifier,
+ );
+ }
+ }
+ }
+ }
+ }
+ }
+
if (positionalArgumentCount < requiredParameterCount && noBlankArguments) {
if (onError != null) {
onError(CompileTimeErrorCode.NOT_ENOUGH_POSITIONAL_ARGUMENTS,
@@ -2417,8 +2457,8 @@
} else {
errorCode = CompileTimeErrorCode.EXTRA_POSITIONAL_ARGUMENTS;
}
- if (onError != null) {
- onError(errorCode, firstUnresolvedArgument!,
+ if (onError != null && firstUnresolvedArgument != null) {
+ onError(errorCode, firstUnresolvedArgument,
[unnamedParameterCount, positionalArgumentCount]);
}
}
diff --git a/pkg/analyzer/messages.yaml b/pkg/analyzer/messages.yaml
index fc0b1e9..0f6e2fe 100644
--- a/pkg/analyzer/messages.yaml
+++ b/pkg/analyzer/messages.yaml
@@ -9866,6 +9866,10 @@
```dart
part of 'test.dart';
```
+ POSITIONAL_SUPER_FORMAL_PARAMETER_WITH_POSITIONAL_ARGUMENT:
+ problemMessage: Positional super-formal parameters can't be used when the super-constructor invocation has a positional argument.
+ comment: No parameters.
+ correctionMessage: Try removing the 'super' modifier, or changing the super-constructor to use named parameters.
PREFIX_COLLIDES_WITH_TOP_LEVEL_MEMBER:
problemMessage: "The name '{0}' is already used as an import prefix and can't be used to name a top-level element."
correctionMessage: Try renaming either the top-level element or the prefix.
@@ -11098,6 +11102,14 @@
int f(C c) => c.b;
```
+ SUPER_FORMAL_PARAMETER_WITHOUT_ASSOCIATED_NAMED:
+ problemMessage: No associated named super-constructor parameter.
+ comment: No parameters.
+ correctionMessage: Try changing the name to the name of an existing named super-constructor parameter, or creating such named parameter.
+ SUPER_FORMAL_PARAMETER_WITHOUT_ASSOCIATED_POSITIONAL:
+ problemMessage: No associated positional super-constructor parameter.
+ comment: No parameters.
+ correctionMessage: Try using named parameters instead, or adding more positional parameters to the super-constructor.
IMPLEMENTS_DEFERRED_CLASS:
sharedName: SUBTYPE_OF_DEFERRED_CLASS
problemMessage: "Classes and mixins can't implement deferred classes."
diff --git a/pkg/analyzer/test/src/diagnostics/not_enough_positional_arguments_test.dart b/pkg/analyzer/test/src/diagnostics/not_enough_positional_arguments_test.dart
index e8e1418..dcb8a8f 100644
--- a/pkg/analyzer/test/src/diagnostics/not_enough_positional_arguments_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/not_enough_positional_arguments_test.dart
@@ -86,4 +86,28 @@
error(CompileTimeErrorCode.NOT_ENOUGH_POSITIONAL_ARGUMENTS, 65, 2),
]);
}
+
+ test_superParameter_optional() async {
+ await assertNoErrorsInCode(r'''
+class A {
+ A(int? a);
+}
+
+class B extends A {
+ B([super.a]) : super();
+}
+''');
+ }
+
+ test_superParameter_required() async {
+ await assertNoErrorsInCode(r'''
+class A {
+ A(int a);
+}
+
+class B extends A {
+ B(super.a) : super();
+}
+''');
+ }
}
diff --git a/pkg/analyzer/test/src/diagnostics/positional_super_formal_parameter_with_positional_argument_test.dart b/pkg/analyzer/test/src/diagnostics/positional_super_formal_parameter_with_positional_argument_test.dart
new file mode 100644
index 0000000..ddde7c2
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/positional_super_formal_parameter_with_positional_argument_test.dart
@@ -0,0 +1,49 @@
+// Copyright (c) 2021, 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.
+
+import 'package:analyzer/src/error/codes.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../dart/resolution/context_collection_resolution.dart';
+
+main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(
+ PositionalSuperFormalParameterWithPositionalArgumentTest);
+ });
+}
+
+@reflectiveTest
+class PositionalSuperFormalParameterWithPositionalArgumentTest
+ extends PubPackageResolutionTest {
+ test_notReported() async {
+ await assertNoErrorsInCode(r'''
+class A {
+ A(int a);
+}
+
+class B extends A {
+ B(super.a) : super();
+}
+''');
+ }
+
+ test_reported() async {
+ await assertErrorsInCode(r'''
+class A {
+ A(int a, int b);
+}
+
+class B extends A {
+ B(super.b) : super(0);
+}
+''', [
+ error(
+ CompileTimeErrorCode
+ .POSITIONAL_SUPER_FORMAL_PARAMETER_WITH_POSITIONAL_ARGUMENT,
+ 62,
+ 1)
+ ]);
+ }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/super_formal_parameter_without_associated_named_test.dart b/pkg/analyzer/test/src/diagnostics/super_formal_parameter_without_associated_named_test.dart
new file mode 100644
index 0000000..049858a
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/super_formal_parameter_without_associated_named_test.dart
@@ -0,0 +1,52 @@
+// Copyright (c) 2021, 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.
+
+import 'package:analyzer/src/error/codes.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../dart/resolution/context_collection_resolution.dart';
+
+main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(SuperFormalParameterWithoutAssociatedNamedTest);
+ });
+}
+
+@reflectiveTest
+class SuperFormalParameterWithoutAssociatedNamedTest
+ extends PubPackageResolutionTest {
+ test_optional() async {
+ await assertErrorsInCode(r'''
+class A {
+ A([int? a]);
+}
+
+class B extends A {
+ B({super.a}) : super();
+}
+''', [
+ error(
+ CompileTimeErrorCode.SUPER_FORMAL_PARAMETER_WITHOUT_ASSOCIATED_NAMED,
+ 59,
+ 1)
+ ]);
+ }
+
+ test_required() async {
+ await assertErrorsInCode(r'''
+class A {
+ A([int? a]);
+}
+
+class B extends A {
+ B({required super.a}) : super();
+}
+''', [
+ error(
+ CompileTimeErrorCode.SUPER_FORMAL_PARAMETER_WITHOUT_ASSOCIATED_NAMED,
+ 68,
+ 1)
+ ]);
+ }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/super_formal_parameter_without_associated_positional_test.dart b/pkg/analyzer/test/src/diagnostics/super_formal_parameter_without_associated_positional_test.dart
new file mode 100644
index 0000000..47d5cde
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/super_formal_parameter_without_associated_positional_test.dart
@@ -0,0 +1,54 @@
+// Copyright (c) 2021, 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.
+
+import 'package:analyzer/src/error/codes.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../dart/resolution/context_collection_resolution.dart';
+
+main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(SuperFormalParameterWithoutAssociatedPositionalTest);
+ });
+}
+
+@reflectiveTest
+class SuperFormalParameterWithoutAssociatedPositionalTest
+ extends PubPackageResolutionTest {
+ test_optional() async {
+ await assertErrorsInCode(r'''
+class A {
+ A({int? a});
+}
+
+class B extends A {
+ B([super.a]) : super();
+}
+''', [
+ error(
+ CompileTimeErrorCode
+ .SUPER_FORMAL_PARAMETER_WITHOUT_ASSOCIATED_POSITIONAL,
+ 59,
+ 1)
+ ]);
+ }
+
+ test_required() async {
+ await assertErrorsInCode(r'''
+class A {
+ A({int? a});
+}
+
+class B extends A {
+ B(super.a) : super();
+}
+''', [
+ error(
+ CompileTimeErrorCode
+ .SUPER_FORMAL_PARAMETER_WITHOUT_ASSOCIATED_POSITIONAL,
+ 58,
+ 1)
+ ]);
+ }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/test_all.dart b/pkg/analyzer/test/src/diagnostics/test_all.dart
index 8bed89d..445a54b 100644
--- a/pkg/analyzer/test/src/diagnostics/test_all.dart
+++ b/pkg/analyzer/test/src/diagnostics/test_all.dart
@@ -546,6 +546,8 @@
import 'packed_nesting_non_packed_test.dart' as packed_nesting_non_packed;
import 'part_of_different_library_test.dart' as part_of_different_library;
import 'part_of_non_part_test.dart' as part_of_non_part;
+import 'positional_super_formal_parameter_with_positional_argument_test.dart'
+ as positional_super_formal_parameter_with_positional_argument;
import 'prefix_collides_with_top_level_member_test.dart'
as prefix_collides_with_top_level_member;
import 'prefix_identifier_not_followed_by_dot_test.dart'
@@ -626,6 +628,10 @@
import 'subtype_of_ffi_class_test.dart' as subtype_of_ffi_class;
import 'subtype_of_sealed_class_test.dart' as subtype_of_sealed_class;
import 'subtype_of_struct_class_test.dart' as subtype_of_struct_class;
+import 'super_formal_parameter_without_associated_named_test.dart'
+ as super_formal_parameter_without_associated_named;
+import 'super_formal_parameter_without_associated_positional_test.dart'
+ as super_formal_parameter_without_associated_positional;
import 'super_in_extension_test.dart' as super_in_extension;
import 'super_in_invalid_context_test.dart' as super_in_invalid_context;
import 'super_in_redirecting_constructor_test.dart'
@@ -1089,6 +1095,7 @@
packed_nesting_non_packed.main();
part_of_different_library.main();
part_of_non_part.main();
+ positional_super_formal_parameter_with_positional_argument.main();
prefix_collides_with_top_level_member.main();
prefix_identifier_not_followed_by_dot.main();
prefix_shadowed_by_local_declaration.main();
@@ -1139,6 +1146,8 @@
subtype_of_ffi_class.main();
subtype_of_sealed_class.main();
subtype_of_struct_class.main();
+ super_formal_parameter_without_associated_named.main();
+ super_formal_parameter_without_associated_positional.main();
super_in_extension.main();
super_in_invalid_context.main();
super_in_redirecting_constructor.main();
diff --git a/pkg/analyzer/test/src/summary/resynthesize_common.dart b/pkg/analyzer/test/src/summary/resynthesize_common.dart
index 9ab1bc5..b5fdd89 100644
--- a/pkg/analyzer/test/src/summary/resynthesize_common.dart
+++ b/pkg/analyzer/test/src/summary/resynthesize_common.dart
@@ -1615,6 +1615,38 @@
''');
}
+ test_class_constructor_parameters_super_optionalNamed_unresolved2() async {
+ var library = await checkLibrary('''
+class A {
+ A(int a);
+}
+
+class B extends A {
+ B({super.a});
+}
+''');
+ checkElementText(library, r'''
+library
+ definingUnit
+ classes
+ class A @6
+ constructors
+ @12
+ parameters
+ requiredPositional a @18
+ type: int
+ class B @31
+ supertype: A
+ constructors
+ @47
+ parameters
+ optionalNamed final super.a @56
+ type: dynamic
+ superConstructorParameter: <null>
+ superConstructor: self::@class::A::@constructor::•
+''');
+ }
+
test_class_constructor_parameters_super_optionalPositional() async {
var library = await checkLibrary('''
class A {
@@ -1770,6 +1802,38 @@
''');
}
+ test_class_constructor_parameters_super_requiredPositional_unresolved2() async {
+ var library = await checkLibrary('''
+class A {
+ A({required int a})
+}
+
+class B extends A {
+ B(super.a);
+}
+''');
+ checkElementText(library, r'''
+library
+ definingUnit
+ classes
+ class A @6
+ constructors
+ @12
+ parameters
+ requiredNamed a @28
+ type: int
+ class B @41
+ supertype: A
+ constructors
+ @57
+ parameters
+ requiredPositional final super.a @65
+ type: dynamic
+ superConstructorParameter: <null>
+ superConstructor: self::@class::A::@constructor::•
+''');
+ }
+
test_class_constructor_params() async {
var library = await checkLibrary('class C { C(x, int y); }');
checkElementText(library, r'''