Skip to content

Commit

Permalink
SONARPHP-1563 Add support for PropertyHooks in constructor parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
jonas-wielage-sonarsource committed Nov 7, 2024
1 parent ab99908 commit 710994d
Show file tree
Hide file tree
Showing 12 changed files with 83 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,8 @@ public ParameterTree PARAMETER() {
b.optional(
f.newTuple(
b.token(PHPPunctuator.EQU),
STATIC_SCALAR()))));
STATIC_SCALAR())),
b.optional(PROPERTY_HOOK_LIST())));
}

public SeparatedListImpl<NamespaceNameTree> INTERFACE_LIST() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -560,7 +560,8 @@ public ParameterTree parameter(
Optional<InternalSyntaxToken> ampersand,
Optional<InternalSyntaxToken> ellipsis,
InternalSyntaxToken identifier,
Optional<Tuple<InternalSyntaxToken, ExpressionTree>> eqAndInitValue) {
Optional<Tuple<InternalSyntaxToken, ExpressionTree>> eqAndInitValue,
Optional<PropertyHookListTree> propertyHookList) {
InternalSyntaxToken eqToken = null;
ExpressionTree initValue = null;
if (eqAndInitValue.isPresent()) {
Expand All @@ -576,7 +577,8 @@ public ParameterTree parameter(
ellipsis.orNull(),
varIdentifier,
eqToken,
initValue);
initValue,
propertyHookList.orNull());
}

