Page Menu
Home
Sealhub
Search
Configure Global Search
Log In
Files
F969570
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
9 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/lint/linter/xhpast/rules/ArcanistInvalidModifiersXHPASTLinterRule.php b/src/lint/linter/xhpast/rules/ArcanistInvalidModifiersXHPASTLinterRule.php
index c1e6cc6f..fce47012 100644
--- a/src/lint/linter/xhpast/rules/ArcanistInvalidModifiersXHPASTLinterRule.php
+++ b/src/lint/linter/xhpast/rules/ArcanistInvalidModifiersXHPASTLinterRule.php
@@ -1,103 +1,135 @@
<?php
final class ArcanistInvalidModifiersXHPASTLinterRule
extends ArcanistXHPASTLinterRule {
const ID = 72;
public function getLintName() {
return pht('Invalid Modifiers');
}
public function process(XHPASTNode $root) {
$methods = $root->selectDescendantsOfTypes(array(
'n_CLASS_MEMBER_MODIFIER_LIST',
'n_METHOD_MODIFIER_LIST',
));
foreach ($methods as $method) {
$modifiers = $method->getChildren();
$is_abstract = false;
$is_final = false;
$is_static = false;
$visibility = null;
+ $is_property = ($method->getTypeName() == 'n_CLASS_MEMBER_MODIFIER_LIST');
+ $is_method = !$is_property;
+
+ $final_modifier = null;
+ $visibility_modifier = null;
+ $abstract_modifier = null;
foreach ($modifiers as $modifier) {
switch ($modifier->getConcreteString()) {
case 'abstract':
- if ($method->getTypeName() == 'n_CLASS_MEMBER_MODIFIER_LIST') {
+ if ($is_property) {
$this->raiseLintAtNode(
$modifier,
pht(
- 'Properties cannot be declared `%s`.',
- 'abstract'));
+ 'Properties cannot be declared "abstract".'));
}
if ($is_abstract) {
$this->raiseLintAtNode(
$modifier,
pht(
- 'Multiple `%s` modifiers are not allowed.',
- 'abstract'));
- }
-
- if ($is_final) {
- $this->raiseLintAtNode(
- $modifier,
- pht(
- 'Cannot use the `%s` modifier on an `%s` class member',
- 'final',
- 'abstract'));
+ 'Multiple "abstract" modifiers are not allowed.'));
}
+ $abstract_modifier = $modifier;
$is_abstract = true;
break;
case 'final':
- if ($is_abstract) {
+ if ($is_property) {
$this->raiseLintAtNode(
$modifier,
pht(
- 'Cannot use the `%s` modifier on an `%s` class member',
- 'final',
- 'abstract'));
+ 'Properties can not be declared "final".'));
}
if ($is_final) {
$this->raiseLintAtNode(
$modifier,
pht(
- 'Multiple `%s` modifiers are not allowed.',
- 'final'));
+ 'Multiple "final" modifiers are not allowed.'));
}
+ $final_modifier = $modifier;
$is_final = true;
break;
case 'public':
case 'protected':
case 'private':
- if ($visibility) {
+ if ($visibility !== null) {
$this->raiseLintAtNode(
$modifier,
pht('Multiple access type modifiers are not allowed.'));
}
+ $visibility_modifier = $modifier;
+
$visibility = $modifier->getConcreteString();
+ $visibility = phutil_utf8_strtolower($visibility);
break;
case 'static':
if ($is_static) {
$this->raiseLintAtNode(
$modifier,
pht(
- 'Multiple `%s` modifiers are not allowed.',
- 'static'));
+ 'Multiple "static" modifiers are not allowed.'));
}
break;
}
}
+
+ $is_private = ($visibility === 'private');
+
+ if ($is_final && $is_abstract) {
+ if ($is_method) {
+ $this->raiseLintAtNode(
+ $final_modifier,
+ pht('Methods may not be both "abstract" and "final".'));
+ } else {
+ // Properties can't be "abstract" and "final" either, but they can't
+ // ever be "abstract" at all, and we've already raise a message about
+ // that earlier.
+ }
+ }
+
+ if ($is_private && $is_final) {
+ if ($is_method) {
+ $final_tokens = $final_modifier->getTokens();
+ $space_tokens = last($final_tokens)->getWhitespaceTokensAfter();
+
+ $final_offset = head($final_tokens)->getOffset();
+
+ $final_string = array_merge($final_tokens, $space_tokens);
+ $final_string = mpull($final_string, 'getValue');
+ $final_string = implode('', $final_string);
+
+ $this->raiseLintAtOffset(
+ $final_offset,
+ pht('Methods may not be both "private" and "final".'),
+ $final_string,
+ '');
+ } else {
+ // Properties can't be "final" at all, and we already raised a
+ // message about this.
+ }
+ }
}
}
}
diff --git a/src/lint/linter/xhpast/rules/__tests__/invalid-modifiers/invalid-modifiers.lint-test b/src/lint/linter/xhpast/rules/__tests__/invalid-modifiers/invalid-modifiers.lint-test
index 1490d748..ae74d7fd 100644
--- a/src/lint/linter/xhpast/rules/__tests__/invalid-modifiers/invalid-modifiers.lint-test
+++ b/src/lint/linter/xhpast/rules/__tests__/invalid-modifiers/invalid-modifiers.lint-test
@@ -1,18 +1,40 @@
<?php
class SomeClass {
public $a;
public public $b;
public protected $c;
private abstract $d;
+ private final $e;
public function foo() {}
public protected function bar() {}
abstract final public function baz() {}
+ final private function wuff() {}
+ private final function fizz() {}
}
~~~~~~~~~~
error:5:10:XHP72:Invalid Modifiers
error:6:10:XHP72:Invalid Modifiers
error:7:11:XHP72:Invalid Modifiers
-error:10:10:XHP72:Invalid Modifiers
-error:11:12:XHP72:Invalid Modifiers
+error:8:11:XHP72:Invalid Modifiers
+error:11:10:XHP72:Invalid Modifiers
+error:12:12:XHP72:Invalid Modifiers
+error:13:3:XHP72:Invalid Modifiers
+error:14:11:XHP72:Invalid Modifiers
+~~~~~~~~~~
+<?php
+
+class SomeClass {
+ public $a;
+ public public $b;
+ public protected $c;
+ private abstract $d;
+ private final $e;
+
+ public function foo() {}
+ public protected function bar() {}
+ abstract final public function baz() {}
+ private function wuff() {}
+ private function fizz() {}
+}
diff --git a/src/parser/aast/api/AASTToken.php b/src/parser/aast/api/AASTToken.php
index aebe9ced..c15ae208 100644
--- a/src/parser/aast/api/AASTToken.php
+++ b/src/parser/aast/api/AASTToken.php
@@ -1,91 +1,102 @@
<?php
abstract class AASTToken extends Phobject {
protected $id;
protected $typeID;
protected $typeName;
protected $value;
protected $offset;
protected $tree;
public function __construct($id, $type, $value, $offset, AASTTree $tree) {
$this->id = $id;
$this->typeID = $type;
$this->value = $value;
$this->offset = $offset;
$this->tree = $tree;
}
final public function getTokenID() {
return $this->id;
}
final public function getTypeID() {
return $this->typeID;
}
public function getTypeName() {
if (empty($this->typeName)) {
$this->typeName = $this->tree->getTokenTypeNameFromTypeID($this->typeID);
}
return $this->typeName;
}
final public function getValue() {
return $this->value;
}
final public function overwriteValue($value) {
$this->value = $value;
return $this;
}
final public function getOffset() {
return $this->offset;
}
abstract public function isComment();
abstract public function isAnyWhitespace();
public function isSemantic() {
return !($this->isComment() || $this->isAnyWhitespace());
}
public function getPrevToken() {
$tokens = $this->tree->getRawTokenStream();
return idx($tokens, $this->id - 1);
}
public function getNextToken() {
$tokens = $this->tree->getRawTokenStream();
return idx($tokens, $this->id + 1);
}
public function getNonsemanticTokensBefore() {
$tokens = $this->tree->getRawTokenStream();
$result = array();
$ii = $this->id - 1;
while ($ii >= 0 && !$tokens[$ii]->isSemantic()) {
$result[$ii] = $tokens[$ii];
--$ii;
}
return array_reverse($result);
}
public function getNonsemanticTokensAfter() {
$tokens = $this->tree->getRawTokenStream();
$result = array();
$ii = $this->id + 1;
while ($ii < count($tokens) && !$tokens[$ii]->isSemantic()) {
$result[$ii] = $tokens[$ii];
++$ii;
}
return $result;
}
+ public function getWhitespaceTokensAfter() {
+ $tokens = $this->tree->getRawTokenStream();
+ $result = array();
+ $ii = $this->id + 1;
+ while ($ii < count($tokens) && $tokens[$ii]->isAnyWhitespace()) {
+ $result[$ii] = $tokens[$ii];
+ ++$ii;
+ }
+ return $result;
+ }
+
final public function getLineNumber() {
return idx($this->tree->getOffsetToLineNumberMap(), $this->getOffset());
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Nov 23, 06:02 (1 d, 16 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
547883
Default Alt Text
(9 KB)
Attached To
Mode
R118 Arcanist - fork
Attached
Detach File
Event Timeline
Log In to Comment