Add fixes for a couple of hints
Change-Id: I2e1b6c74c30857788b3bfbfbca46fc932dd7817a
Reviewed-on: https://dart-review.googlesource.com/c/90440
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analysis_server/lib/src/services/correction/fix.dart b/pkg/analysis_server/lib/src/services/correction/fix.dart
index e9bf18d..2f872621 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix.dart
@@ -201,6 +201,8 @@
'MOVE_TYPE_ARGUMENTS_TO_CLASS',
50,
"Move type arguments to after class name");
+ static const REMOVE_ANNOTATION =
+ const FixKind('REMOVE_ANNOTATION', 50, "Remove the '{0}' annotation");
static const REMOVE_AWAIT = const FixKind('REMOVE_AWAIT', 50, "Remove await");
static const REMOVE_DEAD_CODE =
const FixKind('REMOVE_DEAD_CODE', 50, "Remove dead code");
@@ -220,6 +222,8 @@
"Remove unnecessary interpolation braces");
static const REMOVE_METHOD_DECLARATION = const FixKind(
'REMOVE_METHOD_DECLARATION', 50, "Remove method declaration");
+ static const REMOVE_NAME_FROM_COMBINATOR = const FixKind(
+ 'REMOVE_NAME_FROM_COMBINATOR', 50, "Remove name from '{0}'");
static const REMOVE_PARAMETERS_IN_GETTER_DECLARATION = const FixKind(
'REMOVE_PARAMETERS_IN_GETTER_DECLARATION',
50,
diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
index 0db0fb2..2d8e769 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -282,27 +282,107 @@
if (errorCode == HintCode.DEAD_CODE) {
await _addFix_removeDeadCode();
}
+ if (errorCode == HintCode.DEAD_CODE_CATCH_FOLLOWING_CATCH ||
+ errorCode == HintCode.DEAD_CODE_ON_CATCH_SUBTYPE) {
+ await _addFix_removeDeadCode();
+ // TODO(brianwilkerson) Add a fix to move the unreachable catch clause to
+ // a place where it can be reached (when possible).
+ }
+ // TODO(brianwilkerson) Define a syntax for deprecated members to indicate
+ // how to update the code and implement a fix to apply the update.
+// if (errorCode == HintCode.DEPRECATED_MEMBER_USE ||
+// errorCode == HintCode.DEPRECATED_MEMBER_USE_FROM_SAME_PACKAGE) {
+// await _addFix_replaceDeprecatedMemberUse();
+// }
if (errorCode == HintCode.DIVISION_OPTIMIZATION) {
await _addFix_useEffectiveIntegerDivision();
}
+ if (errorCode == HintCode.DUPLICATE_IMPORT) {
+ await _addFix_removeUnusedImport();
+ }
+ if (errorCode == HintCode.DUPLICATE_HIDDEN_NAME ||
+ errorCode == HintCode.DUPLICATE_SHOWN_NAME) {
+ await _addFix_removeNameFromCombinator();
+ }
+ // TODO(brianwilkerson) Add a fix to convert the path to a package: import.
+// if (errorCode == HintCode.FILE_IMPORT_OUTSIDE_LIB_REFERENCES_FILE_INSIDE) {
+// await _addFix_convertPathToPackageUri();
+// }
+ if (errorCode == HintCode.INVALID_FACTORY_ANNOTATION ||
+ errorCode == HintCode.INVALID_IMMUTABLE_ANNOTATION ||
+ errorCode == HintCode.INVALID_LITERAL_ANNOTATION ||
+ errorCode == HintCode.INVALID_REQUIRED_PARAM ||
+ errorCode == HintCode.INVALID_SEALED_ANNOTATION) {
+ await _addFix_removeAnnotation();
+ }
+ if (errorCode == HintCode.MISSING_REQUIRED_PARAM ||
+ errorCode == HintCode.MISSING_REQUIRED_PARAM_WITH_DETAILS) {
+ await _addFix_addMissingRequiredArgument();
+ }
+ if (errorCode == HintCode.OVERRIDE_ON_NON_OVERRIDING_GETTER ||
+ errorCode == HintCode.OVERRIDE_ON_NON_OVERRIDING_FIELD ||
+ errorCode == HintCode.OVERRIDE_ON_NON_OVERRIDING_METHOD ||
+ errorCode == HintCode.OVERRIDE_ON_NON_OVERRIDING_SETTER) {
+ await _addFix_removeAnnotation();
+ }
+ // TODO(brianwilkerson) Add a fix to normalize the path.
+// if (errorCode == HintCode.PACKAGE_IMPORT_CONTAINS_DOT_DOT) {
+// await _addFix_normalizeUri();
+// }
+ if (errorCode == HintCode.SDK_VERSION_ASYNC_EXPORTED_FROM_CORE) {
+ await _addFix_importAsync();
+ await _addFix_updateSdkConstraints();
+ }
if (errorCode == HintCode.TYPE_CHECK_IS_NOT_NULL) {
await _addFix_isNotNull();
}
if (errorCode == HintCode.TYPE_CHECK_IS_NULL) {
await _addFix_isNull();
}
+ if (errorCode == HintCode.UNDEFINED_HIDDEN_NAME ||
+ errorCode == HintCode.UNDEFINED_SHOWN_NAME) {
+ await _addFix_removeNameFromCombinator();
+ }
if (errorCode == HintCode.UNNECESSARY_CAST) {
await _addFix_removeUnnecessaryCast();
}
+ // TODO(brianwilkerson) Add a fix to remove the method.
+// if (errorCode == HintCode.UNNECESSARY_NO_SUCH_METHOD) {
+// await _addFix_removeMethodDeclaration();
+// }
+ // TODO(brianwilkerson) Add a fix to remove the type check.
+// if (errorCode == HintCode.UNNECESSARY_TYPE_CHECK_FALSE ||
+// errorCode == HintCode.UNNECESSARY_TYPE_CHECK_TRUE) {
+// await _addFix_removeUnnecessaryTypeCheck();
+// }
if (errorCode == HintCode.UNUSED_CATCH_CLAUSE) {
await _addFix_removeUnusedCatchClause();
}
if (errorCode == HintCode.UNUSED_CATCH_STACK) {
await _addFix_removeUnusedCatchStack();
}
+ // TODO(brianwilkerson) Add a fix to remove the declaration. Decide whether
+ // this should be a single general fix, or multiple more specific fixes
+ // such as [_addFix_removeMethodDeclaration].
+// if (errorCode == HintCode.UNUSED_ELEMENT ||
+// errorCode == HintCode.UNUSED_FIELD) {
+// await _addFix_removeUnusedDeclaration();
+// }
if (errorCode == HintCode.UNUSED_IMPORT) {
await _addFix_removeUnusedImport();
}
+ // TODO(brianwilkerson) Add a fix to remove the label.
+// if (errorCode == HintCode.UNUSED_LABEL) {
+// await _addFix_removeUnusedLabel();
+// }
+ // TODO(brianwilkerson) Add a fix to remove the local variable, either with
+ // or without the initialization code.
+// if (errorCode == HintCode.UNUSED_LOCAL_VARIABLE) {
+// await _addFix_removeUnusedLocalVariable();
+// }
+ if (errorCode == HintCode.UNUSED_SHOWN_NAME) {
+ await _addFix_removeNameFromCombinator();
+ }
if (errorCode == ParserErrorCode.EXPECTED_TOKEN) {
await _addFix_insertSemicolon();
}
@@ -327,10 +407,6 @@
await _addFix_createConstructor_insteadOfSyntheticDefault();
await _addFix_addMissingParameter();
}
- if (errorCode == HintCode.MISSING_REQUIRED_PARAM ||
- errorCode == HintCode.MISSING_REQUIRED_PARAM_WITH_DETAILS) {
- await _addFix_addMissingRequiredArgument();
- }
if (errorCode == StaticWarningCode.NEW_WITH_UNDEFINED_CONSTRUCTOR) {
await _addFix_createConstructor_named();
}
@@ -469,10 +545,6 @@
CompileTimeErrorCode.MIXIN_APPLICATION_NOT_IMPLEMENTED_INTERFACE) {
await _addFix_extendClassForMixin();
}
- if (errorCode == HintCode.SDK_VERSION_ASYNC_EXPORTED_FROM_CORE) {
- await _addFix_importAsync();
- await _addFix_updateSdkConstraints();
- }
// lints
if (errorCode is LintCode) {
String name = errorCode.name;
@@ -2509,6 +2581,49 @@
_addFixFromBuilder(changeBuilder, DartFixKind.ADD_NE_NULL);
}
+ Future<void> _addFix_removeAnnotation() async {
+ void addFix(Annotation node) async {
+ if (node == null) {
+ return;
+ }
+ Token followingToken = node.endToken.next;
+ followingToken = followingToken.precedingComments ?? followingToken;
+ DartChangeBuilder changeBuilder = _newDartChangeBuilder();
+ await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
+ builder.addDeletion(range.startStart(node, followingToken));
+ });
+ _addFixFromBuilder(changeBuilder, DartFixKind.REMOVE_ANNOTATION,
+ args: [node.name.name]);
+ }
+
+ Annotation findAnnotation(
+ NodeList<Annotation> metadata, String targetName) {
+ return metadata.firstWhere(
+ (annotation) => annotation.name.name == targetName,
+ orElse: () => null);
+ }
+
+ AstNode node = this.coveredNode;
+ if (node is Annotation) {
+ await addFix(node);
+ } else if (node is DefaultFormalParameter) {
+ await addFix(findAnnotation(node.parameter.metadata, 'required'));
+ } else if (node is NormalFormalParameter) {
+ await addFix(findAnnotation(node.metadata, 'required'));
+ } else if (node is DeclaredSimpleIdentifier) {
+ AstNode parent = node.parent;
+ if (parent is MethodDeclaration) {
+ await addFix(findAnnotation(parent.metadata, 'override'));
+ } else if (parent is VariableDeclaration) {
+ FieldDeclaration fieldDeclaration =
+ parent.thisOrAncestorOfType<FieldDeclaration>();
+ if (fieldDeclaration != null) {
+ await addFix(findAnnotation(fieldDeclaration.metadata, 'override'));
+ }
+ }
+ }
+ }
+
Future<void> _addFix_removeAwait() async {
// TODO(brianwilkerson) Determine whether this await is necessary.
await null;
@@ -2524,8 +2639,6 @@
}
Future<void> _addFix_removeDeadCode() async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
AstNode coveringNode = this.coveredNode;
if (coveringNode is Expression) {
AstNode parent = coveredNode.parent;
@@ -2564,6 +2677,17 @@
builder.addDeletion(rangeToRemove);
});
_addFixFromBuilder(changeBuilder, DartFixKind.REMOVE_DEAD_CODE);
+ } else if (coveringNode is CatchClause) {
+ TryStatement tryStatement = coveringNode.parent;
+ NodeList<CatchClause> catchClauses = tryStatement.catchClauses;
+ int index = catchClauses.indexOf(coveringNode);
+ AstNode previous =
+ index == 0 ? tryStatement.body : catchClauses[index - 1];
+ DartChangeBuilder changeBuilder = _newDartChangeBuilder();
+ await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
+ builder.addDeletion(range.endEnd(previous, coveringNode));
+ });
+ _addFixFromBuilder(changeBuilder, DartFixKind.REMOVE_DEAD_CODE);
}
}
@@ -2673,6 +2797,68 @@
}
}
+ Future<void> _addFix_removeNameFromCombinator() async {
+ SourceRange rangeForCombinator(Combinator combinator) {
+ AstNode parent = combinator.parent;
+ if (parent is NamespaceDirective) {
+ NodeList<Combinator> combinators = parent.combinators;
+ if (combinators.length == 1) {
+ Token previousToken =
+ combinator.parent.findPrevious(combinator.beginToken);
+ return range.endEnd(previousToken, combinator);
+ }
+ int index = combinators.indexOf(combinator);
+ if (index < 0) {
+ return null;
+ } else if (index == combinators.length - 1) {
+ return range.endEnd(combinators[index - 1], combinator);
+ }
+ return range.startStart(combinator, combinators[index + 1]);
+ }
+ return null;
+ }
+
+ SourceRange rangeForNameInCombinator(
+ Combinator combinator, SimpleIdentifier name) {
+ NodeList<SimpleIdentifier> names;
+ if (combinator is HideCombinator) {
+ names = combinator.hiddenNames;
+ } else if (combinator is ShowCombinator) {
+ names = combinator.shownNames;
+ } else {
+ return null;
+ }
+ if (names.length == 1) {
+ return rangeForCombinator(combinator);
+ }
+ int index = names.indexOf(name);
+ if (index < 0) {
+ return null;
+ } else if (index == names.length - 1) {
+ return range.endEnd(names[index - 1], name);
+ }
+ return range.startStart(name, names[index + 1]);
+ }
+
+ AstNode node = this.coveredNode;
+ if (node is SimpleIdentifier) {
+ AstNode parent = coveredNode.parent;
+ if (parent is Combinator) {
+ SourceRange rangeToRemove = rangeForNameInCombinator(parent, node);
+ if (rangeToRemove == null) {
+ return;
+ }
+ DartChangeBuilder changeBuilder = _newDartChangeBuilder();
+ await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
+ builder.addDeletion(rangeToRemove);
+ });
+ _addFixFromBuilder(
+ changeBuilder, DartFixKind.REMOVE_NAME_FROM_COMBINATOR,
+ args: [parent is HideCombinator ? 'hide' : 'show']);
+ }
+ }
+ }
+
Future<void> _addFix_removeParameters_inGetterDeclaration() async {
// TODO(brianwilkerson) Determine whether this await is necessary.
await null;
diff --git a/pkg/analysis_server/test/abstract_context.dart b/pkg/analysis_server/test/abstract_context.dart
index 2d94d63..d9ffdb2 100644
--- a/pkg/analysis_server/test/abstract_context.dart
+++ b/pkg/analysis_server/test/abstract_context.dart
@@ -67,17 +67,67 @@
addPackageFile('meta', 'meta.dart', r'''
library meta;
+const _AlwaysThrows alwaysThrows = const _AlwaysThrows();
+
+@deprecated
+const _Checked checked = const _Checked();
+
+const _Experimental experimental = const _Experimental();
+
+const _Factory factory = const _Factory();
+
+const Immutable immutable = const Immutable();
+
const _IsTest isTest = const _IsTest();
const _IsTestGroup isTestGroup = const _IsTestGroup();
+const _Literal literal = const _Literal();
+
+const _MustCallSuper mustCallSuper = const _MustCallSuper();
+
+const _OptionalTypeArgs optionalTypeArgs = const _OptionalTypeArgs();
+
+const _Protected protected = const _Protected();
+
const Required required = const Required();
+const _Sealed sealed = const _Sealed();
+
+@deprecated
+const _Virtual virtual = const _Virtual();
+
+const _VisibleForOverriding visibleForOverriding =
+ const _VisibleForOverriding();
+
+const _VisibleForTesting visibleForTesting = const _VisibleForTesting();
+
+class Immutable {
+ final String reason;
+ const Immutable([this.reason]);
+}
+
class Required {
final String reason;
const Required([this.reason]);
}
+class _AlwaysThrows {
+ const _AlwaysThrows();
+}
+
+class _Checked {
+ const _Checked();
+}
+
+class _Experimental {
+ const _Experimental();
+}
+
+class _Factory {
+ const _Factory();
+}
+
class _IsTest {
const _IsTest();
}
@@ -85,6 +135,39 @@
class _IsTestGroup {
const _IsTestGroup();
}
+
+class _Literal {
+ const _Literal();
+}
+
+class _MustCallSuper {
+ const _MustCallSuper();
+}
+
+class _OptionalTypeArgs {
+ const _OptionalTypeArgs();
+}
+
+class _Protected {
+ const _Protected();
+}
+
+class _Sealed {
+ const _Sealed();
+}
+
+@deprecated
+class _Virtual {
+ const _Virtual();
+}
+
+class _VisibleForOverriding {
+ const _VisibleForOverriding();
+}
+
+class _VisibleForTesting {
+ const _VisibleForTesting();
+}
''');
}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_annotation_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_annotation_test.dart
new file mode 100644
index 0000000..1874b6f
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/fix/remove_annotation_test.dart
@@ -0,0 +1,178 @@
+// Copyright (c) 2019, 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:analyzer_plugin/utilities/fixes/fixes.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'fix_processor.dart';
+
+main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(RemoveAnnotationTest);
+ });
+}
+
+@reflectiveTest
+class RemoveAnnotationTest extends FixProcessorTest {
+ @override
+ FixKind get kind => DartFixKind.REMOVE_ANNOTATION;
+
+ @override
+ void setUp() {
+ super.setUp();
+ addMetaPackage();
+ }
+
+ test_factory() async {
+ await resolveTestUnit('''
+import 'package:meta/meta.dart';
+
+@factory
+f() {}
+''');
+ await assertHasFix('''
+import 'package:meta/meta.dart';
+
+f() {}
+''');
+ }
+
+ test_immutable() async {
+ await resolveTestUnit('''
+import 'package:meta/meta.dart';
+
+@immutable
+f() {}
+''');
+ await assertHasFix('''
+import 'package:meta/meta.dart';
+
+f() {}
+''');
+ }
+
+ test_literal() async {
+ await resolveTestUnit('''
+import 'package:meta/meta.dart';
+
+@literal
+f() {}
+''');
+ await assertHasFix('''
+import 'package:meta/meta.dart';
+
+f() {}
+''');
+ }
+
+ test_override_field() async {
+ await resolveTestUnit('''
+class A {
+ @override
+ String name;
+}
+''');
+ await assertHasFix('''
+class A {
+ String name;
+}
+''');
+ }
+
+ test_override_getter() async {
+ await resolveTestUnit('''
+class A {
+ @override
+ int get zero => 0;
+}
+''');
+ await assertHasFix('''
+class A {
+ int get zero => 0;
+}
+''');
+ }
+
+ test_override_method() async {
+ await resolveTestUnit('''
+class A {
+ @override
+ void m() {}
+}
+''');
+ await assertHasFix('''
+class A {
+ void m() {}
+}
+''');
+ }
+
+ test_override_setter() async {
+ await resolveTestUnit('''
+class A {
+ @override
+ set value(v) {}
+}
+''');
+ await assertHasFix('''
+class A {
+ set value(v) {}
+}
+''');
+ }
+
+ test_required_namedWithDefault() async {
+ await resolveTestUnit('''
+import 'package:meta/meta.dart';
+
+f({@required int x = 0}) {}
+''');
+ await assertHasFix('''
+import 'package:meta/meta.dart';
+
+f({int x = 0}) {}
+''');
+ }
+
+ test_required_positional() async {
+ await resolveTestUnit('''
+import 'package:meta/meta.dart';
+
+f([@required int x]) {}
+''');
+ await assertHasFix('''
+import 'package:meta/meta.dart';
+
+f([int x]) {}
+''');
+ }
+
+ test_required_required() async {
+ await resolveTestUnit('''
+import 'package:meta/meta.dart';
+
+f(@required int x) {}
+''');
+ await assertHasFix('''
+import 'package:meta/meta.dart';
+
+f(int x) {}
+''');
+ }
+
+ test_sealed() async {
+ await resolveTestUnit('''
+import 'package:meta/meta.dart';
+
+@sealed
+f() {}
+''');
+ await assertHasFix('''
+import 'package:meta/meta.dart';
+
+f() {}
+''');
+ }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_dead_code_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_dead_code_test.dart
index eec8f7c..7f2fc41 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/remove_dead_code_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/remove_dead_code_test.dart
@@ -19,6 +19,73 @@
@override
FixKind get kind => DartFixKind.REMOVE_DEAD_CODE;
+ test_catch_afterCatchAll_catch() async {
+ await resolveTestUnit('''
+main() {
+ try {
+ } catch (e) {
+ print('a');
+ } catch (e) {
+ print('b');
+ }
+}
+''');
+ await assertHasFix('''
+main() {
+ try {
+ } catch (e) {
+ print('a');
+ }
+}
+''');
+ }
+
+ test_catch_afterCatchAll_on() async {
+ await resolveTestUnit('''
+main() {
+ try {
+ } on Object {
+ print('a');
+ } catch (e) {
+ print('b');
+ }
+}
+''');
+ await assertHasFix('''
+main() {
+ try {
+ } on Object {
+ print('a');
+ }
+}
+''');
+ }
+
+ test_catch_subtype() async {
+ await resolveTestUnit('''
+class A {}
+class B extends A {}
+main() {
+ try {
+ } on A {
+ print('a');
+ } on B {
+ print('b');
+ }
+}
+''');
+ await assertHasFix('''
+class A {}
+class B extends A {}
+main() {
+ try {
+ } on A {
+ print('a');
+ }
+}
+''');
+ }
+
test_condition() async {
await resolveTestUnit('''
main(int p) {
diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_name_from_combinator_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_name_from_combinator_test.dart
new file mode 100644
index 0000000..3ed155e
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/fix/remove_name_from_combinator_test.dart
@@ -0,0 +1,357 @@
+// Copyright (c) 2018, 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:analyzer_plugin/utilities/fixes/fixes.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'fix_processor.dart';
+
+main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(RemoveNameFromCombinatorTest);
+ });
+}
+
+@reflectiveTest
+class RemoveNameFromCombinatorTest extends FixProcessorTest {
+ @override
+ FixKind get kind => DartFixKind.REMOVE_NAME_FROM_COMBINATOR;
+
+ test_duplicateHiddenName_last() async {
+ await resolveTestUnit('''
+import 'dart:math' hide cos, sin, sin;
+
+main() {
+ print(min(0, 1));
+}
+''');
+ await assertHasFix('''
+import 'dart:math' hide cos, sin;
+
+main() {
+ print(min(0, 1));
+}
+''');
+ }
+
+ test_duplicateHiddenName_middle() async {
+ await resolveTestUnit('''
+import 'dart:math' hide cos, cos, sin;
+
+main() {
+ print(min(0, 1));
+}
+''');
+ await assertHasFix('''
+import 'dart:math' hide cos, sin;
+
+main() {
+ print(min(0, 1));
+}
+''');
+ }
+
+ @failingTest
+ test_duplicateHiddenName_only_last() async {
+ // It appears that the hint does not detect names that are duplicated across
+ // multiple combinators.
+ await resolveTestUnit('''
+import 'dart:math' hide cos, sin hide sin;
+
+main() {
+ print(min(0, 1));
+}
+''');
+ await assertHasFix('''
+import 'dart:math' hide cos, sin;
+
+main() {
+ print(min(0, 1));
+}
+''');
+ }
+
+ @failingTest
+ test_duplicateHiddenName_only_middle() async {
+ // It appears that the hint does not detect names that are duplicated across
+ // multiple combinators.
+ await resolveTestUnit('''
+import 'dart:math' hide cos hide cos hide sin;
+
+main() {
+ print(min(0, 1));
+}
+''');
+ await assertHasFix('''
+import 'dart:math' hide cos hide sin;
+
+main() {
+ print(min(0, 1));
+}
+''');
+ }
+
+ test_duplicateShownName_last() async {
+ await resolveTestUnit(
+ '''
+import 'dart:math' show cos, sin, sin;
+
+f(x) {
+ print(cos(x) + sin(x));
+}
+''',
+ );
+ await assertHasFix('''
+import 'dart:math' show cos, sin;
+
+f(x) {
+ print(cos(x) + sin(x));
+}
+''');
+ }
+
+ test_duplicateShownName_middle() async {
+ await resolveTestUnit('''
+import 'dart:math' show cos, cos, sin;
+
+f(x) {
+ print(cos(x) + sin(x));
+}
+''');
+ await assertHasFix('''
+import 'dart:math' show cos, sin;
+
+f(x) {
+ print(cos(x) + sin(x));
+}
+''');
+ }
+
+ test_undefinedHiddenName_first() async {
+ await resolveTestUnit('''
+import 'dart:math' hide aaa, sin, tan;
+
+f(x) {
+ print(cos(x));
+}
+''');
+ await assertHasFix('''
+import 'dart:math' hide sin, tan;
+
+f(x) {
+ print(cos(x));
+}
+''');
+ }
+
+ test_undefinedHiddenName_last() async {
+ await resolveTestUnit('''
+import 'dart:math' hide cos, sin, xxx;
+
+f(x) {
+ print(tan(x));
+}
+''');
+ await assertHasFix('''
+import 'dart:math' hide cos, sin;
+
+f(x) {
+ print(tan(x));
+}
+''');
+ }
+
+ test_undefinedHiddenName_middle() async {
+ await resolveTestUnit('''
+import 'dart:math' hide cos, mmm, tan;
+
+f(x) {
+ print(sin(x));
+}
+''');
+ await assertHasFix('''
+import 'dart:math' hide cos, tan;
+
+f(x) {
+ print(sin(x));
+}
+''');
+ }
+
+ test_undefinedHiddenName_only_first() async {
+ await resolveTestUnit('''
+import 'dart:math' hide aaa hide cos, sin;
+
+main() {
+ print(min(0, 1));
+}
+''');
+ await assertHasFix('''
+import 'dart:math' hide cos, sin;
+
+main() {
+ print(min(0, 1));
+}
+''');
+ }
+
+ test_undefinedHiddenName_only_last() async {
+ await resolveTestUnit('''
+import 'dart:math' hide cos, sin hide aaa;
+
+main() {
+ print(min(0, 1));
+}
+''');
+ await assertHasFix('''
+import 'dart:math' hide cos, sin;
+
+main() {
+ print(min(0, 1));
+}
+''');
+ }
+
+ test_undefinedHiddenName_only_middle() async {
+ await resolveTestUnit('''
+import 'dart:math' hide cos hide aaa hide sin;
+
+main() {
+ print(min(0, 1));
+}
+''');
+ await assertHasFix('''
+import 'dart:math' hide cos hide sin;
+
+main() {
+ print(min(0, 1));
+}
+''');
+ }
+
+ test_undefinedHiddenName_only_only() async {
+ await resolveTestUnit('''
+import 'dart:math' hide aaa;
+var c = sin(0.3);
+''');
+ await assertHasFix('''
+import 'dart:math';
+var c = sin(0.3);
+''');
+ }
+
+ test_undefinedHiddenName_only_only_withAs() async {
+ await resolveTestUnit('''
+import 'dart:math' as math hide aaa;
+var c = math.sin(0.3);
+''');
+ await assertHasFix('''
+import 'dart:math' as math;
+var c = math.sin(0.3);
+''');
+ }
+
+ test_undefinedShownName_first() async {
+ await resolveTestUnit('''
+import 'dart:math' show aaa, sin, tan;
+
+f(x) {
+ print(sin(x) + tan(x));
+}
+''');
+ await assertHasFix('''
+import 'dart:math' show sin, tan;
+
+f(x) {
+ print(sin(x) + tan(x));
+}
+''');
+ }
+
+ test_undefinedShownName_last() async {
+ await resolveTestUnit('''
+import 'dart:math' show cos, sin, xxx;
+
+f(x) {
+ print(cos(x) + sin(x));
+}
+''');
+ await assertHasFix('''
+import 'dart:math' show cos, sin;
+
+f(x) {
+ print(cos(x) + sin(x));
+}
+''');
+ }
+
+ test_undefinedShownName_middle() async {
+ await resolveTestUnit('''
+import 'dart:math' show cos, mmm, tan;
+
+f(x) {
+ print(cos(x) + tan(x));
+}
+''');
+ await assertHasFix('''
+import 'dart:math' show cos, tan;
+
+f(x) {
+ print(cos(x) + tan(x));
+}
+''');
+ }
+
+ test_unusedShownName_first() async {
+ await resolveTestUnit('''
+import 'dart:math' show cos, sin, tan;
+
+f(x) {
+ print(sin(x) + tan(x));
+}
+''');
+ await assertHasFix('''
+import 'dart:math' show sin, tan;
+
+f(x) {
+ print(sin(x) + tan(x));
+}
+''');
+ }
+
+ test_unusedShownName_last() async {
+ await resolveTestUnit('''
+import 'dart:math' show cos, sin, tan;
+
+f(x) {
+ print(cos(x) + sin(x));
+}
+''');
+ await assertHasFix('''
+import 'dart:math' show cos, sin;
+
+f(x) {
+ print(cos(x) + sin(x));
+}
+''');
+ }
+
+ test_unusedShownName_middle() async {
+ await resolveTestUnit('''
+import 'dart:math' show cos, sin, tan;
+
+f(x) {
+ print(cos(x) + tan(x));
+}
+''');
+ await assertHasFix('''
+import 'dart:math' show cos, tan;
+
+f(x) {
+ print(cos(x) + tan(x));
+}
+''');
+ }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_unused_import_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_unused_import_test.dart
index 96ee02f..e7a4db9 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/remove_unused_import_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/remove_unused_import_test.dart
@@ -86,6 +86,24 @@
''');
}
+ test_duplicateImport() async {
+ await resolveTestUnit('''
+import 'dart:math';
+import 'dart:math';
+
+main() {
+ print(min(0, 1));
+}
+''');
+ await assertHasFix('''
+import 'dart:math';
+
+main() {
+ print(min(0, 1));
+}
+''');
+ }
+
test_multipleOfSame_all() async {
await resolveTestUnit('''
import 'dart:math';
diff --git a/pkg/analysis_server/test/src/services/correction/fix/test_all.dart b/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
index de6f043..30cad40 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
@@ -54,6 +54,7 @@
import 'make_field_not_final_test.dart' as make_field_not_final;
import 'make_final_test.dart' as make_final;
import 'move_type_arguments_to_class_test.dart' as move_type_arguments_to_class;
+import 'remove_annotation_test.dart' as remove_annotation;
import 'remove_await_test.dart' as remove_await;
import 'remove_dead_code_test.dart' as remove_dead_code;
import 'remove_empty_catch_test.dart' as remove_empty_catch;
@@ -64,6 +65,7 @@
import 'remove_initializer_test.dart' as remove_initializer;
import 'remove_interpolation_braces_test.dart' as remove_interpolation_braces;
import 'remove_method_declaration_test.dart' as remove_method_declaration;
+import 'remove_name_from_combinator_test.dart' as remove_name_from_combinator;
import 'remove_parameters_in_getter_declaration_test.dart'
as remove_parameters_in_getter_declaration;
import 'remove_parentheses_in_getter_invocation_test.dart'
@@ -141,6 +143,7 @@
make_field_not_final.main();
make_final.main();
move_type_arguments_to_class.main();
+ remove_annotation.main();
remove_await.main();
remove_dead_code.main();
remove_empty_catch.main();
@@ -150,6 +153,7 @@
remove_initializer.main();
remove_interpolation_braces.main();
remove_method_declaration.main();
+ remove_name_from_combinator.main();
remove_parameters_in_getter_declaration.main();
remove_parentheses_in_getter_invocation.main();
remove_this_expression.main();
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index 2147e57..a0c3768 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -338,8 +338,8 @@
}
} else if (element?.isSealed == true) {
if (!(parent is ClassDeclaration || parent is ClassTypeAlias)) {
- _errorReporter.reportErrorForNode(HintCode.INVALID_SEALED_ANNOTATION,
- node.parent, [node.element.name]);
+ _errorReporter.reportErrorForNode(
+ HintCode.INVALID_SEALED_ANNOTATION, node, [node.element.name]);
}
}
super.visitAnnotation(node);
@@ -3382,26 +3382,31 @@
}
}
- /// Report an [HintCode.UNUSED_SHOWN_NAME] hint for each unused shown name.
+ /// Use the error [reporter] to report an [HintCode.UNUSED_SHOWN_NAME] hint
+ /// for each unused shown name.
///
- /// Only call this method after all of the compilation units have been visited
- /// by this visitor.
- ///
- /// @param errorReporter the error reporter used to report the set of
- /// [HintCode.UNUSED_SHOWN_NAME] hints
+ /// This method should only be invoked after all of the compilation units have
+ /// been visited by this visitor.
void generateUnusedShownNameHints(ErrorReporter reporter) {
_unusedShownNamesMap.forEach(
(ImportDirective importDirective, List<SimpleIdentifier> identifiers) {
if (_unusedImports.contains(importDirective)) {
- // This import is actually wholly unused, not just one or more shown names from it.
- // This is then an "unused import", rather than unused shown names.
+ // The whole import is unused, not just one or more shown names from it,
+ // so an "unused_import" hint will be generated, making it unnecessary
+ // to generate hints for the individual names.
return;
}
int length = identifiers.length;
for (int i = 0; i < length; i++) {
Identifier identifier = identifiers[i];
- reporter.reportErrorForNode(
- HintCode.UNUSED_SHOWN_NAME, identifier, [identifier.name]);
+ List<SimpleIdentifier> duplicateNames =
+ _duplicateShownNamesMap[importDirective];
+ if (duplicateNames == null || !duplicateNames.contains(identifier)) {
+ // Only generate a hint if we won't also generate a
+ // "duplicate_shown_name" hint for the same identifier.
+ reporter.reportErrorForNode(
+ HintCode.UNUSED_SHOWN_NAME, identifier, [identifier.name]);
+ }
}
});
}
diff --git a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
index b68d13e..7420ee9 100644
--- a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
+++ b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
@@ -435,6 +435,7 @@
external double cos(num radians);
external double sin(num radians);
external double sqrt(num radians);
+external double tan(num radians);
class Random {
bool nextBool() => true;
double nextDouble() => 2.0;