Skip to content
This repository was archived by the owner on Feb 22, 2018. It is now read-only.

Commit 6402f7d

Browse files
author
John Messerly
committed
add special case for min/max, fixes #281
[email protected] Review URL: https://codereview.chromium.org/1253903004 .
1 parent ce68cfa commit 6402f7d

File tree

4 files changed

+66
-0
lines changed

4 files changed

+66
-0
lines changed

lib/src/checker/resolver.dart

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,29 @@ class RestrictedStaticTypeAnalyzer extends StaticTypeAnalyzer {
637637
}
638638
}
639639
}
640+
641+
// Pretend dart:math's min and max are generic:
642+
//
643+
// T min<T extends num>(T x, T y);
644+
//
645+
// and infer T. In practice, this just means if the type of x and y are
646+
// both double or both int, we treat that as the return type.
647+
//
648+
// The Dart spec has similar treatment for binary operations on numbers.
649+
//
650+
// TODO(jmesserly): remove this when we have a fix for
651+
// https://github.com/dart-lang/dev_compiler/issues/28
652+
if (isDartMathMinMax(e)) {
653+
var args = node.argumentList.arguments;
654+
if (args.length == 2) {
655+
var tx = args[0].staticType;
656+
var ty = args[1].staticType;
657+
if (tx == ty &&
658+
(tx == _typeProvider.intType || tx == _typeProvider.doubleType)) {
659+
node.staticType = tx;
660+
}
661+
}
662+
}
640663
}
641664

642665
void _inferObjectAccess(

lib/src/dart_sdk.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,5 +171,7 @@ final Map<String, String> mockSdkSources = {
171171
class Random {
172172
bool nextBool() {}
173173
}
174+
num min(num x, num y) {}
175+
num max(num x, num y) {}
174176
''',
175177
};

lib/src/utils.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,3 +452,7 @@ String errorCodeName(ErrorCode errorCode) {
452452
bool isInlineJS(Element e) => e is FunctionElement &&
453453
e.library.source.uri.toString() == 'dart:_foreign_helper' &&
454454
e.name == 'JS';
455+
456+
bool isDartMathMinMax(Element e) => e is FunctionElement &&
457+
e.library.source.uri.toString() == 'dart:math' &&
458+
(e.name == 'min' || e.name == 'max');

test/checker/checker_test.dart

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2764,5 +2764,42 @@ void main() {
27642764
Iterable<int> baz5() sync* { yield* (/*info:InferredTypeAllocation*/new Iterable()); }
27652765
'''
27662766
}));
2767+
2768+
test(
2769+
'dart:math min/max',
2770+
() => testChecker({
2771+
'/main.dart': '''
2772+
import 'dart:math';
2773+
2774+
void printInt(int x) => print(x);
2775+
void printDouble(double x) => print(x);
2776+
2777+
num myMax(num x, num y) => max(x, y);
2778+
2779+
main() {
2780+
// Okay if static types match.
2781+
printInt(max(1, 2));
2782+
printInt(min(1, 2));
2783+
printDouble(max(1.0, 2.0));
2784+
printDouble(min(1.0, 2.0));
2785+
2786+
// No help for user-defined functions from num->num->num.
2787+
printInt(/*warning:DownCastImplicit*/myMax(1, 2));
2788+
printInt(myMax(1, 2) as int);
2789+
2790+
// Mixing int and double means return type is num.
2791+
printInt(/*warning:DownCastImplicit*/max(1, 2.0));
2792+
printInt(/*warning:DownCastImplicit*/min(1, 2.0));
2793+
printDouble(/*warning:DownCastImplicit*/max(1, 2.0));
2794+
printDouble(/*warning:DownCastImplicit*/min(1, 2.0));
2795+
2796+
// Types other than int and double are not accepted.
2797+
printInt(
2798+
/*warning:DownCastImplicit*/min(
2799+
/*severe:StaticTypeError*/"hi",
2800+
/*severe:StaticTypeError*/"there"));
2801+
}
2802+
'''
2803+
}));
27672804
});
27682805
}

0 commit comments

Comments
 (0)