Skip to content

Commit aa8ccf5

Browse files
kallentuCommit Queue
authored and
Commit Queue
committed
[stable] Add errors for base transitivity and base/final implements of a core lib declaration through a 2.19 library and a 3.0 library.
Cherry pick for the following behaviour from 3 different CLs. Error produced when a post-feature library implements a pre-feature library declaration that has a final/base core library class as a super declaration. Error produced when a post-feature library subtypes (but not implements) a pre-feature library declaration that has a final/base core library class as a super declaration. Fixes: #52406 Bug: #52315, #52115, #52078 Cherry-pick: https://dart-review.googlesource.com/c/sdk/+/302365 Cherry-pick: https://dart-review.googlesource.com/c/sdk/+/301503 Cherry-pick: https://dart-review.googlesource.com/c/sdk/+/300861 Cherry-pick: https://dart-review.googlesource.com/c/sdk/+/301442 Cherry-pick: https://dart-review.googlesource.com/c/sdk/+/300880 Change-Id: I2a192a157d417c1de7656aa624d3b10d9e67626e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/302851 Commit-Queue: Kallen Tu <[email protected]> Reviewed-by: Konstantin Shcheglov <[email protected]> Reviewed-by: Johnni Winther <[email protected]>
1 parent 4305d55 commit aa8ccf5

File tree

38 files changed

+1207
-131
lines changed

38 files changed

+1207
-131
lines changed

pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -367,34 +367,6 @@ Message _withArgumentsBaseClassImplementedOutsideOfLibrary(String name) {
367367
arguments: {'name': name});
368368
}
369369

370-
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
371-
const Template<Message Function(String name, String name2)>
372-
templateBaseClassImplementedOutsideOfLibraryCause =
373-
const Template<Message Function(String name, String name2)>(
374-
problemMessageTemplate:
375-
r"""The type '#name' is a subtype of '#name2', and '#name2' is defined here.""",
376-
withArguments: _withArgumentsBaseClassImplementedOutsideOfLibraryCause);
377-
378-
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
379-
const Code<Message Function(String name, String name2)>
380-
codeBaseClassImplementedOutsideOfLibraryCause =
381-
const Code<Message Function(String name, String name2)>(
382-
"BaseClassImplementedOutsideOfLibraryCause",
383-
severity: Severity.context);
384-
385-
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
386-
Message _withArgumentsBaseClassImplementedOutsideOfLibraryCause(
387-
String name, String name2) {
388-
if (name.isEmpty) throw 'No name provided';
389-
name = demangleMixinApplicationName(name);
390-
if (name2.isEmpty) throw 'No name provided';
391-
name2 = demangleMixinApplicationName(name2);
392-
return new Message(codeBaseClassImplementedOutsideOfLibraryCause,
393-
problemMessage:
394-
"""The type '${name}' is a subtype of '${name2}', and '${name2}' is defined here.""",
395-
arguments: {'name': name, 'name2': name2});
396-
}
397-
398370
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
399371
const Template<
400372
Message Function(
@@ -422,6 +394,35 @@ Message _withArgumentsBaseMixinImplementedOutsideOfLibrary(String name) {
422394
arguments: {'name': name});
423395
}
424396

