Skip to content

Commit 13f6d82

Browse files
stereotype441commit-bot@chromium.org
authored andcommitted
Migration: add FixBuilder support for prefix/postfix expressions.
Change-Id: I62a705af1c8d9944a417d074373aca4c93e99b6d Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/121402 Reviewed-by: Mike Fairhurst <mfairhurst@google.com>
1 parent 0121a1f commit 13f6d82

File tree

2 files changed

+317
-0
lines changed

2 files changed

+317
-0
lines changed

pkg/nnbd_migration/lib/src/fix_builder.dart

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,49 @@ abstract class FixBuilder extends GeneralizingAstVisitor<DartType> {
334334
return result;
335335
}
336336

337+
@override
338+
DartType visitPostfixExpression(PostfixExpression node) {
339+
if (node.operator.type == TokenType.BANG) {
340+
throw UnimplementedError(
341+
'TODO(paulberry): re-migration of already migrated code not '
342+
'supported yet');
343+
} else {
344+
var targetInfo = visitAssignmentTarget(node.operand);
345+
_handleIncrementOrDecrement(node.staticElement, targetInfo, node);
346+
return targetInfo.readType;
347+
}
348+
}
349+
350+
@override
351+
DartType visitPrefixExpression(PrefixExpression node) {
352+
var operand = node.operand;
353+
switch (node.operator.type) {
354+
case TokenType.BANG:
355+
visitSubexpression(operand, _typeProvider.boolType);
356+
_flowAnalysis.logicalNot_end(node, operand);
357+
return _typeProvider.boolType;
358+
case TokenType.MINUS:
359+
case TokenType.TILDE:
360+
var targetType = visitSubexpression(operand, _typeProvider.objectType);
361+
var staticElement = node.staticElement;
362+
if (staticElement == null) {
363+
return _typeProvider.dynamicType;
364+
} else {
365+
var methodType =
366+
_computeMigratedType(staticElement, targetType: targetType)
367+
as FunctionType;
368+
return methodType.returnType;
369+
}
370+
break;
371+
case TokenType.PLUS_PLUS:
372+
case TokenType.MINUS_MINUS:
373+
return _handleIncrementOrDecrement(
374+
node.staticElement, visitAssignmentTarget(operand), node);
375+
default:
376+
throw StateError('Unexpected prefix operator: ${node.operator}');
377+
}
378+
}
379+
337380
@override
338381
DartType visitSimpleIdentifier(SimpleIdentifier node) {
339382
assert(!node.inSetterContext(),
@@ -496,6 +539,26 @@ abstract class FixBuilder extends GeneralizingAstVisitor<DartType> {
496539
}
497540
}
498541

542+
DartType _handleIncrementOrDecrement(MethodElement combiner,
543+
AssignmentTargetInfo targetInfo, Expression node) {
544+
DartType combinedType;
545+
if (combiner == null) {
546+
combinedType = _typeProvider.dynamicType;
547+
} else {
548+
if (_typeSystem.isNullable(targetInfo.readType)) {
549+
addProblem(node, const CompoundAssignmentReadNullable());
550+
}
551+
var combinerType = _computeMigratedType(combiner) as FunctionType;
552+
combinedType = _fixNumericTypes(combinerType.returnType, node.staticType);
553+
}
554+
if (_doesAssignmentNeedCheck(
555+
from: combinedType, to: targetInfo.writeType)) {
556+
addProblem(node, const CompoundAssignmentCombinedNullable());
557+
combinedType = _typeSystem.promoteToNonNull(combinedType as TypeImpl);
558+
}
559+
return combinedType;
560+
}
561+
499562
/// Visits all the type arguments in a [TypeArgumentList] and returns the
500563
/// types they ger migrated to.
501564
List<DartType> _visitTypeArgumentList(TypeArgumentList arguments) =>

pkg/nnbd_migration/test/fix_builder_test.dart

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -698,6 +698,260 @@ _f(bool/*?*/ x) => ((x) != (null)) && x;
698698
visitSubexpression(findNode.binary('&&'), 'bool');
699699
}
700700

