Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add lint implicit_call_tearoffs #3592

Merged
merged 26 commits into from
Oct 27, 2022
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
dce7fbe
Add lint avoid_implicit_call_tearoffs
natebosch Aug 10, 2022
e8893cc
Fix the registration to the correct node type
natebosch Aug 12, 2022
ef24737
Merge branch 'main' into avoid-implicit-call-tearoffs
natebosch Aug 12, 2022
4fe066b
Use unpublished analyzer
natebosch Aug 12, 2022
aa544de
Remove expectations for casting to Function
natebosch Aug 12, 2022
cdc6e7f
Expand test cases
natebosch Aug 19, 2022
09a716e
Rename rule without "avoid"
natebosch Aug 19, 2022
47c8f93
Merge branch 'main' into avoid-implicit-call-tearoffs
natebosch Aug 19, 2022
ba26a19
Report lint for the entire implicit call node
natebosch Aug 22, 2022
3fffa04
Merge branch 'main' into avoid-implicit-call-tearoffs
natebosch Sep 1, 2022
401f742
Remove dependency override, analyzer is published
natebosch Sep 1, 2022
f685194
Match version bump
natebosch Sep 1, 2022
fb8be4d
Add to all.yaml
natebosch Sep 1, 2022
a096283
Merge branch 'main' into avoid-implicit-call-tearoffs
natebosch Sep 6, 2022
5734d9f
Merge branch 'main' into avoid-implicit-call-tearoffs
natebosch Sep 20, 2022
3673cab
Move changelog entry
natebosch Sep 20, 2022
3197844
Do not change sorting for capital
natebosch Sep 20, 2022
c6def98
Merge branch 'main' into avoid-implicit-call-tearoffs
natebosch Oct 4, 2022
4f131f1
End description with .
natebosch Oct 4, 2022
4ef3b92
Prune leading newline from description
natebosch Oct 4, 2022
72d306e
Rename to plural
natebosch Oct 11, 2022
58f4507
Expand description
natebosch Oct 11, 2022
f3ba6c8
Add a bunch of test cases with a generic
natebosch Oct 11, 2022
8e8876f
Merge branch 'main' into avoid-implicit-call-tearoffs
natebosch Oct 11, 2022
d18710a
Remove unnecessary ellipsis
natebosch Oct 12, 2022
e940a65
Merge branch 'main' into avoid-implicit-call-tearoffs
natebosch Oct 27, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# 1.29.0-dev

- new lint: `implicit_call_tearoffs`

# 1.28.0

