Skip to content

Commit b08f029

Browse files
pqCommit Queue
authored and
Commit Queue
committed
verify that members marked as redeclaring actually do
See: #53121 Change-Id: I39294952a3adf0f037417d6e04811e3294128e6d Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/320886 Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Phil Quitslund <[email protected]>
1 parent 2dddd42 commit b08f029

File tree

12 files changed

+299
-3
lines changed

12 files changed

+299
-3
lines changed

pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3650,6 +3650,10 @@ WarningCode.RECEIVER_OF_TYPE_NEVER:
36503650
We _could_ remove the thing being received (method invocation, etc.)
36513651
WarningCode.RECORD_LITERAL_ONE_POSITIONAL_NO_TRAILING_COMMA:
36523652
status: hasFix
3653+
WarningCode.REDECLARE_ON_NON_REDECLARING_MEMBER:
3654+
status: needsFix
3655+
notes: |-
3656+
The fix is to remove the annotation.
36533657
WarningCode.REMOVED_LINT_USE:
36543658
status: needsFix
36553659
WarningCode.REPLACED_LINT_USE:

pkg/analyzer/lib/dart/element/element.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,9 @@ abstract class Element implements AnalysisTarget {
593593
/// Whether the element has an annotation of the form `@protected`.
594594
bool get hasProtected;
595595

596+
/// Whether the element has an annotation of the form `@redeclare`.
597+
bool get hasRedeclare;
598+
596599
/// Whether the element has an annotation of the form `@reopen`.
597600
bool get hasReopen;
598601

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import 'package:analyzer/src/error/imports_verifier.dart';
3232
import 'package:analyzer/src/error/inheritance_override.dart';
3333
import 'package:analyzer/src/error/language_version_override_verifier.dart';
3434
import 'package:analyzer/src/error/override_verifier.dart';
35+
import 'package:analyzer/src/error/redeclare_verifier.dart';
3536
import 'package:analyzer/src/error/todo_finder.dart';
3637
import 'package:analyzer/src/error/unicode_text_verifier.dart';
3738
import 'package:analyzer/src/error/unused_local_elements_verifier.dart';
@@ -448,6 +449,12 @@ class LibraryAnalyzer {
448449
errorReporter,
449450
));
450451

452+
unit.accept(RedeclareVerifier(
453+
_inheritance,
454+
_libraryElement,
455+
errorReporter,
456+
));
457+
451458
TodoFinder(errorReporter).findIn(unit);
452459
LanguageVersionOverrideVerifier(errorReporter).verify(unit);
453460

pkg/analyzer/lib/src/dart/element/element.dart

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2112,6 +2112,18 @@ abstract class ElementImpl implements Element {
21122112
return false;
21132113
}
21142114

