Implement reporting WarningCode.SDK_VERSION_SINCE

It is not enabled by default yet, because there are existing violations.

Bug: https://github.com/dart-lang/sdk/issues/34978
Change-Id: I60147d4c240d63d2f631513c8dfbd4917c35d47c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/287660
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Konstantin Shcheglov 2023-03-09 18:59:27 +00:00 committed by Commit Queue
parent 919eb05b27
commit 10ee5fdf94
9 changed files with 1119 additions and 0 deletions

View file

@ -2805,6 +2805,8 @@ WarningCode.SDK_VERSION_NEVER:
notes: Deprecated
WarningCode.SDK_VERSION_SET_LITERAL:
status: hasFix
WarningCode.SDK_VERSION_SINCE:
status: needsEvaluation
WarningCode.SDK_VERSION_UI_AS_CODE:
status: hasFix
WarningCode.SDK_VERSION_UI_AS_CODE_IN_CONST_CONTEXT:

View file

@ -165,6 +165,15 @@ extension IdentifierExtension on Identifier {
return _readElement(this);
}
SimpleIdentifier get simpleName {
final self = this;
if (self is SimpleIdentifier) {
return self;
} else {
return (self as PrefixedIdentifier).identifier;
}
}
Element? get writeElement {
return _writeElement(this);
}

View file

