-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fixes: dart-lang/linter#4747 Change-Id: I7bf3e83dbe279f5b1a03dc5e5806c7c0fe3e3486 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/326263 Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Phil Quitslund <[email protected]>
- Loading branch information
Showing
14 changed files
with
375 additions
and
1 deletion.
There are no files selected for viewing
40 changes: 40 additions & 0 deletions
40
pkg/analysis_server/lib/src/services/correction/dart/add_redeclare.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file | ||
// for details. All rights reserved. Use of this source code is governed by a | ||
// BSD-style license that can be found in the LICENSE file. | ||
|
||
import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart'; | ||
import 'package:analysis_server/src/services/correction/fix.dart'; | ||
import 'package:analyzer/dart/ast/ast.dart'; | ||
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart'; | ||
import 'package:analyzer_plugin/utilities/fixes/fixes.dart'; | ||
|
||
class AddRedeclare extends ResolvedCorrectionProducer { | ||
@override | ||
bool get canBeAppliedInBulk => true; | ||
|
||
@override | ||
bool get canBeAppliedToFile => true; | ||
|
||
@override | ||
FixKind get fixKind => DartFixKind.ADD_REDECLARE; | ||
|
||
@override | ||
FixKind get multiFixKind => DartFixKind.ADD_REDECLARE_MULTI; | ||
|
||
@override | ||
Future<void> compute(ChangeBuilder builder) async { | ||
var member = node.thisOrAncestorOfType<ClassMember>(); | ||
if (member == null) return; | ||
|
||
var token = member.firstTokenAfterCommentAndMetadata; | ||
var indent = utils.getIndent(1); | ||
await builder.addDartFileEdit(file, (builder) { | ||
builder.addInsertion(token.offset, (builder) { | ||
builder.write('@'); | ||
builder.writeImportedName( | ||
[Uri.parse('package:meta/meta.dart')], 'redeclare'); | ||
builder.write('$eol$indent'); | ||
}); | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
125 changes: 125 additions & 0 deletions
125
pkg/analysis_server/test/src/services/correction/fix/add_redeclare_test.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file | ||
// for details. All rights reserved. Use of this source code is governed by a | ||
// BSD-style license that can be found in the LICENSE file. | ||
|
||
import 'package:analysis_server/src/services/correction/fix.dart'; | ||
import 'package:analysis_server/src/services/linter/lint_names.dart'; | ||
import 'package:analyzer_plugin/utilities/fixes/fixes.dart'; | ||
import 'package:test_reflective_loader/test_reflective_loader.dart'; | ||
|
||
import 'fix_processor.dart'; | ||
|
||
void main() { | ||
defineReflectiveSuite(() { | ||
defineReflectiveTests(AddRedeclareBulkTest); | ||
defineReflectiveTests(AddRedeclareTest); | ||
}); | ||
} | ||
|
||
@reflectiveTest | ||
class AddRedeclareBulkTest extends BulkFixProcessorTest { | ||
@override | ||
String get lintCode => LintNames.annotate_redeclares; | ||
|
||
@override | ||
void setUp() { | ||
super.setUp(); | ||
writeTestPackageConfig(meta: true); | ||
} | ||
|
||
Future<void> test_singleFile() async { | ||
await resolveTestCode(''' | ||
class C { | ||
void f() {} | ||
void g() {} | ||
} | ||
extension type E(C c) implements C { | ||
/// Comment. | ||
void f() {} | ||
void g() {} | ||
} | ||
'''); | ||
await assertHasFix(''' | ||
import 'package:meta/meta.dart'; | ||
class C { | ||
void f() {} | ||
void g() {} | ||
} | ||
extension type E(C c) implements C { | ||
/// Comment. | ||
@redeclare | ||
void f() {} | ||
@redeclare | ||
void g() {} | ||
} | ||
'''); | ||
} | ||
} | ||
|
||
@reflectiveTest | ||
class AddRedeclareTest extends FixProcessorLintTest { | ||
@override | ||
FixKind get kind => DartFixKind.ADD_REDECLARE; | ||
|
||
@override | ||
String get lintCode => LintNames.annotate_redeclares; | ||
|
||
@override | ||
void setUp() { | ||
super.setUp(); | ||
writeTestPackageConfig(meta: true); | ||
} | ||
|
||
Future<void> test_method() async { | ||
await resolveTestCode(''' | ||
class C { | ||
void f() {} | ||
} | ||
extension type E(C c) implements C { | ||
void f() {} | ||
} | ||
'''); | ||
await assertHasFix(''' | ||
import 'package:meta/meta.dart'; | ||
class C { | ||
void f() {} | ||
} | ||
extension type E(C c) implements C { | ||
@redeclare | ||
void f() {} | ||
} | ||
'''); | ||
} | ||
|
||
Future<void> test_method_withComment() async { | ||
await resolveTestCode(''' | ||
class C { | ||
void f() {} | ||
} | ||
extension type E(C c) implements C { | ||
/// Comment. | ||
void f() {} | ||
} | ||
'''); | ||
await assertHasFix(''' | ||
import 'package:meta/meta.dart'; | ||
class C { | ||
void f() {} | ||
} | ||
extension type E(C c) implements C { | ||
/// Comment. | ||
@redeclare | ||
void f() {} | ||
} | ||
'''); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file | ||
// for details. All rights reserved. Use of this source code is governed by a | ||
// BSD-style license that can be found in the LICENSE file. | ||
|
||
import 'package:analyzer/dart/ast/ast.dart'; | ||
import 'package:analyzer/dart/ast/visitor.dart'; | ||
import 'package:analyzer/dart/element/element.dart'; | ||
|
||
import '../analyzer.dart'; | ||
|
||
const _desc = r'Annotate redeclared members.'; | ||
|
||
const _details = r''' | ||
**DO** annotate redeclared members. | ||
This practice improves code readability and helps protect against | ||
unintentionally redeclaring members or being surprised when a member ceases to | ||
redeclare (due for example to a rename refactoring). | ||
**BAD:** | ||
```dart | ||
class C { | ||
void f() { } | ||
} | ||
extension type E(C c) implements C { | ||
void f() { | ||
... | ||
} | ||
} | ||
``` | ||
**GOOD:** | ||
```dart | ||
import 'package:meta/meta.dart'; | ||
class C { | ||
void f() { } | ||
} | ||
extension type E(C c) implements C { | ||
@redeclare | ||
void f() { | ||
... | ||
} | ||
} | ||
``` | ||
'''; | ||
|
||
class AnnotateRedeclares extends LintRule { | ||
static const LintCode code = LintCode('annotate_redeclares', | ||
"The member '{0}' is redeclaring but isn't annotated with '@redeclare'.", | ||
correctionMessage: "Try adding the '@redeclare' annotation."); | ||
|
||
AnnotateRedeclares() | ||
: super( | ||
name: 'annotate_redeclares', | ||
description: _desc, | ||
details: _details, | ||
group: Group.style, | ||
state: State.experimental()); | ||
|
||
@override | ||
LintCode get lintCode => code; | ||
|
||
@override | ||
void registerNodeProcessors( | ||
NodeLintRegistry registry, LinterContext context) { | ||
var visitor = _Visitor(this, context); | ||
registry.addExtensionTypeDeclaration(this, visitor); | ||
} | ||
} | ||
|
||
class _Visitor extends SimpleAstVisitor<void> { | ||
final LintRule rule; | ||
final LinterContext context; | ||
|
||
_Visitor(this.rule, this.context); | ||
|
||
@override | ||
void visitExtensionTypeDeclaration(ExtensionTypeDeclaration node) { | ||
node.members.whereType<MethodDeclaration>().forEach(_check); | ||
} | ||
|
||
void _check(MethodDeclaration node) { | ||
if (node.isStatic) return; | ||
var parent = node.parent; | ||
// Shouldn't happen. | ||
if (parent is! ExtensionTypeDeclaration) return; | ||
|
||
var element = node.declaredElement; | ||
if (element == null || element.hasRedeclare) return; | ||
|
||
var extensionType = parent.declaredElement; | ||
if (extensionType == null) return; | ||
|
||
if (_redeclaresMember(element, extensionType)) { | ||
rule.reportLintForToken(node.name, arguments: [element.displayName]); | ||
} | ||
} | ||
|
||
/// Return `true` if the [member] redeclares a member from a superinterface. | ||
bool _redeclaresMember( | ||
ExecutableElement member, InterfaceElement extensionType) { | ||
// todo(pq): unify with similar logic in `redeclare_verifier` and move to inheritanceManager | ||
var uri = member.library.source.uri; | ||
var interface = context.inheritanceManager.getInterface(extensionType); | ||
return interface.redeclared.containsKey(Name(uri, member.name)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.