2115+
@override
2116+
bool get hasRedeclare {
2117+
final metadata = this.metadata;
2118+
for (var i = 0; i < metadata.length; i++) {
2119+
var annotation = metadata[i];
2120+
if (annotation.isRedeclare) {
2121+
return true;
2122+
}
2123+
}
2124+
return false;
2125+
}
2126+
21152127
@override
21162128
bool get hasReopen {
21172129
final metadata = this.metadata;
@@ -5213,6 +5225,9 @@ class MultiplyDefinedElementImpl implements MultiplyDefinedElement {
52135225
@override
52145226
bool get hasProtected => false;
52155227

5228+
@override
5229+
bool get hasRedeclare => false;
5230+
52165231
@override
52175232
bool get hasReopen => false;
52185233

pkg/analyzer/lib/src/dart/element/member.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,9 @@ abstract class Member implements Element {
570570
@override
571571
bool get hasProtected => _declaration.hasProtected;
572572

573+
@override
574+
bool get hasRedeclare => _declaration.hasRedeclare;
575+
573576
@override
574577
bool get hasReopen => _declaration.hasReopen;
575578

pkg/analyzer/lib/src/error/codes.g.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6925,6 +6925,18 @@ class WarningCode extends AnalyzerErrorCode {
69256925
hasPublishedDocs: true,
69266926
);
69276927

6928+
/// An error code indicating the use of a redeclare annotation on a member that does not redeclare.
6929+
///
6930+
/// Parameters:
6931+
/// 0: the kind of member
6932+
static const WarningCode REDECLARE_ON_NON_REDECLARING_MEMBER = WarningCode(
6933+
'REDECLARE_ON_NON_REDECLARING_MEMBER',
6934+
"The {0} doesn't redeclare a {0} declared in a superinterface.",
6935+
correctionMessage:
6936+
"Try updating this member to match a declaration in a superinterface, "
6937+
"or removing the redeclare annotation.",
6938+
);
6939+
69286940
/// An error code indicating use of a removed lint rule.
69296941
///
69306942
/// Parameters:

pkg/analyzer/lib/src/error/error_code_values.g.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,6 +1013,7 @@ const List<ErrorCode> errorCodeValues = [
10131013
WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE,
10141014
WarningCode.RECEIVER_OF_TYPE_NEVER,
10151015
WarningCode.RECORD_LITERAL_ONE_POSITIONAL_NO_TRAILING_COMMA,
1016+
WarningCode.REDECLARE_ON_NON_REDECLARING_MEMBER,
10161017
WarningCode.REMOVED_LINT_USE,
10171018
WarningCode.REPLACED_LINT_USE,
10181019
WarningCode.RETURN_OF_DO_NOT_STORE,
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:analyzer/dart/ast/ast.dart';
6+
import 'package:analyzer/dart/ast/visitor.dart';
7+
import 'package:analyzer/dart/element/element.dart';
8+
import 'package:analyzer/error/listener.dart';
9+
import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
10+
import 'package:analyzer/src/error/codes.dart';
11+
12+
/// Instances of the class `RedeclareVerifier` visit all of the members of any
13+
/// extension type declarations in a compilation unit to verify that if they
14+
/// have a redeclare annotation it is being used correctly.
15+
class RedeclareVerifier extends RecursiveAstVisitor<void> {
16+
/// The inheritance manager used to find redeclared members.
17+
final InheritanceManager3 _inheritance;
18+
19+
/// The URI of the library being verified.
20+
final Uri _libraryUri;
21+
22+
/// The error reporter used to report errors.
23+
final ErrorReporter _errorReporter;
24+
25+
/// The current extension type.
26+
InterfaceElement? _currentExtensionType;
27+
28+
RedeclareVerifier(
29+
this._inheritance, LibraryElement library, this._errorReporter)
30+
: _libraryUri = library.source.uri;
31+
32+
@override
33+
void visitExtensionTypeDeclaration(ExtensionTypeDeclaration node) {
34+
_currentExtensionType = node.declaredElement;
35+
super.visitExtensionTypeDeclaration(node);
36+
_currentExtensionType = null;
37+
}
38+
39+
@override
40+
void visitMethodDeclaration(MethodDeclaration node) {
41+
// Only check if we're in an extension type declaration.
42+
if (_currentExtensionType == null) return;
43+
44+
var element = node.declaredElement!;
45+
46+
// Static members can't redeclare.
47+
if (element.isStatic) return;
48+
49+
if (element.hasRedeclare && !_redeclaresMember(element)) {
50+
if (element is MethodElement) {
51+
_errorReporter.reportErrorForToken(
52+
WarningCode.REDECLARE_ON_NON_REDECLARING_MEMBER,
53+
node.name,
54+
[
55+
'method',
56+
],
57+
);
58+
} else if (element is PropertyAccessorElement) {
59+
if (element.isGetter) {
60+
_errorReporter.reportErrorForToken(
61+
WarningCode.REDECLARE_ON_NON_REDECLARING_MEMBER,
62+
node.name,
63+
[
64+
'getter',
65+
],
66+
);
67+
} else {
68+
_errorReporter.reportErrorForToken(
69+
WarningCode.REDECLARE_ON_NON_REDECLARING_MEMBER,
70+
node.name,
71+
[
72+
'setter',
73+
],
74+
);
75+
}
76+
}
77+
}
78+
}
79+
80+
/// Return `true` if the [member] redeclares a member from a superinterface.
81+
bool _redeclaresMember(ExecutableElement member) {
82+
var currentType = _currentExtensionType;
83+
if (currentType != null) {
84+
var interface = _inheritance.getInterface(currentType);
85+
var redeclared = interface.redeclared;
86+
var name = Name(_libraryUri, member.name);
87+
return redeclared.containsKey(name);
88+
}
89+
90+
return false;
91+
}
92+
}

pkg/analyzer/messages.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24102,6 +24102,14 @@ WarningCode:
2410224102
problemMessage: "A record literal with exactly one positional field requires a trailing comma."
2410324103
correctionMessage: Try adding a trailing comma.
2410424104
hasPublishedDocs: true
24105+
REDECLARE_ON_NON_REDECLARING_MEMBER:
24106+
problemMessage: "The {0} doesn't redeclare a {0} declared in a superinterface."
24107+
correctionMessage: Try updating this member to match a declaration in a superinterface, or removing the redeclare annotation.
24108+
comment: |-
24109+
An error code indicating the use of a redeclare annotation on a member that does not redeclare.
24110+
24111+
Parameters:
24112+
0: the kind of member
2410524113
REMOVED_LINT_USE:
2410624114
problemMessage: "'{0}' was removed in Dart '{1}'"
2410724115
correctionMessage: "Remove the reference to '{0}'."

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,7 @@ class C {
373373
int get g => 0;
374374
}
375375
376-
extension type E(C c) {
376+
extension type E(C c) implements C {
377377
@redeclare
378378
int get g => 0;
379379
}
@@ -388,7 +388,7 @@ class C {
388388
void m() {}
389389
}
390390
391-
extension type E(C c) {
391+
extension type E(C c) implements C {
392392
@redeclare
393393
void m() {}
394394
}
@@ -403,7 +403,7 @@ class C {
403403
set g(int i) {}
404404
}
405405
406-
extension type E(C c) {
406+
extension type E(C c) implements C {
407407
@redeclare
408408
set g(int i) {}
409409
}
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:analyzer/src/error/codes.dart';
6+
import 'package:test_reflective_loader/test_reflective_loader.dart';
7+
8+
import '../dart/resolution/context_collection_resolution.dart';
9+
10+
main() {
11+
defineReflectiveSuite(() {
12+
defineReflectiveTests(RedeclareOnNonRedeclaringMemberTest);
13+
});
14+
}
15+
16+
@reflectiveTest
17+
class RedeclareOnNonRedeclaringMemberTest extends PubPackageResolutionTest {
18+
@override
19+
List<String> get experiments => ['inline-class'];
20+
21+
@override
22+
void setUp() {
23+
super.setUp();
24+
writeTestPackageConfig(PackageConfigFileBuilder(), meta: true);
25+
}
26+
27+
test_getter() async {
28+
await assertErrorsInCode(r'''
29+
import 'package:meta/meta.dart';
30+
31+
class C {}
32+
33+
extension type E(C c) implements C {
34+
@redeclare
35+
int get i => 0;
36+
}
37+
''', [
38+
error(WarningCode.REDECLARE_ON_NON_REDECLARING_MEMBER, 106, 1),
39+
]);
40+
}
41+
42+
test_getter_redeclares() async {
43+
await assertNoErrorsInCode(r'''
44+
import 'package:meta/meta.dart';
45+
46+
class C {
47+
int get i => 0;
48+
}
49+
50+
extension type E(C c) implements C {
51+
@redeclare
52+
int get i => 0;
53+
}
54+
''');
55+
}
56+
57+
test_method() async {
58+
await assertErrorsInCode(r'''
59+
import 'package:meta/meta.dart';
60+
61+
class C {}
62+
63+
extension type E(C c) implements C {
64+
@redeclare
65+
void n() {}
66+
}
67+
''', [
68+
error(WarningCode.REDECLARE_ON_NON_REDECLARING_MEMBER, 103, 1),
69+
]);
70+
}
71+
72+
test_method_inClass() async {
73+
await assertErrorsInCode(r'''
74+
import 'package:meta/meta.dart';
75+
76+
class C {}
77+
78+
class D implements C {
79+
@redeclare
80+
void n() {}
81+
}
82+
''', [
83+
// No REDECLARE_ON_NON_REDECLARING_MEMBER warning.
84+
error(WarningCode.INVALID_ANNOTATION_TARGET, 71, 10),
85+
]);
86+
}
87+
88+
test_method_redeclared() async {
89+
await assertNoErrorsInCode(r'''
90+
import 'package:meta/meta.dart';
91+
92+
class C {
93+
void m() {}
94+
}
95+
96+
extension type E(C c) implements C {
97+
@redeclare
98+
void m() {}
99+
}
100+
''');
101+
}
102+
103+
test_method_static() async {
104+
await assertErrorsInCode(r'''
105+
import 'package:meta/meta.dart';
106+
107+
class C {}
108+
109+
extension type E(C c) implements C {
110+
@redeclare
111+
static void n() {}
112+
}
113+
''', [
114+
// No REDECLARE_ON_NON_REDECLARING_MEMBER warning.
115+
error(WarningCode.INVALID_ANNOTATION_TARGET, 85, 10),
116+
]);
117+
}
118+
119+
test_setter() async {
120+
await assertErrorsInCode(r'''
121+
import 'package:meta/meta.dart';
122+
123+
class C {}
124+
125+
extension type E(C c) implements C {
126+
@redeclare
127+
set i(int i) {}
128+
}
129+
''', [
130+
error(WarningCode.REDECLARE_ON_NON_REDECLARING_MEMBER, 102, 1),
131+
]);
132+
}
133+
134+
test_setter_redeclares() async {
135+
await assertNoErrorsInCode(r'''
136+
import 'package:meta/meta.dart';
137+
138+
class C {
139+
set i(int i) {}
140+
}
141+
142+
extension type E(C c) implements C {
143+
@redeclare
144+
set i(int i) {}
145+
}
146+
''');
147+
}
148+
}

0 commit comments

Comments
 (0)