1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-11-29 10:12:41 +01:00

Add a linter rule to ensure that namespace is the first statement in a file

Summary: If a `namespace` is used within a PHP script, it must be the first statement.

Test Plan: Added unit tests.

Reviewers: epriestley, #blessed_reviewers

Reviewed By: epriestley, #blessed_reviewers

Subscribers: Korvin

Differential Revision: https://secure.phabricator.com/D14526
This commit is contained in:
Joshua Spence 2015-11-26 07:32:01 +11:00
parent 600dcf83dd
commit eeaa176cfc
7 changed files with 82 additions and 0 deletions

View file

@ -234,6 +234,8 @@ phutil_register_library_map(array(
'ArcanistMissingLinterException' => 'lint/linter/exception/ArcanistMissingLinterException.php', 'ArcanistMissingLinterException' => 'lint/linter/exception/ArcanistMissingLinterException.php',
'ArcanistModifierOrderingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistModifierOrderingXHPASTLinterRule.php', 'ArcanistModifierOrderingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistModifierOrderingXHPASTLinterRule.php',
'ArcanistModifierOrderingXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistModifierOrderingXHPASTLinterRuleTestCase.php', 'ArcanistModifierOrderingXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistModifierOrderingXHPASTLinterRuleTestCase.php',
'ArcanistNamespaceFirstStatementXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistNamespaceFirstStatementXHPASTLinterRule.php',
'ArcanistNamespaceFirstStatementXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistNamespaceFirstStatementXHPASTLinterRuleTestCase.php',
'ArcanistNamingConventionsXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistNamingConventionsXHPASTLinterRule.php', 'ArcanistNamingConventionsXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistNamingConventionsXHPASTLinterRule.php',
'ArcanistNamingConventionsXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistNamingConventionsXHPASTLinterRuleTestCase.php', 'ArcanistNamingConventionsXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistNamingConventionsXHPASTLinterRuleTestCase.php',
'ArcanistNestedNamespacesXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistNestedNamespacesXHPASTLinterRule.php', 'ArcanistNestedNamespacesXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistNestedNamespacesXHPASTLinterRule.php',
@ -626,6 +628,8 @@ phutil_register_library_map(array(
'ArcanistMissingLinterException' => 'Exception', 'ArcanistMissingLinterException' => 'Exception',
'ArcanistModifierOrderingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', 'ArcanistModifierOrderingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistModifierOrderingXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase', 'ArcanistModifierOrderingXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
'ArcanistNamespaceFirstStatementXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistNamespaceFirstStatementXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
'ArcanistNamingConventionsXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', 'ArcanistNamingConventionsXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistNamingConventionsXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase', 'ArcanistNamingConventionsXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
'ArcanistNestedNamespacesXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', 'ArcanistNestedNamespacesXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',

View file

@ -0,0 +1,36 @@
<?php
final class ArcanistNamespaceFirstStatementXHPASTLinterRule
extends ArcanistXHPASTLinterRule {
const ID = 98;
public function getLintName() {
return pht('`%s` Statement Must Be The First Statement', 'namespace');
}
public function process(XHPASTNode $root) {
$namespaces = $root->selectDescendantsOfType('n_NAMESPACE');
if (!count($namespaces)) {
return;
}
$statements = $root->getChildOfType(0, 'n_STATEMENT_LIST');
// Ignore the first statement, which should be `n_OPEN_TAG`.
$second_statement = $statements->getChildByIndex(1)->getChildByIndex(0);
if ($second_statement->getTypeName() != 'n_NAMESPACE') {
$this->raiseLintAtNode(
$second_statement,
pht(
'A script which contains a `%s` statement expects the very first '.
'statement to be a `%s` statement. Otherwise, a PHP fatal error '.
'will occur. %s',
'namespace',
'namespace', $second_statement->getTypeName()));
}
}
}

View file

@ -0,0 +1,11 @@
<?php
final class ArcanistNamespaceFirstStatementXHPASTLinterRuleTestCase
extends ArcanistXHPASTLinterRuleTestCase {
public function testLinter() {
$this->executeTestsInDirectory(
dirname(__FILE__).'/namespace-first-statement/');
}
}

View file

@ -0,0 +1,5 @@
<?php
class X {}
namespace X;
~~~~~~~~~~
error:2:1

View file

@ -0,0 +1,16 @@
<?php
/**
* This is actually perfectly valid, for some unknown reason.
*
* TODO: Maybe fix the handling of this edge case.
*/
1;
namespace X {
class C { }
echo 2;
}
~~~~~~~~~~
error:9:1

View file

@ -0,0 +1,7 @@
<?php
namespace X;
class X {}
namespace Y;
class Y {}
~~~~~~~~~~

View file

@ -0,0 +1,3 @@
<?php
class X {}
~~~~~~~~~~