Skip to content

Commit 74804fd

Browse files
FMorschelCommit Queue
authored andcommitted
[DAS] Add fix for swapping division operators when undefined
[email protected] Fixes #55512 Change-Id: Ifb8822f98483e68afa21b3a5a49045149604fa55 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/388074 Commit-Queue: Phil Quitslund <[email protected]> Reviewed-by: Phil Quitslund <[email protected]> Reviewed-by: Samuel Rawlins <[email protected]> Auto-Submit: Felipe Morschel <[email protected]> Commit-Queue: Samuel Rawlins <[email protected]>
1 parent 3ef3190 commit 74804fd

File tree

5 files changed

+504
-0
lines changed

5 files changed

+504
-0
lines changed
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
// Copyright (c) 2024, 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:analysis_server/src/services/correction/fix.dart';
6+
import 'package:analysis_server/src/utilities/extensions/element.dart';
7+
import 'package:analysis_server_plugin/edit/dart/correction_producer.dart';
8+
import 'package:analyzer/dart/ast/ast.dart';
9+
import 'package:analyzer/dart/ast/token.dart';
10+
import 'package:analyzer/dart/element/element.dart';
11+
import 'package:analyzer/dart/element/type.dart';
12+
import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
13+
import 'package:analyzer/src/dart/resolver/applicable_extensions.dart';
14+
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
15+
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
16+
import 'package:analyzer_plugin/utilities/range_factory.dart';
17+
18+
class UseDifferentDivisionOperator extends MultiCorrectionProducer {
19+
UseDifferentDivisionOperator({required super.context});
20+
21+
@override
22+
Future<List<ResolvedCorrectionProducer>> get producers async {
23+
var exp = node;
24+
if (exp case BinaryExpression _) {
25+
return switch (exp.operator.type) {
26+
TokenType.SLASH => [
27+
_UseDifferentDivisionOperator(
28+
context: context,
29+
fixKind: DartFixKind.USE_EFFECTIVE_INTEGER_DIVISION)
30+
],
31+
TokenType.TILDE_SLASH => [
32+
_UseDifferentDivisionOperator(
33+
context: context, fixKind: DartFixKind.USE_DIVISION)
34+
],
35+
_ => const []
36+
};
37+
} else if (exp case AssignmentExpression _) {
38+
return switch (exp.operator.type) {
39+
TokenType.SLASH_EQ => [
40+
_UseDifferentDivisionOperator(
41+
context: context,
42+
fixKind: DartFixKind.USE_EFFECTIVE_INTEGER_DIVISION)
43+
],
44+
TokenType.TILDE_SLASH_EQ => [
45+
_UseDifferentDivisionOperator(
46+
context: context, fixKind: DartFixKind.USE_DIVISION)
47+
],
48+
_ => const []
49+
};
50+
}
51+
return const [];
52+
}
53+
}
54+
55+
enum _DivisionOperator {
56+
division,
57+
effectiveIntegerDivision,
58+
}
59+
60+
class _UseDifferentDivisionOperator extends ResolvedCorrectionProducer {
61+
@override
62+
final FixKind fixKind;
63+
final CorrectionProducerContext _context;
64+
65+
_UseDifferentDivisionOperator({required super.context, required this.fixKind})
66+
: _context = context;
67+
68+
@override
69+
CorrectionApplicability get applicability =>
70+
CorrectionApplicability.singleLocation;
71+
72+
@override
73+
Future<void> compute(ChangeBuilder builder) async {
74+
var exp = node;
75+
DartType? leftType;
76+
Token operator;
77+
if (exp case BinaryExpression _) {
78+
leftType = exp.leftOperand.staticType;
79+
operator = exp.operator;
80+
} else if (exp case AssignmentExpression _) {
81+
leftType = exp.writeType;
82+
operator = exp.operator;
83+
} else {
84+
return;
85+
}
86+
if (leftType == null) {
87+
return;
88+
}
89+
var operators = leftType.divisionOperators;
90+
var otherOperator = switch (operator.type) {
91+
TokenType.SLASH => TokenType.TILDE_SLASH,
92+
TokenType.TILDE_SLASH => TokenType.SLASH,
93+
TokenType.SLASH_EQ => TokenType.TILDE_SLASH_EQ,
94+
TokenType.TILDE_SLASH_EQ => TokenType.SLASH_EQ,
95+
_ => null,
96+
};
97+
if (otherOperator == null) {
98+
return;
99+
}
100+
// All extensions available in the current scope for the left operand that
101+
// define the other division operator.
102+
var name = Name(
103+
_context.dartFixContext!.resolvedResult.libraryElement.source.uri,
104+
otherOperator.lexeme);
105+
var hasNoExtensionWithOtherDivisionOperator = await _context.dartFixContext!
106+
.librariesWithExtensions(otherOperator.lexeme)
107+
.where((library) {
108+
return library.exportedExtensions
109+
.havingMemberWithBaseName(name)
110+
.applicableTo(targetLibrary: libraryElement, targetType: leftType!)
111+
.isNotEmpty;
112+
}).isEmpty;
113+
if (hasNoExtensionWithOtherDivisionOperator && operators.isEmpty) {
114+
return;
115+
}
116+
if (operators.length > 1) {
117+
return;
118+
}
119+
await builder.addDartFileEdit(file, (builder) {
120+
builder.addSimpleReplacement(range.token(operator), otherOperator.lexeme);
121+
});
122+
}
123+
}
124+
125+
extension on DartType {
126+
Set<_DivisionOperator> get divisionOperators {
127+
// See operators defined for this type element.
128+
if (element case InterfaceElement interfaceElement) {
129+
return {
130+
for (var method in interfaceElement.methods)
131+
// No need to test for eq operators, as they are not explicitly defined.
132+
if (method.name == TokenType.SLASH.lexeme)
133+
_DivisionOperator.division
134+
else if (method.name == TokenType.TILDE_SLASH.lexeme)
135+
_DivisionOperator.effectiveIntegerDivision,
136+
...interfaceElement.allSupertypes
137+
.expand((type) => type.divisionOperators)
138+
};
139+
} else if (element case TypeParameterElement typeParameterElement) {
140+
return typeParameterElement.bound?.divisionOperators ?? const {};
141+
}
142+
143+
return const {};
144+
}
145+
}

