ffi
diff --git a/example/all.yaml b/example/all.yaml
index 31eb754..661395b 100644
--- a/example/all.yaml
+++ b/example/all.yaml
@@ -56,6 +56,7 @@
- empty_catches
- empty_constructor_bodies
- empty_statements
+ - ffi
- file_names
- flutter_style_todos
- hash_and_equals
diff --git a/lib/src/rules.dart b/lib/src/rules.dart
index 2e977c8..5a0ae49 100644
--- a/lib/src/rules.dart
+++ b/lib/src/rules.dart
@@ -57,6 +57,7 @@
import 'package:linter/src/rules/empty_catches.dart';
import 'package:linter/src/rules/empty_constructor_bodies.dart';
import 'package:linter/src/rules/empty_statements.dart';
+import 'package:linter/src/rules/ffi.dart';
import 'package:linter/src/rules/file_names.dart';
import 'package:linter/src/rules/flutter_style_todos.dart';
import 'package:linter/src/rules/hash_and_equals.dart';
@@ -212,6 +213,7 @@
..register(EmptyCatches())
..register(EmptyConstructorBodies())
..register(EmptyStatements())
+ ..register(Ffi())
..register(FileNames())
..register(FlutterStyleTodos())
..register(HashAndEquals())
diff --git a/lib/src/rules/ffi.dart b/lib/src/rules/ffi.dart
new file mode 100644
index 0000000..f4e501c
--- /dev/null
+++ b/lib/src/rules/ffi.dart
@@ -0,0 +1,90 @@
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/visitor.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:linter/src/analyzer.dart';
+
+const _desc = r'Static checks on "dart:ffi".';
+
+const _details = r'''
+Applies static rules of the "dart:ffi" package.
+See the "dart:ffi" API documentation for details.
+''';
+
+class Ffi extends LintRule implements NodeLintRule {
+ Ffi()
+ : super(
+ name: 'ffi',
+ description: _desc,
+ details: _details,
+ group: Group.style);
+
+ @override
+ void registerNodeProcessors(
+ NodeLintRegistry registry, LinterContext context) {
+ final visitor = _Visitor(this, context);
+ registry.addCompilationUnit(this, visitor);
+ registry.addClassDeclaration(this, visitor);
+ }
+}
+
+class _Visitor extends SimpleAstVisitor<void> {
+ static const LintCode invalidSuperclass = LintCode(
+ 'ffi', //
+ '{0} may not be extended.',
+ correction: 'Considering extending dart:ffi.Struct instead.');
+
+ static const LintCode invalidSupertype = LintCode(
+ 'ffi', //
+ '{0} may not be implemented.',
+ correction: 'Considering extending dart:ffi.Struct instead.');
+
+ static const LintCode genericStruct = LintCode(
+ 'ffi', //
+ 'Subclasses of Struct may not be generic.');
+
+ final LintRule rule;
+ final LinterContext context;
+
+ _Visitor(this.rule, this.context);
+
+ bool _isFfiCoreClass(TypeName cls) {
+ final Element target = cls.name.staticElement;
+ if (target is ClassElement) {
+ return target.library.name == 'dart.ffi';
+ }
+ return false;
+ }
+
+ @override
+ void visitClassDeclaration(ClassDeclaration node) {
+ bool isStruct = false;
+
+ // Only the Struct class may be extended.
+ if (node.extendsClause != null) {
+ final TypeName superclass = node.extendsClause.superclass;
+ if (_isFfiCoreClass(superclass) && !isStruct) {
+ if (superclass.name.staticElement.name == 'Struct') {
+ isStruct = true;
+ } else {
+ rule.reportLint(superclass.name,
+ errorCode: invalidSuperclass, arguments: [superclass.name]);
+ }
+ }
+ }
+
+ // No classes from the FFI may be explicitly implemented.
+ void checkSupertype(TypeName typename) {
+ if (_isFfiCoreClass(typename)) {
+ rule.reportLint(typename.name,
+ errorCode: invalidSupertype, arguments: [typename.name]);
+ }
+ }
+
+ node.implementsClause?.interfaces?.forEach(checkSupertype);
+ node.withClause?.mixinTypes?.forEach(checkSupertype);
+
+ if (isStruct && node.declaredElement.typeParameters?.isNotEmpty == true) {
+ rule.reportLint(node.name, errorCode: genericStruct);
+ }
+ }
+}
diff --git a/pubspec.yaml b/pubspec.yaml
index 50d858c..62d3286 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
name: linter
-version: 0.1.97+1
+version: 0.1.98
author: Dart Team <misc@dartlang.org>
diff --git a/test/mock_sdk.dart b/test/mock_sdk.dart
index 6e35d27..b13c732 100644
--- a/test/mock_sdk.dart
+++ b/test/mock_sdk.dart
@@ -22,6 +22,7 @@
"collection": const LibraryInfo("collection/collection.dart"),
"convert": const LibraryInfo("convert/convert.dart"),
"core": const LibraryInfo("core/core.dart"),
+ "ffi": const LibraryInfo("ffi/ffi.dart"),
"io": const LibraryInfo("io/io.dart'"),
"html": const LibraryInfo(
"html/dartium/html_dartium.dart",
@@ -147,7 +148,7 @@
factory Map.fromIterable(Iterable iterable,
{K key(element), V value(element)}) = LinkedHashMap<K, V>.fromIterable;
factory Map.fromIterables(Iterable<K> keys, Iterable<V> values) =
- LinkedHashMap<K, V>.fromIterables;
+ LinkedHashMap<K, V>.fromIterables;
factory Map.of(Map<K, V> other) = LinkedHashMap<K, V>.of;
factory Map.identity() = LinkedHashMap<K, V>.identity;
/* external */ factory Map.unmodifiable(Map other);
@@ -204,6 +205,25 @@
''')
]);
+ static _MockSdkLibrary LIB_FFI =
+ _MockSdkLibrary('dart:ffi', '/lib/ffi/ffi.dart', '''
+library dart.ffi;
+class NativeType {}
+class Void extends NativeType {}
+class Int8 extends NativeType {}
+class Uint8 extends NativeType {}
+class Int16 extends NativeType {}
+class Uint16 extends NativeType {}
+class Int32 extends NativeType {}
+class Uint32 extends NativeType {}
+class Int64 extends NativeType {}
+class Uint64 extends NativeType {}
+class Float extends NativeType {}
+class Double extends NativeType {}
+class Pointer<T extends NativeType> extends NativeType {}
+class Struct<S extends NativeType> extends NativeType {}
+ ''');
+
static _MockSdkLibrary LIB_COLLECTION =
_MockSdkLibrary('dart:collection', '/lib/collection/collection.dart', '''
library dart.collection;
@@ -218,7 +238,7 @@
factory LinkedHashMap.from(Map other);
factory LinkedHashMap.fromIterable(Iterable iterable,
{K key(element), V value(element)});
- factory LinkedHashMap.fromIterables(Iterable<K> keys, Iterable<V> values);
+ factory LinkedHashMap.fromIterables(Iterable<K> keys, Iterable<V> values);
factory LinkedHashMap.of(Map<K, V> other) =>
new LinkedHashMap<K, V>()..addAll(other);
}
@@ -229,7 +249,7 @@
bool isValidKey(potentialKey)});
/* external factory */ LinkedHashSet.identity();
factory LinkedHashSet.from(Iterable elements);
-
+
factory LinkedHashSet.of(Iterable<E> elements) =>
new LinkedHashSet<E>()..addAll(elements);
}
@@ -339,6 +359,7 @@
LIB_ASYNC,
LIB_COLLECTION,
LIB_CONVERT,
+ LIB_FFI,
LIB_IO,
LIB_MATH,
LIB_HTML,
@@ -460,6 +481,7 @@
'dart:async/stream.dart': '/lib/async/stream.dart',
'dart:collection': '/lib/collection/collection.dart',
'dart:convert': '/lib/convert/convert.dart',
+ 'dart:ffi': '/lib/ffi/ffi.dart',
'dart:io': '/lib/io/io.dart',
'dart:math': '/lib/math/math.dart'
};
diff --git a/test/rules/ffi.dart b/test/rules/ffi.dart
new file mode 100644
index 0000000..e8c1ce7
--- /dev/null
+++ b/test/rules/ffi.dart
@@ -0,0 +1,40 @@
+// Copyright (c) 2019, 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.
+
+// test w/ `pub run test -N ffi`
+
+import 'dart:ffi';
+
+// No FFI types except Struct may be extended.
+class X extends Void {} //LINT
+class X extends Int8 {} //LINT
+class X extends Uint8 {} //LINT
+class X extends Int16 {} //LINT
+class X extends Uint16 {} //LINT
+class X extends Int32 {} //LINT
+class X extends Uint32 {} //LINT
+class X extends Int64 {} //LINT
+class X extends Uint64 {} //LINT
+class X extends Float {} //LINT
+class X extends Double {} //LINT
+class X extends Pointer {} //LINT
+class X extends Struct<X> {} //OK
+
+// No FFI types may be implemented.
+class X implements Void {} //LINT
+class X implements Int8 {} //LINT
+class X implements Uint8 {} //LINT
+class X implements Int16 {} //LINT
+class X implements Uint16 {} //LINT
+class X implements Int32 {} //LINT
+class X implements Uint32 {} //LINT
+class X implements Int64 {} //LINT
+class X implements Uint64 {} //LINT
+class X implements Float {} //LINT
+class X implements Double {} //LINT
+class X implements Pointer {} //LINT
+class X implements Struct<X> {} //LINT
+
+// Structs may not be generic.
+class X<T> extends Struct<X<T>> {} //LINT
diff --git a/tool/since/linter.yaml b/tool/since/linter.yaml
index c0d99c7..f3c171e 100644
--- a/tool/since/linter.yaml
+++ b/tool/since/linter.yaml
@@ -52,6 +52,7 @@
empty_catches: 0.1.22
empty_constructor_bodies: 0.1.1
empty_statements: 0.1.21
+ffi: 0.1.98
file_names: 0.1.54
flutter_style_todos: 0.1.61
hash_and_equals: 0.1.11