From 8ea4484f771c6a0e2253806b1c14d72a45ff3de1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bl=C3=A4sing?= Date: Sun, 5 Jul 2026 21:12:31 +0200 Subject: [PATCH] PHP: Support variadic marked variables in PHPDoc Variadic variables (...$variable) were not correctly parsed by the PHPDoc parser. The issue manifests like this: - variadic variables in PHPDoc are not highlighted by usage marker - renaming does not work for variadic marked variables Closes: #9437 --- .../editor/parser/PHPDocCommentParser.java | 21 +++++++++++++------ .../NonvarargsParam01.pass | 12 +++++++++++ .../NonvarargsParam02.pass | 11 ++++++++++ .../VarargsParam01.pass | 12 +++++++++++ .../VarargsParam02.pass | 11 ++++++++++ .../parser/PHPDocCommentParserTest.java | 20 ++++++++++++++++++ 6 files changed, 81 insertions(+), 6 deletions(-) create mode 100644 php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/parser/PHPDocCommentParserTest/NonvarargsParam01.pass create mode 100644 php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/parser/PHPDocCommentParserTest/NonvarargsParam02.pass create mode 100644 php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/parser/PHPDocCommentParserTest/VarargsParam01.pass create mode 100644 php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/parser/PHPDocCommentParserTest/VarargsParam02.pass diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/parser/PHPDocCommentParser.java b/php/php.editor/src/org/netbeans/modules/php/editor/parser/PHPDocCommentParser.java index 1eb715c94903..ac0c143a68d5 100644 --- a/php/php.editor/src/org/netbeans/modules/php/editor/parser/PHPDocCommentParser.java +++ b/php/php.editor/src/org/netbeans/modules/php/editor/parser/PHPDocCommentParser.java @@ -204,9 +204,13 @@ private PHPDocTag createTag(int start, int end, AnnotationParsedLine type, Strin if (types.isEmpty()) { List docTypes = findTypes(description, start, originalComment, originalCommentStart, type); if (PHP_DOC_VAR_TYPE_TAGS.contains(type)) { - String variable = getVaribleName(description); + String variable = getVariableName(description); PHPDocNode varibaleNode = null; if (variable != null) { + // Strip variadic prefix from variable + if (variable.startsWith("...")) { //NOI18N + variable = variable.substring(3); + } int startOfVariable = findStartOfDocNode(originalComment, originalCommentStart, variable, start); if (startOfVariable != -1) { varibaleNode = new PHPDocNode(startOfVariable, startOfVariable + variable.length(), variable); @@ -298,7 +302,7 @@ private List getTypes(String description, AnnotationParsedLine tagType) tokens = Arrays.copyOfRange(tokens, 1, tokens.length); } ArrayList types = new ArrayList<>(); - if (tokens.length > 0 && (isReturnTag(tagType) || !tokens[0].startsWith("$"))) { //NOI18N + if (tokens.length > 0 && (isReturnTag(tagType) || !isVariableName(tokens[0]))) { if (findParameterStartPosition(tokens[0]) != -1) { // e.g. @method voidReturn((X&Y)|Z $param) types.add(Type.VOID); @@ -320,18 +324,23 @@ private List getTypes(String description, AnnotationParsedLine tagType) return types; } - private String getVaribleName(String description) { + private String getVariableName(String description) { String[] tokens = description.trim().split("[ \n\t]+"); //NOI18N String variable = null; - if (tokens.length > 0 && tokens[0].length() > 0 && tokens[0].charAt(0) == '$') { + if (tokens.length > 0 && tokens[0].length() > 0 && isVariableName(tokens[0])) { variable = tokens[0].trim(); - } else if ((tokens.length > 1) && (tokens[1].charAt(0) == '$')) { + } else if ((tokens.length > 1) && isVariableName(tokens[1])) { variable = tokens[1].trim(); } return variable; } + public static boolean isVariableName(String token) { + return token.startsWith("$") /* variable */ //NOI18N + || token.startsWith("...$") /* variadic variable */; //NOI18N + } + private String getMethodName(String description) { String name = null; int index = findParameterStartPosition(description); @@ -366,7 +375,7 @@ private List findMethodParams(String description, int startOfD String[] tokens = parameters.split("[,]+"); //NOI18N String paramName; for (String token : tokens) { - paramName = getVaribleName(token.trim()); + paramName = getVariableName(token.trim()); if (paramName != null) { int startOfParamName = findStartOfDocNode(description, startOfDescription, paramName, position); if (startOfParamName != -1) { diff --git a/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/parser/PHPDocCommentParserTest/NonvarargsParam01.pass b/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/parser/PHPDocCommentParserTest/NonvarargsParam01.pass new file mode 100644 index 000000000000..eaa2cad8de6c --- /dev/null +++ b/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/parser/PHPDocCommentParserTest/NonvarargsParam01.pass @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/parser/PHPDocCommentParserTest/NonvarargsParam02.pass b/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/parser/PHPDocCommentParserTest/NonvarargsParam02.pass new file mode 100644 index 000000000000..fad8e7ec8cfb --- /dev/null +++ b/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/parser/PHPDocCommentParserTest/NonvarargsParam02.pass @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/parser/PHPDocCommentParserTest/VarargsParam01.pass b/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/parser/PHPDocCommentParserTest/VarargsParam01.pass new file mode 100644 index 000000000000..ad27c0e07691 --- /dev/null +++ b/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/parser/PHPDocCommentParserTest/VarargsParam01.pass @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/parser/PHPDocCommentParserTest/VarargsParam02.pass b/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/parser/PHPDocCommentParserTest/VarargsParam02.pass new file mode 100644 index 000000000000..ba1b74170cc8 --- /dev/null +++ b/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/parser/PHPDocCommentParserTest/VarargsParam02.pass @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/php/php.editor/test/unit/src/org/netbeans/modules/php/editor/parser/PHPDocCommentParserTest.java b/php/php.editor/test/unit/src/org/netbeans/modules/php/editor/parser/PHPDocCommentParserTest.java index 84bdab81b71e..d5e103f0c149 100644 --- a/php/php.editor/test/unit/src/org/netbeans/modules/php/editor/parser/PHPDocCommentParserTest.java +++ b/php/php.editor/test/unit/src/org/netbeans/modules/php/editor/parser/PHPDocCommentParserTest.java @@ -430,6 +430,26 @@ public void testReturnTypeObjectShapes02() throws Exception { perform(comment, "ReturnTypeObjectShapes02"); } + public void testVarargsParam01() throws Exception { + String comment = " * @param string ...$param"; + perform(comment, "VarargsParam01"); + } + + public void testVarargsParam02() throws Exception { + String comment = " * @param ...$param"; + perform(comment, "VarargsParam02"); + } + + public void testNonvarargsParam01() throws Exception { + String comment = " * @param string $param"; + perform(comment, "NonvarargsParam01"); + } + + public void testNonvarargsParam02() throws Exception { + String comment = " * @param $param"; + perform(comment, "NonvarargsParam02"); + } + public void perform(String comment, String filename) throws Exception { PHPDocCommentParser parser = new PHPDocCommentParser(); PHPDocBlock block = parser.parse(0, comment.length(), comment);