Skip to content

Commit

Permalink
Add lint implicit_call_tearoffs (#3592)
Browse files Browse the repository at this point in the history
In an effort to simplify the language we will consider removing the
implicit `call` tearoff which can coerce an instance of any class which
defines a `call` method into a `Function` type. Authors should
explicitly add the `.call` so there is less magic.
  • Loading branch information
natebosch authored Oct 27, 2022
1 parent 22cc957 commit 3533db1
Show file tree
Hide file tree
Showing 7 changed files with 204 additions and 2 deletions.
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
}

0 comments on commit 3533db1

Please sign in to comment.