pkg/analysis_server/lib/src/services/correction/fix.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1969,6 +1969,11 @@ abstract final class DartFixKind {
19691969
DartFixKindPriority.standard,
19701970
'Update the SDK constraints',
19711971
);
1972+
static const USE_DIVISION = FixKind(
1973+
'dart.fix.use.division',
1974+
DartFixKindPriority.standard,
1975+
'Use / instead of undefined ~/',
1976+
);
19721977
static const USE_EFFECTIVE_INTEGER_DIVISION = FixKind(
19731978
'dart.fix.use.effectiveIntegerDivision',
19741979
DartFixKindPriority.standard,

pkg/analysis_server/lib/src/services/correction/fix_internal.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ import 'package:analysis_server/src/services/correction/dart/split_multiple_decl
233233
import 'package:analysis_server/src/services/correction/dart/surround_with_parentheses.dart';
234234
import 'package:analysis_server/src/services/correction/dart/update_sdk_constraints.dart';
235235
import 'package:analysis_server/src/services/correction/dart/use_curly_braces.dart';
236+
import 'package:analysis_server/src/services/correction/dart/use_different_division_operator.dart';
236237
import 'package:analysis_server/src/services/correction/dart/use_effective_integer_division.dart';
237238
import 'package:analysis_server/src/services/correction/dart/use_eq_eq_null.dart';
238239
import 'package:analysis_server/src/services/correction/dart/use_is_not_empty.dart';
@@ -881,6 +882,7 @@ final _builtInNonLintMultiProducers = {
881882
],
882883
CompileTimeErrorCode.UNDEFINED_OPERATOR: [
883884
ImportLibrary.forExtensionMember,
885+
UseDifferentDivisionOperator.new,
884886
],
885887
CompileTimeErrorCode.UNDEFINED_PREFIXED_NAME: [
886888
DataDriven.new,

pkg/analysis_server/test/src/services/correction/fix/test_all.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,8 @@ import 'split_multiple_declarations_test.dart'
291291
import 'surround_with_parentheses_test.dart' as surround_with_parentheses;
292292
import 'update_sdk_constraints_test.dart' as update_sdk_constraints;
293293
import 'use_curly_braces_test.dart' as use_curly_braces;
294+
import 'use_different_division_operator_test.dart'
295+
as use_different_division_operator;
294296
import 'use_effective_integer_division_test.dart'
295297
as use_effective_integer_division;
296298
import 'use_eq_eq_null_test.dart' as use_eq_eq_null;
@@ -549,6 +551,7 @@ void main() {
549551
surround_with_parentheses.main();
550552
update_sdk_constraints.main();
551553
use_curly_braces.main();
554+
use_different_division_operator.main();
552555
use_effective_integer_division.main();
553556
use_eq_eq_null.main();
554557
use_is_not_empty.main();

0 commit comments

Comments
 (0)