- update `avoid_redundant_argument_values` to work with enum declarations
Expand Down
1 change: 1 addition & 0 deletions example/all.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ linter:
- flutter_style_todos
- hash_and_equals
- implementation_imports
- implicit_call_tearoffs
- iterable_contains_unrelated_type
- join_return_with_assignment
- leading_newlines_in_multiline_strings
Expand Down
2 changes: 2 additions & 0 deletions lib/src/rules.dart
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ import 'rules/file_names.dart';
import 'rules/flutter_style_todos.dart';
import 'rules/hash_and_equals.dart';
import 'rules/implementation_imports.dart';
import 'rules/implicit_call_tearoffs.dart';
import 'rules/invariant_booleans.dart';
import 'rules/iterable_contains_unrelated_type.dart';
import 'rules/join_return_with_assignment.dart';
Expand Down Expand Up @@ -297,6 +298,7 @@ void registerLintRules({bool inTestMode = false}) {
..register(FlutterStyleTodos())
..register(HashAndEquals())
..register(ImplementationImports())
..register(ImplicitCallTearoffs())
..register(InvariantBooleans())
..register(IterableContainsUnrelatedType())
..register(JoinReturnWithAssignment())
Expand Down
72 changes: 72 additions & 0 deletions lib/src/rules/implicit_call_tearoffs.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright (c) 2022, 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/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/visitor.dart';

import '../analyzer.dart';

const _desc =
r'Explicitly tear-off `call` methods when using an object as a Function.';

const _details = r'''
**DO**
Explicitly tear off `.call` methods from objects when assigning to a Function
type. There is less magic with an explicit tear off. Future language versions
may remove the implicit call tear off.

**BAD:**
```dart
class Callable {
void call() {}
}
void callIt(void Function() f) {
f();
}

callIt(Callable());
```

**GOOD:**
```dart
class Callable {
void call() {}
}
void callIt(void Function() f) {
f();
}

callIt(Callable().call);
```

''';

class ImplicitCallTearoffs extends LintRule {
ImplicitCallTearoffs()
: super(
name: 'implicit_call_tearoffs',
description: _desc,
details: _details,
group: Group.style,
maturity: Maturity.experimental,
);

@override
void registerNodeProcessors(
NodeLintRegistry registry, LinterContext context) {
var visitor = _Visitor(this);
registry.addImplicitCallReference(this, visitor);
}
}

class _Visitor extends SimpleAstVisitor<void> {
final LintRule rule;

_Visitor(this.rule);

@override
void visitImplicitCallReference(ImplicitCallReference node) {
rule.reportLint(node);
}
}
2 changes: 1 addition & 1 deletion lib/src/version.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
// BSD-style license that can be found in the LICENSE file.

/// Package version. Synchronized w/ pubspec.yaml.
const String version = '1.28.0';
const String version = '1.29.0-dev';
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: linter
version: 1.28.0
version: 1.29.0-dev

description: >-
The implementation of the lint rules supported by the analyzer framework.
Expand Down
123 changes: 123 additions & 0 deletions test_data/rules/implicit_call_tearoffs.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// Copyright (c) 2022, 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/ `dart test -N implicit_call_tearoffs`

class C {
void call() {}
void other() {}
}

class C2 {
void call<T>(T arg) {}
}

void callIt(void Function() f) {
f();
}

void callIt2(void Function(int) f) {
f(0);
}

void Function() r1() => C(); // LINT
void Function() r2() => C().call; // OK
void Function() r3(C c) => c; // LINT
void Function() r4(C c) => c.call; // OK

void Function() r5(C? c1, C c2) {
return c1 ?? c2; // LINT
}

void Function() r6(C? c1, C c2) {
return c1?.call ?? c2.call; // OK
}

void Function() r7() {
return C()..other(); // LINT
}

void Function() r8() {
return (C()..other()).call; // OK
}

List<void Function()> r9(C c) {
return [c]; // LINT
}

List<void Function()> r10(C c) {
return [c.call]; // OK
}

void Function(int) r11(C2 c) => c; // LINT
void Function(int) r12(C2 c) => c.call; // OK

void main() {
callIt(C()); // LINT
callIt(C().call); // OK
Function f1 = C(); // LINT
Function f2 = C().call; // OK
void Function() f3 = C(); // LINT
void Function() f4 = C().call; // OK

final c = C();
callIt(c); // LINT
callIt(c.call); // OK
Function f5 = c; // LINT
Function f6 = c.call; // OK
void Function() f7 = c; // LINT
void Function() f8 = c.call; // OK

<void Function()>[
C(), // LINT
C().call, //OK
c, // LINT
c.call, // OK
];

callIt2(C2()); // LINT
callIt2(C2().call); // OK
callIt2(C2()<int>); // LINT
callIt2(C2().call<int>); // OK
Function f9 = C2(); // LINT
Function f10 = C2().call; // OK
Function f11 = C2()<int>; // LINT
Function f12 = C2().call<int>; // OK
void Function<T>(T) f13 = C2(); // LINT
void Function<T>(T) f14 = C2().call; // OK
void Function(int) f15 = C2(); // LINT
void Function(int) f16 = C2().call; // OK
void Function(int) f17 = C2()<int>; // LINT
void Function(int) f18 = C2().call<int>; // OK

final c2 = C2();
callIt2(c2); // LINT
callIt2(c2.call); // OK
callIt2(c2<int>); // LINT
callIt2(c2.call<int>); // OK
Function f19 = c2; // LINT
Function f20 = c2.call; // OK
Function f21 = c2<int>; // LINT
Function f22 = c2.call<int>; // OK
void Function<T>(T) f23 = c2; // LINT
void Function<T>(T) f24 = c2.call; // OK
void Function(int) f25 = c2; // LINT
void Function(int) f26 = c2.call; // OK
void Function(int) f27 = c2<int>; // LINT
void Function(int) f28 = c2.call<int>; // OK

<void Function(int)>[
C2(), // LINT
C2().call, //OK
C2()<int>, // LINT
C2().call<int>, //OK
c2, // LINT
c2.call, // OK
c2<int>, // LINT
c2.call<int>, // OK
];

C2()<int>; // LINT
c2<int>; // LINT
}