Page MenuHomePhabricator

D14526.diff
No OneTemporary

D14526.diff

diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -234,6 +234,8 @@
'ArcanistMissingLinterException' => 'lint/linter/exception/ArcanistMissingLinterException.php',
'ArcanistModifierOrderingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistModifierOrderingXHPASTLinterRule.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',
'ArcanistNamingConventionsXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistNamingConventionsXHPASTLinterRuleTestCase.php',
'ArcanistNestedNamespacesXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistNestedNamespacesXHPASTLinterRule.php',
@@ -626,6 +628,8 @@
'ArcanistMissingLinterException' => 'Exception',
'ArcanistModifierOrderingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistModifierOrderingXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
+ 'ArcanistNamespaceFirstStatementXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
+ 'ArcanistNamespaceFirstStatementXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
'ArcanistNamingConventionsXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistNamingConventionsXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
'ArcanistNestedNamespacesXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
diff --git a/src/lint/linter/xhpast/rules/ArcanistNamespaceFirstStatementXHPASTLinterRule.php b/src/lint/linter/xhpast/rules/ArcanistNamespaceFirstStatementXHPASTLinterRule.php
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/xhpast/rules/ArcanistNamespaceFirstStatementXHPASTLinterRule.php
@@ -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()));
+ }
+ }
+
+}
diff --git a/src/lint/linter/xhpast/rules/__tests__/ArcanistNamespaceFirstStatementXHPASTLinterRuleTestCase.php b/src/lint/linter/xhpast/rules/__tests__/ArcanistNamespaceFirstStatementXHPASTLinterRuleTestCase.php
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/xhpast/rules/__tests__/ArcanistNamespaceFirstStatementXHPASTLinterRuleTestCase.php
@@ -0,0 +1,11 @@
+<?php
+
+final class ArcanistNamespaceFirstStatementXHPASTLinterRuleTestCase
+ extends ArcanistXHPASTLinterRuleTestCase {
+
+ public function testLinter() {
+ $this->executeTestsInDirectory(
+ dirname(__FILE__).'/namespace-first-statement/');
+ }
+
+}
diff --git a/src/lint/linter/xhpast/rules/__tests__/namespace-first-statement/class-before-namespace.lint-test b/src/lint/linter/xhpast/rules/__tests__/namespace-first-statement/class-before-namespace.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/xhpast/rules/__tests__/namespace-first-statement/class-before-namespace.lint-test
@@ -0,0 +1,5 @@
+<?php
+class X {}
+namespace X;
+~~~~~~~~~~
+error:2:1
diff --git a/src/lint/linter/xhpast/rules/__tests__/namespace-first-statement/incorrect.lint-test b/src/lint/linter/xhpast/rules/__tests__/namespace-first-statement/incorrect.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/xhpast/rules/__tests__/namespace-first-statement/incorrect.lint-test
@@ -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
diff --git a/src/lint/linter/xhpast/rules/__tests__/namespace-first-statement/multiple-namespaces.lint-test b/src/lint/linter/xhpast/rules/__tests__/namespace-first-statement/multiple-namespaces.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/xhpast/rules/__tests__/namespace-first-statement/multiple-namespaces.lint-test
@@ -0,0 +1,7 @@
+<?php
+namespace X;
+class X {}
+
+namespace Y;
+class Y {}
+~~~~~~~~~~
diff --git a/src/lint/linter/xhpast/rules/__tests__/namespace-first-statement/no-namespace.lint-test b/src/lint/linter/xhpast/rules/__tests__/namespace-first-statement/no-namespace.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/xhpast/rules/__tests__/namespace-first-statement/no-namespace.lint-test
@@ -0,0 +1,3 @@
+<?php
+class X {}
+~~~~~~~~~~

File Metadata

Mime Type
text/plain
Expires
Sun, May 12, 4:18 AM (2 w, 6 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6288740
Default Alt Text
D14526.diff (5 KB)

Event Timeline