Chromium Code Reviews| Index: tests/compiler/dart2js/type_checker_test.dart |
| diff --git a/tests/compiler/dart2js/type_checker_test.dart b/tests/compiler/dart2js/type_checker_test.dart |
| index 1381be8a03a56f82fbae12d4f2a28dc5ac82f137..46a162104e19fa116038bb1aa391c3acb584e4b2 100644 |
| --- a/tests/compiler/dart2js/type_checker_test.dart |
| +++ b/tests/compiler/dart2js/type_checker_test.dart |
| @@ -3,14 +3,16 @@ |
| // BSD-style license that can be found in the LICENSE file. |
| import "package:expect/expect.dart"; |
| +import '../../../sdk/lib/_internal/compiler/compiler.dart' as api; |
| import '../../../sdk/lib/_internal/compiler/implementation/elements/elements.dart'; |
| import '../../../sdk/lib/_internal/compiler/implementation/tree/tree.dart'; |
| import '../../../sdk/lib/_internal/compiler/implementation/util/util.dart'; |
| +import '../../../sdk/lib/_internal/compiler/implementation/source_file.dart'; |
| import 'mock_compiler.dart'; |
| import 'parser_helper.dart'; |
| import '../../../sdk/lib/_internal/compiler/implementation/elements/modelx.dart' |
| - show ElementX; |
| + show ElementX, CompilationUnitElementX; |
| import '../../../sdk/lib/_internal/compiler/implementation/dart2jslib.dart' |
| hide SourceString; |
| @@ -38,7 +40,8 @@ main() { |
| // testNewExpression, |
| testConditionalExpression, |
| testIfStatement, |
| - testThis]; |
| + testThis, |
| + testOperatorsAssignability]; |
| for (Function test in tests) { |
| setup(); |
| test(); |
| @@ -111,19 +114,22 @@ testOperators() { |
| analyze("{ var i = 1 ${op} 2; }"); |
| analyze("{ var i = 1; i ${op}= 2; }"); |
| analyze("{ int i; var j = (i = true) ${op} 2; }", |
| - MessageKind.NOT_ASSIGNABLE); |
| + [MessageKind.NOT_ASSIGNABLE, MessageKind.OPERATOR_NOT_FOUND]); |
| analyze("{ int i; var j = 1 ${op} (i = true); }", |
| - MessageKind.NOT_ASSIGNABLE); |
| + [MessageKind.NOT_ASSIGNABLE, MessageKind.NOT_ASSIGNABLE]); |
| } |
| for (final op in ['-', '~']) { |
| analyze("{ var i = ${op}1; }"); |
| - analyze("{ int i; var j = ${op}(i = true); }", MessageKind.NOT_ASSIGNABLE); |
| + analyze("{ int i; var j = ${op}(i = true); }", |
| + [MessageKind.NOT_ASSIGNABLE, MessageKind.OPERATOR_NOT_FOUND]); |
| } |
| for (final op in ['++', '--']) { |
| analyze("{ int i = 1; int j = i${op}; }"); |
| analyze("{ int i = 1; bool j = i${op}; }", MessageKind.NOT_ASSIGNABLE); |
| - analyze("{ bool b = true; bool j = b${op}; }"); |
| - analyze("{ bool b = true; int j = ${op}b; }"); |
| + analyze("{ bool b = true; bool j = b${op}; }", |
| + MessageKind.OPERATOR_NOT_FOUND); |
| + analyze("{ bool b = true; int j = ${op}b; }", |
| + MessageKind.OPERATOR_NOT_FOUND); |
| } |
| for (final op in ['||', '&&']) { |
| analyze("{ bool b = (true ${op} false); }"); |
| @@ -131,13 +137,21 @@ testOperators() { |
| analyze("{ bool b = (1 ${op} false); }", MessageKind.NOT_ASSIGNABLE); |
| analyze("{ bool b = (true ${op} 2); }", MessageKind.NOT_ASSIGNABLE); |
| } |
| - for (final op in ['>', '<', '<=', '>=', '==', '!=']) { |
| + for (final op in ['>', '<', '<=', '>=']) { |
| analyze("{ bool b = 1 ${op} 2; }"); |
| analyze("{ int i = 1 ${op} 2; }", MessageKind.NOT_ASSIGNABLE); |
| analyze("{ int i; bool b = (i = true) ${op} 2; }", |
| - MessageKind.NOT_ASSIGNABLE); |
| + [MessageKind.NOT_ASSIGNABLE, MessageKind.OPERATOR_NOT_FOUND]); |
| analyze("{ int i; bool b = 1 ${op} (i = true); }", |
| - MessageKind.NOT_ASSIGNABLE); |
| + [MessageKind.NOT_ASSIGNABLE, MessageKind.NOT_ASSIGNABLE]); |
| + } |
| + for (final op in ['==', '!=']) { |
| + analyze("{ bool b = 1 ${op} 2; }"); |
| + analyze("{ int i = 1 ${op} 2; }", MessageKind.NOT_ASSIGNABLE); |
| + analyze("{ int i; bool b = (i = true) ${op} 2; }", |
| + MessageKind.NOT_ASSIGNABLE); |
| + analyze("{ int i; bool b = 1 ${op} (i = true); }", |
| + MessageKind.NOT_ASSIGNABLE); |
| } |
| } |
| @@ -495,6 +509,277 @@ testThis() { |
| analyzeIn(foo, "{ Foo f = this; }"); |
| } |
| +const String CLASSES_WITH_OPERATORS = ''' |
| +class Operators { |
| + Operators operator+(Operators other) => this; |
|
karlklose
2013/05/17 09:36:53
Add a space between 'operator' and the symbol.
Johnni Winther
2013/05/17 11:46:33
Done.
|
| + Operators operator-(Operators other) => this; |
| + Operators operator-() => this; |
| + Operators operator*(Operators other) => this; |
| + Operators operator/(Operators other) => this; |
| + Operators operator%(Operators other) => this; |
| + Operators operator~/(Operators other) => this; |
| + |
| + Operators operator&(Operators other) => this; |
| + Operators operator|(Operators other) => this; |
| + Operators operator^(Operators other) => this; |
| + |
| + Operators operator~() => this; |
| + |
| + Operators operator<(Operators other) => true; |
| + Operators operator>(Operators other) => false; |
| + Operators operator<=(Operators other) => this; |
| + Operators operator>=(Operators other) => this; |
| + |
| + Operators operator<<(Operators other) => this; |
| + Operators operator>>(Operators other) => this; |
| + |
| + bool operator==(Operators other) => true; |
| + |
| + Operators operator[](Operators key) => this; |
| + void operator[]=(Operators key, Operators value) {} |
| +} |
| + |
| +class MismatchA { |
| + int operator+(MismatchA other) => 0; |
| + MismatchA operator-(int other) => this; |
| + |
| + MismatchA operator[](int key) => this; |
| + void operator[]=(int key, MismatchA value) {} |
| +} |
| + |
| +class MismatchB { |
| + MismatchB operator+(MismatchB other) => this; |
| + |
| + MismatchB operator[](int key) => this; |
| + void operator[]=(String key, MismatchB value) {} |
| +} |
| + |
| +class MismatchC { |
| + MismatchC operator+(MismatchC other) => this; |
| + |
| + MismatchC operator[](int key) => this; |
| + void operator[]=(int key, String value) {} |
| +} |
| +'''; |
| + |
| +testOperatorsAssignability() { |
| + compiler.parseScript(CLASSES_WITH_OPERATORS); |
| + |
| + // Tests against Operators. |
| + |
| + String header = """{ |
| + bool z; |
|
karlklose
2013/05/17 09:36:53
It would be nice if the name 'b' was available for
|
| + Operators a = new Operators(); |
|
karlklose
2013/05/17 09:36:53
Why do we need the 'new Operator()'?
Johnni Winther
2013/05/17 11:46:33
We don't.
|
| + Operators b = new Operators(); |
| + Operators c; |
| + """; |
| + |
| + // Positive tests on operators. |
| + |
| + analyze('$header c = a + b; }'); |
|
karlklose
2013/05/17 09:36:53
Perhaps add a local function
check(text) => anal
Johnni Winther
2013/05/17 11:46:33
Done.
|
| + analyze('$header c = a - b; }'); |
| + analyze('$header c = -a; }'); |
| + analyze('$header c = a * b; }'); |
| + analyze('$header c = a / b; }'); |
| + analyze('$header c = a % b; }'); |
| + analyze('$header c = a ~/ b; }'); |
| + |
| + analyze('$header c = a & b; }'); |
| + analyze('$header c = a | b; }'); |
| + analyze('$header c = a ^ b; }'); |
| + |
| + analyze('$header c = ~a; }'); |
| + |
| + analyze('$header c = a < b; }'); |
| + analyze('$header c = a > b; }'); |
| + analyze('$header c = a <= b; }'); |
| + analyze('$header c = a >= b; }'); |
| + |
| + analyze('$header c = a << b; }'); |
| + analyze('$header c = a >> b; }'); |
| + |
| + analyze('$header c = a[b]; }'); |
| + |
| + analyze('$header a[b] = c; }'); |
| + analyze('$header a[b] += c; }'); |
| + analyze('$header a[b] -= c; }'); |
| + analyze('$header a[b] *= c; }'); |
| + analyze('$header a[b] /= c; }'); |
| + analyze('$header a[b] %= c; }'); |
| + analyze('$header a[b] ~/= c; }'); |
| + analyze('$header a[b] <<= c; }'); |
| + analyze('$header a[b] >>= c; }'); |
| + analyze('$header a[b] &= c; }'); |
| + analyze('$header a[b] |= c; }'); |
| + analyze('$header a[b] ^= c; }'); |
| + |
| + analyze('$header a += b; }'); |
| + analyze('$header a -= b; }'); |
| + analyze('$header a *= b; }'); |
| + analyze('$header a /= b; }'); |
| + analyze('$header a %= b; }'); |
| + analyze('$header a ~/= b; }'); |
| + |
| + analyze('$header a <<= b; }'); |
| + analyze('$header a >>= b; }'); |
| + |
| + analyze('$header a &= b; }'); |
| + analyze('$header a |= b; }'); |
| + analyze('$header a ^= b; }'); |
| + |
| + // Negative tests on operators. |
| + |
| + // `0` is not assignable to operator+ on `a`. |
|
karlklose
2013/05/17 09:36:53
Actually "The type of `0` is not assignable to the
Johnni Winther
2013/05/17 11:46:33
Added a comment on the terminology.
|
| + analyze('$header c = a + 0; }', MessageKind.NOT_ASSIGNABLE); |
| + // `a + b` is not assignable to `z`. |
| + analyze('$header z = a + b; }', MessageKind.NOT_ASSIGNABLE); |
| + |
| + // `-a` is not assignable to `z`. |
| + analyze('$header z = -a; }', MessageKind.NOT_ASSIGNABLE); |
| + |
| + // `0` is not assignable to operator[] on `a`. |
| + analyze('$header c = a[0]; }', MessageKind.NOT_ASSIGNABLE); |
| + // `a[b]` is not assignable to `z`. |
| + analyze('$header z = a[b]; }', MessageKind.NOT_ASSIGNABLE); |
| + |
| + // `0` is not assignable to operator[] on `a`. |
| + // Warning suppressed for `0` is not assignable to operator[]= on `a`. |
| + analyze('$header a[0] *= c; }', MessageKind.NOT_ASSIGNABLE); |
| + // `z` is not assignable to operator* on `a[0]`. |
| + analyze('$header a[b] *= z; }', MessageKind.NOT_ASSIGNABLE); |
| + |
| + analyze('$header b = a++; }', MessageKind.NOT_ASSIGNABLE); |
| + analyze('$header b = ++a; }', MessageKind.NOT_ASSIGNABLE); |
| + analyze('$header b = a--; }', MessageKind.NOT_ASSIGNABLE); |
| + analyze('$header b = --a; }', MessageKind.NOT_ASSIGNABLE); |
| + |
| + analyze('$header c = a[b]++; }', MessageKind.NOT_ASSIGNABLE); |
| + analyze('$header c = ++a[b]; }', MessageKind.NOT_ASSIGNABLE); |
| + analyze('$header c = a[b]--; }', MessageKind.NOT_ASSIGNABLE); |
| + analyze('$header c = --a[b]; }', MessageKind.NOT_ASSIGNABLE); |
| + |
| + analyze('$header z = a == b; }'); |
| + analyze('$header z = a != b; }'); |
| + |
| + analyze('$header z = z && z; }'); |
| + analyze('$header z = a && z; }', MessageKind.NOT_ASSIGNABLE); |
| + analyze('$header z = z && b; }', MessageKind.NOT_ASSIGNABLE); |
| + analyze('$header z = a && b; }', |
| + [MessageKind.NOT_ASSIGNABLE, MessageKind.NOT_ASSIGNABLE]); |
| + analyze('$header a = a && b; }', |
| + [MessageKind.NOT_ASSIGNABLE, MessageKind.NOT_ASSIGNABLE, |
| + MessageKind.NOT_ASSIGNABLE]); |
| + |
| + analyze('$header z = z || z; }'); |
|
karlklose
2013/05/17 09:36:53
Perhaps you can combine l.664-671 and l.673-680 in
Johnni Winther
2013/05/17 11:46:33
Done.
|
| + analyze('$header z = a || z; }', MessageKind.NOT_ASSIGNABLE); |
| + analyze('$header z = z || b; }', MessageKind.NOT_ASSIGNABLE); |
| + analyze('$header z = a || b; }', |
| + [MessageKind.NOT_ASSIGNABLE, MessageKind.NOT_ASSIGNABLE]); |
| + analyze('$header a = a || b; }', |
| + [MessageKind.NOT_ASSIGNABLE, MessageKind.NOT_ASSIGNABLE, |
| + MessageKind.NOT_ASSIGNABLE]); |
| + |
| + analyze('$header z = !z; }'); |
| + analyze('$header z = !a; }', MessageKind.NOT_ASSIGNABLE); |
| + analyze('$header a = !z; }', MessageKind.NOT_ASSIGNABLE); |
| + analyze('$header a = !a; }', |
| + [MessageKind.NOT_ASSIGNABLE, MessageKind.NOT_ASSIGNABLE]); |
| + |
| + |
| + // Tests against MismatchA. |
| + |
| + header = """{ |
| + MismatchA a = new MismatchA(); |
| + MismatchA b = new MismatchA(); |
| + MismatchA c; |
| + """; |
| + |
| + // Tests against int operator+(MismatchA other) => 0; |
| + |
| + // `a + b` is not assignable to `c`. |
| + analyze('$header c = a + b; }', MessageKind.NOT_ASSIGNABLE); |
| + // `a + b` is not assignable to `a`. |
| + analyze('$header a += b; }', MessageKind.NOT_ASSIGNABLE); |
| + // `a[0] + b` is not assignable to `a[0]`. |
| + analyze('$header a[0] += b; }', MessageKind.NOT_ASSIGNABLE); |
| + |
| + // 1 is not applicable to operator+ |
| + analyze('$header b = a++; }', MessageKind.NOT_ASSIGNABLE); |
| + // 1 is not applicable to operator+. |
| + // `++a` of type int is not assignable to `b`. |
| + analyze('$header b = ++a; }', |
| + [MessageKind.NOT_ASSIGNABLE, MessageKind.NOT_ASSIGNABLE]); |
| + |
| + // 1 is not applicable to operator+. |
| + analyze('$header b = a[0]++; }', MessageKind.NOT_ASSIGNABLE); |
| + // 1 is not applicable to operator+. |
| + // `++a[0]` of type int is not assignable to `b`. |
| + analyze('$header b = ++a[0]; }', |
| + [MessageKind.NOT_ASSIGNABLE, MessageKind.NOT_ASSIGNABLE]); |
| + |
| + // Tests against: MismatchA operator-(int other) => this; |
| + |
| + // `a - b` is not assignable to `c`. |
| + analyze('$header c = a + b; }', MessageKind.NOT_ASSIGNABLE); |
| + // `a - b` is not assignable to `a`. |
| + analyze('$header a += b; }', MessageKind.NOT_ASSIGNABLE); |
| + // `a[0] - b` is not assignable to `a[0]`. |
| + analyze('$header a[0] += b; }', MessageKind.NOT_ASSIGNABLE); |
| + |
| + analyze('$header b = a--; }'); |
| + analyze('$header b = --a; }'); |
| + |
| + analyze('$header b = a[0]--; }'); |
| + analyze('$header b = --a[0]; }'); |
| + |
| + // Tests against MismatchB. |
| + |
| + header = """{ |
| + MismatchB a = new MismatchB(); |
| + MismatchB b = new MismatchB(); |
| + MismatchB c; |
| + """; |
| + |
| + // Tests against: |
| + // MismatchB operator[](int key) => this; |
| + // void operator[]=(String key, MismatchB value) {} |
| + |
| + // `0` is not applicable to operator[]= on `a`. |
| + analyze('$header a[0] = b; }', MessageKind.NOT_ASSIGNABLE); |
| + |
| + // `0` is not applicable to operator[]= on `a`. |
| + analyze('$header a[0] += b; }', MessageKind.NOT_ASSIGNABLE); |
| + // `""` is not applicable to operator[] on `a`. |
| + analyze('$header a[""] += b; }', MessageKind.NOT_ASSIGNABLE); |
| + // `c` is not applicable to operator[] on `a`. |
| + // `c` is not applicable to operator[]= on `a`. |
| + analyze('$header a[c] += b; }', |
| + [MessageKind.NOT_ASSIGNABLE, MessageKind.NOT_ASSIGNABLE]); |
| + |
| + |
| + // Tests against MismatchB. |
| + |
| + header = """{ |
| + MismatchC a = new MismatchC(); |
| + MismatchC b = new MismatchC(); |
| + MismatchC c; |
| + """; |
| + |
| + // Tests against: |
| + // MismatchC operator[](int key) => this; |
| + // void operator[]=(int key, String value) {} |
| + |
| + // `b` is not assignable to `a[0]`. |
| + analyze('$header a[0] += b; }', MessageKind.NOT_ASSIGNABLE); |
| + // `0` is not applicable to operator+ on `a[0]`. |
| + analyze('$header a[0] += ""; }', |
| + [MessageKind.NOT_ASSIGNABLE, MessageKind.NOT_ASSIGNABLE]); |
| + // `true` is not applicable to operator+ on `a[0]`. |
| + // `true` is not assignable to `a[0]`. |
| + analyze('$header a[0] += true; }', |
| + [MessageKind.NOT_ASSIGNABLE, MessageKind.NOT_ASSIGNABLE]); |
| +} |
| + |
| const CLASS_WITH_METHODS = ''' |
| class ClassWithMethods { |
| untypedNoArgumentMethod() {} |
| @@ -531,8 +816,45 @@ String returnWithType(String type, expression) { |
| Node parseExpression(String text) => |
| parseBodyCode(text, (parser, token) => parser.parseExpression(token)); |
| +const String NUM_SOURCE = ''' |
| +abstract class num { |
| + num operator +(num other); |
| + num operator -(num other); |
| + num operator *(num other); |
| + num operator %(num other); |
| + double operator /(num other); |
| + int operator ~/(num other); |
| + num operator -(); |
| + bool operator <(num other); |
| + bool operator <=(num other); |
| + bool operator >(num other); |
| + bool operator >=(num other); |
| +} |
| +'''; |
| + |
| +const String INT_SOURCE = ''' |
| +abstract class int extends num { |
| + int operator &(int other); |
| + int operator |(int other); |
| + int operator ^(int other); |
| + int operator ~(); |
| + int operator <<(int shiftAmount); |
| + int operator >>(int shiftAmount); |
| + int operator -(); |
| +} |
| +'''; |
| + |
| void setup() { |
| - compiler = new MockCompiler(); |
| + RegExp classNum = new RegExp(r'abstract class num {}'); |
| + Expect.isTrue(DEFAULT_CORELIB.contains(classNum)); |
| + RegExp classInt = new RegExp(r'abstract class int extends num { }'); |
| + Expect.isTrue(DEFAULT_CORELIB.contains(classInt)); |
| + |
| + String CORE_SOURCE = DEFAULT_CORELIB |
|
karlklose
2013/05/17 09:36:53
Should we make sure that classNum and ClassInt are
Johnni Winther
2013/05/17 11:46:33
We do - in the lines above.
|
| + .replaceAll(classNum, NUM_SOURCE) |
| + .replaceAll(classInt, INT_SOURCE); |
| + |
| + compiler = new MockCompiler(coreSource: CORE_SOURCE); |
| types = compiler.types; |
| voidType = compiler.types.voidType; |
| intType = compiler.intClass.computeType(compiler); |
| @@ -570,23 +892,56 @@ analyzeTopLevel(String text, [expectedWarnings]) { |
| } |
| } |
| +api.DiagnosticHandler createHandler(String text) { |
| + return (uri, int begin, int end, String message, kind) { |
| + SourceFile sourceFile; |
| + if (uri == null) { |
| + sourceFile = new SourceFile('analysis', text); |
| + } else { |
| + sourceFile = compiler.sourceFiles[uri.toString()]; |
| + } |
| + if (sourceFile != null) { |
| + print(sourceFile.getLocationMessage(message, begin, end, true, (x) => x)); |
| + } else { |
| + print(message); |
| + } |
| + }; |
| +} |
| + |
| analyze(String text, [expectedWarnings]) { |
| if (expectedWarnings == null) expectedWarnings = []; |
| if (expectedWarnings is !List) expectedWarnings = [expectedWarnings]; |
| + compiler.diagnosticHandler = createHandler(text); |
| + |
| Token tokens = scan(text); |
| NodeListener listener = new NodeListener(compiler, null); |
| Parser parser = new Parser(listener); |
| parser.parseStatement(tokens); |
| Node node = listener.popNode(); |
| + Element compilationUnit = |
| + new CompilationUnitElementX(new Script(null, null), compiler.mainApp); |
| Element function = new ElementX( |
| - buildSourceString(''), ElementKind.FUNCTION, compiler.mainApp); |
| + buildSourceString(''), ElementKind.FUNCTION, compilationUnit); |
| TreeElements elements = compiler.resolveNodeStatement(node, function); |
| TypeCheckerVisitor checker = new TypeCheckerVisitor(compiler, elements, |
| types); |
| compiler.clearWarnings(); |
| checker.analyze(node); |
| compareWarningKinds(text, expectedWarnings, compiler.warnings); |
| + compiler.diagnosticHandler = null; |
| +} |
| + |
| +void generateOutput(String text) { |
| + for (WarningMessage message in compiler.warnings) { |
| + var beginToken = message.node.getBeginToken(); |
| + var endToken = message.node.getEndToken(); |
| + int begin = beginToken.charOffset; |
| + int end = endToken.charOffset+endToken.slowCharCount; |
|
karlklose
2013/05/17 09:36:53
Add spaces around +.
Johnni Winther
2013/05/17 11:46:33
Done.
|
| + SourceFile sourceFile = new SourceFile('analysis', text); |
| + print(sourceFile.getLocationMessage(message.message.toString(), |
| + begin, end, true, (str) => str)); |
| + } |
| } |
| analyzeIn(ClassElement classElement, String text, [expectedWarnings]) { |
| @@ -603,7 +958,7 @@ analyzeIn(ClassElement classElement, String text, [expectedWarnings]) { |
| TypeCheckerVisitor checker = new TypeCheckerVisitor(compiler, elements, |
| types); |
| compiler.clearWarnings(); |
| - checker.currentClass = classElement; |
| checker.analyze(node); |
| + generateOutput(text); |
| compareWarningKinds(text, expectedWarnings, compiler.warnings); |
| } |