Generate more mixin errors.
R=brianwilkerson@google.com
Change-Id: Ia858efb270caa5664728395d47b4e31e34a0ed91
Reviewed-on: https://dart-review.googlesource.com/74667
Reviewed-by: Brian Wilkerson <brianwilkerson@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 d0000d9..e403d5b 100644
--- a/pkg/analyzer/lib/error/error.dart
+++ b/pkg/analyzer/lib/error/error.dart
@@ -216,6 +216,7 @@
CompileTimeErrorCode.NO_DEFAULT_SUPER_CONSTRUCTOR_EXPLICIT,
CompileTimeErrorCode.NO_DEFAULT_SUPER_CONSTRUCTOR_IMPLICIT,
CompileTimeErrorCode.OBJECT_CANNOT_EXTEND_ANOTHER_CLASS,
+ CompileTimeErrorCode.ON_REPEATED,
CompileTimeErrorCode.OPTIONAL_PARAMETER_IN_OPERATOR,
CompileTimeErrorCode.PART_OF_NON_PART,
CompileTimeErrorCode.PREFIX_COLLIDES_WITH_TOP_LEVEL_MEMBER,
diff --git a/pkg/analyzer/lib/src/error/codes.dart b/pkg/analyzer/lib/src/error/codes.dart
index 8f136c6..0a15bc0 100644
--- a/pkg/analyzer/lib/src/error/codes.dart
+++ b/pkg/analyzer/lib/src/error/codes.dart
@@ -408,6 +408,17 @@
correction: "Try converting the getter to a method, or "
"renaming the field to a name that doesn't conflit.");
+ /**
+ * 10.10 Superinterfaces: It is a compile-time error if a class `C` has two
+ * superinterfaces that are different instantiations of the same generic
+ * class. For example, a class may not have both `List<int>` and `List<num>`
+ * as superinterfaces.
+ *
+ * Parameters:
+ * 0: the name of the class implementing the conflicting interface
+ * 1: the first conflicting type
+ * 1: the second conflicting type
+ */
static const CompileTimeErrorCode CONFLICTING_GENERIC_INTERFACES =
const CompileTimeErrorCode(
'CONFLICTING_GENERIC_INTERFACES',
@@ -1174,11 +1185,12 @@
"Try specifying a class, or remove the name from the list.");
/**
- * 7.10 Superinterfaces: It is a compile-time error if a type <i>T</i> appears
- * more than once in the implements clause of a class.
+ * 10.10 Superinterfaces: It is a compile-time error if two elements in the
+ * type list of the implements clause of a class `C` specifies the same
+ * type `T`.
*
* Parameters:
- * 0: the name of the class that is implemented more than once
+ * 0: the name of the interface that is implemented more than once
*/
static const CompileTimeErrorCode IMPLEMENTS_REPEATED =
const CompileTimeErrorCode(
@@ -2105,6 +2117,19 @@
"The class 'Object' can't extend any other class.");
/**
+ * 10.10 Superinterfaces: It is a compile-time error if two elements in the
+ * type list of the implements clause of a class `C` specifies the same
+ * type `T`.
+ *
+ * Parameters:
+ * 0: the name of the interface that is implemented more than once
+ */
+ static const CompileTimeErrorCode ON_REPEATED = const CompileTimeErrorCode(
+ 'ON_REPEATED',
+ "'{0}' can only be used in super-class constraints only once.",
+ correction: "Try removing all but one occurance of the class name.");
+
+ /**
* 7.1.1 Operators: It is a compile-time error to declare an optional
* parameter in an operator.
*/
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index 8d11163..2bfb759 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -1047,8 +1047,6 @@
_initializeInitialFieldElementsMap(_enclosingClass.fields);
_checkForFinalNotInitializedInClass(members);
-// _checkForDuplicateDefinitionInheritance();
-// _checkForConflictingInstanceMethodSetter(node);
// _checkForBadFunctionUse(node);
return super.visitMixinDeclaration(node);
} finally {
@@ -1353,6 +1351,8 @@
_checkForInconsistentMethodInheritance();
_checkForRecursiveInterfaceInheritance(_enclosingClass);
_checkForConflictingClassMembers();
+ _checkForRepeatedType(implementsClause?.interfaces,
+ CompileTimeErrorCode.IMPLEMENTS_REPEATED);
_checkImplementsSuperClass(implementsClause);
_checkForMixinHasNoConstructors(node);
_checkMixinInference(node, withClause);
@@ -2553,6 +2553,7 @@
}
visit(type.superclass);
type.mixins.forEach(visit);
+ type.superclassConstraints.forEach(visit);
type.interfaces.forEach(visit);
visitedClasses.removeLast();
}
@@ -5098,6 +5099,31 @@
}
}
+ void _checkForRepeatedType(List<TypeName> typeNames, ErrorCode errorCode) {
+ if (typeNames == null) {
+ return;
+ }
+
+ int count = typeNames.length;
+ List<bool> detectedRepeatOnIndex = new List<bool>.filled(count, false);
+ for (int i = 0; i < detectedRepeatOnIndex.length; i++) {
+ detectedRepeatOnIndex[i] = false;
+ }
+ for (int i = 0; i < count; i++) {
+ if (!detectedRepeatOnIndex[i]) {
+ Element element = typeNames[i].name.staticElement;
+ for (int j = i + 1; j < count; j++) {
+ TypeName typeName = typeNames[j];
+ if (typeName.name.staticElement == element) {
+ detectedRepeatOnIndex[j] = true;
+ _errorReporter
+ .reportErrorForNode(errorCode, typeName, [typeName.name.name]);
+ }
+ }
+ }
+ }
+ }
+
/**
* Check that the given rethrow [expression] is inside of a catch clause.
*
@@ -5821,23 +5847,25 @@
*/
void _checkMixinInheritance(MixinDeclaration node, OnClause onClause,
ImplementsClause implementsClause) {
- // TODO(scheglov) Verify for all mixin errors.
// Only check for all of the inheritance logic around clauses if there
// isn't an error code such as "Cannot implement double" already.
if (!_checkForOnClauseErrorCodes(onClause) &&
!_checkForImplementsClauseErrorCodes(implementsClause)) {
// _checkForImplicitDynamicType(superclass);
-// _checkForNonAbstractClassInheritsAbstractMember(node.name);
_checkForInconsistentMethodInheritance();
_checkForRecursiveInterfaceInheritance(_enclosingClass);
_checkForConflictingClassMembers();
-// _checkImplementsSuperClass(implementsClause);
-// _checkForMixinHasNoConstructors(node);
-// _checkMixinInference(node, onClause);
-// _checkForMixinWithConflictingPrivateMember(onClause, superclass);
-// if (!disableConflictingGenericsCheck) {
-// _checkForConflictingGenerics(node);
-// }
+ _checkForRepeatedType(
+ onClause?.superclassConstraints,
+ CompileTimeErrorCode.ON_REPEATED,
+ );
+ _checkForRepeatedType(
+ implementsClause?.interfaces,
+ CompileTimeErrorCode.IMPLEMENTS_REPEATED,
+ );
+ if (!disableConflictingGenericsCheck) {
+ _checkForConflictingGenerics(node);
+ }
}
}
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index a082af4..7d74d96 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -10287,29 +10287,6 @@
if (classElement != null) {
classElement.interfaces = interfaceTypes;
}
- // TODO(brianwilkerson) Move the following checks to ErrorVerifier.
- int count = interfaces.length;
- List<bool> detectedRepeatOnIndex = new List<bool>.filled(count, false);
- for (int i = 0; i < detectedRepeatOnIndex.length; i++) {
- detectedRepeatOnIndex[i] = false;
- }
- for (int i = 0; i < count; i++) {
- TypeName typeName = interfaces[i];
- if (!detectedRepeatOnIndex[i]) {
- Element element = typeName.name.staticElement;
- for (int j = i + 1; j < count; j++) {
- TypeName typeName2 = interfaces[j];
- Identifier identifier2 = typeName2.name;
- String name2 = identifier2.name;
- Element element2 = identifier2.staticElement;
- if (element != null && element == element2) {
- detectedRepeatOnIndex[j] = true;
- errorReporter.reportErrorForNode(
- CompileTimeErrorCode.IMPLEMENTS_REPEATED, typeName2, [name2]);
- }
- }
- }
- }
}
}
diff --git a/pkg/analyzer/test/generated/compile_time_error_code_driver_test.dart b/pkg/analyzer/test/generated/compile_time_error_code_driver_test.dart
index fdda0cd..4e73768 100644
--- a/pkg/analyzer/test/generated/compile_time_error_code_driver_test.dart
+++ b/pkg/analyzer/test/generated/compile_time_error_code_driver_test.dart
@@ -18,31 +18,6 @@
bool get enableNewAnalysisDriver => true;
@override // Passes with driver
- test_conflictingGenericInterfaces_simple() =>
- super.test_conflictingGenericInterfaces_simple();
-
- @override // Passes with driver
- test_conflictingGenericInterfaces_viaMixin() =>
- super.test_conflictingGenericInterfaces_viaMixin();
-
- @override // Passes with driver
- test_mixinInference_conflictingSubstitution() =>
- super.test_mixinInference_conflictingSubstitution();
-
- @override // Passes with driver
- test_mixinInference_doNotIgnorePreviousExplicitMixins() =>
- super.test_mixinInference_doNotIgnorePreviousExplicitMixins();
-
- @override // Passes with driver
- test_mixinInference_impossibleSubstitution() =>
- super.test_mixinInference_impossibleSubstitution();
-
- @override // Passes with driver
- test_mixinInference_noMatchingClass_constraintSatisfiedByImplementsClause() =>
- super
- .test_mixinInference_noMatchingClass_constraintSatisfiedByImplementsClause();
-
- @override // Passes with driver
test_mixinInference_recursiveSubtypeCheck() =>
super.test_mixinInference_recursiveSubtypeCheck();
}
diff --git a/pkg/analyzer/test/generated/compile_time_error_code_test.dart b/pkg/analyzer/test/generated/compile_time_error_code_test.dart
index 3ac833f..1955f4a 100644
--- a/pkg/analyzer/test/generated/compile_time_error_code_test.dart
+++ b/pkg/analyzer/test/generated/compile_time_error_code_test.dart
@@ -730,30 +730,6 @@
assertNoErrors(source);
}
- @failingTest // Does not work with old task model
- test_conflictingGenericInterfaces_simple() async {
- Source source = addSource('''
-class I<T> {}
-class A implements I<int> {}
-class B implements I<String> {}
-class C extends A implements B {}
-''');
- await computeAnalysisResult(source);
- assertErrors(source, [CompileTimeErrorCode.CONFLICTING_GENERIC_INTERFACES]);
- }
-
- @failingTest // Does not work with old task model
- test_conflictingGenericInterfaces_viaMixin() async {
- Source source = addSource('''
-class I<T> {}
-class A implements I<int> {}
-class B implements I<String> {}
-class C extends A with B {}
-''');
- await computeAnalysisResult(source);
- assertErrors(source, [CompileTimeErrorCode.CONFLICTING_GENERIC_INTERFACES]);
- }
-
test_conflictingTypeVariableAndClass() async {
Source source = addSource(r'''
class T<T> {
@@ -2769,28 +2745,6 @@
verify([source]);
}
- test_implementsRepeated() async {
- Source source = addSource(r'''
-class A {}
-class B implements A, A {}''');
- await computeAnalysisResult(source);
- assertErrors(source, [CompileTimeErrorCode.IMPLEMENTS_REPEATED]);
- verify([source]);
- }
-
- test_implementsRepeated_3times() async {
- Source source = addSource(r'''
-class A {} class C{}
-class B implements A, A, A, A {}''');
- await computeAnalysisResult(source);
- assertErrors(source, [
- CompileTimeErrorCode.IMPLEMENTS_REPEATED,
- CompileTimeErrorCode.IMPLEMENTS_REPEATED,
- CompileTimeErrorCode.IMPLEMENTS_REPEATED
- ]);
- verify([source]);
- }
-
test_implementsSuperClass() async {
Source source = addSource(r'''
class A {}
@@ -3949,56 +3903,6 @@
verify([source]);
}
- @failingTest // Does not work with old task model
- test_mixinInference_conflictingSubstitution() async {
- AnalysisOptionsImpl options = new AnalysisOptionsImpl();
- options.enableSuperMixins = true;
- resetWith(options: options);
- Source source = addSource('''
-abstract class A<T> {}
-class M<T> extends A<Map<T, T>> {}
-class C extends A<Map<int, String>> with M {}
-''');
- await computeAnalysisResult(source);
- assertErrors(source, [
- CompileTimeErrorCode.MIXIN_INFERENCE_NO_POSSIBLE_SUBSTITUTION,
- CompileTimeErrorCode.CONFLICTING_GENERIC_INTERFACES
- ]);
- }
-
- @failingTest // Does not work with old task model
- test_mixinInference_doNotIgnorePreviousExplicitMixins() async {
- AnalysisOptionsImpl options = new AnalysisOptionsImpl();
- options.enableSuperMixins = true;
- resetWith(options: options);
- Source source = addSource('''
-class A extends Object with B<String>, C {}
-class B<T> {}
-class C<T> extends B<T> {}
-''');
- var analysisResult = await computeAnalysisResult(source);
- assertNoErrors(source);
- var mixins = analysisResult.unit.declaredElement.getType('A').mixins;
- expect(mixins[1].toString(), 'C<String>');
- }
-
- @failingTest // Does not work with old task model
- test_mixinInference_impossibleSubstitution() async {
- AnalysisOptionsImpl options = new AnalysisOptionsImpl();
- options.enableSuperMixins = true;
- resetWith(options: options);
- Source source = addSource('''
-abstract class A<T> {}
-class M<T> extends A<Map<T, T>> {}
-class C extends A<List<int>> with M {}
-''');
- await computeAnalysisResult(source);
- assertErrors(source, [
- CompileTimeErrorCode.MIXIN_INFERENCE_NO_POSSIBLE_SUBSTITUTION,
- CompileTimeErrorCode.CONFLICTING_GENERIC_INTERFACES
- ]);
- }
-
test_mixinInference_matchingClass() async {
AnalysisOptionsImpl options = new AnalysisOptionsImpl();
options.enableSuperMixins = true;
@@ -4043,24 +3947,6 @@
source, [CompileTimeErrorCode.MIXIN_INFERENCE_NO_MATCHING_CLASS]);
}
- @failingTest // Does not work with old task model
- test_mixinInference_noMatchingClass_constraintSatisfiedByImplementsClause() async {
- AnalysisOptionsImpl options = new AnalysisOptionsImpl();
- options.enableSuperMixins = true;
- resetWith(options: options);
- Source source = addSource('''
-abstract class A<T> {}
-class B {}
-class M<T> extends A<T> {}
-class C extends Object with M implements A<B> {}
-''');
- await computeAnalysisResult(source);
- assertErrors(source, [
- CompileTimeErrorCode.MIXIN_INFERENCE_NO_MATCHING_CLASS,
- CompileTimeErrorCode.CONFLICTING_GENERIC_INTERFACES
- ]);
- }
-
test_mixinInference_noMatchingClass_namedMixinApplication() async {
AnalysisOptionsImpl options = new AnalysisOptionsImpl();
options.enableSuperMixins = true;
diff --git a/pkg/analyzer/test/src/dart/resolution/class_test.dart b/pkg/analyzer/test/src/dart/resolution/class_test.dart
index f3496cc..533ae56 100644
--- a/pkg/analyzer/test/src/dart/resolution/class_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/class_test.dart
@@ -251,6 +251,28 @@
assertElement(findNode.simple('foo = 0;'), findElement.setter('foo'));
}
+ test_conflictingGenericInterfaces_simple() async {
+ addTestFile('''
+class I<T> {}
+class A implements I<int> {}
+class B implements I<String> {}
+class C extends A implements B {}
+''');
+ await resolveTestFile();
+ assertTestErrors([CompileTimeErrorCode.CONFLICTING_GENERIC_INTERFACES]);
+ }
+
+ test_conflictingGenericInterfaces_viaMixin() async {
+ addTestFile('''
+class I<T> {}
+class A implements I<int> {}
+class B implements I<String> {}
+class C extends A with B {}
+''');
+ await resolveTestFile();
+ assertTestErrors([CompileTimeErrorCode.CONFLICTING_GENERIC_INTERFACES]);
+ }
+
test_error_conflictingConstructorAndStaticField_field() async {
addTestFile(r'''
class C {
@@ -1081,6 +1103,31 @@
assertElementType(a.supertype, objectType);
}
+ test_error_implementsRepeated() async {
+ addTestFile(r'''
+class A {}
+class B implements A, A {} // ref
+''');
+ await resolveTestFile();
+ assertTestErrors([CompileTimeErrorCode.IMPLEMENTS_REPEATED]);
+
+ var a = findElement.class_('A');
+ assertTypeName(findNode.typeName('A, A {} // ref'), a, 'A');
+ assertTypeName(findNode.typeName('A {} // ref'), a, 'A');
+ }
+
+ test_error_implementsRepeated_3times() async {
+ addTestFile(r'''
+class A {} class C{}
+class B implements A, A, A, A {}''');
+ await resolveTestFile();
+ assertTestErrors([
+ CompileTimeErrorCode.IMPLEMENTS_REPEATED,
+ CompileTimeErrorCode.IMPLEMENTS_REPEATED,
+ CompileTimeErrorCode.IMPLEMENTS_REPEATED
+ ]);
+ }
+
test_error_memberWithClassName_getter() async {
addTestFile(r'''
class C {
@@ -1210,6 +1257,62 @@
]);
}
+ test_mixinInference_conflictingSubstitution() async {
+ setAnalysisOptions(enableSuperMixins: true);
+ addTestFile('''
+abstract class A<T> {}
+class M<T> extends A<Map<T, T>> {}
+class C extends A<Map<int, String>> with M {}
+''');
+ await resolveTestFile();
+ assertTestErrors([
+ CompileTimeErrorCode.MIXIN_INFERENCE_NO_POSSIBLE_SUBSTITUTION,
+ CompileTimeErrorCode.CONFLICTING_GENERIC_INTERFACES
+ ]);
+ }
+
+ test_mixinInference_doNotIgnorePreviousExplicitMixins() async {
+ setAnalysisOptions(enableSuperMixins: true);
+ addTestFile('''
+class A extends Object with B<String>, C {}
+class B<T> {}
+class C<T> extends B<T> {}
+''');
+ await resolveTestFile();
+ assertNoTestErrors();
+ var mixins = result.unit.declaredElement.getType('A').mixins;
+ expect(mixins[1].toString(), 'C<String>');
+ }
+
+ test_mixinInference_impossibleSubstitution() async {
+ setAnalysisOptions(enableSuperMixins: true);
+ addTestFile('''
+abstract class A<T> {}
+class M<T> extends A<Map<T, T>> {}
+class C extends A<List<int>> with M {}
+''');
+ await resolveTestFile();
+ assertTestErrors([
+ CompileTimeErrorCode.MIXIN_INFERENCE_NO_POSSIBLE_SUBSTITUTION,
+ CompileTimeErrorCode.CONFLICTING_GENERIC_INTERFACES
+ ]);
+ }
+
+ test_mixinInference_noMatchingClass_constraintSatisfiedByImplementsClause() async {
+ setAnalysisOptions(enableSuperMixins: true);
+ addTestFile('''
+abstract class A<T> {}
+class B {}
+class M<T> extends A<T> {}
+class C extends Object with M implements A<B> {}
+''');
+ await resolveTestFile();
+ assertTestErrors([
+ CompileTimeErrorCode.MIXIN_INFERENCE_NO_MATCHING_CLASS,
+ CompileTimeErrorCode.CONFLICTING_GENERIC_INTERFACES,
+ ]);
+ }
+
test_recursiveInterfaceInheritance_extends() async {
addTestFile(r'''
class A extends B {}
@@ -1438,4 +1541,35 @@
@reflectiveTest
class ClassTaskResolutionTest extends TaskResolutionTest
- with ClassResolutionMixin {}
+ with ClassResolutionMixin {
+ @failingTest
+ test_conflictingGenericInterfaces_simple() {
+ return super.test_conflictingGenericInterfaces_simple();
+ }
+
+ @failingTest
+ test_conflictingGenericInterfaces_viaMixin() {
+ return super.test_conflictingGenericInterfaces_viaMixin();
+ }
+
+ @failingTest
+ test_mixinInference_conflictingSubstitution() {
+ return super.test_mixinInference_conflictingSubstitution();
+ }
+
+ @failingTest
+ test_mixinInference_doNotIgnorePreviousExplicitMixins() {
+ return super.test_mixinInference_doNotIgnorePreviousExplicitMixins();
+ }
+
+ @failingTest
+ test_mixinInference_impossibleSubstitution() {
+ return super.test_mixinInference_impossibleSubstitution();
+ }
+
+ @failingTest
+ test_mixinInference_noMatchingClass_constraintSatisfiedByImplementsClause() {
+ return super
+ .test_mixinInference_noMatchingClass_constraintSatisfiedByImplementsClause();
+ }
+}
diff --git a/pkg/analyzer/test/src/dart/resolution/mixin_test.dart b/pkg/analyzer/test/src/dart/resolution/mixin_test.dart
index 180ea83..92750a6 100644
--- a/pkg/analyzer/test/src/dart/resolution/mixin_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/mixin_test.dart
@@ -139,6 +139,17 @@
assertTypeNull(aRef);
}
+ test_conflictingGenericInterfaces() async {
+ addTestFile('''
+class I<T> {}
+class A implements I<int> {}
+class B implements I<String> {}
+mixin M on A implements B {}
+''');
+ await resolveTestFile();
+ assertTestErrors([CompileTimeErrorCode.CONFLICTING_GENERIC_INTERFACES]);
+ }
+
test_element() async {
addTestFile(r'''
mixin M {}
@@ -743,6 +754,16 @@
assertTypeName(typeRef, null, 'void');
}
+ test_error_implementsRepeated() async {
+ addTestFile(r'''
+class A {}
+mixin M implements A, A {}
+''');
+ await resolveTestFile();
+ CompileTimeErrorCode.IMPLEMENTS_REPEATED;
+ assertTestErrors([CompileTimeErrorCode.IMPLEMENTS_REPEATED]);
+ }
+
test_error_memberWithClassName_getter() async {
addTestFile(r'''
mixin M {
@@ -1108,6 +1129,16 @@
assertElementTypes(b.superclassConstraints, [a.type]);
}
+ test_error_onRepeated() async {
+ addTestFile(r'''
+class A {}
+mixin M on A, A {}
+''');
+ await resolveTestFile();
+ CompileTimeErrorCode.IMPLEMENTS_REPEATED;
+ assertTestErrors([CompileTimeErrorCode.ON_REPEATED]);
+ }
+
test_error_undefinedSuperMethod() async {
addTestFile(r'''
class A {}
@@ -1497,4 +1528,9 @@
@reflectiveTest
class MixinTaskResolutionTest extends TaskResolutionTest
- with MixinResolutionMixin {}
+ with MixinResolutionMixin {
+ @failingTest
+ test_conflictingGenericInterfaces() {
+ return super.test_conflictingGenericInterfaces();
+ }
+}