From 8da371ae70e8d6ed00b195ea47e9e1d9eaaa08ab Mon Sep 17 00:00:00 2001 From: pq Date: Thu, 2 Sep 2021 10:26:46 -0700 Subject: [PATCH] cons tearoff support for `unnecessary_parenthesis` --- lib/src/rules/unnecessary_parenthesis.dart | 15 ++- .../rules/unnecessary_parenthesis.dart | 108 ++++++++++++++++++ 2 files changed, 119 insertions(+), 4 deletions(-) create mode 100644 test_data/rules/experiments/constructor_tearoffs/rules/unnecessary_parenthesis.dart diff --git a/lib/src/rules/unnecessary_parenthesis.dart b/lib/src/rules/unnecessary_parenthesis.dart index e9bd1eca5..d982c2193 100644 --- a/lib/src/rules/unnecessary_parenthesis.dart +++ b/lib/src/rules/unnecessary_parenthesis.dart @@ -48,8 +48,9 @@ class _Visitor extends SimpleAstVisitor { @override void visitParenthesizedExpression(ParenthesizedExpression node) { - if (node.expression is SimpleIdentifier) { - var parent = node.parent; + var parent = node.parent; + var expression = node.expression; + if (expression is SimpleIdentifier) { if (parent is PropertyAccess) { if (parent.propertyName.name == 'hashCode' || parent.propertyName.name == 'runtimeType') { @@ -67,7 +68,13 @@ class _Visitor extends SimpleAstVisitor { return; } - var parent = node.parent; + if (expression is ConstructorReference) { + if (parent is! FunctionExpressionInvocation || + parent.typeArguments == null) { + rule.reportLint(node); + return; + } + } if (parent is ParenthesizedExpression) { rule.reportLint(node); @@ -75,7 +82,7 @@ class _Visitor extends SimpleAstVisitor { } // `a..b=(c..d)` is OK. - if (node.expression is CascadeExpression || + if (expression is CascadeExpression || node.thisOrAncestorMatching( (n) => n is Statement || n is CascadeExpression) is CascadeExpression) { diff --git a/test_data/rules/experiments/constructor_tearoffs/rules/unnecessary_parenthesis.dart b/test_data/rules/experiments/constructor_tearoffs/rules/unnecessary_parenthesis.dart new file mode 100644 index 000000000..4e47536d4 --- /dev/null +++ b/test_data/rules/experiments/constructor_tearoffs/rules/unnecessary_parenthesis.dart @@ -0,0 +1,108 @@ +// Copyright (c) 2021, 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_parenthesis.dart` + +import 'dart:async'; + +class D { + D.d([int? x, int? y]); +} + +/// https://github.com/dart-lang/linter/issues/2907 +void constructorTearOffs() { + var makeD = D.d; + (makeD)(1); // LINT + (D.d)(1); // LINT + (List.filled)(3, 0); // LINT + (List.filled)(3, 0); // OK + var tearoff = (List.filled); // LINT + (List).toString(); //OK +} + +var a, b, c, d; + +main() async { + 1; // OK + (1); // LINT + print(1); // OK + print((1)); // LINT + if (a && b || c && d) true; // OK + // OK because it may be hard to know all of the precedence rules. + if ((a && b) || c && d) true; // OK + (await new Future.value(1)).toString(); // OK + ('' as String).toString(); // OK + !(true as bool); // OK + a = (a); // LINT + (a) ? true : false; // LINT + true ? (a) : false; // LINT + true ? true : (a); // LINT + // OK because it is unobvious that the space-involving ternary binds tighter + // than the cascade. + (true ? [] : [])..add(''); // OK + (a ?? true) ? true : true; // OK + true ? [] : [] + ..add(''); // OK + m(p: (1 + 3)); // LINT + (a++).toString(); // OK + + // OK because it is unobvious where cascades fall in precedence. + a..b = (c..d); // OK + a.b = (c..d); // OK + a..b = (c.d); // OK + ((x) => x is bool ? x : false)(a); // OK + (fn)(a); // LINT + + // OK because unary operators mixed with space-separated tokens may have + // unexpected ordering. + !(const [7].contains(42)); // OK + !(new List(3).contains(42)); // OK + !(await Future.value(false)); // OK + -(new List(3).length); // OK + !(new List(3).length.isEven); // OK + -(new List(3).length.abs().abs().abs()); // OK + -(new List(3).length.sign.sign.sign); // OK + !(const [7]).contains(42); // OK + + // OK because some methods are defined on Type, but removing the parentheses + // would attempt to call a _static_ method on the target. + (String).hashCode; + (int).runtimeType; + (bool).noSuchMethod(); + (double).toString(); + + ({false: 'false', true: 'true'}).forEach((k, v) => print('$k: $v')); + ({false, true}).forEach(print); + ({false, true}).length; + print(({1, 2, 3}).length); // LINT + ([false, true]).forEach(print); // LINT +} + +m({p}) => null; + +bool Function(dynamic) get fn => (x) => x is bool ? x : false; + +class ClassWithFunction { + Function f; + int number; + + ClassWithFunction.named(int a) : this.number = (a + 2); // LINT + // https://github.com/dart-lang/linter/issues/1473 + ClassWithFunction.named2(Function value) : this.f = (value ?? (_) => 42); // OK +} + +class ClassWithClassWithFunction { + ClassWithFunction c; + + // https://github.com/dart-lang/linter/issues/1395 + ClassWithClassWithFunction() : c = (ClassWithFunction()..f = () => 42); // OK +} + +class UnnecessaryParenthesis { + ClassWithClassWithFunction c; + + UnnecessaryParenthesis() + : c = (ClassWithClassWithFunction() + ..c = ClassWithFunction().f = () => 42); // OK +}