diff --git a/pkg/analysis_server/lib/src/services/correction/dart/remove_character.dart b/pkg/analysis_server/lib/src/services/correction/dart/remove_character.dart new file mode 100644 index 00000000000..58f1448da5b --- /dev/null +++ b/pkg/analysis_server/lib/src/services/correction/dart/remove_character.dart @@ -0,0 +1,41 @@ +// Copyright (c) 2022, 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_plugin/utilities/change_builder/change_builder_core.dart'; +import 'package:analyzer_plugin/utilities/fixes/fixes.dart'; +import 'package:analyzer_plugin/utilities/range_factory.dart'; + +class RemoveCharacter extends CorrectionProducer { + String _codePoint = ''; + + @override + // Not predictably the correct action. + bool get canBeAppliedInBulk => false; + + @override + // Not predictably the correct action. + bool get canBeAppliedToFile => false; + + @override + List get fixArguments => [_codePoint]; + + @override + FixKind get fixKind => DartFixKind.REMOVE_CHARACTER; + + @override + Future compute(ChangeBuilder builder) async { + var problemMessage = diagnostic?.problemMessage; + if (problemMessage == null) return; + + var offset = problemMessage.offset; + var content = resolvedResult.content; + var codeUnit = content.codeUnitAt(offset); + _codePoint = codeUnit.toRadixString(16).toUpperCase(); + await builder.addDartFileEdit(file, (builder) { + builder.addDeletion(range.startOffsetEndOffset(offset, offset + 1)); + }); + } +} diff --git a/pkg/analysis_server/lib/src/services/correction/dart/replace_with_unicode_escape.dart b/pkg/analysis_server/lib/src/services/correction/dart/replace_with_unicode_escape.dart new file mode 100644 index 00000000000..02dd0fc1097 --- /dev/null +++ b/pkg/analysis_server/lib/src/services/correction/dart/replace_with_unicode_escape.dart @@ -0,0 +1,37 @@ +// Copyright (c) 2022, 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_plugin/utilities/change_builder/change_builder_core.dart'; +import 'package:analyzer_plugin/utilities/fixes/fixes.dart'; +import 'package:analyzer_plugin/utilities/range_factory.dart'; + +class ReplaceWithUnicodeEscape extends CorrectionProducer { + @override + // Not predictably the correct action. + bool get canBeAppliedInBulk => false; + + @override + // Not predictably the correct action. + bool get canBeAppliedToFile => false; + + @override + FixKind get fixKind => DartFixKind.REPLACE_WITH_UNICODE_ESCAPE; + + @override + Future compute(ChangeBuilder builder) async { + var problemMessage = diagnostic?.problemMessage; + if (problemMessage == null) return; + + var offset = problemMessage.offset; + var content = resolvedResult.content; + var codeUnit = content.codeUnitAt(offset); + var code = codeUnit.toRadixString(16).toUpperCase(); + await builder.addDartFileEdit(file, (builder) { + builder.addSimpleReplacement( + range.startOffsetEndOffset(offset, offset + 1), '\\u$code'); + }); + } +} diff --git a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml index 1dbccc70dbf..7d78ddda71f 100644 --- a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml +++ b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml @@ -1642,11 +1642,9 @@ HintCode.UNNECESSARY_TYPE_CHECK_TRUE: status: needsFix issue: https://github.com/dart-lang/sdk/issues/47793 HintCode.TEXT_DIRECTION_CODE_POINT_IN_COMMENT: - status: needsFix - since: ~2.16 + status: hasFix HintCode.TEXT_DIRECTION_CODE_POINT_IN_LITERAL: - status: needsFix - since: ~2.16 + status: hasFix HintCode.UNUSED_CATCH_CLAUSE: status: hasFix HintCode.UNUSED_CATCH_STACK: diff --git a/pkg/analysis_server/lib/src/services/correction/fix.dart b/pkg/analysis_server/lib/src/services/correction/fix.dart index 633a16c1c39..e9dd3996e7e 100644 --- a/pkg/analysis_server/lib/src/services/correction/fix.dart +++ b/pkg/analysis_server/lib/src/services/correction/fix.dart @@ -924,6 +924,11 @@ class DartFixKind { DartFixKindPriority.IN_FILE, 'Remove awaits in file', ); + static const REMOVE_CHARACTER = FixKind( + 'dart.fix.remove.character', + DartFixKindPriority.DEFAULT, + "Remove the 'U+{0}' code point", + ); static const REMOVE_COMPARISON = FixKind( 'dart.fix.remove.comparison', DartFixKindPriority.DEFAULT, @@ -1595,6 +1600,11 @@ class DartFixKind { DartFixKindPriority.IN_FILE, 'Replace function literals with tear-offs everywhere in file', ); + static const REPLACE_WITH_UNICODE_ESCAPE = FixKind( + 'dart.fix.replace.withUnicodeEscape', + DartFixKindPriority.DEFAULT, + "Replace with Unicode escape", + ); static const REPLACE_WITH_VAR = FixKind( 'dart.fix.replace.withVar', DartFixKindPriority.DEFAULT, 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 3d657a18f72..4fcbac0b897 100644 --- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart +++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart @@ -117,6 +117,7 @@ import 'package:analysis_server/src/services/correction/dart/remove_argument.dar import 'package:analysis_server/src/services/correction/dart/remove_assertion.dart'; import 'package:analysis_server/src/services/correction/dart/remove_assignment.dart'; import 'package:analysis_server/src/services/correction/dart/remove_await.dart'; +import 'package:analysis_server/src/services/correction/dart/remove_character.dart'; import 'package:analysis_server/src/services/correction/dart/remove_comparison.dart'; import 'package:analysis_server/src/services/correction/dart/remove_const.dart'; import 'package:analysis_server/src/services/correction/dart/remove_constructor_name.dart'; @@ -192,6 +193,7 @@ import 'package:analysis_server/src/services/correction/dart/replace_with_is_emp import 'package:analysis_server/src/services/correction/dart/replace_with_not_null_aware.dart'; import 'package:analysis_server/src/services/correction/dart/replace_with_null_aware.dart'; import 'package:analysis_server/src/services/correction/dart/replace_with_tear_off.dart'; +import 'package:analysis_server/src/services/correction/dart/replace_with_unicode_escape.dart'; import 'package:analysis_server/src/services/correction/dart/replace_with_var.dart'; import 'package:analysis_server/src/services/correction/dart/sort_child_property_last.dart'; import 'package:analysis_server/src/services/correction/dart/sort_combinators.dart'; @@ -1416,6 +1418,14 @@ class FixProcessor extends BaseProcessor { HintCode.OVERRIDE_ON_NON_OVERRIDING_SETTER: [ RemoveAnnotation.new, ], + HintCode.TEXT_DIRECTION_CODE_POINT_IN_COMMENT: [ + RemoveCharacter.new, + ReplaceWithUnicodeEscape.new, + ], + HintCode.TEXT_DIRECTION_CODE_POINT_IN_LITERAL: [ + RemoveCharacter.new, + ReplaceWithUnicodeEscape.new, + ], // TODO(brianwilkerson) Add a fix to normalize the path. // HintCode.PACKAGE_IMPORT_CONTAINS_DOT_DOT: [], HintCode.TYPE_CHECK_IS_NOT_NULL: [ diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_character_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_character_test.dart new file mode 100644 index 00000000000..3e8ba959ed2 --- /dev/null +++ b/pkg/analysis_server/test/src/services/correction/fix/remove_character_test.dart @@ -0,0 +1,39 @@ +// Copyright (c) 2022, 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'; + +void main() { + defineReflectiveSuite(() { + defineReflectiveTests(RemoveCharacterTest); + }); +} + +@reflectiveTest +class RemoveCharacterTest extends FixProcessorTest { + @override + FixKind get kind => DartFixKind.REMOVE_CHARACTER; + + Future test_comment() async { + await resolveTestCode(''' +// some\u2066thing +'''); + await assertHasFix(''' +// something +'''); + } + + Future test_literal() async { + await resolveTestCode(''' +var ch = '\u202A'; +'''); + await assertHasFix(''' +var ch = ''; +'''); + } +} diff --git a/pkg/analysis_server/test/src/services/correction/fix/replace_with_unicode_escape_test.dart b/pkg/analysis_server/test/src/services/correction/fix/replace_with_unicode_escape_test.dart new file mode 100644 index 00000000000..6d97cb736fa --- /dev/null +++ b/pkg/analysis_server/test/src/services/correction/fix/replace_with_unicode_escape_test.dart @@ -0,0 +1,39 @@ +// Copyright (c) 2022, 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'; + +void main() { + defineReflectiveSuite(() { + defineReflectiveTests(ReplaceWithUnicodeEscapeTest); + }); +} + +@reflectiveTest +class ReplaceWithUnicodeEscapeTest extends FixProcessorTest { + @override + FixKind get kind => DartFixKind.REPLACE_WITH_UNICODE_ESCAPE; + + Future test_comment() async { + await resolveTestCode(''' +// some\u2066thing +'''); + await assertHasFix(r''' +// some\u2066thing +'''); + } + + Future test_literal() async { + await resolveTestCode(''' +var ch = '\u202A'; +'''); + await assertHasFix(r''' +var ch = '\u202A'; +'''); + } +} 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 e080dfdb167..7a67ea33ded 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 @@ -145,6 +145,7 @@ import 'remove_argument_test.dart' as remove_argument; import 'remove_assertion_test.dart' as remove_assertion; import 'remove_assignment_test.dart' as remove_assignment; import 'remove_await_test.dart' as remove_await; +import 'remove_character_test.dart' as remove_character; import 'remove_comparison_test.dart' as remove_comparison; import 'remove_const_test.dart' as remove_const; import 'remove_constructor_name_test.dart' as remove_constructor_name; @@ -234,6 +235,7 @@ import 'replace_with_is_not_empty_test.dart' as replace_with_is_not_empty; import 'replace_with_not_null_aware_test.dart' as replace_with_not_null_aware; import 'replace_with_null_aware_test.dart' as replace_with_null_aware; import 'replace_with_tear_off_test.dart' as replace_with_tear_off; +import 'replace_with_unicode_escape_test.dart' as replace_with_unicode_escape_; import 'replace_with_var_test.dart' as replace_with_var; import 'sort_child_property_last_test.dart' as sort_properties_last; import 'sort_combinators_test.dart' as sort_combinators_test; @@ -377,6 +379,7 @@ void main() { remove_assertion.main(); remove_assignment.main(); remove_await.main(); + remove_character.main(); remove_comparison.main(); remove_const.main(); remove_constructor_name.main(); @@ -454,6 +457,7 @@ void main() { replace_with_not_null_aware.main(); replace_with_null_aware.main(); replace_with_tear_off.main(); + replace_with_unicode_escape_.main(); replace_with_var.main(); sort_properties_last.main(); sort_constructor_first_test.main();