Skip to content

Commit 6643320

Browse files
fshcheglovCommit Queue
authored andcommitted
Implement augmentation for analyzer mixin error-checking.
Added additional tests to ensure functionality. Change-Id: I904902bb8358840646a601fcf53de00f8353ffe8 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/490301 Reviewed-by: Konstantin Shcheglov <scheglov@google.com> Reviewed-by: Johnni Winther <johnniwinther@google.com> Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
1 parent 71512ec commit 6643320

7 files changed

Lines changed: 462 additions & 58 deletions

File tree

pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ class LibraryAnalyzer {
8686
final Map<FileState, FileAnalysis> _libraryFiles = {};
8787
late final LibraryVerificationContext _libraryVerificationContext;
8888

89+
/// One verifier per library so that state of elements can be shared across
90+
/// fragments in all files of this library.
91+
late final InheritanceOverrideVerifier _inheritanceOverrideVerifier;
92+
8993
final TestingData? _testingData;
9094
final TypeSystemOperations _typeSystemOperations;
9195

@@ -111,6 +115,10 @@ class LibraryAnalyzer {
111115
typeSystem: _typeSystem,
112116
),
113117
);
118+
_inheritanceOverrideVerifier = InheritanceOverrideVerifier(
119+
_typeSystem,
120+
_inheritance,
121+
);
114122
}
115123

