Support for converting 'if' statements with bool conditions into patterns.

Bug: https://github.com/dart-lang/sdk/issues/52068
Change-Id: I187b3e3cad66eff7a266478df0c09ffb3d642e34
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/296067
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
Konstantin Shcheglov 2023-04-19 17:24:54 +00:00 committed by Commit Queue
parent dc34fe9f86
commit ac1bdebff7
4 changed files with 156 additions and 25 deletions

View file

@ -40,7 +40,7 @@ class ConvertIfStatementToSwitchStatement extends CorrectionProducer {
await builder.addDartFileEdit(file, (builder) {
builder.addReplacement(range.node(ifStatement), (builder) {
final expressionCode = firstThen.identifier;
final expressionCode = firstThen.expressionCode;
builder.writeln('switch ($expressionCode) {');
for (final case_ in cases) {
@ -69,26 +69,11 @@ class ConvertIfStatementToSwitchStatement extends CorrectionProducer {
}
List<_IfCase>? _buildCases(IfStatement ifStatement) {
final expression = ifStatement.expression;
if (expression is! SimpleIdentifier) {
final thenCase = _buildThenCase(ifStatement);
if (thenCase == null) {
return null;
}
final String patternCode;
final caseClause = ifStatement.caseClause;
if (caseClause != null) {
patternCode = utils.getNodeText(caseClause.guardedPattern);
} else {
// TODO(scheglov) support converting conditions to patterns
return null;
}
final thenCase = _IfCaseThen(
identifier: expression.token,
patternCode: patternCode,
statement: ifStatement.thenStatement,
);
final cases = <_IfCase>[];
cases.add(thenCase);
@ -100,7 +85,7 @@ class ConvertIfStatementToSwitchStatement extends CorrectionProducer {
}
for (final elseCase in elseCases) {
if (elseCase is _IfCaseThen) {
if (elseCase.identifier.lexeme != thenCase.identifier.lexeme) {
if (elseCase.expressionCode != thenCase.expressionCode) {
return null;
}
}
@ -117,6 +102,36 @@ class ConvertIfStatementToSwitchStatement extends CorrectionProducer {
return cases;
}
_IfCaseThen? _buildThenCase(IfStatement ifStatement) {
final expression = ifStatement.expression;
final caseClause = ifStatement.caseClause;
if (caseClause != null) {
if (expression is! SimpleIdentifier) {
return null;
}
final guardedPattern = caseClause.guardedPattern;
final patternCode = utils.getNodeText(guardedPattern);
return _IfCaseThen(
expressionCode: expression.token.lexeme,
patternCode: patternCode,
statement: ifStatement.thenStatement,
);
}
// The expression is the bool condition.
final result = utils.patternOfBoolCondition(expression);
if (result == null) {
return null;
}
return _IfCaseThen(
expressionCode: result.expressionCode,
patternCode: result.patternCode,
statement: ifStatement.thenStatement,
);
}
/// Writes [statement], if it is a [Block], inlines it.
void _writeStatement({
required DartEditBuilder builder,
@ -356,11 +371,11 @@ class _IfCaseElse extends _IfCase {
}
class _IfCaseThen extends _IfCase {
final Token identifier;
final String expressionCode;
final String patternCode;
_IfCaseThen({
required this.identifier,
required this.expressionCode,
required this.patternCode,
required super.statement,
});

View file

@ -1038,6 +1038,26 @@ class CorrectionUtils {
return InsertionLocation(prefix, offset, suffix);
}
ExpressionCasePattern? patternOfBoolCondition(Expression node) {
if (node is BinaryExpression) {
if (node.isNotEqNull) {
final expressionCode = getNodeText(node.leftOperand);
return ExpressionCasePattern(
expressionCode: expressionCode,
patternCode: '_?',
);
}
} else if (node is IsExpression) {
final expressionCode = getNodeText(node.expression);
final typeCode = getNodeText(node.type);
return ExpressionCasePattern(
expressionCode: expressionCode,
patternCode: '$typeCode()',
);
}
return null;
}
InsertionLocation? prepareEnumNewConstructorLocation(
EnumDeclaration enumDeclaration,
) {
@ -1502,6 +1522,16 @@ class CorrectionUtils_InsertDesc {
String suffix = '';
}
class ExpressionCasePattern {
final String expressionCode;
final String patternCode;
ExpressionCasePattern({
required this.expressionCode,
required this.patternCode,
});
}
class InsertionLocation {
final String prefix;
final int offset;

View file

@ -122,6 +122,12 @@ extension AstNodeExtensions on AstNode {
}
}
extension BinaryExpressionExtensions on BinaryExpression {
bool get isNotEqNull {
return operator.type == TokenType.BANG_EQ && rightOperand is NullLiteral;
}
}
extension CompilationUnitExtension on CompilationUnit {
/// Return the list of tokens that comprise the file header comment for this
/// compilation unit.

View file

@ -20,7 +20,7 @@ class ConvertIfStatementToSwitchStatementTest extends AssistProcessorTest {
@override
AssistKind get kind => DartAssistKind.CONVERT_TO_SWITCH_STATEMENT;
Future<void> test_chain2_case_case_differentIdentifier() async {
Future<void> test_chain_case2_differentIdentifier() async {
await resolveTestCode('''
void f(Object? x, Object? y) {
if (x case int()) {
@ -33,7 +33,7 @@ void f(Object? x, Object? y) {
await assertNoAssistAt('if');
}
Future<void> test_chain2_case_case_elseBlock() async {
Future<void> test_chain_case2_elseBlock() async {
await resolveTestCode('''
void f(Object? x) {
if (x case int()) {
@ -59,7 +59,7 @@ void f(Object? x) {
''');
}
Future<void> test_chain2_case_case_noElse() async {
Future<void> test_chain_case2_noElse() async {
await resolveTestCode('''
void f(Object? x) {
if (x case int()) {
@ -81,7 +81,7 @@ void f(Object? x) {
''');
}
Future<void> test_chain2_case_case_notIdentifier() async {
Future<void> test_chain_case2_notIdentifier() async {
await resolveTestCode('''
void f(Object? x) {
if (x case int()) {
@ -94,6 +94,50 @@ void f(Object? x) {
await assertNoAssistAt('if');
}
Future<void> test_chain_case_expression() async {
await resolveTestCode('''
void f(Object? x) {
if (x case int()) {
0;
} else if (x is double) {
1;
}
}
''');
await assertHasAssistAt('if', '''
void f(Object? x) {
switch (x) {
case int():
0;
case double():
1;
}
}
''');
}
Future<void> test_chain_expression2() async {
await resolveTestCode('''
void f(Object? x) {
if (x is int) {
0;
} else if (x is double) {
1;
}
}
''');
await assertHasAssistAt('if', '''
void f(Object? x) {
switch (x) {
case int():
0;
case double():
1;
}
}
''');
}
Future<void> test_single_case_thenBlock() async {
await resolveTestCode('''
void f(Object? x) {
@ -191,6 +235,42 @@ void f(Object? x) {
''');
}
Future<void> test_single_expression_isType() async {
await resolveTestCode('''
void f(Object? x) {
if (x is List<int>) {
0;
}
}
''');
await assertHasAssistAt('if', '''
void f(Object? x) {
switch (x) {
case List<int>():
0;
}
}
''');
}
Future<void> test_single_expression_notEqNull() async {
await resolveTestCode('''
void f(Object? x) {
if (x != null) {
0;
}
}
''');
await assertHasAssistAt('if', '''
void f(Object? x) {
switch (x) {
case _?:
0;
}
}
''');
}
Future<void> test_single_expression_notSupported() async {
await resolveTestCode('''
void f(Object? x) {