-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add unnecessary_null_aware_operator_on_extension_on_nullable (dart-la…
…ng/linter#3392) * add unnecessary_null_aware_operator_on_extension_on_nullable * Address review comment
- Loading branch information
Showing
4 changed files
with
143 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
100 changes: 100 additions & 0 deletions
100
lib/src/rules/unnecessary_null_aware_operator_on_extension_on_nullable.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
// 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 'package:analyzer/dart/element/element.dart'; | ||
|
||
import '../analyzer.dart'; | ||
|
||
const _desc = | ||
r'Unnecessary null aware operator on extension on a nullable type'; | ||
|
||
const _details = r''' | ||
Avoid null aware operators for members defined in an extension on a nullable type. | ||
**BAD:** | ||
```dart | ||
extension E on int? { | ||
int m() => 1; | ||
} | ||
f(int? i) => i?.m(); | ||
``` | ||
**GOOD:** | ||
```dart | ||
extension E on int? { | ||
int m() => 1; | ||
} | ||
f(int? i) => i.m(); | ||
``` | ||
'''; | ||
|
||
class UnnecessaryNullAwareOperatorOnExtensionOnNullable extends LintRule { | ||
UnnecessaryNullAwareOperatorOnExtensionOnNullable() | ||
: super( | ||
name: 'unnecessary_null_aware_operator_on_extension_on_nullable', | ||
description: _desc, | ||
details: _details, | ||
group: Group.style, | ||
); | ||
|
||
@override | ||
void registerNodeProcessors( | ||
NodeLintRegistry registry, LinterContext context) { | ||
var visitor = _Visitor(this, context); | ||
registry.addIndexExpression(this, visitor); | ||
registry.addMethodInvocation(this, visitor); | ||
registry.addPropertyAccess(this, visitor); | ||
} | ||
} | ||
|
||
class _Visitor extends SimpleAstVisitor<void> { | ||
_Visitor(this.rule, this.context); | ||
|
||
final LintRule rule; | ||
final LinterContext context; | ||
|
||
@override | ||
void visitIndexExpression(IndexExpression node) { | ||
if (node.isNullAware && | ||
_isExtensionOnNullableType(node.inSetterContext() | ||
? (node.thisOrAncestorOfType<AssignmentExpression>()) | ||
?.writeElement | ||
?.enclosingElement | ||
: node.staticElement?.enclosingElement)) { | ||
rule.reportLintForToken(node.question); | ||
} | ||
} | ||
|
||
@override | ||
void visitMethodInvocation(MethodInvocation node) { | ||
if (node.isNullAware && | ||
_isExtensionOnNullableType( | ||
node.methodName.staticElement?.enclosingElement)) { | ||
rule.reportLintForToken(node.operator); | ||
} | ||
} | ||
|
||
@override | ||
void visitPropertyAccess(PropertyAccess node) { | ||
if (node.isNullAware) { | ||
var realParent = node.thisOrAncestorMatching( | ||
(p) => p != node && p is! ParenthesizedExpression); | ||
if (_isExtensionOnNullableType(realParent is AssignmentExpression | ||
? realParent.writeElement?.enclosingElement | ||
: node.propertyName.staticElement?.enclosingElement)) { | ||
rule.reportLintForToken(node.operator); | ||
} | ||
} | ||
} | ||
|
||
bool _isExtensionOnNullableType(Element? enclosingElement) => | ||
enclosingElement is ExtensionElement && | ||
context.typeSystem.isNullable(enclosingElement.extendedType); | ||
} |
40 changes: 40 additions & 0 deletions
40
test_data/rules/unnecessary_null_aware_operator_on_extension_on_nullable.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
// 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 unnecessary_null_aware_operator_on_extension_on_nullable` | ||
|
||
extension E on int? { | ||
int get foo => 1; | ||
void set foo(int v) {} | ||
String operator [](int i) => ''; | ||
void operator []=(int i, String v) {} | ||
int m() => 1; | ||
} | ||
|
||
f(int? i) { | ||
i?.foo; // LINT | ||
i.foo; // OK | ||
E(i)?.foo; // LINT | ||
E(i).foo; // OK | ||
|
||
i?.foo = 1; // LINT | ||
i.foo = 1; // OK | ||
E(i)?.foo = 1; // LINT | ||
E(i).foo = 1; // OK | ||
|
||
i?[0]; // LINT | ||
i[0]; // OK | ||
E(i)?[0]; // LINT | ||
E(i)[0]; // OK | ||
|
||
i?[0] = ''; // LINT | ||
i[0] = ''; // OK | ||
E(i)?[0] = ''; // LINT | ||
E(i)[0] = ''; // OK | ||
|
||
i?.m(); // LINT | ||
i.m(); // OK | ||
E(i)?.m(); // LINT | ||
E(i).m(); // OK | ||
} |