116124
TypeProviderImpl get _typeProvider => _libraryElement.typeProvider;
@@ -463,11 +471,7 @@ class LibraryAnalyzer {
463471
_computeConstantErrors(fileAnalysis);
464472

465473
// Compute inheritance and override errors.
466-
InheritanceOverrideVerifier(
467-
_typeSystem,
468-
_inheritance,
469-
diagnosticReporter,
470-
).verifyUnit(unit);
474+
_inheritanceOverrideVerifier.verifyUnit(unit, diagnosticReporter);
471475

472476
// Use the ErrorVerifier to compute errors.
473477
ErrorVerifier errorVerifier = ErrorVerifier(

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

Lines changed: 45 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import 'package:analyzer/src/error/correct_override.dart';
2222
import 'package:analyzer/src/error/getter_setter_types_verifier.dart';
2323
import 'package:analyzer/src/error/inference_error.dart';
2424
import 'package:analyzer/src/error/listener.dart';
25+
import 'package:analyzer/src/summary2/types_builder.dart';
2526
import 'package:analyzer/src/utilities/extensions/element.dart';
2627

2728
final _missingMustBeOverridden = Expando<List<ExecutableElement>>();
@@ -36,16 +37,20 @@ class InheritanceOverrideVerifier {
3637
final TypeSystemImpl _typeSystem;
3738
final TypeProvider _typeProvider;
3839
final InheritanceManager3 _inheritance;
39-
final DiagnosticReporter _reporter;
4040

41-
InheritanceOverrideVerifier(
42-
this._typeSystem,
43-
this._inheritance,
44-
this._reporter,
45-
) : _typeProvider = _typeSystem.typeProvider;
41+
final Map<InterfaceElementImpl, _InterfaceElementState>
42+
_interfaceElementStates = {};
4643

47-
void verifyUnit(CompilationUnitImpl unit) {
44+
InheritanceOverrideVerifier(this._typeSystem, this._inheritance)
45+
: _typeProvider = _typeSystem.typeProvider;
46+
47+
void verifyUnit(CompilationUnitImpl unit, DiagnosticReporter reporter) {
4848
var library = unit.declaredFragment!.element;
49+
50+
_InterfaceElementState interfaceElementState(InterfaceElementImpl element) {
51+
return _interfaceElementStates[element] ??= _InterfaceElementState();
52+
}
53+
4954
for (var declaration in unit.declarations) {
5055
_ClassVerifier verifier;
5156
if (declaration is ClassDeclarationImpl) {
@@ -54,85 +59,76 @@ class InheritanceOverrideVerifier {
5459
typeSystem: _typeSystem,
5560
typeProvider: _typeProvider,
5661
inheritance: _inheritance,
57-
reporter: _reporter,
62+
reporter: reporter,
5863
featureSet: unit.featureSet,
5964
library: library,
6065
node: declaration,
6166
classNameToken: declaration.namePart.typeName,
6267
classElement: fragment.element,
68+
classFragment: fragment,
6369
diagnosticSource: unit.declaredFragment!.source,
6470
implementsClause: declaration.implementsClause,
6571
members: declaration.body.members,
6672
superclass: declaration.extendsClause?.superclass,
6773
withClause: declaration.withClause,
74+
interfaceElementState: interfaceElementState(fragment.element),
6875
);
69-
if (fragment.isAugmentation) {
70-
verifier._checkDirectSuperTypes();
71-
continue;
72-
}
7376
} else if (declaration is ClassTypeAliasImpl) {
7477
var fragment = declaration.declaredFragment!;
7578
verifier = _ClassVerifier(
7679
typeSystem: _typeSystem,
7780
typeProvider: _typeProvider,
7881
inheritance: _inheritance,
79-
reporter: _reporter,
82+
reporter: reporter,
8083
featureSet: unit.featureSet,
8184
library: library,
8285
node: declaration,
8386
classNameToken: declaration.name,
8487
classElement: fragment.element,
88+
classFragment: fragment,
8589
diagnosticSource: unit.declaredFragment!.source,
8690
implementsClause: declaration.implementsClause,
8791
superclass: declaration.superclass,
8892
withClause: declaration.withClause,
93+
interfaceElementState: interfaceElementState(fragment.element),
8994
);
90-
if (fragment.isAugmentation) {
91-
verifier._checkDirectSuperTypes();
92-
continue;
93-
}
9495
} else if (declaration is EnumDeclarationImpl) {
9596
var fragment = declaration.declaredFragment!;
9697
verifier = _ClassVerifier(
9798
typeSystem: _typeSystem,
9899
typeProvider: _typeProvider,
99100
inheritance: _inheritance,
100-
reporter: _reporter,
101+
reporter: reporter,
101102
featureSet: unit.featureSet,
102103
library: library,
103104
node: declaration,
104105
classNameToken: declaration.namePart.typeName,
105106
classElement: fragment.element,
107+
classFragment: fragment,
106108
diagnosticSource: unit.declaredFragment!.source,
107109
implementsClause: declaration.implementsClause,
108110
members: declaration.body.members,
109111
withClause: declaration.withClause,
112+
interfaceElementState: interfaceElementState(fragment.element),
110113
);
111-
if (fragment.isAugmentation) {
112-
verifier._checkDirectSuperTypes();
113-
continue;
114-
}
115114
} else if (declaration is MixinDeclarationImpl) {
116115
var fragment = declaration.declaredFragment!;
117116
verifier = _ClassVerifier(
118117
typeSystem: _typeSystem,
119118
typeProvider: _typeProvider,
120119
inheritance: _inheritance,
121-
reporter: _reporter,
120+
reporter: reporter,
122121
featureSet: unit.featureSet,
123122
library: library,
124123
node: declaration,
125124
classNameToken: declaration.name,
126125
classElement: fragment.element,
126+
classFragment: fragment,
127127
diagnosticSource: unit.declaredFragment!.source,
128128
implementsClause: declaration.implementsClause,
129129
members: declaration.body.members,
130130
onClause: declaration.onClause,
131131
);
132-
if (fragment.isAugmentation) {
133-
verifier._checkDirectSuperTypes();
134-
continue;
135-
}
136132
} else {
137133
continue;
138134
}
@@ -170,6 +166,7 @@ class _ClassVerifier {
170166
final LibraryElementImpl library;
171167
final Uri libraryUri;
172168
final InterfaceElementImpl classElement;
169+
final InterfaceFragmentImpl classFragment;
173170

174171
final CompilationUnitMember node;
175172
final Token classNameToken;
@@ -178,6 +175,7 @@ class _ClassVerifier {
178175
final MixinOnClause? onClause;
179176
final NamedType? superclass;
180177
final WithClause? withClause;
178+
final _InterfaceElementState? interfaceElementState;
181179

182180
final List<InterfaceType> directSuperInterfaces = [];
183181

@@ -198,12 +196,14 @@ class _ClassVerifier {
198196
required this.node,
199197
required this.classNameToken,
200198
required this.classElement,
199+
required this.classFragment,
201200
required this.diagnosticSource,
202201
this.implementsClause,
203202
this.members = const [],
204203
this.onClause,
205204
this.superclass,
206205
this.withClause,
206+
this.interfaceElementState,
207207
}) : libraryUri = library.uri;
208208

209209
/// Verify inheritance overrides, and return `true` if an error was
@@ -250,12 +250,17 @@ class _ClassVerifier {
250250
// class C extends S&M2 { ...members of C... }
251251
// So, we need to check members of each mixin against superinterfaces
252252
// of `S`, and superinterfaces of all previous mixins.
253-
var mixinNodes = withClause?.mixinTypes;
254-
var mixinTypes = element.mixins;
255-
for (var i = 0; i < mixinTypes.length; i++) {
256-
var mixinType = mixinTypes[i];
257-
_checkDeclaredMembers(mixinNodes![i], mixinType, mixinIndex: i);
258-
directSuperInterfaces.add(mixinType);
253+
var mixinNodes = withClause?.mixinTypes ?? <NamedType>[];
254+
for (var node in mixinNodes) {
255+
var mixinType = node.type;
256+
// When building the element model, we skip incorrect types.
257+
// So, here we skip corresponding nodes to keep the index in sync.
258+
if (mixinType is InterfaceTypeImpl &&
259+
isInterfaceTypeInterface(mixinType)) {
260+
var index = interfaceElementState!.mixinIndex++;
261+
_checkDeclaredMembers(node, mixinType, mixinIndex: index);
262+
directSuperInterfaces.add(mixinType);
263+
}
259264
}
260265

261266
directSuperInterfaces.addAll(element.interfaces);
@@ -1044,3 +1049,10 @@ class _ClassVerifier {
10441049
reporter.report(locatableDiagnostic.at(classNameToken));
10451050
}
10461051
}
1052+
1053+
/// Maintains an [InterfaceElementImpl]'s mixin index across multiple fragments.
1054+
class _InterfaceElementState {
1055+
int mixinIndex = 0;
1056+
1057+
_InterfaceElementState();
1058+
}

pkg/analyzer/lib/src/summary2/types_builder.dart

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,8 @@ import 'package:analyzer/src/summary2/type_builder.dart';
1919
import 'package:analyzer/src/utilities/extensions/collection.dart';
2020
import 'package:analyzer/src/utilities/extensions/element.dart';
2121

22-
/// Return `true` if [type] can be used as a class.
23-
bool _isInterfaceTypeClass(InterfaceType type) {
24-
if (type.element is! ClassElement) {
25-
return false;
26-
}
27-
return _isInterfaceTypeInterface(type);
28-
}
29-
3022
/// Return `true` if [type] can be used as an interface or a mixin.
31-
bool _isInterfaceTypeInterface(InterfaceType type) {
23+
bool isInterfaceTypeInterface(InterfaceType type) {
3224
if (type.element is EnumElement) {
3325
return false;
3426
}
@@ -44,12 +36,20 @@ bool _isInterfaceTypeInterface(InterfaceType type) {
4436
return true;
4537
}
4638

39+
/// Return `true` if [type] can be used as a class.
40+
bool _isInterfaceTypeClass(InterfaceType type) {
41+
if (type.element is! ClassElement) {
42+
return false;
43+
}
44+
return isInterfaceTypeInterface(type);
45+
}
46+
4747
List<InterfaceTypeImpl> _toInterfaceTypeList(List<NamedType>? nodeList) {
4848
if (nodeList != null) {
4949
return nodeList
5050
.map((e) => e.type)
5151
.whereType<InterfaceTypeImpl>()
52-
.where(_isInterfaceTypeInterface)
52+
.where(isInterfaceTypeInterface)
5353
.toList();
5454
}
5555
return const [];
@@ -502,7 +502,7 @@ class _MixinInference {
502502
var result = <InterfaceTypeImpl>[];
503503
for (var mixinNode in withClause.mixinTypes) {
504504
var mixinType = _inferSingle(mixinNode);
505-
if (mixinType != null && _isInterfaceTypeInterface(mixinType)) {
505+
if (mixinType != null && isInterfaceTypeInterface(mixinType)) {
506506
result.add(mixinType);
507507
interfacesMerger.addWithSupertypes(mixinType);
508508
}
@@ -628,7 +628,7 @@ class _MixinInference {
628628
}
629629

630630
InterfaceTypeImpl? _interfaceType(DartType type) {
631-
if (type is InterfaceTypeImpl && _isInterfaceTypeInterface(type)) {
631+
if (type is InterfaceTypeImpl && isInterfaceTypeInterface(type)) {
632632
return type;
633633
}
634634
return null;

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

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ main() {
1515

1616
@reflectiveTest
1717
class InconsistentInheritanceTest extends PubPackageResolutionTest {
18-
@SkippedTest() // TODO(scheglov): implement augmentation
1918
test_class_augmentWithInterface_augmentWithMixin() async {
2019
var a = newFile('$testPackageLibPath/a.dart', r'''
2120
part 'b.dart';
@@ -45,11 +44,10 @@ augment abstract class C with A {}
4544
''');
4645

4746
await assertErrorsInFile2(a, [error(diag.inconsistentInheritance, 122, 1)]);
48-
await assertErrorsInFile2(b, []);
49-
await assertErrorsInFile2(c, []);
47+
await assertErrorsInFile2(b, [error(diag.inconsistentInheritance, 42, 1)]);
48+
await assertErrorsInFile2(c, [error(diag.inconsistentInheritance, 42, 1)]);
5049
}
5150

52-
@SkippedTest() // TODO(scheglov): implement augmentation
5351
test_class_augmentWithMixin_augmentWithInterface() async {
5452
var a = newFile('$testPackageLibPath/a.dart', r'''
5553
part 'b.dart';
@@ -79,8 +77,30 @@ augment abstract class C implements B {}
7977
''');
8078

8179
await assertErrorsInFile2(a, [error(diag.inconsistentInheritance, 122, 1)]);
82-
await assertErrorsInFile2(b, []);
83-
await assertErrorsInFile2(c, []);
80+
await assertErrorsInFile2(b, [error(diag.inconsistentInheritance, 42, 1)]);
81+
await assertErrorsInFile2(c, [error(diag.inconsistentInheritance, 42, 1)]);
82+
}
83+
84+
test_class_augmentWithMixin_sameFile() async {
85+
await assertErrorsInCode(
86+
r'''
87+
abstract class I {
88+
String foo();
89+
}
90+
91+
mixin M {
92+
int foo() => 0;
93+
}
94+
95+
class A implements I {}
96+
97+
augment class A with M {}
98+
''',
99+
[
100+
error(diag.inconsistentInheritance, 75, 1),
101+
error(diag.inconsistentInheritance, 108, 1),
102+
],
103+
);
84104
}
85105

86106
/// https://github.com/dart-lang/sdk/issues/47026
@@ -275,7 +295,6 @@ enum E implements A, B {v}
275295
);
276296
}
277297

278-
@SkippedTest() // TODO(scheglov): implement augmentation
279298
test_enum_returnType_augmentation() async {
280299
await assertErrorsInCode(
281300
r'''
@@ -293,7 +312,10 @@ augment enum E implements B {
293312
augment v;
294313
}
295314
''',
296-
[error(diag.inconsistentInheritance, 78, 1)],
315+
[
316+
error(diag.inconsistentInheritance, 78, 1),
317+
error(diag.inconsistentInheritance, 111, 1),
318+
],
297319
);
298320
}
299321

0 commit comments

Comments
 (0)