From 42495c26a4e5779758c54d3ff498687f913e43ae Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Tue, 29 Oct 2024 11:53:51 -0700 Subject: [PATCH] Enable avoid_dynamic_calls lint (#2410) Add argument types to `typedMatches` arguments which were unnecessarily widening it back to `dynamic`. Add some missing `as dynamic` and `as Function` which is the pattern the lint uses to allow intentional dynamic calls. Ignore some dynamic calls in a test for a legacy API. Ignore a couple false positives for calling methods (as opposed to getters) on a cast expression. The false positive was fixed in https://dart-review.googlesource.com/c/sdk/+/390640 but not yet published. Remove the deprecated lint `package_api_docs`. Replaces https://github.com/dart-lang/matcher/pull/205 --- pkgs/matcher/CHANGELOG.md | 4 ++++ pkgs/matcher/analysis_options.yaml | 1 + pkgs/matcher/lib/src/core_matchers.dart | 3 ++- .../lib/src/expect/throws_matcher.dart | 3 +-- pkgs/matcher/lib/src/iterable_matchers.dart | 2 +- pkgs/matcher/lib/src/map_matchers.dart | 19 +++++++++---------- pkgs/matcher/lib/src/numeric_matchers.dart | 8 ++++---- pkgs/matcher/lib/src/operator_matchers.dart | 2 +- pkgs/matcher/lib/src/order_matchers.dart | 2 +- pkgs/matcher/lib/src/string_matchers.dart | 6 +++--- pkgs/matcher/pubspec.yaml | 2 +- pkgs/matcher/test/expect_async_test.dart | 8 ++++---- .../test/matcher/throws_type_test.dart | 1 + pkgs/matcher/test/mirror_matchers_test.dart | 1 + 14 files changed, 34 insertions(+), 28 deletions(-) diff --git a/pkgs/matcher/CHANGELOG.md b/pkgs/matcher/CHANGELOG.md index 1ec153e47..0522c4cee 100644 --- a/pkgs/matcher/CHANGELOG.md +++ b/pkgs/matcher/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.12.18-wip + +* Remove some dynamic invocations. + ## 0.12.17 * Require Dart 3.4 diff --git a/pkgs/matcher/analysis_options.yaml b/pkgs/matcher/analysis_options.yaml index 0e8718611..d183f7bb3 100644 --- a/pkgs/matcher/analysis_options.yaml +++ b/pkgs/matcher/analysis_options.yaml @@ -3,6 +3,7 @@ include: package:lints/recommended.yaml linter: rules: - always_declare_return_types + - avoid_dynamic_calls - avoid_private_typedef_functions - avoid_unused_constructor_parameters - cancel_subscriptions diff --git a/pkgs/matcher/lib/src/core_matchers.dart b/pkgs/matcher/lib/src/core_matchers.dart index 70e34491d..afb835b09 100644 --- a/pkgs/matcher/lib/src/core_matchers.dart +++ b/pkgs/matcher/lib/src/core_matchers.dart @@ -150,7 +150,8 @@ class _ReturnsNormally extends FeatureMatcher { @override bool typedMatches(Function f, Map matchState) { try { - f(); + // ignore: unnecessary_cast + (f as Function)(); return true; } catch (e, s) { addStateInfo(matchState, {'exception': e, 'stack': s}); diff --git a/pkgs/matcher/lib/src/expect/throws_matcher.dart b/pkgs/matcher/lib/src/expect/throws_matcher.dart index 3a8182131..37676ef72 100644 --- a/pkgs/matcher/lib/src/expect/throws_matcher.dart +++ b/pkgs/matcher/lib/src/expect/throws_matcher.dart @@ -82,8 +82,7 @@ class Throws extends AsyncMatcher { } try { - item as Function; - var value = item(); + var value = (item as Function)(); if (value is Future) { return _matchFuture(value, 'returned a Future that emitted '); } diff --git a/pkgs/matcher/lib/src/iterable_matchers.dart b/pkgs/matcher/lib/src/iterable_matchers.dart index 918650a26..abc9688e8 100644 --- a/pkgs/matcher/lib/src/iterable_matchers.dart +++ b/pkgs/matcher/lib/src/iterable_matchers.dart @@ -204,7 +204,7 @@ class _UnorderedMatches extends _IterableMatcher { .add(' unordered'); @override - Description describeTypedMismatch(dynamic item, + Description describeTypedMismatch(Iterable item, Description mismatchDescription, Map matchState, bool verbose) => mismatchDescription.add(_test(item.toList())!); diff --git a/pkgs/matcher/lib/src/map_matchers.dart b/pkgs/matcher/lib/src/map_matchers.dart index 97be1ea7d..5328b88b9 100644 --- a/pkgs/matcher/lib/src/map_matchers.dart +++ b/pkgs/matcher/lib/src/map_matchers.dart @@ -2,20 +2,20 @@ // 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 'feature_matcher.dart'; import 'interfaces.dart'; import 'util.dart'; /// Returns a matcher which matches maps containing the given [value]. Matcher containsValue(Object? value) => _ContainsValue(value); -class _ContainsValue extends Matcher { +class _ContainsValue extends FeatureMatcher { final Object? _value; const _ContainsValue(this._value); @override - bool matches(Object? item, Map matchState) => - (item as dynamic).containsValue(_value); + bool typedMatches(Map item, Map matchState) => item.containsValue(_value); @override Description describe(Description description) => description.add('contains value ').addDescriptionOf(_value); @@ -26,16 +26,15 @@ class _ContainsValue extends Matcher { Matcher containsPair(Object? key, Object? valueOrMatcher) => _ContainsMapping(key, wrapMatcher(valueOrMatcher)); -class _ContainsMapping extends Matcher { +class _ContainsMapping extends FeatureMatcher { final Object? _key; final Matcher _valueMatcher; const _ContainsMapping(this._key, this._valueMatcher); @override - bool matches(Object? item, Map matchState) => - (item as dynamic).containsKey(_key) && - _valueMatcher.matches(item[_key], matchState); + bool typedMatches(Map item, Map matchState) => + item.containsKey(_key) && _valueMatcher.matches(item[_key], matchState); @override Description describe(Description description) { @@ -47,9 +46,9 @@ class _ContainsMapping extends Matcher { } @override - Description describeMismatch(Object? item, Description mismatchDescription, - Map matchState, bool verbose) { - if (!(item as dynamic).containsKey(_key)) { + Description describeTypedMismatch( + Map item, Description mismatchDescription, Map matchState, bool verbose) { + if (!item.containsKey(_key)) { return mismatchDescription .add(" doesn't contain key ") .addDescriptionOf(_key); diff --git a/pkgs/matcher/lib/src/numeric_matchers.dart b/pkgs/matcher/lib/src/numeric_matchers.dart index 5193d302e..a7981609e 100644 --- a/pkgs/matcher/lib/src/numeric_matchers.dart +++ b/pkgs/matcher/lib/src/numeric_matchers.dart @@ -18,7 +18,7 @@ class _IsCloseTo extends FeatureMatcher { const _IsCloseTo(this._value, this._delta); @override - bool typedMatches(dynamic item, Map matchState) { + bool typedMatches(num item, Map matchState) { var diff = item - _value; if (diff < 0) diff = -diff; return diff <= _delta; @@ -32,8 +32,8 @@ class _IsCloseTo extends FeatureMatcher { .addDescriptionOf(_value); @override - Description describeTypedMismatch(dynamic item, - Description mismatchDescription, Map matchState, bool verbose) { + Description describeTypedMismatch( + num item, Description mismatchDescription, Map matchState, bool verbose) { var diff = item - _value; if (diff < 0) diff = -diff; return mismatchDescription.add(' differs by ').addDescriptionOf(diff); @@ -67,7 +67,7 @@ class _InRange extends FeatureMatcher { this._low, this._high, this._lowMatchValue, this._highMatchValue); @override - bool typedMatches(dynamic value, Map matchState) { + bool typedMatches(num value, Map matchState) { if (value < _low || value > _high) { return false; } diff --git a/pkgs/matcher/lib/src/operator_matchers.dart b/pkgs/matcher/lib/src/operator_matchers.dart index ced25fcd7..15e50ffca 100644 --- a/pkgs/matcher/lib/src/operator_matchers.dart +++ b/pkgs/matcher/lib/src/operator_matchers.dart @@ -57,7 +57,7 @@ class _AllOf extends Matcher { @override Description describeMismatch(dynamic item, Description mismatchDescription, Map matchState, bool verbose) { - var matcher = matchState['matcher']; + var matcher = matchState['matcher'] as Matcher; matcher.describeMismatch( item, mismatchDescription, matchState['state'], verbose); return mismatchDescription; diff --git a/pkgs/matcher/lib/src/order_matchers.dart b/pkgs/matcher/lib/src/order_matchers.dart index 1146f6ab2..6fe7c76d4 100644 --- a/pkgs/matcher/lib/src/order_matchers.dart +++ b/pkgs/matcher/lib/src/order_matchers.dart @@ -80,7 +80,7 @@ class _OrderingMatcher extends Matcher { return _equalValue; } else if ((item as dynamic) < _value) { return _lessThanValue; - } else if (item > _value) { + } else if ((item as dynamic) > _value) { return _greaterThanValue; } else { return false; diff --git a/pkgs/matcher/lib/src/string_matchers.dart b/pkgs/matcher/lib/src/string_matchers.dart index 8b2f95ae1..b819fa5a5 100644 --- a/pkgs/matcher/lib/src/string_matchers.dart +++ b/pkgs/matcher/lib/src/string_matchers.dart @@ -80,7 +80,7 @@ class _StringStartsWith extends FeatureMatcher { const _StringStartsWith(this._prefix); @override - bool typedMatches(dynamic item, Map matchState) => item.startsWith(_prefix); + bool typedMatches(String item, Map matchState) => item.startsWith(_prefix); @override Description describe(Description description) => @@ -97,7 +97,7 @@ class _StringEndsWith extends FeatureMatcher { const _StringEndsWith(this._suffix); @override - bool typedMatches(dynamic item, Map matchState) => item.endsWith(_suffix); + bool typedMatches(String item, Map matchState) => item.endsWith(_suffix); @override Description describe(Description description) => @@ -119,7 +119,7 @@ class _StringContainsInOrder extends FeatureMatcher { const _StringContainsInOrder(this._substrings); @override - bool typedMatches(dynamic item, Map matchState) { + bool typedMatches(String item, Map matchState) { var fromIndex = 0; for (var s in _substrings) { var index = item.indexOf(s, fromIndex); diff --git a/pkgs/matcher/pubspec.yaml b/pkgs/matcher/pubspec.yaml index 291b3e9b4..237e5593e 100644 --- a/pkgs/matcher/pubspec.yaml +++ b/pkgs/matcher/pubspec.yaml @@ -1,5 +1,5 @@ name: matcher -version: 0.12.17 +version: 0.12.18-wip description: >- Support for specifying test expectations via an extensible Matcher class. Also includes a number of built-in Matcher implementations for common cases. diff --git a/pkgs/matcher/test/expect_async_test.dart b/pkgs/matcher/test/expect_async_test.dart index dd564ea5c..7619893b7 100644 --- a/pkgs/matcher/test/expect_async_test.dart +++ b/pkgs/matcher/test/expect_async_test.dart @@ -336,7 +336,7 @@ void main() { test('works with no arguments', () async { var callbackRun = false; var monitor = await TestCaseMonitor.run(() { - // ignore: deprecated_member_use_from_same_package + // ignore: deprecated_member_use_from_same_package, avoid_dynamic_calls expectAsync(() { callbackRun = true; })(); @@ -349,7 +349,7 @@ void main() { test('works with dynamic arguments', () async { var callbackRun = false; var monitor = await TestCaseMonitor.run(() { - // ignore: deprecated_member_use_from_same_package + // ignore: deprecated_member_use_from_same_package, avoid_dynamic_calls expectAsync((arg1, arg2) { callbackRun = true; })(1, 2); @@ -362,7 +362,7 @@ void main() { test('works with non-nullable arguments', () async { var callbackRun = false; var monitor = await TestCaseMonitor.run(() { - // ignore: deprecated_member_use_from_same_package + // ignore: deprecated_member_use_from_same_package, avoid_dynamic_calls expectAsync((int arg1, int arg2) { callbackRun = true; })(1, 2); @@ -375,7 +375,7 @@ void main() { test('works with 6 arguments', () async { var callbackRun = false; var monitor = await TestCaseMonitor.run(() { - // ignore: deprecated_member_use_from_same_package + // ignore: deprecated_member_use_from_same_package, avoid_dynamic_calls expectAsync((arg1, arg2, arg3, arg4, arg5, arg6) { callbackRun = true; })(1, 2, 3, 4, 5, 6); diff --git a/pkgs/matcher/test/matcher/throws_type_test.dart b/pkgs/matcher/test/matcher/throws_type_test.dart index f4c55b014..557a73aac 100644 --- a/pkgs/matcher/test/matcher/throws_type_test.dart +++ b/pkgs/matcher/test/matcher/throws_type_test.dart @@ -95,6 +95,7 @@ void main() { group('[throwsNoSuchMethodError]', () { test('passes when a NoSuchMethodError is thrown', () { expect(() { + // ignore: avoid_dynamic_calls (1 as dynamic).notAMethodOnInt(); }, throwsNoSuchMethodError); }); diff --git a/pkgs/matcher/test/mirror_matchers_test.dart b/pkgs/matcher/test/mirror_matchers_test.dart index b19fe3ea6..06f0df411 100644 --- a/pkgs/matcher/test/mirror_matchers_test.dart +++ b/pkgs/matcher/test/mirror_matchers_test.dart @@ -5,6 +5,7 @@ // ignore_for_file: deprecated_member_use_from_same_package @TestOn('vm') +library; import 'package:matcher/mirror_matchers.dart'; import 'package:test/test.dart';