@ -6736,6 +6736,16 @@ class WarningCode extends AnalyzerErrorCode {
hasPublishedDocs: true,
);
/// Parameters:
/// 0: the version specified in the `@Since()` annotation
/// 1: the SDK version constraints
static const WarningCode SDK_VERSION_SINCE = WarningCode(
'SDK_VERSION_SINCE',
"This API is available since SDK {0}, but constraints '{1}' don't "
"guarantee it.",
correctionMessage: "Try updating the SDK constraints.",
);
/// No parameters.
static const WarningCode SDK_VERSION_UI_AS_CODE = WarningCode(
'SDK_VERSION_UI_AS_CODE',

View file

@ -993,6 +993,7 @@ const List<ErrorCode> errorCodeValues = [
WarningCode.SDK_VERSION_IS_EXPRESSION_IN_CONST_CONTEXT,
WarningCode.SDK_VERSION_NEVER,
WarningCode.SDK_VERSION_SET_LITERAL,
WarningCode.SDK_VERSION_SINCE,
WarningCode.SDK_VERSION_UI_AS_CODE,
WarningCode.SDK_VERSION_UI_AS_CODE_IN_CONST_CONTEXT,
WarningCode.SUBTYPE_OF_SEALED_CLASS,

View file

@ -16,6 +16,9 @@ import 'package:pub_semver/pub_semver.dart';
/// A visitor that finds code that assumes a later version of the SDK than the
/// minimum version required by the SDK constraints in `pubspec.yaml`.
class SdkConstraintVerifier extends RecursiveAstVisitor<void> {
/// TODO(scheglov) Fix pre-existing violations and remove.
static bool shouldCheckSinceSdkVersion = false;
/// The error reporter to be used to report errors.
final ErrorReporter _errorReporter;
@ -123,6 +126,20 @@ class SdkConstraintVerifier extends RecursiveAstVisitor<void> {
bool get checkUiAsCode =>
_checkUiAsCode ??= !before_2_2_2.intersect(_versionConstraint).isEmpty;
@override
void visitArgumentList(ArgumentList node) {
// Check (optional) positional arguments.
// Named arguments are checked in [NamedExpression].
for (final argument in node.arguments) {
if (argument is! NamedExpression) {
final parameter = argument.staticParameterElement;
_checkSinceSdkVersion(parameter, node, errorNode: argument);
}
}
super.visitArgumentList(node);
}
@override
void visitAsExpression(AsExpression node) {
if (checkConstantUpdate2018 && node.inConstantContext) {
@ -132,6 +149,13 @@ class SdkConstraintVerifier extends RecursiveAstVisitor<void> {
super.visitAsExpression(node);
}
@override
void visitAssignmentExpression(AssignmentExpression node) {
_checkSinceSdkVersion(node.readElement, node);
_checkSinceSdkVersion(node.writeElement, node);
super.visitAssignmentExpression(node);
}
@override
void visitBinaryExpression(BinaryExpression node) {
if (checkTripleShift) {
@ -171,6 +195,12 @@ class SdkConstraintVerifier extends RecursiveAstVisitor<void> {
super.visitBinaryExpression(node);
}
@override
void visitConstructorName(ConstructorName node) {
_checkSinceSdkVersion(node.staticElement, node);
super.visitConstructorName(node);
}
@override
void visitExtensionDeclaration(ExtensionDeclaration node) {
if (checkExtensionMethods) {
@ -199,6 +229,12 @@ class SdkConstraintVerifier extends RecursiveAstVisitor<void> {
_inUiAsCode = wasInUiAsCode;
}
@override
void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
_checkSinceSdkVersion(node.staticElement, node);
super.visitFunctionExpressionInvocation(node);
}
@override
void visitHideCombinator(HideCombinator node) {
// Don't flag references to either `Future` or `Stream` within a combinator.
@ -232,6 +268,30 @@ class SdkConstraintVerifier extends RecursiveAstVisitor<void> {
super.visitMethodDeclaration(node);
}
@override
void visitMethodInvocation(MethodInvocation node) {
_checkSinceSdkVersion(node.methodName.staticElement, node);
super.visitMethodInvocation(node);
}
@override
void visitNamedType(NamedType node) {
_checkSinceSdkVersion(node.name.staticElement, node);
super.visitNamedType(node);
}
@override
void visitPrefixedIdentifier(PrefixedIdentifier node) {
_checkSinceSdkVersion(node.staticElement, node);
super.visitPrefixedIdentifier(node);
}
@override
void visitPropertyAccess(PropertyAccess node) {
_checkSinceSdkVersion(node.propertyName.staticElement, node);
super.visitPropertyAccess(node);
}
@override
void visitSetOrMapLiteral(SetOrMapLiteral node) {
if (node.isSet && checkSetLiterals && !_inSetLiteral) {
@ -254,6 +314,7 @@ class SdkConstraintVerifier extends RecursiveAstVisitor<void> {
if (node.inDeclarationContext()) {
return;
}
_checkSinceSdkVersion(node.staticElement, node);
var element = node.staticElement;
if (checkFutureAndStream &&
element is InterfaceElement &&
@ -287,6 +348,54 @@ class SdkConstraintVerifier extends RecursiveAstVisitor<void> {
_inUiAsCode = wasInUiAsCode;
}
void _checkSinceSdkVersion(
Element? element,
AstNode target, {
AstNode? errorNode,
}) {
if (!shouldCheckSinceSdkVersion) {
return;
}
if (element != null) {
final sinceSdkVersion = element.sinceSdkVersion;
if (sinceSdkVersion != null) {
if (!_versionConstraint.requiresAtLeast(sinceSdkVersion)) {
if (errorNode == null) {
if (target is AssignmentExpression) {
target = target.leftHandSide;
}
if (target is ConstructorName) {
errorNode = target.name ?? target.type.name.simpleName;
} else if (target is FunctionExpressionInvocation) {
errorNode = target.argumentList;
} else if (target is MethodInvocation) {
errorNode = target.methodName;
} else if (target is NamedType) {
errorNode = target.name.simpleName;
} else if (target is PrefixedIdentifier) {
errorNode = target.identifier;
} else if (target is PropertyAccess) {
errorNode = target.propertyName;
} else if (target is SimpleIdentifier) {
errorNode = target;
} else {
throw UnimplementedError('(${target.runtimeType}) $target');
}
}
_errorReporter.reportErrorForNode(
WarningCode.SDK_VERSION_SINCE,
errorNode,
[
sinceSdkVersion.toString(),
_versionConstraint.toString(),
],
);
}
}
}
}
/// Given that the [node] is only valid when the ui-as-code feature is
/// enabled, check that the code will not be executed with a version of the
/// SDK that does not support the feature.
@ -309,3 +418,22 @@ class SdkConstraintVerifier extends RecursiveAstVisitor<void> {
}
}
}
extension on VersionConstraint {
bool requiresAtLeast(Version version) {
final self = this;
if (self is Version) {
return self == version;
}
if (self is VersionRange) {
final min = self.min;
if (min == null) {
return false;
} else {
return min >= version;
}
}
// We don't know, but will not complain.
return true;
}
}

View file

@ -22889,6 +22889,13 @@ WarningCode:
```dart
var s = new Set<int>();
```
SDK_VERSION_SINCE:
problemMessage: "This API is available since SDK {0}, but constraints '{1}' don't guarantee it."
correctionMessage: Try updating the SDK constraints.
comment: |-
Parameters:
0: the version specified in the `@Since()` annotation
1: the SDK version constraints
SDK_VERSION_UI_AS_CODE:
problemMessage: "The for, if, and spread elements weren't supported until version 2.3.0, but this code is required to be able to run on earlier versions."
correctionMessage: Try updating the SDK constraints.

View file

@ -20,4 +20,17 @@ class SdkConstraintVerifierTest extends PubPackageResolutionTest {
await assertErrorsInCode(source, expectedErrors ?? []);
}
/// Verify that the [expectedErrors] are produced if the [source] is analyzed
/// in a context that uses given SDK [constraints].
Future<void> verifyVersion2(String constraints, String source,
{List<ExpectedError>? expectedErrors}) async {
writeTestPackagePubspecYamlFile(
PubspecYamlFileConfig(
sdkVersion: constraints,
),
);
await assertErrorsInCode(source, expectedErrors ?? []);
}
}

View file

@ -0,0 +1,947 @@
// Copyright (c) 2023, 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.
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/hint/sdk_constraint_verifier.dart';
import 'package:analyzer/src/test_utilities/mock_sdk.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'sdk_constraint_verifier_support.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(SdkVersionSinceTest);
});
}
@reflectiveTest
class SdkVersionSinceTest extends SdkConstraintVerifierTest {
@override
List<MockSdkLibrary> additionalMockSdkLibraries = [];
@override
void setUp() {
SdkConstraintVerifier.shouldCheckSinceSdkVersion = true;
super.setUp();
}
@override
Future<void> tearDown() async {
SdkConstraintVerifier.shouldCheckSinceSdkVersion = false;
await super.tearDown();
}
test_class_constructor_formalParameter_optionalNamed() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
class A {
void A({@Since('2.15') int? foo});
}
''');
await verifyVersion2('>=2.14.0', '''
import 'dart:foo';
void f() {
A(foo: 0);
}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 35, 3),
]);
}
test_class_constructor_formalParameter_optionalPositional() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
class A {
void A([@Since('2.15') int? foo]);
}
''');
await verifyVersion2('>=2.14.0', '''
import 'dart:foo';
void f() {
A(42);
}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 35, 2),
]);
}
test_class_constructor_named_instanceCreation() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
class A<T> {
@Since('2.15')
A.named();
}
''');
await verifyVersion2('>=2.14.0', '''
import 'dart:foo';
void f() {
A<int>.named();
}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 40, 5),
]);
}
test_class_constructor_named_tearOff() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
class A<T> {
@Since('2.15')
A.named();
}
''');
await verifyVersion2('>=2.14.0', '''
import 'dart:foo';
void f() {
A<int>.named;
}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 40, 5),
]);
}
test_class_constructor_unnamed_instanceCreation() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
class A<T> {
@Since('2.15')
A();
}
''');
await verifyVersion2('>=2.14.0', '''
import 'dart:foo';
void f() {
A<int>();
}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 33, 1),
]);
}
test_class_field_read() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
class A {
@Since('2.15')
int foo = 0;
}
''');
await verifyVersion2('>=2.14.0', '''
import 'dart:foo';
void f(A a) {
(a).foo;
a.foo;
}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 40, 3),
error(WarningCode.SDK_VERSION_SINCE, 49, 3),
]);
}
test_class_field_readWrite() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
class A {
@Since('2.15')
int foo = 0;
}
''');
await verifyVersion2('>=2.14.0', '''
import 'dart:foo';
void f(A a) {
(a).foo += 0;
a.foo += 0;
}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 40, 3),
error(WarningCode.SDK_VERSION_SINCE, 54, 3),
]);
}
test_class_field_write() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
class A {
@Since('2.15')
int foo = 0;
}
''');
await verifyVersion2('>=2.14.0', '''
import 'dart:foo';
void f(A a) {
(a).foo = 0;
a.foo = 0;
}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 40, 3),
error(WarningCode.SDK_VERSION_SINCE, 53, 3),
]);
}
test_class_getter() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
class A {
@Since('2.15')
int get foo => 0;
}
''');
await verifyVersion2('>=2.14.0', '''
import 'dart:foo';
void f(A a) {
(a).foo;
a.foo;
}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 40, 3),
error(WarningCode.SDK_VERSION_SINCE, 49, 3),
]);
}
test_class_getterSetter_readWrite_both() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
class A {
@Since('2.15')
int get foo => 0;
@Since('2.15')
set foo(int _) {}
}
''');
await verifyVersion2('>=2.14.0', '''
import 'dart:foo';
void f(A a) {
a.foo += 0;
}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 38, 3),
]);
}
test_class_getterSetter_readWrite_getter() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
class A {
@Since('2.15')
int get foo => 0;
set foo(int _) {}
}
''');
await verifyVersion2('>=2.14.0', '''
import 'dart:foo';
void f(A a) {
a.foo += 0;
}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 38, 3),
]);
}
test_class_getterSetter_readWrite_setter() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
class A {
int get foo => 0;
@Since('2.15')
set foo(int _) {}
}
''');
await verifyVersion2('>=2.14.0', '''
import 'dart:foo';
void f(A a) {
a.foo += 0;
}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 38, 3),
]);
}
test_class_instanceCreation_prefixed() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
@Since('2.15')
class A<T> {}
''');
await verifyVersion2('>=2.14.0', '''
import 'dart:foo' as foo;
void f() {
foo.A<int>();
}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 44, 1),
]);
}
test_class_instanceCreation_unprefixed() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
@Since('2.15')
class A<T> {}
''');
await verifyVersion2('>=2.14.0', '''
import 'dart:foo';
void f() {
A<int>();
}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 33, 1),
]);
}
test_class_method_call_functionExpressionInvocation() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
class A {
@Since('2.15')
void call() {}
}
''');
await verifyVersion2('>=2.14.0', '''
import 'dart:foo';
void f(A a) {
a();
}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 37, 2),
]);
}
test_class_method_formalParameter_optionalNamed() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
void foo(
int a, {
@Since('2.15')
int? bar,
}) {}
''');
await verifyVersion2('>=2.14.0', '''
import 'dart:foo';
void f() {
foo(0, bar: 1);
}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 40, 3),
]);
}
test_class_method_formalParameter_optionalPositional() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
void foo(
int a, [
@Since('2.15')
int? bar,
]) {}
''');
await verifyVersion2('>=2.14.0', '''
import 'dart:foo';
void f() {
foo(0, 42);
}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 40, 2),
]);
}
test_class_method_methodInvocation() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
class A {
@Since('2.15')
void foo() {}
}
''');
await verifyVersion2('>=2.14.0', '''
import 'dart:foo';
void f(A a) {
a.foo();
}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 38, 3),
]);
}
test_class_method_methodTearOff_prefixedIdentifier() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
class A {
@Since('2.15')
void foo() {}
}
''');
await verifyVersion2('>=2.14.0', '''
import 'dart:foo';
void f(A a) {
a.foo;
}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 38, 3),
]);
final node = findNode.prefixed('.foo');
assertResolvedNodeText(node, r'''
PrefixedIdentifier
prefix: SimpleIdentifier
token: a
staticElement: self::@function::f::@parameter::a
staticType: A
period: .
identifier: SimpleIdentifier
token: foo
staticElement: dart:foo::@class::A::@method::foo
staticType: void Function()
staticElement: dart:foo::@class::A::@method::foo
staticType: void Function()
''');
}
test_class_method_methodTearOff_propertyAccess() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
class A {
@Since('2.15')
void foo() {}
}
''');
await verifyVersion2('>=2.14.0', '''
import 'dart:foo';
void f(A a) {
(a).foo;
}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 40, 3),
]);
final node = findNode.propertyAccess('.foo');
assertResolvedNodeText(node, r'''
PropertyAccess
target: ParenthesizedExpression
leftParenthesis: (
expression: SimpleIdentifier
token: a
staticElement: self::@function::f::@parameter::a
staticType: A
rightParenthesis: )
staticType: A
operator: .
propertyName: SimpleIdentifier
token: foo
staticElement: dart:foo::@class::A::@method::foo
staticType: void Function()
staticType: void Function()
''');
}
test_class_setter() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
class A {
@Since('2.15')
set foo(int _) {}
}
''');
await verifyVersion2('>=2.14.0', '''
import 'dart:foo';
void f(A a) {
(a).foo = 0;
a.foo = 0;
}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 40, 3),
error(WarningCode.SDK_VERSION_SINCE, 53, 3),
]);
}
test_class_typeAnnotation_prefixed() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
@Since('2.15')
class A<T> {}
''');
await verifyVersion2('>=2.14.0', '''
import 'dart:foo' as foo;
void f(foo.A<int> a) {}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 38, 1),
]);
}
test_class_typeAnnotation_unprefixed() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
@Since('2.15')
class A<T> {}
''');
await verifyVersion2('>=2.14.0', '''
import 'dart:foo';
void f(A<int> a) {}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 27, 1),
]);
}
test_constraints_exact_equal() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
@Since('2.15')
class A {}
''');
await verifyVersion2('2.15.0', '''
import 'dart:foo';
void f(A a) {}
''', expectedErrors: []);
}
test_constraints_exact_greater() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
@Since('2.15')
class A {}
''');
await verifyVersion2('2.16.0', '''
import 'dart:foo';
void f(A a) {}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 27, 1),
]);
}
test_constraints_exact_less() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
@Since('2.15')
class A {}
''');
await verifyVersion2('2.14.0', '''
import 'dart:foo';
void f(A a) {}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 27, 1),
]);
}
test_constraints_greater_equal() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
@Since('2.15')
class A {}
''');
await verifyVersion2('>2.15.0', '''
import 'dart:foo';
void f(A a) {}
''', expectedErrors: []);
}
test_constraints_greaterOrEq_equal() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
@Since('2.15')
class A {}
''');
await verifyVersion2('>=2.15.0', '''
import 'dart:foo';
void f(A a) {}
''', expectedErrors: []);
}
test_constraints_greaterOrEq_greater() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
@Since('2.15')
class A {}
''');
await verifyVersion2('>=2.16.0', '''
import 'dart:foo';
void f(A a) {}
''', expectedErrors: []);
}
test_constraints_greaterOrEq_less() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
@Since('2.15')
class A {}
''');
await verifyVersion2('>=2.14.0', '''
import 'dart:foo';
void f(A a) {}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 27, 1),
]);
}
test_enum_constant() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
enum E {
v1,
@Since('2.15')
v2
}
''');
await verifyVersion2('>=2.14.0', '''
import 'dart:foo';
void f() {
E.v2;
}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 35, 2),
]);
}
test_enum_typeAnnotation() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
@Since('2.15')
enum E {
v
}
''');
await verifyVersion2('>=2.14.0', '''
import 'dart:foo';
void f(E a) {}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 27, 1),
]);
}
test_extension_getter() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
extension E on int {
@Since('2.15')
int get foo => 0;
}
''');
await verifyVersion2('>=2.14.0', '''
import 'dart:foo';
void f() {
0.foo;
}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 35, 3),
]);
}
test_extension_itself_extensionOverride_methodInvocation() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
@Since('2.15')
extension E on int {
void foo() {}
}
''');
await verifyVersion2('>=2.14.0', '''
import 'dart:foo';
void f() {
E(0).foo();
}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 33, 1),
error(WarningCode.SDK_VERSION_SINCE, 38, 3),
]);
}
test_extension_itself_methodInvocation() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
@Since('2.15')
extension E on int {
void foo() {}
}
''');
await verifyVersion2('>=2.14.0', '''
import 'dart:foo';
void f() {
0.foo();
}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 35, 3),
]);
}
test_extension_method_methodInvocation() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
extension E on int {
@Since('2.15')
void foo() {}
}
''');
await verifyVersion2('>=2.14.0', '''
import 'dart:foo';
void f() {
0.foo();
}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 35, 3),
]);
}
test_extension_setter() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
extension E on int {
@Since('2.15')
set foo(int _) {}
}
''');
await verifyVersion2('>=2.14.0', '''
import 'dart:foo';
void f() {
0.foo = 1;
}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 35, 3),
]);
}
test_functionTypeAlias() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
@Since('2.15')
typedef void X(int _);
''');
await verifyVersion2('>=2.14.0', '''
import 'dart:foo';
void f(X a) {}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 27, 1),
]);
}
test_genericTypeAlias() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
@Since('2.15')
typedef X = List<int>;
''');
await verifyVersion2('>=2.14.0', '''
import 'dart:foo';
void f(X a) {}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 27, 1),
]);
}
test_mixin_typeAnnotation() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
@Since('2.15')
mixin M<T> {}
''');
await verifyVersion2('>=2.14.0', '''
import 'dart:foo';
void f(M<int> a) {}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 27, 1),
]);
}
test_topLevelFunction_prefixed() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
@Since('2.15')
void bar() {}
''');
await verifyVersion2('>=2.14.0', '''
import 'dart:foo' as foo;
void f() {
foo.bar();
}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 44, 3),
]);
}
test_topLevelFunction_unprefixed() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
@Since('2.15')
void foo() {}
''');
await verifyVersion2('>=2.14.0', '''
import 'dart:foo';
void f() {
foo();
}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 33, 3),
]);
}
test_topLevelVariable_prefixed() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
@Since('2.15')
const v = 0;
''');
await verifyVersion2('>=2.14.0', '''
import 'dart:foo' as foo;
void f() {
foo.v;
}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 44, 1),
]);
}
test_topLevelVariable_unprefixed() async {
_addDartFooLibrary(r'''
import 'dart:_internal';
@Since('2.15')
const v = 0;
''');
await verifyVersion2('>=2.14.0', '''
import 'dart:foo';
void f() {
v;
}
''', expectedErrors: [
error(WarningCode.SDK_VERSION_SINCE, 33, 1),
]);
}
void _addDartFooLibrary(String content) {
additionalMockSdkLibraries.add(
MockSdkLibrary('foo', [
MockSdkLibraryUnit('foo/foo.dart', content),
]),
);
}
}

View file

@ -723,6 +723,7 @@ import 'sdk_version_is_expression_in_const_context_test.dart'
as sdk_version_is_expression_in_const_context;
import 'sdk_version_never_test.dart' as sdk_version_never;
import 'sdk_version_set_literal_test.dart' as sdk_version_set_literal;
import 'sdk_version_since_test.dart' as sdk_version_since;
import 'sdk_version_ui_as_code_in_const_context_test.dart'
as sdk_version_ui_as_code_in_const_context;
import 'sdk_version_ui_as_code_test.dart' as sdk_version_ui_as_code;
@ -1345,6 +1346,7 @@ main() {
sdk_version_is_expression_in_const_context.main();
sdk_version_never.main();
sdk_version_set_literal.main();
sdk_version_since.main();
sdk_version_ui_as_code.main();
sdk_version_ui_as_code_in_const_context.main();
sealed_class_subtype_outside_of_library.main();