diff --git a/pkg/analyzer/lib/src/error/type_arguments_verifier.dart b/pkg/analyzer/lib/src/error/type_arguments_verifier.dart index 83fbc885965..3c3ce2d40e7 100644 --- a/pkg/analyzer/lib/src/error/type_arguments_verifier.dart +++ b/pkg/analyzer/lib/src/error/type_arguments_verifier.dart @@ -8,12 +8,14 @@ import 'package:analyzer/dart/analysis/features.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/dart/element/type.dart'; +import 'package:analyzer/diagnostic/diagnostic.dart'; import 'package:analyzer/error/error.dart'; import 'package:analyzer/error/listener.dart'; import 'package:analyzer/src/dart/ast/extensions.dart'; import 'package:analyzer/src/dart/element/type_algebra.dart'; import 'package:analyzer/src/dart/element/type_schema.dart'; import 'package:analyzer/src/dart/element/type_system.dart'; +import 'package:analyzer/src/diagnostic/diagnostic.dart'; import 'package:analyzer/src/error/codes.dart'; import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl; import 'package:analyzer/src/generated/resolver.dart'; @@ -265,18 +267,21 @@ class TypeArgumentsVerifier { /// Verify that the type arguments in the given [namedType] are all within /// their bounds. void _checkForTypeArgumentNotMatchingBounds(NamedType namedType) { - var type = namedType.type; + final type = namedType.type; if (type == null) { return; } - List typeParameters; - List typeArguments; - var alias = type.alias; + final List typeParameters; + final String elementName; + final List typeArguments; + final alias = type.alias; if (alias != null) { + elementName = alias.element.name; typeParameters = alias.element.typeParameters; typeArguments = alias.typeArguments; } else if (type is InterfaceType) { + elementName = type.element.name; typeParameters = type.element.typeParameters; typeArguments = type.typeArguments; } else { @@ -289,7 +294,7 @@ class TypeArgumentsVerifier { // Check for regular-bounded. List<_TypeArgumentIssue>? issues; - var substitution = Substitution.fromPairs(typeParameters, typeArguments); + final substitution = Substitution.fromPairs(typeParameters, typeArguments); for (var i = 0; i < typeArguments.length; i++) { var typeParameter = typeParameters[i]; var typeArgument = typeArguments[i]; @@ -325,6 +330,49 @@ class TypeArgumentsVerifier { return; } + List? buildContextMessages({ + List? invertedTypeArguments, + }) { + final messages = []; + + void addMessage(String message) { + messages.add( + DiagnosticMessageImpl( + filePath: _errorReporter.source.fullName, + length: namedType.length, + message: message, + offset: namedType.offset, + url: null, + ), + ); + } + + String typeArgumentsToString(List typeArguments) { + return typeArguments + .map((e) => e.getDisplayString(withNullability: true)) + .join(', '); + } + + if (namedType.typeArguments == null) { + var typeStr = '$elementName<${typeArgumentsToString(typeArguments)}>'; + addMessage( + "The raw type was instantiated as '$typeStr', " + "and is not regular-bounded.", + ); + } + + if (invertedTypeArguments != null) { + var invertedTypeStr = + '$elementName<${typeArgumentsToString(invertedTypeArguments)}>'; + addMessage( + "The inverted type '$invertedTypeStr' is also not regular-bounded, " + "so the type is not well-bounded.", + ); + } + + return messages.isNotEmpty ? messages : null; + } + // If not allowed to be super-bounded, report issues. if (!_shouldAllowSuperBoundedTypes(namedType)) { for (var issue in issues) { @@ -332,27 +380,32 @@ class TypeArgumentsVerifier { CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, _typeArgumentErrorNode(namedType, issue.index), [issue.argument, issue.parameter.name, issue.parameterBound], + buildContextMessages(), ); } return; } // Prepare type arguments for checking for super-bounded. - type = _typeSystem.replaceTopAndBottom(type); - alias = type.alias; - if (alias != null) { - typeArguments = alias.typeArguments; - } else if (type is InterfaceType) { - typeArguments = type.typeArguments; + final invertedType = _typeSystem.replaceTopAndBottom(type); + final List invertedTypeArguments; + final invertedAlias = invertedType.alias; + if (invertedAlias != null) { + invertedTypeArguments = invertedAlias.typeArguments; + } else if (invertedType is InterfaceType) { + invertedTypeArguments = invertedType.typeArguments; } else { return; } // Check for super-bounded. - substitution = Substitution.fromPairs(typeParameters, typeArguments); - for (var i = 0; i < typeArguments.length; i++) { + final invertedSubstitution = Substitution.fromPairs( + typeParameters, + invertedTypeArguments, + ); + for (var i = 0; i < invertedTypeArguments.length; i++) { var typeParameter = typeParameters[i]; - var typeArgument = typeArguments[i]; + var typeArgument = invertedTypeArguments[i]; var bound = typeParameter.bound; if (bound == null) { @@ -360,13 +413,16 @@ class TypeArgumentsVerifier { } bound = _libraryElement.toLegacyTypeIfOptOut(bound); - bound = substitution.substituteType(bound); + bound = invertedSubstitution.substituteType(bound); if (!_typeSystem.isSubtypeOf(typeArgument, bound)) { _errorReporter.reportErrorForNode( CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, _typeArgumentErrorNode(namedType, i), [typeArgument, typeParameter.name, bound], + buildContextMessages( + invertedTypeArguments: invertedTypeArguments, + ), ); } } diff --git a/pkg/analyzer/test/generated/strong_mode_test.dart b/pkg/analyzer/test/generated/strong_mode_test.dart index 11b6ce109f4..e23271d4401 100644 --- a/pkg/analyzer/test/generated/strong_mode_test.dart +++ b/pkg/analyzer/test/generated/strong_mode_test.dart @@ -3508,8 +3508,10 @@ void test() { class C, T1 extends List> {} class D extends C {} ''', [ - error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 69, 1), - error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 69, 1), + error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 69, 1, + contextMessages: [message('/home/test/lib/test.dart', 69, 1)]), + error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 69, 1, + contextMessages: [message('/home/test/lib/test.dart', 69, 1)]), ]); } @@ -3523,8 +3525,9 @@ void test() { } ''', [ error(HintCode.UNUSED_LOCAL_VARIABLE, 73, 1), - error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 81, 1), error(CompileTimeErrorCode.COULD_NOT_INFER, 81, 1), + error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 81, 1, + contextMessages: [message('/home/test/lib/test.dart', 81, 1)]), ]); _assertLocalVarType('c', 'C, List>>'); } @@ -3560,7 +3563,11 @@ typedef T F(T x); class C> {} C c; ''', [ - error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 48, 1), + error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 48, 1, + contextMessages: [ + message('/home/test/lib/test.dart', 48, 1), + message('/home/test/lib/test.dart', 48, 1) + ]), ]); _assertTopVarType('c', 'C'); } diff --git a/pkg/analyzer/test/src/dart/resolution/constructor_reference_test.dart b/pkg/analyzer/test/src/dart/resolution/constructor_reference_test.dart index 21f09c6a8db..46d6e0d5198 100644 --- a/pkg/analyzer/test/src/dart/resolution/constructor_reference_test.dart +++ b/pkg/analyzer/test/src/dart/resolution/constructor_reference_test.dart @@ -562,7 +562,8 @@ A Function() bar() { return A.foo; } ''', [ - error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 41, 6), + error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 41, 6, + contextMessages: [message('/home/test/lib/test.dart', 39, 9)]), ]); var classElement = findElement.class_('A'); diff --git a/pkg/analyzer/test/src/dart/resolution/type_literal_test.dart b/pkg/analyzer/test/src/dart/resolution/type_literal_test.dart index 817267045d1..866b8c30f36 100644 --- a/pkg/analyzer/test/src/dart/resolution/type_literal_test.dart +++ b/pkg/analyzer/test/src/dart/resolution/type_literal_test.dart @@ -75,7 +75,8 @@ var t = C; class C {} var t = C; ''', [ - error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 36, 6), + error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 36, 6, + contextMessages: [message('/home/test/lib/test.dart', 34, 9)]), ]); var typeLiteral = findNode.typeLiteral('C;'); @@ -142,7 +143,8 @@ class C {} typedef CA = C; var t = CA; ''', [ - error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 59, 6), + error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 59, 6, + contextMessages: [message('/home/test/lib/test.dart', 56, 10)]), ]); var typeLiteral = findNode.typeLiteral('CA;'); @@ -374,7 +376,8 @@ var t = Fn; typedef Fn = void Function(T); var t = Fn; ''', [ - error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 57, 6), + error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 57, 6, + contextMessages: [message('/home/test/lib/test.dart', 54, 10)]), ]); var typeLiteral = findNode.typeLiteral('Fn;'); diff --git a/pkg/analyzer/test/src/diagnostics/could_not_infer_test.dart b/pkg/analyzer/test/src/diagnostics/could_not_infer_test.dart index 40b9bce2544..3e89e8fe8cb 100644 --- a/pkg/analyzer/test/src/diagnostics/could_not_infer_test.dart +++ b/pkg/analyzer/test/src/diagnostics/could_not_infer_test.dart @@ -33,10 +33,12 @@ main() { P._(); } ''', [ + error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 154, 1, + contextMessages: [message('/home/test/lib/test.dart', 154, 1)]), error(CompileTimeErrorCode.COULD_NOT_INFER, 154, 3), + error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 154, 1, + contextMessages: [message('/home/test/lib/test.dart', 154, 1)]), error(CompileTimeErrorCode.COULD_NOT_INFER, 154, 3), - error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 154, 1), - error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 154, 1), ]); } @@ -58,8 +60,9 @@ main() { } ''', [ error(HintCode.UNUSED_LOCAL_VARIABLE, 120, 1), - error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 124, 1), error(CompileTimeErrorCode.COULD_NOT_INFER, 124, 1), + error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 124, 1, + contextMessages: [message('/home/test/lib/test.dart', 124, 1)]), ]); } diff --git a/pkg/analyzer/test/src/diagnostics/type_argument_not_matching_bounds_test.dart b/pkg/analyzer/test/src/diagnostics/type_argument_not_matching_bounds_test.dart index 79ab24abf92..3149899b2c4 100644 --- a/pkg/analyzer/test/src/diagnostics/type_argument_not_matching_bounds_test.dart +++ b/pkg/analyzer/test/src/diagnostics/type_argument_not_matching_bounds_test.dart @@ -129,7 +129,8 @@ class C { C(G this.f) {} } ''', [ - error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 71, 1), + error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 71, 1, + contextMessages: [message('/home/test/lib/test.dart', 69, 4)]), ]); } @@ -140,7 +141,8 @@ class B {} class G {} G f() => throw 0; ''', [ - error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 48, 1), + error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 48, 1, + contextMessages: [message('/home/test/lib/test.dart', 46, 4)]), ]); } @@ -151,7 +153,8 @@ class B {} class G {} typedef G f(); ''', [ - error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 56, 1), + error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 56, 1, + contextMessages: [message('/home/test/lib/test.dart', 54, 4)]), ]); } @@ -162,7 +165,8 @@ class B {} class G {} f(G h()) {} ''', [ - error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 50, 1), + error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 50, 1, + contextMessages: [message('/home/test/lib/test.dart', 48, 4)]), ]); } @@ -184,7 +188,8 @@ class B {} class G {} var b = 1 is G; ''', [ - error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 61, 1), + error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 61, 1, + contextMessages: [message('/home/test/lib/test.dart', 59, 4)]), ]); } @@ -252,7 +257,8 @@ class C { G m() => throw 0; } ''', [ - error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 60, 1), + error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 60, 1, + contextMessages: [message('/home/test/lib/test.dart', 58, 4)]), ]); } @@ -297,7 +303,8 @@ class Bar> {} class Baz extends Bar {} void main() {} ''', [ - error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 65, 3), + error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 65, 3, + contextMessages: [message('/home/test/lib/test.dart', 65, 3)]), ]); // Instantiate-to-bounds should have instantiated "Bar" to "Bar". assertType(result.unit.declaredElement!.getType('Baz')!.supertype, @@ -311,7 +318,8 @@ class B {} typedef F(); F fff = (throw 42); ''', [ - error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 50, 1), + error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 50, 1, + contextMessages: [message('/home/test/lib/test.dart', 48, 4)]), ]); } @@ -350,7 +358,8 @@ class B {} class G {} f(G g) {} ''', [ - error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 50, 1), + error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 50, 1, + contextMessages: [message('/home/test/lib/test.dart', 48, 4)]), ]); } @@ -376,7 +385,8 @@ class C {} class D {} C> c = (throw 0); ''', [ - error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 64, 1), + error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 64, 1, + contextMessages: [message('/home/test/lib/test.dart', 62, 4)]), ]); } @@ -388,7 +398,8 @@ class C {} class G {} class D> {} ''', [ - error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 77, 1), + error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 77, 1, + contextMessages: [message('/home/test/lib/test.dart', 75, 4)]), ]); } @@ -399,7 +410,8 @@ class B {} class G {} G g = (throw 0); ''', [ - error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 48, 1), + error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 48, 1, + contextMessages: [message('/home/test/lib/test.dart', 46, 4)]), ]); } @@ -487,7 +499,8 @@ typedef FB = S Function(S); class CB {} void f(CB> a) {} ''', [ - error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 119, 5), + error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 119, 5, + contextMessages: [message('/home/test/lib/test.dart', 116, 9)]), ]); } @@ -613,7 +626,8 @@ typedef X = A; class A> {} typedef X = A; ''', [ - error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 42, 1), + error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 42, 1, + contextMessages: [message('/home/test/lib/test.dart', 42, 1)]), ]); } @@ -623,7 +637,8 @@ class A {} typedef X = Map; void f(X a) {} ''', [ - error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 58, 6), + error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 58, 6, + contextMessages: [message('/home/test/lib/test.dart', 56, 9)]), ]); } @@ -642,7 +657,11 @@ typedef A = X Function(X); typedef G> = void Function(); foo(G g) {} ''', [ - error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 92, 1), + error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 92, 1, + contextMessages: [ + message('/home/test/lib/test.dart', 92, 1), + message('/home/test/lib/test.dart', 92, 1) + ]), ]); } @@ -698,7 +717,8 @@ A get foo => throw 0; class C {} var t = C; ''', [ - error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 36, 6), + error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 36, 6, + contextMessages: [message('/home/test/lib/test.dart', 34, 9)]), ]); } @@ -707,7 +727,8 @@ var t = C; typedef Cb = void Function(); var t = Cb; ''', [ - error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 56, 6), + error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 56, 6, + contextMessages: [message('/home/test/lib/test.dart', 53, 10)]), ]); } @@ -717,7 +738,8 @@ class C {} typedef D = C; var t = D; ''', [ - error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 51, 6), + error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 51, 6, + contextMessages: [message('/home/test/lib/test.dart', 49, 9)]), ]); } }