Version 2.13.0-19.0.dev
Merge commit 'b60e0aa5a10090bce296dbba032350143f0a7dfd' into 'dev'
diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart
index f8ef9b5..9d84c71 100644
--- a/pkg/analyzer/lib/error/error.dart
+++ b/pkg/analyzer/lib/error/error.dart
@@ -461,6 +461,7 @@
FfiCode.EMPTY_STRUCT,
FfiCode.EMPTY_STRUCT_WARNING,
FfiCode.EXTRA_ANNOTATION_ON_STRUCT_FIELD,
+ FfiCode.EXTRA_SIZE_ANNOTATION_CARRAY,
FfiCode.FIELD_IN_STRUCT_WITH_INITIALIZER,
FfiCode.FIELD_INITIALIZER_IN_STRUCT,
FfiCode.GENERIC_STRUCT_SUBCLASS,
@@ -470,11 +471,13 @@
FfiCode.MISSING_ANNOTATION_ON_STRUCT_FIELD,
FfiCode.MISSING_EXCEPTION_VALUE,
FfiCode.MISSING_FIELD_TYPE_IN_STRUCT,
+ FfiCode.MISSING_SIZE_ANNOTATION_CARRAY,
FfiCode.MUST_BE_A_NATIVE_FUNCTION_TYPE,
FfiCode.MUST_BE_A_SUBTYPE,
FfiCode.NON_CONSTANT_TYPE_ARGUMENT,
FfiCode.NON_CONSTANT_TYPE_ARGUMENT_WARNING,
FfiCode.NON_NATIVE_FUNCTION_TYPE_ARGUMENT_TO_POINTER,
+ FfiCode.NON_SIZED_TYPE_ARGUMENT,
FfiCode.SUBTYPE_OF_FFI_CLASS_IN_EXTENDS,
FfiCode.SUBTYPE_OF_FFI_CLASS_IN_IMPLEMENTS,
FfiCode.SUBTYPE_OF_FFI_CLASS_IN_WITH,
diff --git a/pkg/analyzer/lib/src/dart/error/ffi_code.dart b/pkg/analyzer/lib/src/dart/error/ffi_code.dart
index 250c01c..b44213b 100644
--- a/pkg/analyzer/lib/src/dart/error/ffi_code.dart
+++ b/pkg/analyzer/lib/src/dart/error/ffi_code.dart
@@ -53,6 +53,14 @@
/**
* No parameters.
*/
+ static const FfiCode EXTRA_SIZE_ANNOTATION_CARRAY = FfiCode(
+ name: 'EXTRA_SIZE_ANNOTATION_CARRAY',
+ message: "'CArray's must have exactly one 'CArraySize' annotation.",
+ correction: "Try removing the extra annotation.");
+
+ /**
+ * No parameters.
+ */
static const FfiCode FIELD_IN_STRUCT_WITH_INITIALIZER = FfiCode(
name: 'FIELD_IN_STRUCT_WITH_INITIALIZER',
message: "Fields in subclasses of 'Struct' can't have initializers.",
@@ -97,9 +105,11 @@
name: 'INVALID_FIELD_TYPE_IN_STRUCT',
message:
"Fields in struct classes can't have the type '{0}'. They can only "
- "be declared as 'int', 'double', 'Pointer', or subtype of 'Struct'.",
+ "be declared as 'int', 'double', 'CArray', 'Pointer', or subtype of "
+ "'Struct'.",
correction:
- "Try using 'int', 'double', 'Pointer', or subtype of 'Struct'.");
+ "Try using 'int', 'double', 'CArray', 'Pointer', or subtype of "
+ "'Struct'.");
/**
* No parameters.
@@ -143,6 +153,14 @@
correction: "Try using 'int', 'double' or 'Pointer'.");
/**
+ * No parameters.
+ */
+ static const FfiCode MISSING_SIZE_ANNOTATION_CARRAY = FfiCode(
+ name: 'MISSING_SIZE_ANNOTATION_CARRAY',
+ message: "'CArray's must have exactly one 'CArraySize' annotation.",
+ correction: "Try adding a 'CArraySize' annotation.");
+
+ /**
* Parameters:
* 0: the type that should be a valid dart:ffi native type.
* 1: the name of the function whose invocation depends on this relationship
@@ -202,6 +220,19 @@
/**
* Parameters:
+ * 0: the type of the field
+ */
+ static const FfiCode NON_SIZED_TYPE_ARGUMENT = FfiCode(
+ name: 'NON_SIZED_TYPE_ARGUMENT',
+ message:
+ "Type arguments to '{0}' can't have the type '{1}'. They can only "
+ "be declared as native integer, 'Float', 'Double', 'Pointer', or "
+ "subtype of Struct'.",
+ correction: "Try using a native integer, 'Float', 'Double', 'Pointer', "
+ "or subtype of 'Struct'.");
+
+ /**
+ * Parameters:
* 0: the name of the subclass
* 1: the name of the class being extended, implemented, or mixed in
*/
diff --git a/pkg/analyzer/lib/src/generated/ffi_verifier.dart b/pkg/analyzer/lib/src/generated/ffi_verifier.dart
index feca32d..012b9ac 100644
--- a/pkg/analyzer/lib/src/generated/ffi_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/ffi_verifier.dart
@@ -17,6 +17,7 @@
static const _allocatorClassName = 'Allocator';
static const _allocateExtensionMethodName = 'call';
static const _allocatorExtensionName = 'AllocatorAlloc';
+ static const _carrayClassName = 'CArray';
static const _dartFfiLibraryName = 'dart.ffi';
static const _opaqueClassName = 'Opaque';
@@ -158,7 +159,8 @@
if (element is MethodElement) {
var enclosingElement = element.enclosingElement;
if (enclosingElement is ExtensionElement) {
- if (_isNativeStructPointerExtension(enclosingElement)) {
+ if (_isNativeStructPointerExtension(enclosingElement) ||
+ _isNativeStructCArrayExtension(enclosingElement)) {
if (element.name == '[]') {
_validateRefIndexed(node);
}
@@ -242,6 +244,12 @@
element.name == _allocatorExtensionName &&
element.library?.name == _dartFfiLibraryName;
+ /// Return `true` if the given [element] represents the class `CArray`.
+ bool _isCArray(Element? element) =>
+ element != null &&
+ element.name == _carrayClassName &&
+ element.library?.name == _dartFfiLibraryName;
+
/// Return `true` if the [typeName] is the name of a type from `dart:ffi`.
bool _isDartFfiClass(TypeName typeName) =>
_isDartFfiElement(typeName.name.staticElement);
@@ -274,6 +282,8 @@
structFieldCount++;
} else if (_isStructClass(declaredType)) {
structFieldCount++;
+ } else if (_isCArray(declaredType.element)) {
+ structFieldCount++;
}
}
return structFieldCount == 0;
@@ -301,6 +311,9 @@
element.name == 'NativeFunctionPointer' &&
element.library?.name == _dartFfiLibraryName;
+ bool _isNativeStructCArrayExtension(Element element) =>
+ element.name == 'StructCArray' && element.library?.name == 'dart.ffi';
+
bool _isNativeStructPointerExtension(Element element) =>
element.name == 'StructPointer' && element.library?.name == 'dart.ffi';
@@ -348,6 +361,29 @@
return false;
}
+ /// Returns `true` if [nativeType] is a C type that has a size.
+ bool _isSized(DartType nativeType) {
+ switch (_primitiveNativeType(nativeType)) {
+ case _PrimitiveDartType.double:
+ return true;
+ case _PrimitiveDartType.int:
+ return true;
+ case _PrimitiveDartType.void_:
+ return false;
+ case _PrimitiveDartType.handle:
+ return false;
+ case _PrimitiveDartType.none:
+ break;
+ }
+ if (_isStructClass(nativeType)) {
+ return true;
+ }
+ if (_isPointer(nativeType.element)) {
+ return true;
+ }
+ return false;
+ }
+
/// Returns `true` iff [nativeType] is a struct type.
bool _isStructClass(DartType nativeType) {
if (nativeType is InterfaceType) {
@@ -389,12 +425,14 @@
nativeType.optionalParameterTypes.isNotEmpty) {
return false;
}
- if (!_isValidFfiNativeType(nativeType.returnType, true, false)) {
+ if (!_isValidFfiNativeType(nativeType.returnType,
+ allowVoid: true, allowEmptyStruct: false)) {
return false;
}
for (final DartType typeArg in nativeType.normalParameterTypes) {
- if (!_isValidFfiNativeType(typeArg, false, false)) {
+ if (!_isValidFfiNativeType(typeArg,
+ allowVoid: false, allowEmptyStruct: false)) {
return false;
}
}
@@ -404,9 +442,10 @@
}
/// Validates that the given [nativeType] is a valid dart:ffi native type.
- // TODO(https://dartbug.com/44747): Change to named arguments.
- bool _isValidFfiNativeType(
- DartType? nativeType, bool allowVoid, bool allowEmptyStruct) {
+ bool _isValidFfiNativeType(DartType? nativeType,
+ {bool allowVoid = false,
+ bool allowEmptyStruct = false,
+ bool allowCArray = false}) {
if (nativeType is InterfaceType) {
// Is it a primitive integer/double type (or ffi.Void if we allow it).
final primitiveType = _primitiveNativeType(nativeType);
@@ -419,7 +458,8 @@
}
if (_isPointerInterfaceType(nativeType)) {
final nativeArgumentType = nativeType.typeArguments.single;
- return _isValidFfiNativeType(nativeArgumentType, true, true) ||
+ return _isValidFfiNativeType(nativeArgumentType,
+ allowVoid: true, allowEmptyStruct: true) ||
_isStructClass(nativeArgumentType) ||
_isNativeTypeInterfaceType(nativeArgumentType);
}
@@ -436,6 +476,10 @@
if (_isOpaqueClass(nativeType)) {
return true;
}
+ if (allowCArray && _isCArray(nativeType.element)) {
+ return _isValidFfiNativeType(nativeType.typeArguments.single,
+ allowVoid: false, allowEmptyStruct: false);
+ }
} else if (nativeType is FunctionType) {
return _isValidFfiNativeFunctionType(nativeType);
}
@@ -484,7 +528,8 @@
return;
}
final DartType dartType = typeArgumentTypes[0];
- if (!_isValidFfiNativeType(dartType, true, true)) {
+ if (!_isValidFfiNativeType(dartType,
+ allowVoid: true, allowEmptyStruct: true)) {
final AstNode errorNode = node;
_errorReporter.reportErrorForNode(
FfiCode.NON_CONSTANT_TYPE_ARGUMENT,
@@ -647,7 +692,7 @@
targetType.typeArguments.length == 1) {
final DartType T = targetType.typeArguments[0];
- if (!_isValidFfiNativeType(T, true, true)) {
+ if (!_isValidFfiNativeType(T, allowVoid: true, allowEmptyStruct: true)) {
final AstNode errorNode = node;
_errorReporter.reportErrorForNode(
FfiCode.NON_CONSTANT_TYPE_ARGUMENT_WARNING,
@@ -677,6 +722,13 @@
_validateAnnotations(fieldType, annotations, _PrimitiveDartType.double);
} else if (_isPointer(declaredType.element)) {
_validateNoAnnotations(annotations);
+ } else if (_isCArray(declaredType.element)) {
+ final typeArg = (declaredType as InterfaceType).typeArguments.single;
+ if (!_isSized(typeArg)) {
+ _errorReporter.reportErrorForNode(FfiCode.NON_SIZED_TYPE_ARGUMENT,
+ fieldType, [_carrayClassName, typeArg.toString()]);
+ }
+ _validateSizeOfAnnotation(fieldType, annotations);
} else if (_isStructClass(declaredType)) {
final clazz = (declaredType as InterfaceType).element;
if (_isEmptyStruct(clazz)) {
@@ -782,7 +834,8 @@
void _validateRefIndexed(IndexExpression node) {
var targetType = node.realTarget.staticType;
- if (!_isValidFfiNativeType(targetType, false, true)) {
+ if (!_isValidFfiNativeType(targetType,
+ allowVoid: false, allowEmptyStruct: true, allowCArray: true)) {
final AstNode errorNode = node;
_errorReporter.reportErrorForNode(
FfiCode.NON_CONSTANT_TYPE_ARGUMENT, errorNode, ['[]']);
@@ -793,7 +846,8 @@
/// `Pointer<T extends Struct>.ref`.
void _validateRefPrefixedIdentifier(PrefixedIdentifier node) {
var targetType = node.prefix.staticType!;
- if (!_isValidFfiNativeType(targetType, false, true)) {
+ if (!_isValidFfiNativeType(targetType,
+ allowVoid: false, allowEmptyStruct: true)) {
final AstNode errorNode = node;
_errorReporter.reportErrorForNode(
FfiCode.NON_CONSTANT_TYPE_ARGUMENT, errorNode, ['ref']);
@@ -802,7 +856,8 @@
void _validateRefPropertyAccess(PropertyAccess node) {
var targetType = node.realTarget.staticType;
- if (!_isValidFfiNativeType(targetType, false, true)) {
+ if (!_isValidFfiNativeType(targetType,
+ allowVoid: false, allowEmptyStruct: true)) {
final AstNode errorNode = node;
_errorReporter.reportErrorForNode(
FfiCode.NON_CONSTANT_TYPE_ARGUMENT, errorNode, ['ref']);
@@ -815,13 +870,41 @@
return;
}
final DartType T = typeArgumentTypes[0];
- if (!_isValidFfiNativeType(T, true, true)) {
+ if (!_isValidFfiNativeType(T, allowVoid: true, allowEmptyStruct: true)) {
final AstNode errorNode = node;
_errorReporter.reportErrorForNode(
FfiCode.NON_CONSTANT_TYPE_ARGUMENT_WARNING, errorNode, ['sizeOf']);
}
}
+ /// Validate that the [annotations] include exactly one size annotation. If
+ /// an error is produced that cannot be associated with an annotation,
+ /// associate it with the [errorNode].
+ void _validateSizeOfAnnotation(
+ AstNode errorNode, NodeList<Annotation> annotations) {
+ final ffiSizeAnnotations = annotations.where((annotation) {
+ if (!_isDartFfiElement(annotation.element)) {
+ return false;
+ }
+ if (annotation.element is! ConstructorElement) {
+ return false;
+ }
+ return annotation.element?.enclosingElement?.name == 'CArraySize';
+ }).toList();
+
+ if (ffiSizeAnnotations.isEmpty) {
+ _errorReporter.reportErrorForNode(
+ FfiCode.MISSING_SIZE_ANNOTATION_CARRAY, errorNode);
+ }
+ if (ffiSizeAnnotations.length > 1) {
+ final extraAnnotations = ffiSizeAnnotations.skip(1);
+ for (final annotation in extraAnnotations) {
+ _errorReporter.reportErrorForNode(
+ FfiCode.EXTRA_SIZE_ANNOTATION_CARRAY, annotation);
+ }
+ }
+ }
+
/// Validate that the given [typeArgument] has a constant value. Return `true`
/// if a diagnostic was produced because it isn't constant.
bool _validateTypeArgument(TypeAnnotation typeArgument, String functionName) {
diff --git a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
index c6c83c4..74bcc94 100644
--- a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
+++ b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
@@ -688,6 +688,14 @@
const DartRepresentationOf(String nativeType);
}
+class CArray<T extends NativeType> extends NativeType {}
+
+class CArraySize {
+ final int numberOfElements;
+
+ const CArraySize(this.numberOfElements);
+}
+
extension StructPointer<T extends Struct> on Pointer<T> {
external T get ref;
diff --git a/pkg/analyzer/test/src/diagnostics/extra_size_annotation_carray_test.dart b/pkg/analyzer/test/src/diagnostics/extra_size_annotation_carray_test.dart
new file mode 100644
index 0000000..e39e560
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/extra_size_annotation_carray_test.dart
@@ -0,0 +1,42 @@
+// Copyright (c) 2021, 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/dart/error/ffi_code.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../dart/resolution/context_collection_resolution.dart';
+
+main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(ExtraSizeAnnotationCArray);
+ });
+}
+
+@reflectiveTest
+class ExtraSizeAnnotationCArray extends PubPackageResolutionTest {
+ test_one() async {
+ await assertNoErrorsInCode(r'''
+import 'dart:ffi';
+
+class C extends Struct {
+ @CArraySize(8)
+ CArray<Uint8> a0;
+}
+''');
+ }
+
+ test_two() async {
+ await assertErrorsInCode(r'''
+import 'dart:ffi';
+
+class C extends Struct {
+ @CArraySize(8)
+ @CArraySize(8)
+ CArray<Uint8> a0;
+}
+''', [
+ error(FfiCode.EXTRA_SIZE_ANNOTATION_CARRAY, 64, 14),
+ ]);
+ }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/missing_size_annotation_carray_test.dart b/pkg/analyzer/test/src/diagnostics/missing_size_annotation_carray_test.dart
new file mode 100644
index 0000000..1bf9a69
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/missing_size_annotation_carray_test.dart
@@ -0,0 +1,40 @@
+// Copyright (c) 2021, 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/dart/error/ffi_code.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../dart/resolution/context_collection_resolution.dart';
+
+main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(MissingSizeAnnotationCArray);
+ });
+}
+
+@reflectiveTest
+class MissingSizeAnnotationCArray extends PubPackageResolutionTest {
+ test_one() async {
+ await assertNoErrorsInCode(r'''
+import 'dart:ffi';
+
+class C extends Struct {
+ @CArraySize(8)
+ CArray<Uint8> a0;
+}
+''');
+ }
+
+ test_two() async {
+ await assertErrorsInCode(r'''
+import 'dart:ffi';
+
+class C extends Struct {
+ CArray<Uint8> a0;
+}
+''', [
+ error(FfiCode.MISSING_SIZE_ANNOTATION_CARRAY, 47, 13),
+ ]);
+ }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/non_sized_type_argument_test.dart b/pkg/analyzer/test/src/diagnostics/non_sized_type_argument_test.dart
new file mode 100644
index 0000000..2a14fcc
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/non_sized_type_argument_test.dart
@@ -0,0 +1,41 @@
+// Copyright (c) 2021, 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/dart/error/ffi_code.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../dart/resolution/context_collection_resolution.dart';
+
+main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(NonSizedTypeArgument);
+ });
+}
+
+@reflectiveTest
+class NonSizedTypeArgument extends PubPackageResolutionTest {
+ test_one() async {
+ await assertNoErrorsInCode(r'''
+import 'dart:ffi';
+
+class C extends Struct {
+ @CArraySize(8)
+ CArray<Uint8> a0;
+}
+''');
+ }
+
+ test_two() async {
+ await assertErrorsInCode(r'''
+import 'dart:ffi';
+
+class C extends Struct {
+ @CArraySize(8)
+ CArray<CArray<Uint8>> a0;
+}
+''', [
+ error(FfiCode.NON_SIZED_TYPE_ARGUMENT, 64, 21),
+ ]);
+ }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/test_all.dart b/pkg/analyzer/test/src/diagnostics/test_all.dart
index 49d3869..8b1a3b1 100644
--- a/pkg/analyzer/test/src/diagnostics/test_all.dart
+++ b/pkg/analyzer/test/src/diagnostics/test_all.dart
@@ -180,6 +180,7 @@
import 'extra_annotation_on_struct_field_test.dart'
as extra_annotation_on_struct_field;
import 'extra_positional_arguments_test.dart' as extra_positional_arguments;
+import 'extra_size_annotation_carray_test.dart' as extra_size_annotation_carray;
import 'field_in_struct_with_initializer_test.dart'
as field_in_struct_with_initializer;
import 'field_initialized_by_multiple_initializers_test.dart'
@@ -376,6 +377,8 @@
import 'missing_js_lib_annotation_test.dart' as missing_js_lib_annotation;
import 'missing_required_param_test.dart' as missing_required_param;
import 'missing_return_test.dart' as missing_return;
+import 'missing_size_annotation_carray_test.dart'
+ as missing_size_annotation_carray;
import 'mixin_application_not_implemented_interface_test.dart'
as mixin_application_not_implemented_interface;
import 'mixin_class_declares_constructor_test.dart'
@@ -451,6 +454,7 @@
import 'non_native_function_type_argument_to_pointer_test.dart'
as non_native_function_type_argument_to_pointer;
import 'non_null_opt_out_test.dart' as non_null_opt_out;
+import 'non_sized_type_argument_test.dart' as non_sized_type_argument;
import 'non_type_as_type_argument_test.dart' as non_type_as_type_argument;
import 'non_type_in_catch_clause_test.dart' as non_type_in_catch_clause;
import 'non_void_return_for_operator_test.dart' as non_void_return_for_operator;
@@ -789,6 +793,7 @@
external_variable_initializer.main();
extra_annotation_on_struct_field.main();
extra_positional_arguments.main();
+ extra_size_annotation_carray.main();
field_in_struct_with_initializer.main();
field_initialized_by_multiple_initializers.main();
final_initialized_in_declaration_and_constructor.main();
@@ -912,6 +917,7 @@
missing_js_lib_annotation.main();
missing_required_param.main();
missing_return.main();
+ missing_size_annotation_carray.main();
mixin_application_not_implemented_interface.main();
mixin_class_declares_constructor.main();
mixin_declares_constructor.main();
@@ -961,6 +967,7 @@
non_generative_implicit_constructor.main();
non_native_function_type_argument_to_pointer.main();
non_null_opt_out.main();
+ non_sized_type_argument.main();
non_type_as_type_argument.main();
non_type_in_catch_clause.main();
non_void_return_for_operator.main();
diff --git a/tools/VERSION b/tools/VERSION
index c79fbca..ffe0133 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 13
PATCH 0
-PRERELEASE 18
+PRERELEASE 19
PRERELEASE_PATCH 0
\ No newline at end of file