public SeparatedListImpl<NamespaceNameTree> interfaceList(NamespaceNameTree first, Optional<List<Tuple<InternalSyntaxToken, NamespaceNameTree>>> others) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.sonar.plugins.php.api.tree.declaration.AttributeGroupTree;
import org.sonar.plugins.php.api.tree.declaration.DeclaredTypeTree;
import org.sonar.plugins.php.api.tree.declaration.ParameterTree;
import org.sonar.plugins.php.api.tree.declaration.PropertyHookListTree;
import org.sonar.plugins.php.api.tree.declaration.TypeTree;
import org.sonar.plugins.php.api.tree.expression.ExpressionTree;
import org.sonar.plugins.php.api.tree.expression.VariableIdentifierTree;
Expand All @@ -47,6 +48,8 @@ public class ParameterTreeImpl extends PHPTree implements ParameterTree {
private final VariableIdentifierTree variableIdentifier;
private final InternalSyntaxToken equalToken;
private final ExpressionTree initValue;
@Nullable
private final PropertyHookListTree propertyHookList;
private SyntaxToken readonlyToken;
private SyntaxToken visibility;

Expand All @@ -58,7 +61,8 @@ public ParameterTreeImpl(
@Nullable InternalSyntaxToken ellipsisToken,
VariableIdentifierTree variableIdentifier,
@Nullable InternalSyntaxToken equalToken,
@Nullable ExpressionTree initValue) {
@Nullable ExpressionTree initValue,
@Nullable PropertyHookListTree propertyHookList) {
this.attributeGroups = attributeGroups;
this.visibilityAndReadonly = visibilityAndReadonly;

Expand All @@ -76,6 +80,7 @@ public ParameterTreeImpl(
this.variableIdentifier = variableIdentifier;
this.equalToken = equalToken;
this.initValue = initValue;
this.propertyHookList = propertyHookList;
}

@Override
Expand Down Expand Up @@ -140,6 +145,12 @@ public ExpressionTree initValue() {
return initValue;
}

@Nullable
@Override
public PropertyHookListTree propertyHookList() {
return propertyHookList;
}

@Nullable
@Override
public SyntaxToken readonlyToken() {
Expand All @@ -156,7 +167,8 @@ public Iterator<Tree> childrenIterator() {
return IteratorUtils.concat(
attributeGroups.iterator(),
visibilityAndReadonly.iterator(),
IteratorUtils.iteratorOf(type, referenceToken, ellipsisToken, variableIdentifier, equalToken, initValue));
IteratorUtils.iteratorOf(type, referenceToken, ellipsisToken, variableIdentifier, equalToken, initValue),
IteratorUtils.nullableIterator(propertyHookList));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ public interface ParameterTree extends Tree, HasAttributes {
@Nullable
ExpressionTree initValue();

/**
* @return the {@link PropertyHookListTree} if it exists.
*/
@Nullable
PropertyHookListTree propertyHookList();

@Nullable
SyntaxToken readonlyToken();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ void test() {
.matches("function f(A|B $prop): A|B {}")
.matches("function f(null|(A&B) $prop): void {}")
.matches("function f(A $prop): null|(A&B) {}")
.matches("function f(int $a { get; set => 123; }) {}")

// readonly is a keyword, but it can be used as a function name
.matches("function readonly() {}")
.matches("function READONLY() {}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ void test() throws Exception {
.matches("#[A1(4)] public function f() {}")
.matches("public function f($prop) {}")
.matches("public function f($prop = null) {}")
.matches("public function f($prop = new Foo()) {}");
.matches("public function f($prop = new Foo()) {}")
.matches("public function f($p { get; }) {}");
}

@Test
Expand All @@ -57,6 +58,7 @@ void construct() {
.matches("public function __construct(readonly protected $prop) {}")
.matches("public function __construct(readonly protected string $prop) {}")
.matches("public function __construct(readonly $prop) {}")
.matches("public function __construct(public $p { get; }) {}")
.notMatches("public function __construct(var $prop) {}");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ void test() {
.matches("()")
.matches("($a)")
.matches("($a, $b)")
.matches("($a, $b,)");
.matches("($a, $b,)")
.matches("(int $a { get; set => 123; }, $b)");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ void test() {
.matches("&$a")
.matches("...$a")
.matches("$a = \"foo\"")
.matches("int|array|Foo $a");
.matches("int|array|Foo $a")
.matches("int $a { get; set => 123; }")
.matches("int $a { final set($value) => $value - 1; }")
.matches("int $a { get { return $this->a+1; } }");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,15 @@ void shouldSupportDnfTypeInParameter() {
assertThat(dnfType.isSimple()).isFalse();
assertThat(dnfType.types()).hasSize(3);
}

@Test
void shouldSupportPropertyHooks() {
FunctionDeclarationTree tree = parse("function f(int $a { get; set => 123; }, $b) {}", PHPLexicalGrammar.FUNCTION_DECLARATION);
assertThat(tree.is(Kind.FUNCTION_DECLARATION)).isTrue();
assertThat(((FunctionDeclarationTreeImpl) tree).childrenIterator()).toIterable().hasSize(6);
assertThat(tree.parameters().parameters()).hasSize(2);
var propertyHooks = tree.parameters().parameters().get(0).propertyHookList();
assertThat(propertyHooks.getKind()).isEqualTo(Kind.PROPERTY_HOOK_LIST);
assertThat(propertyHooks.hooks()).hasSize(2);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,16 @@ void constructorPropertyPromotion() {
assertThat(tree.parameters().parameters().get(0).visibility().text()).isEqualTo("private");
}

@Test
void shouldSupportPropertyHook() {
MethodDeclarationTree tree = parse("public function __construct($p { get; }) {}", PHPLexicalGrammar.METHOD_DECLARATION);

assertThat(tree.is(Kind.METHOD_DECLARATION)).isTrue();
assertThat(((MethodDeclarationTreeImpl) tree).childrenIterator()).toIterable().hasSize(7);
assertThat(tree.parameters().parameters()).hasSize(1);
assertThat(tree.parameters().parameters().get(0).propertyHookList()).isNotNull();
}

@Test
void nonConstructorParameterWithVisibilityModifier() {
assertThatExceptionOfType(RecognitionException.class).isThrownBy(() -> parse("public function nonConstructor(public $p) {}", PHPLexicalGrammar.METHOD_DECLARATION));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,13 @@ private ParameterListTree parameterList(String toParse) {
return tree;
}

@Test
void shouldSupportPropertyHooks() {
ParameterListTree tree = parameterList("(int $a { get; set => 123; }, $b)");
assertThat(tree.is(Kind.PARAMETER_LIST)).isTrue();
assertThat(((ParameterListTreeImpl) tree).childrenIterator()).toIterable().hasSize(5);
assertThat(tree.parameters()).hasSize(2);
assertThat((tree.parameters().get(0).propertyHookList())).isNotNull();
assertThat((tree.parameters().get(0).propertyHookList().hooks())).hasSize(2);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
class ParameterTreeTest extends PHPTreeModelTest {

@Test
void onlyIdentifier() {
void shouldSupportOnlyIdentifier() {
ParameterTree tree = parse("$param1", PHPLexicalGrammar.PARAMETER);
assertThat(tree.is(Kind.PARAMETER)).isTrue();
assertThat(tree.type()).isNull();
Expand All @@ -45,7 +45,7 @@ void onlyIdentifier() {
}

@Test
void full() {
void shouldSupportFullExample() {
ParameterTree tree = parse("Class1&...$param1=$value1", PHPLexicalGrammar.PARAMETER);
assertThat(tree.type().typeName().is(Kind.NAMESPACE_NAME)).isTrue();
assertThat(tree.referenceToken().text()).isEqualTo("&");
Expand All @@ -56,13 +56,13 @@ void full() {
}

@Test
void withAttributes() {
void shouldSupportWithAttributes() {
ParameterTree tree = parse("#[A1(5)] #[A2(5)] int $a", PHPLexicalGrammar.PARAMETER);
assertThat(tree.attributeGroups()).hasSize(2);
}

@Test
void initProperty() {
void shouldSupportInitProperty() {
ParameterTree tree = parse("private int $a", PHPLexicalGrammar.PARAMETER);
assertThat(tree.isPropertyPromotion()).isTrue();
assertThat(tree.visibility().text()).isEqualTo("private");
Expand All @@ -72,12 +72,23 @@ void initProperty() {
}

@Test
void initReadonlyProperty() {
void shouldSupportInitReadonlyProperty() {
ParameterTree tree = parse("private readonly int $a", PHPLexicalGrammar.PARAMETER);
assertThat(tree.isPropertyPromotion()).isTrue();
assertThat(tree.visibility().text()).isEqualTo("private");
assertThat(tree.declaredType()).isNotNull();
assertThat(tree.readonlyToken()).isNotNull();
assertThat(tree.isReadonly()).isTrue();
}

@Test
void shouldSupportPropertyHookList() {
ParameterTree tree = parse("int $a { get; set => 123; }", PHPLexicalGrammar.PARAMETER);
assertThat(tree.is(Kind.PARAMETER)).isTrue();
assertThat(((ParameterTreeImpl) tree).childrenIterator()).toIterable().hasSize(7);
assertThat(tree.propertyHookList()).isNotNull();
assertThat(tree.propertyHookList().openCurlyBrace()).isNotNull();
assertThat(tree.propertyHookList().hooks()).hasSize(2);
assertThat(tree.propertyHookList().closeCurlyBrace()).isNotNull();
}
}

0 comments on commit 710994d

Please sign in to comment.