blob: e5fa7c662a856384501a5d0512af768d75f9e3b5 [file] [log] [blame]
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// These tests are for an experimental feature that treats Dart primitive types
// (int, bool, double, etc.) as non-nullable. This file is not evidence for an
// intention to officially support non-nullable primitives in Dart (or general
// NNBD, for that matter) so don't get too crazy about it.
library analyzer.test.src.task.non_null_primitives.checker_test;
import '../../../reflective_tests.dart';
import '../strong/strong_test_helper.dart';
void main() {
initStrongModeTests();
runReflectiveTests(NonNullCheckerTest);
}
@reflectiveTest
class NonNullCheckerTest {
// Tests simple usage of ints as iterators for a loop. Not directly related to
// non-nullability, but if it is implemented this should be more efficient,
// since languages.length will not be null-checked on every iteration.
void test_forLoop() {
checkFile('''
class MyList {
int length;
MyList() {
length = 6;
}
String operator [](int i) {
return <String>["Dart", "Java", "JS", "C", "C++", "C#"][i];
}
}
main() {
var languages = new MyList();
for (int i = 0; i < languages.length; ++i) {
print(languages[i]);
}
}
''');
}
void test_nullableTypes() {
// By default x can be set to null.
checkFile('int x = null;');
}
void test_initialize_nonnullable_with_null() {
addFile('int x = /*error:INVALID_ASSIGNMENT*/null;');
check(nonnullableTypes: <String>['dart:core,int']);
}
void test_initialize_nonnullable_with_valid_value() {
addFile('int x = 0;');
check(nonnullableTypes: <String>['dart:core,int']);
}
void test_assign_null_to_nonnullable() {
addFile('''
int x = 0;
main() {
x = 1;
x = /*error:INVALID_ASSIGNMENT*/null;
}
''');
check(nonnullableTypes: <String>['dart:core,int']);
}
void test_uninitialized_nonnullable_local_variable() {
// Ideally, we will do flow analysis and throw an error only if a variable
// is used before it has been initialized.
addFile('main() { int /*error:NON_NULLABLE_FIELD_NOT_INITIALIZED*/x; }');
check(nonnullableTypes: <String>['dart:core,int']);
}
void test_uninitialized_nonnullable_top_level_variable_declaration() {
// If `int`s are non-nullable, then this code should throw an error.
addFile('int /*error:NON_NULLABLE_FIELD_NOT_INITIALIZED*/x;');
check(nonnullableTypes: <String>['dart:core,int']);
}
void test_uninitialized_nonnullable_field_declaration() {
addFile('''
void foo() {}
class A {
// Ideally, we should allow x to be init in the constructor, but that requires
// too much complication in the checker, so for now we throw a static error at
// the declaration site.
int /*error:NON_NULLABLE_FIELD_NOT_INITIALIZED*/x;
A();
}
''');
check(nonnullableTypes: <String>['dart:core,int']);
}
void test_prefer_final_to_non_nullable_error() {
addFile('main() { final int /*error:FINAL_NOT_INITIALIZED*/x; }');
addFile('final int /*error:FINAL_NOT_INITIALIZED*/x;');
addFile('''
void foo() {}
class A {
final int x;
/*warning:FINAL_NOT_INITIALIZED_CONSTRUCTOR_1*/A();
}
''');
check(nonnullableTypes: <String>['dart:core,int']);
}
// Default example from NNBD document.
final String defaultNnbdExample = '''
class Point {
final int x, y;
Point(this.x, this.y);
Point operator +(Point other) => new Point(x + other.x, y + other.y);
String toString() => "x: \$x, y: \$y";
}
void main() {
Point p1 = new Point(0, 0);
Point p2 = new Point(10, 10);
print("p1 + p2 = \${p1 + p2}");
}
''';
final String defaultNnbdExampleMod1 = '''
class Point {
final int x, y;
Point(this.x, this.y);
Point operator +(Point other) => new Point(x + other.x, y + other.y);
String toString() => "x: \$x, y: \$y";
}
void main() {
Point p1 = new Point(0, 0);
Point p2 = new Point(10, /*boom*/null); // Change here.
print("p1 + p2 = \${p1 + p2}");
}
''';
final String defaultNnbdExampleMod2 = '''
class Point {
final int x, y;
Point(this.x, this.y);
Point operator +(Point other) => new Point(x + other.x, y + other.y);
String toString() => "x: \$x, y: \$y";
}
void main() {
bool f = false; // Necessary, because dead code is otherwise detected.
Point p1 = new Point(0, 0);
Point p2 = new Point(10, /*boom*/f ? 10 : null); // Change here.
print("p1 + p2 = \${p1 + p2}");
}
''';
void test_nullable_fields() {
addFile(defaultNnbdExample);
// `null` can be passed as an argument to `Point` in default mode.
addFile(defaultNnbdExampleMod1);
// A nullable expression can be passed as an argument to `Point` in default
// mode.
addFile(defaultNnbdExampleMod2);
check();
}
void test_nonnullable_fields() {
addFile(defaultNnbdExample);
// `null` can be passed as an argument to `Point` in default mode.
addFile(_withError(defaultNnbdExampleMod1, "error:INVALID_ASSIGNMENT"));
// A nullable expression can be passed as an argument to `Point` in default
// mode.
addFile(_withError(defaultNnbdExampleMod2, "error:INVALID_ASSIGNMENT"));
check(nonnullableTypes: <String>['dart:core,int']);
}
}
String _withError(String file, String error) {
return ("" + file).replaceFirst("boom", error);
}