397+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
398+
const Template<Message Function(String name, String name2)>
399+
templateBaseOrFinalClassImplementedOutsideOfLibraryCause =
400+
const Template<Message Function(String name, String name2)>(
401+
problemMessageTemplate:
402+
r"""The type '#name' is a subtype of '#name2', and '#name2' is defined here.""",
403+
withArguments:
404+
_withArgumentsBaseOrFinalClassImplementedOutsideOfLibraryCause);
405+
406+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
407+
const Code<Message Function(String name, String name2)>
408+
codeBaseOrFinalClassImplementedOutsideOfLibraryCause =
409+
const Code<Message Function(String name, String name2)>(
410+
"BaseOrFinalClassImplementedOutsideOfLibraryCause",
411+
severity: Severity.context);
412+
413+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
414+
Message _withArgumentsBaseOrFinalClassImplementedOutsideOfLibraryCause(
415+
String name, String name2) {
416+
if (name.isEmpty) throw 'No name provided';
417+
name = demangleMixinApplicationName(name);
418+
if (name2.isEmpty) throw 'No name provided';
419+
name2 = demangleMixinApplicationName(name2);
420+
return new Message(codeBaseOrFinalClassImplementedOutsideOfLibraryCause,
421+
problemMessage:
422+
"""The type '${name}' is a subtype of '${name2}', and '${name2}' is defined here.""",
423+
arguments: {'name': name, 'name2': name2});
424+
}
425+
425426
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
426427
const Template<
427428
Message Function(

pkg/_fe_analyzer_shared/test/inheritance/data/function.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ class B
1616
/*cfe|cfe:builder.class: C:C,Object,_C&Object&Function*/
1717
/*analyzer.class: C:C,Object*/
1818
/*cfe|cfe:builder.class: _C&Object&Function:Object,_C&Object&Function*/
19-
class C extends Object
19+
class /*cfe|cfe:builder.error: SubtypeOfFinalIsNotBaseFinalOrSealed*/ C
20+
extends Object
2021
with /*analyzer.error: CompileTimeErrorCode.CLASS_USED_AS_MIXIN*/
2122
/*cfe|cfe:builder.error: CantUseClassAsMixin*/ Function {}
2223

pkg/analyzer/lib/src/error/base_or_final_type_verifier.dart

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,14 @@ class BaseOrFinalTypeVerifier {
162162
ClassOrMixinElementImpl element, ClassOrMixinElementImpl superElement,
163163
{NamedType? implementsNamedType}) {
164164
ClassOrMixinElementImpl? baseOrFinalSuperElement;
165-
if (superElement.isBase || superElement.isFinal) {
165+
if (superElement.isBase ||
166+
superElement.isFinal ||
167+
(!superElement.library.featureSet.isEnabled(Feature.class_modifiers) &&
168+
element.library.featureSet.isEnabled(Feature.class_modifiers))) {
166169
// The 'base' or 'final' modifier may be an induced modifier. Find the
167170
// explicitly declared 'base' or 'final' in the hierarchy.
171+
// In the case where the super element is in a pre-feature library, we
172+
// need to check if there's an indirect core library super element.
168173
baseOrFinalSuperElement = _getExplicitlyBaseOrFinalElement(superElement);
169174
} else {
170175
// There are no restrictions on this element's modifiers.
@@ -213,11 +218,24 @@ class BaseOrFinalTypeVerifier {
213218

214219
if (!element.isBase && !element.isFinal && !element.isSealed) {
215220
if (baseOrFinalSuperElement.isFinal) {
221+
// If you can't extend, implement or mix in a final element outside of
222+
// its library anyways, it's not helpful to report a subelement
223+
// modifier error.
216224
if (baseOrFinalSuperElement.library != element.library) {
217-
// If you can't extend, implement or mix in a final element outside of
218-
// its library anyways, it's not helpful to report a subelement
219-
// modifier error.
220-
return false;
225+
// In the case where the 'baseOrFinalSuperElement' is a core
226+
// library element and we are subtyping from a super element that's
227+
// from a pre-feature library, we want to produce a final
228+
// transitivity error.
229+
//
230+
// For implements clauses with the above scenario, we avoid
231+
// over-reporting since there will already be a
232+
// [FinalClassImplementedOutsideOfLibrary] error.
233+
if (superElement.library.featureSet
234+
.isEnabled(Feature.class_modifiers) ||
235+
!baseOrFinalSuperElement.library.isInSdk ||
236+
implementsNamedType != null) {
237+
return false;
238+
}
221239
}
222240
_errorReporter.reportErrorForElement(
223241
CompileTimeErrorCode.SUBTYPE_OF_FINAL_IS_NOT_BASE_FINAL_OR_SEALED,

pkg/analyzer/lib/src/generated/error_verifier.dart

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2899,16 +2899,32 @@ class ErrorVerifier extends RecursiveAstVisitor<void>
28992899
for (NamedType namedType in implementsClause.interfaces) {
29002900
final type = namedType.type;
29012901
if (type is InterfaceType) {
2902-
final element = type.element;
2903-
if (element is ClassElement &&
2904-
element.isFinal &&
2905-
!element.isSealed &&
2906-
element.library != _currentLibrary &&
2907-
!_mayIgnoreClassModifiers(element.library)) {
2908-
errorReporter.reportErrorForNode(
2909-
CompileTimeErrorCode.FINAL_CLASS_IMPLEMENTED_OUTSIDE_OF_LIBRARY,
2910-
namedType,
2911-
[element.name]);
2902+
final implementedInterfaces = [
2903+
type,
2904+
...type.element.allSupertypes,
2905+
].map((e) => e.element).toList();
2906+
for (final element in implementedInterfaces) {
2907+
if (element is ClassElement &&
2908+
element.isFinal &&
2909+
!element.isSealed &&
2910+
element.library != _currentLibrary &&
2911+
!_mayIgnoreClassModifiers(element.library)) {
2912+
// If the final interface is an indirect interface and is in a
2913+
// different library that has class modifiers enabled, there is a
2914+
// nearer declaration that would emit an error, if any.
2915+
if (element != type.element &&
2916+
type.element.library.featureSet
2917+
.isEnabled(Feature.class_modifiers)) {
2918+
continue;
2919+
}
2920+
2921+
errorReporter.reportErrorForNode(
2922+
CompileTimeErrorCode
2923+
.FINAL_CLASS_IMPLEMENTED_OUTSIDE_OF_LIBRARY,
2924+
namedType,
2925+
[element.name]);
2926+
break;
2927+
}
29122928
}
29132929
}
29142930
}

pkg/analyzer/lib/src/test_utilities/mock_sdk.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,8 @@ abstract class LinkedHashSet<E> implements Set<E> {
218218
}
219219
}
220220
221+
abstract base mixin class LinkedListEntry<E extends LinkedListEntry<E>> { }
222+
221223
abstract mixin class ListMixin<E> implements List<E> { }
222224
223225
abstract mixin class MapMixin<K, V> implements Map<K, V> { }
@@ -539,7 +541,7 @@ final class Null extends Object {
539541
}
540542
}
541543
542-
class MapEntry<K, V> {
544+
final class MapEntry<K, V> {
543545
final K key;
544546
final V value;
545547
const factory MapEntry(K key, V value) = MapEntry<K, V>._;

pkg/analyzer/test/src/diagnostics/final_class_extended_outside_of_library_test.dart

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,30 @@ final class Bar extends Foo {}
3636
]);
3737
}
3838

39+
test_outside_viaLanguage219AndCore() async {
40+
// There is no error when extending a pre-feature class that subtypes a
41+
// class in the core libraries.
42+
final a = newFile('$testPackageLibPath/a.dart', r'''
43+
// @dart=2.19
44+
import 'dart:core';
45+
class A implements MapEntry<int, int> {
46+
int get key => 0;
47+
int get value => 1;
48+
}
49+
''');
50+
51+
await resolveFile2(a.path);
52+
assertNoErrorsInResult();
53+
54+
await assertNoErrorsInCode(r'''
55+
import 'a.dart';
56+
final class B extends A {
57+
int get key => 0;
58+
int get value => 1;
59+
}
60+
''');
61+
}
62+
3963
test_outside_viaTypedef_inside() async {
4064
newFile('$testPackageLibPath/foo.dart', r'''
4165
final class Foo {}

pkg/analyzer/test/src/diagnostics/final_class_implemented_outside_of_library_test.dart

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,31 @@ final class Bar implements Foo {}
3737
]);
3838
}
3939

40+
test_class_outside_viaLanguage219AndCore() async {
41+
final a = newFile('$testPackageLibPath/a.dart', r'''
42+
// @dart=2.19
43+
import 'dart:core';
44+
class A implements MapEntry<int, int> {
45+
int get key => 0;
46+
int get value => 1;
47+
}
48+
''');
49+
50+
await resolveFile2(a.path);
51+
assertNoErrorsInResult();
52+
53+
await assertErrorsInCode(r'''
54+
import 'a.dart';
55+
final class B implements A {
56+
int get key => 0;
57+
int get value => 1;
58+
}
59+
''', [
60+
error(CompileTimeErrorCode.FINAL_CLASS_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 42,
61+
1),
62+
]);
63+
}
64+
4065
test_class_outside_viaTypedef_inside() async {
4166
newFile('$testPackageLibPath/foo.dart', r'''
4267
final class Foo {}

pkg/analyzer/test/src/diagnostics/subtype_of_base_is_not_base_final_or_sealed_test.dart

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,27 @@ class B extends A {}
5757
]);
5858
}
5959

60+
test_class_extends_outside_viaLanguage219AndCore() async {
61+
final a = newFile('$testPackageLibPath/a.dart', r'''
62+
// @dart=2.19
63+
import 'dart:collection';
64+
abstract class A implements LinkedListEntry<Never> {}
65+
''');
66+
67+
await resolveFile2(a.path);
68+
assertNoErrorsInResult();
69+
70+
await assertErrorsInCode(r'''
71+
import 'a.dart';
72+
abstract class B extends A {}
73+
''', [
74+
error(CompileTimeErrorCode.SUBTYPE_OF_BASE_IS_NOT_BASE_FINAL_OR_SEALED,
75+
32, 1,
76+
text:
77+
"The type 'B' must be 'base', 'final' or 'sealed' because the supertype 'LinkedListEntry' is 'base'."),
78+
]);
79+
}
80+
6081
test_class_implements() async {
6182
await assertErrorsInCode(r'''
6283
base class A {}
@@ -81,6 +102,47 @@ mixin B implements A {}
81102
]);
82103
}
83104

105+
test_class_implements_outside() async {
106+
newFile('$testPackageLibPath/a.dart', r'''
107+
base class A {}
108+
''');
109+
110+
await assertErrorsInCode(r'''
111+
import 'a.dart';
112+
class B implements A {}
113+
''', [
114+
error(CompileTimeErrorCode.SUBTYPE_OF_BASE_IS_NOT_BASE_FINAL_OR_SEALED,
115+
23, 1,
116+
text:
117+
"The type 'B' must be 'base', 'final' or 'sealed' because the supertype 'A' is 'base'."),
118+
error(CompileTimeErrorCode.BASE_CLASS_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 36,
119+
1),
120+
]);
121+
}
122+
123+
test_class_implements_outside_viaLanguage219AndCore() async {
124+
final a = newFile('$testPackageLibPath/a.dart', r'''
125+
// @dart=2.19
126+
import 'dart:collection';
127+
abstract class A implements LinkedListEntry<Never> {}
128+
''');
129+
130+
await resolveFile2(a.path);
131+
assertNoErrorsInResult();
132+
133+
await assertErrorsInCode(r'''
134+
import 'a.dart';
135+
abstract class B implements A {}
136+
''', [
137+
error(CompileTimeErrorCode.SUBTYPE_OF_BASE_IS_NOT_BASE_FINAL_OR_SEALED,
138+
32, 1,
139+
text:
140+
"The type 'B' must be 'base', 'final' or 'sealed' because the supertype 'LinkedListEntry' is 'base'."),
141+
error(CompileTimeErrorCode.BASE_CLASS_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 45,
142+
1),
143+
]);
144+
}
145+
84146
test_class_sealed_extends() async {
85147
await assertErrorsInCode(r'''
86148
base class A {}

0 commit comments

Comments
 (0)