Merge pull request #1789 from tenhobi/add-effective_dart-to-linter-page
Add effective_dart to linter page
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2473bbf..2791f4d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,12 @@
+# 0.1.102
+
+* `avoid_web_libraries_in_flutter` updated to disallow access from all but Flutter web plugin packages
+* updated `avoid_returning_null_for_void` to check only `null` literals (and not expressions having `Null` types)
+* fixed `prefer_final_fields` to respect non-mutating prefix operators
+* new lint: `prefer_is_not_operator`
+* new lint: `avoid_unnecessary_containers`
+* added basic nnbd-awareness to `avoid_init_to_null`
+
# 0.1.101
* fixed `diagnostic_describe_all_properties` to flag properties in `Diagnosticable`s with no debug methods defined
diff --git a/lib/src/rules/always_specify_types.dart b/lib/src/rules/always_specify_types.dart
index ee265bc..77a2a2f 100644
--- a/lib/src/rules/always_specify_types.dart
+++ b/lib/src/rules/always_specify_types.dart
@@ -66,7 +66,7 @@
/// type args.
String _OPTIONAL_TYPE_ARGS_VAR_NAME = 'optionalTypeArgs';
-bool _isOptionallyParameterized(ParameterizedType type) {
+bool _isOptionallyParameterized(InterfaceType type) {
List<ElementAnnotation> metadata = type.element?.metadata;
if (metadata != null) {
return metadata
@@ -126,7 +126,7 @@
visitNamedType(NamedType namedType) {
DartType type = namedType.type;
- if (type is ParameterizedType) {
+ if (type is InterfaceType) {
if (type.typeParameters.isNotEmpty &&
namedType.typeArguments == null &&
namedType.parent is! IsExpression &&
diff --git a/lib/src/rules/avoid_returning_null_for_void.dart b/lib/src/rules/avoid_returning_null_for_void.dart
index 1003014..ec06a90 100644
--- a/lib/src/rules/avoid_returning_null_for_void.dart
+++ b/lib/src/rules/avoid_returning_null_for_void.dart
@@ -77,7 +77,7 @@
}
void _visit(AstNode node, Expression expression) {
- if (expression.staticType?.isDartCoreNull != true) {
+ if (expression is! NullLiteral) {
return;
}
@@ -102,7 +102,7 @@
rule.reportLint(node);
} else if (isAsync &&
type.isDartAsyncFuture &&
- (type as ParameterizedType).typeArguments.first.isVoid) {
+ (type as InterfaceType).typeArguments.first.isVoid) {
rule.reportLint(node);
}
}
diff --git a/lib/src/rules/avoid_web_libraries_in_flutter.dart b/lib/src/rules/avoid_web_libraries_in_flutter.dart
index 78aa2eb..a2ad24e 100644
--- a/lib/src/rules/avoid_web_libraries_in_flutter.dart
+++ b/lib/src/rules/avoid_web_libraries_in_flutter.dart
@@ -10,19 +10,20 @@
import '../analyzer.dart';
import '../ast.dart';
-const _desc = r'Avoid using web-only libraries outside Flutter web projects.';
+const _desc =
+ r'Avoid using web-only libraries outside Flutter web plugin packages.';
const _details = r'''Avoid using web libraries, `dart:html`, `dart:js` and
-`dart:js_util` in non-web Flutter projects. These libraries are not supported
-outside a web context and functionality that depends on them will fail at
-runtime.
+`dart:js_util` in Flutter packages that are not web plugins. These libraries are
+not supported outside a web context; functionality that depends on them will
+fail at runtime in Flutter mobile, and their use is generally discouraged in
+Flutter web.
-Web library access is allowed in:
+Web library access *is* allowed in:
-* projects meant to run on the web (e.g., have a `web/` directory)
* plugin packages that declare `web` as a supported context
-otherwise, imports of `dart:html`, `dart:js` and `dart:js_util` are flagged.
+otherwise, imports of `dart:html`, `dart:js` and `dart:js_util` are disallowed.
''';
/// todo (pq): consider making a utility and sharing w/ `prefer_relative_imports`
@@ -86,10 +87,10 @@
return false;
}
- // Check for a web directory or a web plugin context declaration.
- return !pubspecFile.parent.getChild('web').exists &&
- ((parsedPubspec['flutter'] ?? const {})['plugin'] ?? const {})['web'] ==
- null;
+ // Check for a web plugin context declaration.
+ return ((parsedPubspec['flutter'] ?? const {})['plugin'] ??
+ const {})['web'] ==
+ null;
}
bool isWebUri(String uri) {
diff --git a/lib/src/rules/prefer_final_fields.dart b/lib/src/rules/prefer_final_fields.dart
index 59cb384..308f543 100644
--- a/lib/src/rules/prefer_final_fields.dart
+++ b/lib/src/rules/prefer_final_fields.dart
@@ -5,6 +5,7 @@
import 'dart:collection';
import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/element.dart';
@@ -35,10 +36,8 @@
var _label = 'hola mundo! GoodMutable', _offender = 'mumble mumble!'; // LINT
var _someOther; // LINT
-
MultipleMutable() : _someOther = 5;
-
MultipleMutable(this._someOther);
void changeLabel() {
@@ -85,6 +84,19 @@
```
''';
+bool _containedInFormal(Element element, FormalParameter formal) {
+ final formalField = formal.identifier.staticElement;
+ return formalField is FieldFormalParameterElement &&
+ formalField.field == element;
+}
+
+bool _containedInInitializer(
+ Element element, ConstructorInitializer initializer) =>
+ initializer is ConstructorFieldInitializer &&
+ DartTypeUtilities.getCanonicalElementFromIdentifier(
+ initializer.fieldName) ==
+ element;
+
class PreferFinalFields extends LintRule implements NodeLintRule {
PreferFinalFields()
: super(
@@ -121,7 +133,11 @@
@override
void visitPrefixExpression(PrefixExpression node) {
- _addMutatedFieldElement(node.operand);
+ final operator = node.operator;
+ if (operator.type == TokenType.MINUS_MINUS ||
+ operator.type == TokenType.PLUS_PLUS) {
+ _addMutatedFieldElement(node.operand);
+ }
super.visitPrefixExpression(node);
}
@@ -182,16 +198,3 @@
}
}
}
-
-bool _containedInInitializer(
- Element element, ConstructorInitializer initializer) =>
- initializer is ConstructorFieldInitializer &&
- DartTypeUtilities.getCanonicalElementFromIdentifier(
- initializer.fieldName) ==
- element;
-
-bool _containedInFormal(Element element, FormalParameter formal) {
- final formalField = formal.identifier.staticElement;
- return formalField is FieldFormalParameterElement &&
- formalField.field == element;
-}
diff --git a/lib/src/rules/void_checks.dart b/lib/src/rules/void_checks.dart
index 7fdbef6..c1470f9 100644
--- a/lib/src/rules/void_checks.dart
+++ b/lib/src/rules/void_checks.dart
@@ -64,7 +64,7 @@
if (type.isVoid) return true;
if (type.isDartCoreNull) return true;
if (type.isDartAsyncFuture &&
- type is ParameterizedType &&
+ type is InterfaceType &&
(type.typeArguments.first.isVoid ||
type.typeArguments.first.isDartCoreNull)) {
return true;
diff --git a/lib/src/util/dart_type_utilities.dart b/lib/src/util/dart_type_utilities.dart
index 78e1c35..4e7f966 100644
--- a/lib/src/util/dart_type_utilities.dart
+++ b/lib/src/util/dart_type_utilities.dart
@@ -375,17 +375,17 @@
typeSystem.isSubtypeOf(rightType, leftType)) {
return false;
}
- Element leftElement = leftType.element;
- Element rightElement = rightType.element;
- if (leftElement is ClassElement && rightElement is ClassElement) {
- // In this case, [leftElement] and [rightElement] each represent a class,
- // like `int` or `List` or `Future<T>` or `Iterable<String>`.
- if (isClass(leftType, rightElement.name, rightElement.library.name)) {
+ if (leftType is InterfaceType && rightType is InterfaceType) {
+ // In this case, [leftElement] and [rightElement] each represent
+ // the same class, like `int`, or `Iterable<String>`.
+ var leftElement = leftType.element;
+ var rightElement = rightType.element;
+ if (leftElement == rightElement) {
// In this case, [leftElement] and [rightElement] represent the same
// class, modulo generics, e.g. `List<int>` and `List<dynamic>`. Now we
// need to check type arguments.
- var leftTypeArguments = (leftType as ParameterizedType).typeArguments;
- var rightTypeArguments = (rightType as ParameterizedType).typeArguments;
+ var leftTypeArguments = leftType.typeArguments;
+ var rightTypeArguments = rightType.typeArguments;
if (leftTypeArguments.length != rightTypeArguments.length) {
// I cannot think of how we would enter this block, but it guards
// against RangeError below.
@@ -405,9 +405,10 @@
return leftElement.supertype?.isObject == true ||
leftElement.supertype != rightElement.supertype;
}
- } else if (leftElement is TypeParameterElement &&
- rightElement is TypeParameterElement) {
- return unrelatedTypes(typeSystem, leftElement.bound, rightElement.bound);
+ } else if (leftType is TypeParameterType &&
+ rightType is TypeParameterType) {
+ return unrelatedTypes(
+ typeSystem, leftType.element.bound, rightType.element.bound);
} else if (leftType is FunctionType) {
if (_isFunctionTypeUnrelatedToType(leftType, rightType)) {
return true;
diff --git a/lib/src/util/flutter_utils.dart b/lib/src/util/flutter_utils.dart
index 99a5f1e..15b8dd9 100644
--- a/lib/src/util/flutter_utils.dart
+++ b/lib/src/util/flutter_utils.dart
@@ -26,7 +26,7 @@
if (isWidgetType(type)) {
return true;
}
- if (type is ParameterizedType &&
+ if (type is InterfaceType &&
DartTypeUtilities.implementsAnyInterface(type, _collectionInterfaces)) {
return type.typeParameters.length == 1 &&
isWidgetProperty(type.typeArguments.first);
diff --git a/lib/src/version.dart b/lib/src/version.dart
index 7dd531d..ee5dc7d 100644
--- a/lib/src/version.dart
+++ b/lib/src/version.dart
@@ -3,4 +3,4 @@
// BSD-style license that can be found in the LICENSE file.
/// Package version. Synchronized w/ pubspec.yaml.
-const String version = '0.1.101';
+const String version = '0.1.102';
diff --git a/pubspec.yaml b/pubspec.yaml
index 779a79a..203c8e8 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
name: linter
-version: 0.1.101
+version: 0.1.102
author: Dart Team <misc@dartlang.org>
@@ -23,12 +23,7 @@
dev_dependencies:
cli_util: ^0.1.2
- # Update when 1.3.3 lands.
- # https://github.com/dart-lang/dart_style/issues/866
- dart_style:
- git:
- url: git://github.com/dart-lang/dart_style.git
- ref: 108a94c8b1797d4f535edff2746bf31b185d04b2
+ dart_style: ^1.3.3
github: ^5.0.0
grinder: ^0.8.0
http: ^0.12.0
diff --git a/test/engine_test.dart b/test/engine_test.dart
index 78be356..591c7ec 100644
--- a/test/engine_test.dart
+++ b/test/engine_test.dart
@@ -40,8 +40,6 @@
_test('exception', 'EXCEPTION: LinterException: foo',
(r) => r.exception(LinterException('foo')));
- _test('logError', 'ERROR: foo', (r) => r.logError('foo'));
- _test('logInformation', 'INFO: foo', (r) => r.logInformation('foo'));
_test('warn', 'WARN: foo', (r) => r.warn('foo'));
});
@@ -55,32 +53,6 @@
});
});
- group('analysis logger', () {
- var currentErr = errorSink;
- var currentOut = outSink;
- var errCollector = CollectingSink();
- var outCollector = CollectingSink();
- var logger = StdLogger();
- setUp(() {
- errorSink = errCollector;
- outSink = outCollector;
- });
- tearDown(() {
- errorSink = currentErr;
- outSink = currentOut;
- errCollector.buffer.clear();
- outCollector.buffer.clear();
- });
- test('logError', () {
- logger.logError('logError');
- expect(errCollector.trim(), 'logError');
- });
- test('logInformation', () {
- logger.logInformation('logInformation');
- expect(outCollector.trim(), 'logInformation');
- });
- });
-
group('camel case', () {
test('humanize', () {
expect(CamelCaseString('FooBar').humanized, 'Foo Bar');
diff --git a/test/integration_test.dart b/test/integration_test.dart
index 369fcea..9166328 100644
--- a/test/integration_test.dart
+++ b/test/integration_test.dart
@@ -70,8 +70,8 @@
'--rules=avoid_web_libraries_in_flutter',
], LinterOptions());
expect(collectingOut.trim(),
- contains('2 files analyzed, 0 issues found, in'));
- expect(exitCode, 0);
+ contains('2 files analyzed, 3 issues found, in'));
+ expect(exitCode, 1);
});
test('web plugin', () async {
diff --git a/test/rules/prefer_final_fields.dart b/test/rules/prefer_final_fields.dart
index d45456c..25bd514 100644
--- a/test/rules/prefer_final_fields.dart
+++ b/test/rules/prefer_final_fields.dart
@@ -4,7 +4,56 @@
// test w/ `pub run test -N prefer_final_fields`
-//ignore_for_file: unused_field, unused_local_variable
+//ignore_for_file: unused_field, unused_local_variable, prefer_expression_function_bodies
+
+class PrefixOps {
+ bool _initialized = false; // LINT
+ int _num = 1; // LINT
+ int _num2 = 1; // OK
+ int _bits = 0xffff; // LINT
+ int getValue() {
+ if (!_initialized) {
+ return 0;
+ }
+ if (-_num == -1) {
+ return 0;
+ }
+ if (~_bits == 0) {
+ return 0;
+ }
+ if (--_num2 == 0) {
+ return 0;
+ }
+ return 1;
+ }
+}
+
+typedef bool Predicate();
+
+class PostfixOps {
+ int _num = 1; // OK
+ int _num2 = 1; // OK
+ String _string = ''; // LINT
+
+ Predicate _predicate = () => false; // LINT
+
+ int getValue() {
+ if (_num-- == -1) {
+ return 0;
+ }
+ if (_num2++ == 1) {
+ return 0;
+ }
+ if (_predicate()) {
+ return 1;
+ }
+ if (_string.length == 1) {
+ return 0;
+ }
+ return 1;
+ }
+}
+
class FalsePositiveWhenReturn {
int _value = 0;
int getValue() {