701+
test_postfixExpression_combined_nullable_noProblem() async {
702+
await analyze('''
703+
abstract class _C {
704+
_D/*?*/ operator+(int/*!*/ value);
705+
}
706+
abstract class _D extends _C {}
707+
abstract class _E {
708+
_C/*!*/ get x;
709+
void set x(_C/*?*/ value);
710+
f() => x++;
711+
}
712+
''');
713+
visitSubexpression(findNode.postfix('++'), '_C');
714+
}
715+
716+
@FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/38833')
717+
test_postfixExpression_combined_nullable_noProblem_dynamic() async {
718+
await analyze('''
719+
abstract class _E {
720+
dynamic get x;
721+
void set x(Object/*!*/ value);
722+
f() => x++;
723+
}
724+
''');
725+
visitSubexpression(findNode.postfix('++'), 'dynamic');
726+
}
727+
728+
test_postfixExpression_combined_nullable_problem() async {
729+
await analyze('''
730+
abstract class _C {
731+
_D/*?*/ operator+(int/*!*/ value);
732+
}
733+
abstract class _D extends _C {}
734+
abstract class _E {
735+
_C/*!*/ get x;
736+
void set x(_C/*!*/ value);
737+
f() => x++;
738+
}
739+
''');
740+
var postfix = findNode.postfix('++');
741+
visitSubexpression(postfix, '_C', problems: {
742+
postfix: {const CompoundAssignmentCombinedNullable()}
743+
});
744+
}
745+
746+
test_postfixExpression_dynamic() async {
747+
await analyze('''
748+
_f(dynamic x) => x++;
749+
''');
750+
visitSubexpression(findNode.postfix('++'), 'dynamic');
751+
}
752+
753+
test_postfixExpression_lhs_nullable_problem() async {
754+
await analyze('''
755+
abstract class _C {
756+
_D/*!*/ operator+(int/*!*/ value);
757+
}
758+
abstract class _D extends _C {}
759+
abstract class _E {
760+
_C/*?*/ get x;
761+
void set x(_C/*?*/ value);
762+
f() => x++;
763+
}
764+
''');
765+
var postfix = findNode.postfix('++');
766+
visitSubexpression(postfix, '_C?', problems: {
767+
postfix: {const CompoundAssignmentReadNullable()}
768+
});
769+
}
770+
771+
test_postfixExpression_rhs_nonNullable() async {
772+
await analyze('''
773+
abstract class _C {
774+
_D/*!*/ operator+(int/*!*/ value);
775+
}
776+
abstract class _D extends _C {}
777+
_f(_C/*!*/ x) => x++;
778+
''');
779+
visitSubexpression(findNode.postfix('++'), '_C');
780+
}
781+
782+
test_prefixExpression_bang_flow() async {
783+
await analyze('''
784+
_f(int/*?*/ x) {
785+
if (!(x == null)) {
786+
x + 1;
787+
}
788+
}
789+
''');
790+
// No null check should be needed on `x + 1` because `!(x == null)` promotes
791+
// x's type to `int`.
792+
visitStatement(findNode.statement('if'));
793+
}
794+
795+
test_prefixExpression_bang_nonNullable() async {
796+
await analyze('''
797+
_f(bool/*!*/ x) => !x;
798+
''');
799+
visitSubexpression(findNode.prefix('!x'), 'bool');
800+
}
801+
802+
test_prefixExpression_bang_nullable() async {
803+
await analyze('''
804+
_f(bool/*?*/ x) => !x;
805+
''');
806+
visitSubexpression(findNode.prefix('!x'), 'bool',
807+
nullChecked: {findNode.simple('x;')});
808+
}
809+
810+
test_prefixExpression_combined_nullable_noProblem() async {
811+
await analyze('''
812+
abstract class _C {
813+
_D/*?*/ operator+(int/*!*/ value);
814+
}
815+
abstract class _D extends _C {}
816+
abstract class _E {
817+
_C/*!*/ get x;
818+
void set x(_C/*?*/ value);
819+
f() => ++x;
820+
}
821+
''');
822+
visitSubexpression(findNode.prefix('++'), '_D?');
823+
}
824+
825+
test_prefixExpression_combined_nullable_noProblem_dynamic() async {
826+
await analyze('''
827+
abstract class _E {
828+
dynamic get x;
829+
void set x(Object/*!*/ value);
830+
f() => ++x;
831+
}
832+
''');
833+
var prefix = findNode.prefix('++');
834+
visitSubexpression(prefix, 'dynamic');
835+
}
836+
837+
test_prefixExpression_combined_nullable_problem() async {
838+
await analyze('''
839+
abstract class _C {
840+
_D/*?*/ operator+(int/*!*/ value);
841+
}
842+
abstract class _D extends _C {}
843+
abstract class _E {
844+
_C/*!*/ get x;
845+
void set x(_C/*!*/ value);
846+
f() => ++x;
847+
}
848+
''');
849+
var prefix = findNode.prefix('++');
850+
visitSubexpression(prefix, '_D', problems: {
851+
prefix: {const CompoundAssignmentCombinedNullable()}
852+
});
853+
}
854+
855+
test_prefixExpression_intRules() async {
856+
await analyze('''
857+
_f(int x) => ++x;
858+
''');
859+
visitSubexpression(findNode.prefix('++'), 'int');
860+
}
861+
862+
test_prefixExpression_lhs_nullable_problem() async {
863+
await analyze('''
864+
abstract class _C {
865+
_D/*!*/ operator+(int/*!*/ value);
866+
}
867+
abstract class _D extends _C {}
868+
abstract class _E {
869+
_C/*?*/ get x;
870+
void set x(_C/*?*/ value);
871+
f() => ++x;
872+
}
873+
''');
874+
var prefix = findNode.prefix('++');
875+
visitSubexpression(prefix, '_D', problems: {
876+
prefix: {const CompoundAssignmentReadNullable()}
877+
});
878+
}
879+
880+
test_prefixExpression_minus_dynamic() async {
881+
await analyze('''
882+
_f(dynamic x) => -x;
883+
''');
884+
visitSubexpression(findNode.prefix('-x'), 'dynamic');
885+
}
886+
887+
test_prefixExpression_minus_nonNullable() async {
888+
await analyze('''
889+
_f(int/*!*/ x) => -x;
890+
''');
891+
visitSubexpression(findNode.prefix('-x'), 'int');
892+
}
893+
894+
test_prefixExpression_minus_nullable() async {
895+
await analyze('''
896+
_f(int/*?*/ x) => -x;
897+
''');
898+
visitSubexpression(findNode.prefix('-x'), 'int',
899+
nullChecked: {findNode.simple('x;')});
900+
}
901+
902+
test_prefixExpression_minus_substitution() async {
903+
await analyze('''
904+
abstract class _C<T> {
905+
List<T> operator-();
906+
}
907+
_f(_C<int> x) => -x;
908+
''');
909+
visitSubexpression(findNode.prefix('-x'), 'List<int>');
910+
}
911+
912+
test_prefixExpression_rhs_nonNullable() async {
913+
await analyze('''
914+
abstract class _C {
915+
_D/*!*/ operator+(int/*!*/ value);
916+
}
917+
abstract class _D extends _C {}
918+
_f(_C/*!*/ x) => ++x;
919+
''');
920+
visitSubexpression(findNode.prefix('++'), '_D');
921+
}
922+
923+
test_prefixExpression_tilde_dynamic() async {
924+
await analyze('''
925+
_f(dynamic x) => ~x;
926+
''');
927+
visitSubexpression(findNode.prefix('~x'), 'dynamic');
928+
}
929+
930+
test_prefixExpression_tilde_nonNullable() async {
931+
await analyze('''
932+
_f(int/*!*/ x) => ~x;
933+
''');
934+
visitSubexpression(findNode.prefix('~x'), 'int');
935+
}
936+
937+
test_prefixExpression_tilde_nullable() async {
938+
await analyze('''
939+
_f(int/*?*/ x) => ~x;
940+
''');
941+
visitSubexpression(findNode.prefix('~x'), 'int',
942+
nullChecked: {findNode.simple('x;')});
943+
}
944+
945+
test_prefixExpression_tilde_substitution() async {
946+
await analyze('''
947+
abstract class _C<T> {
948+
List<T> operator~();
949+
}
950+
_f(_C<int> x) => ~x;
951+
''');
952+
visitSubexpression(findNode.prefix('~x'), 'List<int>');
953+
}
954+
701955
test_simpleIdentifier_className() async {
702956
await analyze('''
703957
_f() => int;

0 commit comments

Comments
 (0)