From 0da31dbd7151a4641b62da1d7c75206853274f7d 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 1/2] 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 | 20 +++++++++++++------ .../NonvarargsParam01.pass | 12 +++++++++++ .../NonvarargsParam02.pass | 11 ++++++++++ .../VarargsParam01.pass | 12 +++++++++++ .../VarargsParam02.pass | 11 ++++++++++ .../parser/PHPDocCommentParserTest.java | 20 +++++++++++++++++++ 6 files changed, 80 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..3624f9ce44db 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("...")) { + 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,8 @@ 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) + || !(tokens[0].startsWith("$") /* variable */ || tokens[0].startsWith("...$") /* variadic variable */ ))) { //NOI18N if (findParameterStartPosition(tokens[0]) != -1) { // e.g. @method voidReturn((X&Y)|Z $param) types.add(Type.VOID); @@ -320,13 +325,16 @@ 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) == '$') { + // Check both normal variables ("$"-prefix) and variadic variants ("...$"-prefix) + if (tokens.length > 0 && tokens[0].length() > 0 + && (tokens[0].charAt(0) == '$' || tokens[0].startsWith("...$"))) { variable = tokens[0].trim(); - } else if ((tokens.length > 1) && (tokens[1].charAt(0) == '$')) { + } else if ((tokens.length > 1) + && (tokens[1].charAt(0) == '$' || tokens[1].startsWith("...$"))) { variable = tokens[1].trim(); } return variable; @@ -366,7 +374,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); From 01dd57b2831119323adc1df35d1f0c7ff539aa71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bl=C3=A4sing?= Date: Tue, 7 Jul 2026 22:12:38 +0200 Subject: [PATCH 2/2] Introduce method for recognizing variable names (to be squashed) --- .../php/editor/parser/PHPDocCommentParser.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) 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 3624f9ce44db..924d41f002f0 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 @@ -302,8 +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("$") /* variable */ || tokens[0].startsWith("...$") /* variadic variable */ ))) { //NOI18N + if (tokens.length > 0 && (isReturnTag(tagType) || !isVariableName(tokens[0]))) { //NOI18N if (findParameterStartPosition(tokens[0]) != -1) { // e.g. @method voidReturn((X&Y)|Z $param) types.add(Type.VOID); @@ -329,17 +328,19 @@ private String getVariableName(String description) { String[] tokens = description.trim().split("[ \n\t]+"); //NOI18N String variable = null; - // Check both normal variables ("$"-prefix) and variadic variants ("...$"-prefix) - if (tokens.length > 0 && tokens[0].length() > 0 - && (tokens[0].charAt(0) == '$' || tokens[0].startsWith("...$"))) { + if (tokens.length > 0 && tokens[0].length() > 0 && isVariableName(tokens[0])) { variable = tokens[0].trim(); - } else if ((tokens.length > 1) - && (tokens[1].charAt(0) == '$' || tokens[1].startsWith("...$"))) { + } else if ((tokens.length > 1) && isVariableName(tokens[1])) { variable = tokens[1].trim(); } return variable; } + public static boolean isVariableName(String token) { + return token.startsWith("$") /* variable */ + || token.startsWith("...$") /* variadic variable */; + } + private String getMethodName(String description) { String name = null; int index = findParameterStartPosition(description);