1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-11-25 16:22:42 +01:00

Add an XHPAST lint rule for empty block statements

Summary: Adds an XHPAST linter rule for empty block statements. Basically, if a block statement is empty then it is much neater if the opening (`{`) and closing (`}`) braces are adjacent. Maybe this is just my own personal preference, in which case we could reduce the default severity to `ArcanistLintSeverity::SEVERITY_DISABLED`.

Test Plan: Wrote unit tests. I had to modify a bunch of existing unit tests accordingly.

Reviewers: #blessed_reviewers, epriestley

Reviewed By: #blessed_reviewers, epriestley

Subscribers: epriestley, Korvin

Differential Revision: https://secure.phabricator.com/D10434
This commit is contained in:
Joshua Spence 2014-09-09 22:48:45 +10:00
parent d4f9526e76
commit 04de931151
13 changed files with 172 additions and 138 deletions

View file

@ -46,6 +46,7 @@ final class ArcanistXHPASTLinter extends ArcanistBaseXHPASTLinter {
const LINT_CONCATENATION_OPERATOR = 44; const LINT_CONCATENATION_OPERATOR = 44;
const LINT_PHP_COMPATIBILITY = 45; const LINT_PHP_COMPATIBILITY = 45;
const LINT_LANGUAGE_CONSTRUCT_PAREN = 46; const LINT_LANGUAGE_CONSTRUCT_PAREN = 46;
const LINT_EMPTY_STATEMENT = 47;
private $naminghook; private $naminghook;
private $switchhook; private $switchhook;
@ -103,6 +104,7 @@ final class ArcanistXHPASTLinter extends ArcanistBaseXHPASTLinter {
self::LINT_CONCATENATION_OPERATOR => 'Concatenation Spacing', self::LINT_CONCATENATION_OPERATOR => 'Concatenation Spacing',
self::LINT_PHP_COMPATIBILITY => 'PHP Compatibility', self::LINT_PHP_COMPATIBILITY => 'PHP Compatibility',
self::LINT_LANGUAGE_CONSTRUCT_PAREN => 'Language Construct Parentheses', self::LINT_LANGUAGE_CONSTRUCT_PAREN => 'Language Construct Parentheses',
self::LINT_EMPTY_STATEMENT => 'Empty Block Statement',
); );
} }
@ -141,6 +143,7 @@ final class ArcanistXHPASTLinter extends ArcanistBaseXHPASTLinter {
self::LINT_SEMICOLON_SPACING => $advice, self::LINT_SEMICOLON_SPACING => $advice,
self::LINT_CONCATENATION_OPERATOR => $warning, self::LINT_CONCATENATION_OPERATOR => $warning,
self::LINT_LANGUAGE_CONSTRUCT_PAREN => $warning, self::LINT_LANGUAGE_CONSTRUCT_PAREN => $warning,
self::LINT_EMPTY_STATEMENT => $advice,
); );
} }
@ -259,6 +262,7 @@ final class ArcanistXHPASTLinter extends ArcanistBaseXHPASTLinter {
self::LINT_CONCATENATION_OPERATOR, self::LINT_CONCATENATION_OPERATOR,
'lintPHPCompatibility' => self::LINT_PHP_COMPATIBILITY, 'lintPHPCompatibility' => self::LINT_PHP_COMPATIBILITY,
'lintLanguageConstructParentheses' => self::LINT_LANGUAGE_CONSTRUCT_PAREN, 'lintLanguageConstructParentheses' => self::LINT_LANGUAGE_CONSTRUCT_PAREN,
'lintEmptyBlockStatements' => self::LINT_EMPTY_STATEMENT,
); );
foreach ($method_codes as $method => $codes) { foreach ($method_codes as $method => $codes) {
@ -2555,6 +2559,42 @@ final class ArcanistXHPASTLinter extends ArcanistBaseXHPASTLinter {
} }
} }
protected function lintEmptyBlockStatements(XHPASTNode $root) {
$nodes = $root->selectDescendantsOfType('n_STATEMENT_LIST');
foreach ($nodes as $node) {
$tokens = $node->getTokens();
$token = head($tokens);
if (count($tokens) <= 2) {
continue;
}
// Safety check... if the first token isn't an opening brace then
// there's nothing to do here.
if ($token->getTypeName() != '{') {
continue;
}
$only_whitespace = true;
for ($token = $token->getNextToken();
$token && $token->getTypeName() != '}';
$token = $token->getNextToken()) {
$only_whitespace = $only_whitespace && $token->isAnyWhitespace();
}
if (count($tokens) > 2 && $only_whitespace) {
$this->raiseLintAtNode(
$node,
self::LINT_EMPTY_STATEMENT,
pht(
"Braces for an empty block statement shouldn't ".
"contain only whitespace."),
'{}');
}
}
}
public function getSuperGlobalNames() { public function getSuperGlobalNames() {
return array( return array(
'$GLOBALS', '$GLOBALS',

View file

@ -28,7 +28,9 @@ catch (Exception $x)
function h(){} function h(){}
~~~~~~~~~~ ~~~~~~~~~~
advice:3:14
warning:7:13 warning:7:13
advice:8:1
warning:12:7 warning:12:7
warning:15:20 warning:15:20
warning:18:10 warning:18:10
@ -39,13 +41,9 @@ warning:29:13
~~~~~~~~~~ ~~~~~~~~~~
<?php <?php
function f() { function f() {}
} function g() {}
function g() {
}
if (1) {} if (1) {}

View file

@ -8,20 +8,20 @@ function &i($x ) {}
final class X { final class X {
function a($x) { } function a($x) {}
function b($x ) { } function b($x ) {}
final public static function &c($x) { } final public static function &c($x) {}
final public static function &d($x ) { } final public static function &d($x ) {}
abstract private function e($x); abstract private function e($x);
abstract private function f($x ); abstract private function f($x );
} }
f(function($x) { }); f(function($x) {});
f(function($x ) { }); f(function($x ) {});
f(function($x ) use ($z) { }); f(function($x ) use ($z) {});
~~~~~~~~~~ ~~~~~~~~~~
warning:4:14 warning:4:14
warning:7:15 warning:7:15
@ -42,17 +42,17 @@ function &i($x) {}
final class X { final class X {
function a($x) { } function a($x) {}
function b($x) { } function b($x) {}
final public static function &c($x) { } final public static function &c($x) {}
final public static function &d($x) { } final public static function &d($x) {}
abstract private function e($x); abstract private function e($x);
abstract private function f($x); abstract private function f($x);
} }
f(function($x) { }); f(function($x) {});
f(function($x) { }); f(function($x) {});
f(function($x) use ($z) { }); f(function($x) use ($z) {});

View file

@ -0,0 +1,21 @@
<?php
function w() {}
function x() {
// This is deliberately empty.
}
function y() { }
function z() {
}
~~~~~~~~~~
advice:6:14
advice:7:14
~~~~~~~~~~
<?php
function w() {}
function x() {
// This is deliberately empty.
}
function y() {}
function z() {}

View file

@ -10,9 +10,7 @@ nUlL;
array(); array();
Array(); Array();
function f(array $x, ArRaY $y) { function f(array $x, ArRaY $y) {}
}
new C(); new C();
NeW C(); NeW C();
@ -23,7 +21,7 @@ warning:8:1
warning:9:1 warning:9:1
warning:11:1 warning:11:1
warning:13:22 warning:13:22
warning:18:1 warning:16:1
~~~~~~~~~~ ~~~~~~~~~~
<?php <?php
@ -37,9 +35,7 @@ null;
array(); array();
array(); array();
function f(array $x, array $y) { function f(array $x, array $y) {}
}
new C(); new C();
new C(); new C();

View file

@ -2,29 +2,28 @@
final class a { final class a {
const b = 1, c = d; const b = 1, c = d;
protected $E, $H; protected $E, $H;
public function F($G, $GG) { } public function F($G, $GG) {}
} }
interface i { } interface i {}
function YY($ZZ) { } function YY($ZZ) {}
final class Quack { final class Quack {
const R = 1, S = 2; const R = 1, S = 2;
protected $tX, $uY; protected $tX, $uY;
public function vV($w_w) { } public function vV($w_w) {}
} }
function () use ($this_is_a_closure) { }; function () use ($this_is_a_closure) {};
function f(&$YY) { function f(&$YY) {}
}
function g() { function g() {
$lowerCamelCase = 0; $lowerCamelCase = 0;
@ -62,7 +61,7 @@ warning:8:11
warning:12:10 warning:12:10
warning:12:13 warning:12:13
warning:26:13 warning:26:13
warning:29:3
warning:30:3 warning:30:3
warning:31:3 warning:31:3
warning:32:3 warning:33:3
warning:34:3

View file

@ -1,24 +1,22 @@
<?php <?php
if ( $x ) { } if ( $x ) {}
f( ); f( );
q( ); q( );
g(); g();
if ($x) { } if ($x) {}
else if ( $y ) { } else if ( $y ) {}
$obj->m( $obj->m(
$x, $x,
$y, $y,
$z); $z);
for ( $ii = 0; $ii < 1; $ii++ ) { } for ( $ii = 0; $ii < 1; $ii++ ) {}
foreach ( $x as $y ) { } foreach ( $x as $y ) {}
function q( $x ) { } function q( $x ) {}
final class X { final class X {
public function f( $y ) { public function f( $y ) {}
}
}
foreach ( $z as $k => $v ) {
} }
foreach ( $z as $k => $v ) {}
some_call( /* respect authorial intent */ $b, some_call( /* respect authorial intent */ $b,
$a // if comments are present $a // if comments are present
); );
@ -38,30 +36,28 @@ warning:15:15
error:16:13 XHP19 Class-Filename Mismatch error:16:13 XHP19 Class-Filename Mismatch
warning:17:21 warning:17:21
warning:17:24 warning:17:24
warning:20:10 warning:19:10
warning:20:25 warning:19:25
~~~~~~~~~~ ~~~~~~~~~~
<?php <?php
if ($x) { } if ($x) {}
f(); f();
q(); q();
g(); g();
if ($x) { } if ($x) {}
else if ($y) { } else if ($y) {}
$obj->m( $obj->m(
$x, $x,
$y, $y,
$z); $z);
for ($ii = 0; $ii < 1; $ii++) { } for ($ii = 0; $ii < 1; $ii++) {}
foreach ($x as $y) { } foreach ($x as $y) {}
function q($x) { } function q($x) {}
final class X { final class X {
public function f($y) { public function f($y) {}
}
}
foreach ($z as $k => $v) {
} }
foreach ($z as $k => $v) {}
some_call( /* respect authorial intent */ $b, some_call( /* respect authorial intent */ $b,
$a // if comments are present $a // if comments are present
); );

View file

@ -2,53 +2,53 @@
function assign() { function assign() {
$ar = array(); $ar = array();
foreach ($ar as &$a) { } foreach ($ar as &$a) {}
$a = 1; // Reuse of $a. $a = 1; // Reuse of $a.
} }
function expr() { function expr() {
$ar = array(); $ar = array();
foreach ($ar as &$a) { } foreach ($ar as &$a) {}
$b = $a; // Reuse of $a. $b = $a; // Reuse of $a.
} }
function func_call() { function func_call() {
$ar = array(); $ar = array();
foreach ($ar as &$a) { } foreach ($ar as &$a) {}
$b = x($a); // Reuse of $a. $b = x($a); // Reuse of $a.
} }
function x($b) { } function x($b) {}
function iterator_reuse() { function iterator_reuse() {
$ar1 = array(); $ar1 = array();
$ar2 = array(); $ar2 = array();
foreach ($ar1 as &$a) { } foreach ($ar1 as &$a) {}
foreach ($ar2 as $a) { } // Reuse of $a foreach ($ar2 as $a) {} // Reuse of $a
} }
function key_value() { function key_value() {
$ar = array(); $ar = array();
foreach ($ar as $k => &$v) { } foreach ($ar as $k => &$v) {}
$v++; // Reuse of $v $v++; // Reuse of $v
} }
function key_value2() { function key_value2() {
$ar = array(); $ar = array();
foreach ($ar as $k => $v) { } foreach ($ar as $k => $v) {}
$v++; $v++;
} }
function unset() { function unset() {
$ar = array(); $ar = array();
foreach ($ar as &$a) { } foreach ($ar as &$a) {}
unset($a); unset($a);
$a++; $a++;
} }
function unset2() { function unset2() {
$ar = array(); $ar = array();
foreach ($ar as &$a) { } foreach ($ar as &$a) {}
$a++; // Reuse of $a $a++; // Reuse of $a
unset($a); unset($a);
} }
@ -56,19 +56,19 @@ function unset2() {
function twice_ref() { function twice_ref() {
$ar1 = array(); $ar1 = array();
$ar2 = array(); $ar2 = array();
foreach ($ar1 as &$b) { } foreach ($ar1 as &$b) {}
foreach ($ar2 as &$b) { } foreach ($ar2 as &$b) {}
} }
function assign_ref(&$a) { function assign_ref(&$a) {
$ar = array(); $ar = array();
foreach ($ar as &$b) { } foreach ($ar as &$b) {}
$b = &$a; $b = &$a;
} }
function assign_ref2(&$a) { function assign_ref2(&$a) {
$ar = array(); $ar = array();
foreach ($ar as &$b) { } foreach ($ar as &$b) {}
$b = &$a; $b = &$a;
$c = $b; $c = $b;
} }
@ -82,14 +82,14 @@ function use_inside() {
function variable_variable() { function variable_variable() {
$ar = array(); $ar = array();
foreach ($ar as &$$a) { } foreach ($ar as &$$a) {}
$a++; $a++;
$$a++; $$a++;
} }
function closure1() { function closure1() {
$ar = array(); $ar = array();
foreach ($ar as &$a) { } foreach ($ar as &$a) {}
function($a) { function($a) {
$a++; $a++;
}; };
@ -98,7 +98,7 @@ function closure1() {
function closure2() { function closure2() {
function() { function() {
$ar = array(); $ar = array();
foreach ($ar as &$a) { } foreach ($ar as &$a) {}
}; };
$a++; $a++;
} }
@ -106,14 +106,14 @@ function closure2() {
function closure3() { function closure3() {
function() { function() {
$ar = array(); $ar = array();
foreach ($ar as &$a) { } foreach ($ar as &$a) {}
$a++; // Reuse of $a $a++; // Reuse of $a
}; };
} }
function closure4() { function closure4() {
$ar = array(); $ar = array();
foreach ($ar as &$a) { } foreach ($ar as &$a) {}
function($a) { function($a) {
unset($a); unset($a);
}; };

View file

@ -1,21 +1,15 @@
<?php <?php
function f($stuff, $thing) { function f($stuff, $thing) {
foreach ($stuff as $ii) { foreach ($stuff as $ii) {}
}
// OK: Only reused for iteration. // OK: Only reused for iteration.
foreach ($stuff as $ii) { foreach ($stuff as $ii) {}
}
} }
function g($stuff, $thing) { function g($stuff, $thing) {
foreach ($stuff as $thing) { foreach ($stuff as $thing) {}
}
// OK: Not reused later. // OK: Not reused later.
} }
@ -24,9 +18,7 @@ function h($stuff, $thing) {
// OK: Used afterwards but not before. // OK: Used afterwards but not before.
foreach ($stuff as $key => $val) { foreach ($stuff as $key => $val) {}
}
$key = 1; $key = 1;
$thing = 1; $thing = 1;
@ -68,11 +60,9 @@ function k($stuff, $thing) {
f($thing); f($thing);
$other = array(); $other = array();
foreach ($other as $item) { foreach ($other as $item) {}
}
} }
~~~~~~~~~~ ~~~~~~~~~~
error:51:22 error:43:22
error:61:22 error:53:22

View file

@ -10,8 +10,8 @@ $a -=$b;
$a-= $b; $a-= $b;
$a===$b; $a===$b;
$a.$b; $a.$b;
function x($n=null) { } function x($n=null) {}
function x($n = null) { } function x($n = null) {}
$y /* ! */ += // ? $y /* ! */ += // ?
$z; $z;
@ -19,8 +19,8 @@ $obj->method();
Dog::bark(); Dog::bark();
$prev = ($total == 1) ? $navids[0] : $navids[$total-1]; $prev = ($total == 1) ? $navids[0] : $navids[$total-1];
$next = ($total == 1) ? $navids[0] : $navids[$current+1]; $next = ($total == 1) ? $navids[0] : $navids[$current+1];
if ($x instanceof z &&$w) { } if ($x instanceof z &&$w) {}
if ($x instanceof z && $w) { } if ($x instanceof z && $w) {}
f(1,2); f(1,2);
~~~~~~~~~~ ~~~~~~~~~~
warning:3:3 warning:3:3
@ -48,8 +48,8 @@ $a -= $b;
$a -= $b; $a -= $b;
$a === $b; $a === $b;
$a.$b; $a.$b;
function x($n=null) { } function x($n=null) {}
function x($n = null) { } function x($n = null) {}
$y /* ! */ += // ? $y /* ! */ += // ?
$z; $z;
@ -57,6 +57,6 @@ $obj->method();
Dog::bark(); Dog::bark();
$prev = ($total == 1) ? $navids[0] : $navids[$total - 1]; $prev = ($total == 1) ? $navids[0] : $navids[$total - 1];
$next = ($total == 1) ? $navids[0] : $navids[$current + 1]; $next = ($total == 1) ? $navids[0] : $navids[$current + 1];
if ($x instanceof z && $w) { } if ($x instanceof z && $w) {}
if ($x instanceof z && $w) { } if ($x instanceof z && $w) {}
f(1, 2); f(1, 2);

View file

@ -67,7 +67,7 @@ switch ($x) {
function f() { throw new Exception(); } function f() { throw new Exception(); }
g(function() { return; }); g(function() { return; });
final class C { public function m() { return; } } final class C { public function m() { return; } }
interface I { } interface I {}
case 5: case 5:
do { do {
break; break;

View file

@ -1,16 +1,12 @@
<?php <?php
if ($x == $x) { if ($x == $x) {}
}
if ($x->m(3) < $x->m(3)) { if ($x->m(3) < $x->m(3)) {}
}
if ($y[2] - $y[2]) { if ($y[2] - $y[2]) {}
}
if ($x == $y) { if ($x == $y) {}
}
// See xhpast 0.54 -> 0.55. // See xhpast 0.54 -> 0.55.
return $a->sub->value < $b->sub->value; return $a->sub->value < $b->sub->value;
@ -22,7 +18,7 @@ $skip_cache = f();
~~~~~~~~~~ ~~~~~~~~~~
error:3:5 error:3:5
error:6:5 error:5:5
error:9:5 error:7:5
error:18:15 error:14:15
error:20:15 error:16:15

View file

@ -9,9 +9,7 @@ function f($a, $b) {
global $e, $f; global $e, $f;
$g = $h = x(); $g = $h = x();
list($i, list($j, $k)) = y(); list($i, list($j, $k)) = y();
foreach (q() as $l => $m) { foreach (q() as $l => $m) {}
}
$a++; $a++;
$b++; $b++;
@ -59,9 +57,7 @@ function superglobals() {
} }
function ref_foreach($x) { function ref_foreach($x) {
foreach ($x as &$z) { foreach ($x as &$z) {}
}
$z++; $z++;
} }
@ -110,6 +106,7 @@ function instance_class() {
function exception_vars() { function exception_vars() {
try { try {
// This is intentionally left blank.
} catch (Exception $y) { } catch (Exception $y) {
$y++; $y++;
} }
@ -128,7 +125,8 @@ function twice() {
function more_exceptions() { function more_exceptions() {
try { try {
} catch (Exception $a) { // This is intentionally left blank.
} catch (Exception $a) {
$a++; $a++;
} catch (Exception $b) { } catch (Exception $b) {
$b++; $b++;
@ -175,21 +173,21 @@ function catchy() {
} }
~~~~~~~~~~ ~~~~~~~~~~
error:28:3
error:30:3 error:30:3
error:32:3 error:36:3
error:38:3 error:42:5
error:43:7
error:44:5 error:44:5
error:45:7 error:45:5
error:46:5 error:46:10
error:47:5 error:51:10 worst ever
error:48:10 warning:61:3
error:53:10 worst ever error:87:3 This stuff is basically testing the lexer/parser for function decls.
warning:65:3 error:104:15 Variables in instance derefs should be checked, static should not.
error:91:3 This stuff is basically testing the lexer/parser for function decls. error:118:3 isset() and empty() should not trigger errors.
error:108:15 Variables in instance derefs should be checked, static should not. error:122:3 Should only warn once in this function.
error:121:3 isset() and empty() should not trigger errors. error:144:8
error:125:3 Should only warn once in this function. error:150:9
error:146:8 error:164:9
error:152:9 error:171:5
error:166:9
error:173:5