This is an automated email from the ASF dual-hosted git repository.
junichi11 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans.git
The following commit(s) were added to refs/heads/master by this push:
new aa919a41f0 Add the `IncorrectStaticContextHintError` as a new hint
#6703
new 7c39099393 Merge pull request #6899 from
junichi11/php-gh-6703-this-in-static-context
aa919a41f0 is described below
commit aa919a41f0a8151ca90f25c88c706970a68fcbbf
Author: Junichi Yamamoto <[email protected]>
AuthorDate: Sun Dec 31 20:18:01 2023 +0900
Add the `IncorrectStaticContextHintError` as a new hint #6703
- https://github.com/apache/netbeans/issues/6703
- Show errors when `$this` is used in static context(static methods, static
closures, static arrow functions)
- Add unit tests
Note:
Check only $this in TypeDeclarations because Frameworks may use `$this` in
the global scope.
e.g. CakePHP framework can use `$this`(e.g. `$this->Html->link()`) in the
global scope of view files.
(see: https://book.cakephp.org/5/en/views.html)
Example:
```php
class Example {
private int $field = 1;
public static function staticMethod(): void {
$this->field; // error
}
public function method(): void {
$staticClosure = static function() {
var_dump($this); // error
};
$staticArrow = static fn() => $this; // error
echo $this->field;
}
}
```
---
.../org/netbeans/modules/php/editor/CodeUtils.java | 1 +
.../modules/php/editor/resources/layer.xml | 1 +
.../IncorrectStaticContextHintError.java | 274 +++++++++++++++++++++
.../testStaticClosureAndArrowFunctionInClass.php | 156 ++++++++++++
....testStaticClosureAndArrowFunctionInClass.hints | 72 ++++++
.../testStaticClosureAndArrowFunctionInEnum.php | 115 +++++++++
...p.testStaticClosureAndArrowFunctionInEnum.hints | 54 ++++
.../testStaticClosureAndArrowFunctionInTrait.php | 117 +++++++++
....testStaticClosureAndArrowFunctionInTrait.hints | 54 ++++
.../testThisVariables.php | 76 ++++++
.../testThisVariables.php.testThisVariables.hints | 15 ++
.../IncorrectStaticContextHintErrorTest.java | 51 ++++
12 files changed, 986 insertions(+)
diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/CodeUtils.java
b/php/php.editor/src/org/netbeans/modules/php/editor/CodeUtils.java
index cfdadb5f43..03184cd436 100644
--- a/php/php.editor/src/org/netbeans/modules/php/editor/CodeUtils.java
+++ b/php/php.editor/src/org/netbeans/modules/php/editor/CodeUtils.java
@@ -94,6 +94,7 @@ public final class CodeUtils {
public static final String OVERRIDE_ATTRIBUTE = "#[" +
OVERRIDE_ATTRIBUTE_FQ_NAME + "]"; // NOI18N
public static final String EMPTY_STRING = ""; // NOI18N
public static final String NEW_LINE = "\n"; // NOI18N
+ public static final String THIS_VARIABLE = "$this"; // NOI18N
public static final Pattern WHITE_SPACES_PATTERN =
Pattern.compile("\\s+"); // NOI18N
public static final Pattern SPLIT_TYPES_PATTERN =
Pattern.compile("[()|&]+"); // NOI18N
diff --git
a/php/php.editor/src/org/netbeans/modules/php/editor/resources/layer.xml
b/php/php.editor/src/org/netbeans/modules/php/editor/resources/layer.xml
index b3aecee689..e907da2c56 100644
--- a/php/php.editor/src/org/netbeans/modules/php/editor/resources/layer.xml
+++ b/php/php.editor/src/org/netbeans/modules/php/editor/resources/layer.xml
@@ -433,6 +433,7 @@
<file
name="org-netbeans-modules-php-editor-verification-IncorrectMixedGroupUseHintError.instance"/>
<file
name="org-netbeans-modules-php-editor-verification-IncorrectNamedArgumentsHintError.instance"/>
<file
name="org-netbeans-modules-php-editor-verification-IncorrectNonAbstractMethodHintError.instance"/>
+ <file
name="org-netbeans-modules-php-editor-verification-IncorrectStaticContextHintError.instance"/>
<file
name="org-netbeans-modules-php-editor-verification-LoopOnlyKeywordsUnhandledError.instance"/>
<file
name="org-netbeans-modules-php-editor-verification-MethodRedeclarationHintError.instance"/>
<file
name="org-netbeans-modules-php-editor-verification-ModifiersCheckHintError.instance"/>
diff --git
a/php/php.editor/src/org/netbeans/modules/php/editor/verification/IncorrectStaticContextHintError.java
b/php/php.editor/src/org/netbeans/modules/php/editor/verification/IncorrectStaticContextHintError.java
new file mode 100644
index 0000000000..00409bea05
--- /dev/null
+++
b/php/php.editor/src/org/netbeans/modules/php/editor/verification/IncorrectStaticContextHintError.java
@@ -0,0 +1,274 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.php.editor.verification;
+
+import java.util.ArrayDeque;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.netbeans.modules.csl.api.Hint;
+import org.netbeans.modules.csl.api.HintFix;
+import org.netbeans.modules.csl.api.OffsetRange;
+import org.netbeans.modules.csl.spi.support.CancelSupport;
+import org.netbeans.modules.php.editor.CodeUtils;
+import org.netbeans.modules.php.editor.model.FileScope;
+import org.netbeans.modules.php.editor.parser.PHPParseResult;
+import org.netbeans.modules.php.editor.parser.astnodes.ASTNode;
+import
org.netbeans.modules.php.editor.parser.astnodes.ArrowFunctionDeclaration;
+import org.netbeans.modules.php.editor.parser.astnodes.BodyDeclaration;
+import org.netbeans.modules.php.editor.parser.astnodes.ClassDeclaration;
+import org.netbeans.modules.php.editor.parser.astnodes.ClassInstanceCreation;
+import org.netbeans.modules.php.editor.parser.astnodes.EnumDeclaration;
+import
org.netbeans.modules.php.editor.parser.astnodes.LambdaFunctionDeclaration;
+import org.netbeans.modules.php.editor.parser.astnodes.MethodDeclaration;
+import org.netbeans.modules.php.editor.parser.astnodes.TraitDeclaration;
+import org.netbeans.modules.php.editor.parser.astnodes.Variable;
+import org.netbeans.modules.php.editor.parser.astnodes.visitors.DefaultVisitor;
+import org.openide.filesystems.FileObject;
+import org.openide.util.NbBundle;
+
+/**
+ * Handle incorrect static context statements.
+ */
+public class IncorrectStaticContextHintError extends HintErrorRule {
+
+ private static final boolean DISABLED =
Boolean.getBoolean("nb.php.editor.disableIncorrectStaticContextHintError"); //
NOI18N
+ private FileObject fileObject;
+
+ @Override
+ @NbBundle.Messages("IncorrectStaticContextHintError.displayName=Incorrect
Statements in Static Context")
+ public String getDisplayName() {
+ return Bundle.IncorrectStaticContextHintError_displayName();
+ }
+
+ @Override
+ public void invoke(PHPRuleContext context, List<Hint> hints) {
+ if (DISABLED) {
+ return;
+ }
+ PHPParseResult phpParseResult = (PHPParseResult) context.parserResult;
+ if (phpParseResult.getProgram() == null) {
+ return;
+ }
+ FileScope fileScope = context.fileScope;
+ fileObject = phpParseResult.getSnapshot().getSource().getFileObject();
+ if (fileScope != null && fileObject != null) {
+ if (CancelSupport.getDefault().isCancelled()) {
+ return;
+ }
+ CheckVisitor checkVisitor = new CheckVisitor();
+ phpParseResult.getProgram().accept(checkVisitor);
+ Set<Variable> incorrectThisVariables =
checkVisitor.getThisVariableInStaticContext();
+ addIncorrectThisHints(incorrectThisVariables, hints);
+ }
+ }
+
+ @NbBundle.Messages("IncorrectStaticContextHintError.incorrectThis=Cannot
use \"$this\" in static context.")
+ private void addIncorrectThisHints(Set<Variable> incorrectThisVariables,
List<Hint> hints) {
+ // e.g.
+ // public static function staticMethod(): int {
+ // return $this->field;
+ // } ^^^^^
+ for (Variable thisVariable : incorrectThisVariables) {
+ if (CancelSupport.getDefault().isCancelled()) {
+ return;
+ }
+ addHint(thisVariable,
Bundle.IncorrectStaticContextHintError_incorrectThis(), hints);
+ }
+ }
+
+ private void addHint(ASTNode node, String description, List<Hint> hints) {
+ addHint(node, description, hints, Collections.emptyList());
+ }
+
+ private void addHint(ASTNode node, String description, List<Hint> hints,
List<HintFix> fixes) {
+ hints.add(new Hint(
+ this,
+ description,
+ fileObject,
+ new OffsetRange(node.getStartOffset(), node.getEndOffset()),
+ fixes,
+ 500
+ ));
+ }
+
+ //~ Inner classes
+ private static final class CheckVisitor extends DefaultVisitor {
+
+ private boolean isInTypeDeclaration = false;
+ private boolean isInStaticMethod = false;
+ private ASTNode firstStaticClosure = null;
+ private final ArrayDeque<ASTNode> firstStaticClosureStack = new
ArrayDeque<>();
+ private final ArrayDeque<Boolean> isInStaticMethodStack = new
ArrayDeque<>();
+ private final Set<Variable> thisVariableInStaticContext = new
HashSet<>();
+
+ @Override
+ public void visit(MethodDeclaration node) {
+ if (CancelSupport.getDefault().isCancelled()) {
+ return;
+ }
+ isInStaticMethod =
BodyDeclaration.Modifier.isStatic(node.getModifier());
+ super.visit(node);
+ isInStaticMethod = false;
+ }
+
+ @Override
+ public void visit(LambdaFunctionDeclaration node) {
+ if (CancelSupport.getDefault().isCancelled()) {
+ return;
+ }
+ // e.g.
+ // $closure = function() {
+ // $this->something; // ok
+ // $nestedStaticClosure = static function() {
+ // $this->something; // error
+ // $nestedClosure = function() {
+ // $this->something; // error
+ // };
+ // };
+ // };
+ if (node.isStatic()) {
+ if (firstStaticClosure == null) {
+ firstStaticClosure = node;
+ }
+ }
+ super.visit(node);
+ if (firstStaticClosure == node) {
+ firstStaticClosure = null;
+ }
+ }
+
+ @Override
+ public void visit(ArrowFunctionDeclaration node) {
+ if (CancelSupport.getDefault().isCancelled()) {
+ return;
+ }
+ if (node.isStatic()) {
+ if (firstStaticClosure == null) {
+ firstStaticClosure = node;
+ }
+ }
+ super.visit(node);
+ if (firstStaticClosure == node) {
+ firstStaticClosure = null;
+ }
+ }
+
+ @Override
+ public void visit(ClassDeclaration node) {
+ if (CancelSupport.getDefault().isCancelled()) {
+ return;
+ }
+ isInTypeDeclaration = true;
+ super.visit(node);
+ isInTypeDeclaration = false;
+ }
+
+ @Override
+ public void visit(EnumDeclaration node) {
+ if (CancelSupport.getDefault().isCancelled()) {
+ return;
+ }
+ isInTypeDeclaration = true;
+ super.visit(node);
+ isInTypeDeclaration = false;
+ }
+
+ @Override
+ public void visit(TraitDeclaration node) {
+ if (CancelSupport.getDefault().isCancelled()) {
+ return;
+ }
+ isInTypeDeclaration = true;
+ super.visit(node);
+ isInTypeDeclaration = false;
+ }
+
+ @Override
+ public void visit(ClassInstanceCreation node) {
+ if (CancelSupport.getDefault().isCancelled()) {
+ return;
+ }
+ // static method may have anonymous classes
+ // e.g.
+ // public static function getExample(): int {
+ // self::staticMethod($this); // error
+ // $anon = new class() {
+ // private int $field = 1;
+ //
+ // public static function nestedStaticMethod(): void {
+ // echo $this->field; // error
+ // }
+ //
+ // public function nestedMethod(): void {
+ // echo $this->field; // ok
+ // }
+ // };
+ // return $this->test; // error
+ // }
+ boolean isInPreviousTypeDeclaration = this.isInTypeDeclaration;
+ boolean hasFirstStaticClosure = firstStaticClosure != null;
+ if (node.isAnonymous()) {
+ isInTypeDeclaration = true;
+ isInStaticMethodStack.push(isInStaticMethod);
+ isInStaticMethod = false;
+ if (hasFirstStaticClosure) {
+ firstStaticClosureStack.push(firstStaticClosure);
+ firstStaticClosure = null;
+ }
+ }
+ super.visit(node);
+ if (node.isAnonymous()) {
+ isInTypeDeclaration = isInPreviousTypeDeclaration;
+ isInStaticMethod = isInStaticMethodStack.pop();
+ if (hasFirstStaticClosure) {
+ firstStaticClosure = firstStaticClosureStack.pop();
+ }
+ }
+ }
+
+ @Override
+ public void visit(Variable node) {
+ if (CancelSupport.getDefault().isCancelled()) {
+ return;
+ }
+ if (isInStaticContext()) {
+ String variableName = CodeUtils.extractVariableName(node);
+ if (CodeUtils.THIS_VARIABLE.equals(variableName)) {
+ thisVariableInStaticContext.add(node);
+ }
+ }
+ super.visit(node);
+ }
+
+ private boolean isInStaticContext() {
+ // NOTE:
+ // Check only $this in TypeDeclarations because Frameworks may use
`$this` in the global scope
+ // e.g. CakePHP framework can use `$this` in the global scope of
view files
+ // see: https://book.cakephp.org/5/en/views.html
+ return isInStaticMethod
+ || (isInTypeDeclaration && firstStaticClosure != null);
+ }
+
+ public Set<Variable> getThisVariableInStaticContext() {
+ return Collections.unmodifiableSet(thisVariableInStaticContext);
+ }
+ }
+}
diff --git
a/php/php.editor/test/unit/data/testfiles/verification/IncorrectStaticContextHintError/testStaticClosureAndArrowFunctionInClass.php
b/php/php.editor/test/unit/data/testfiles/verification/IncorrectStaticContextHintError/testStaticClosureAndArrowFunctionInClass.php
new file mode 100644
index 0000000000..1cff6383a2
--- /dev/null
+++
b/php/php.editor/test/unit/data/testfiles/verification/IncorrectStaticContextHintError/testStaticClosureAndArrowFunctionInClass.php
@@ -0,0 +1,156 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+class ThisInStaticContext
+{
+ private int $test = 1;
+
+ public static function staticMethod(): void {
+ $closure = function() {
+ echo $this->test; // error
+ };
+ $staticClosure = static function() {
+ echo $this->test; // error
+ $anon = new class() {
+ private int $test = 1;
+ public function method() {
+ $closure = function() {
+ $this->test; // ok
+ };
+ $staticClosure = static function() {
+ $this->test; // error
+ $closure2 = function() {
+ $this->test; // error
+ };
+ };
+ $staticClosure();
+ return $this; // ok
+ }
+ public static function staticMethod() {
+ $this->method(); // error
+ }
+ };
+ };
+ $staticClosure();
+ $arrowFunction = fn() => $this; // error
+ $staticArrowFunction = static fn() => $this; // error
+ $staticArrowFunction()->test;
+ }
+
+ public function method(): void {
+ $this->test; // ok
+ $closure = function() {
+ $this->test; // ok
+ $nestedStaticClosure = static function() {
+ $this->test; // error
+ };
+ };
+ $staticClosure = static function() {
+ echo $this->test; // error
+ $closuer2 = function() {
+ $this->test; // error
+ };
+ $anon = new class() {
+ private int $test = 1;
+ public function method() {
+ $closure = function() {
+ $this->test; // ok
+ };
+ $staticClosure = static function() {
+ $this->test; // error
+ $closure2 = function() {
+ $this->test; // error
+ };
+ };
+ $staticClosure();
+ return $this; // ok
+ }
+ public static function staticMethod() {
+ $this->method(); // error
+ }
+ };
+ $nestedArrow = fn() => $this; // error
+ };
+ $staticClosure();
+ $arrowFunction = fn() => $this; // ok
+ $staticArrowFunction = static fn() => $this; // error
+ $staticArrowFunction = static fn() => $anon = new class() {
+ private int $test = 1;
+ public function method() {
+ $closure = function () {
+ $this->test; // ok
+ };
+ $staticClosure = static function () {
+ $this->test; // error
+ $closure2 = function () {
+ $this->test; // error
+ };
+ };
+ $staticClosure();
+ return $this; // ok
+ }
+
+ public static function staticMethod() {
+ $this->method(); // error
+ }
+ };
+ $staticArrowFunction()->test;
+ $this->test; // ok
+ }
+}
+
+// anonymous class
+$anon = new class() {
+ private int $test = 1;
+
+ public static function test(): void {
+ $closure = function() {
+ echo $this->test; // error
+ };
+ $staticClosure = static function() {
+ echo $this->test; // error
+ };
+ $staticClosure();
+ $arrowFunction = fn() => $this; // error
+ $staticArrowFunction = static fn() => $this; // error
+ $staticArrowFunction()->test;
+ }
+
+ public function method(): void {
+ $closure = function() {
+ echo $this->test; // ok
+ };
+ $this->test(); // ok
+ $staticClosure = static function() {
+ echo $this->test; // error
+ };
+ $staticClosure();
+ $arrowFunction = fn() => $this; // ok
+ $staticArrowFunction = static fn() => $this; // error
+ $staticArrowFunction()->test;
+ }
+};
+
+$staticClosure = static function(): void {
+ var_dump($this); // ok(ignore)
+};
+$staticClosure();
+$staticArrow = static fn(): object => var_dump($this); // ok(ignore)
+$staticArrow();
diff --git
a/php/php.editor/test/unit/data/testfiles/verification/IncorrectStaticContextHintError/testStaticClosureAndArrowFunctionInClass.php.testStaticClosureAndArrowFunctionInClass.hints
b/php/php.editor/test/unit/data/testfiles/verification/IncorrectStaticContextHintError/testStaticClosureAndArrowFunctionInClass.php.testStaticClosureAndArrowFunctionInClass.hints
new file mode 100644
index 0000000000..c3e1b3451f
--- /dev/null
+++
b/php/php.editor/test/unit/data/testfiles/verification/IncorrectStaticContextHintError/testStaticClosureAndArrowFunctionInClass.php.testStaticClosureAndArrowFunctionInClass.hints
@@ -0,0 +1,72 @@
+ echo $this->test; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ echo $this->test; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $this->test; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $this->test; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $this->method(); // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $arrowFunction = fn() => $this; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $staticArrowFunction = static fn() => $this; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $this->test; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ echo $this->test; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $this->test; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $this->test; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $this->test; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $this->method(); // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $nestedArrow = fn() => $this; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $staticArrowFunction = static fn() => $this; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $this->test; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $this->test; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $this->method(); // error
+ -----
+HINT:Cannot use "$this" in static context.
+ echo $this->test; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ echo $this->test; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $arrowFunction = fn() => $this; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $staticArrowFunction = static fn() => $this; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ echo $this->test; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $staticArrowFunction = static fn() => $this; // error
+ -----
+HINT:Cannot use "$this" in static context.
diff --git
a/php/php.editor/test/unit/data/testfiles/verification/IncorrectStaticContextHintError/testStaticClosureAndArrowFunctionInEnum.php
b/php/php.editor/test/unit/data/testfiles/verification/IncorrectStaticContextHintError/testStaticClosureAndArrowFunctionInEnum.php
new file mode 100644
index 0000000000..e7144c5443
--- /dev/null
+++
b/php/php.editor/test/unit/data/testfiles/verification/IncorrectStaticContextHintError/testStaticClosureAndArrowFunctionInEnum.php
@@ -0,0 +1,115 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+enum ThisInStaticContextEnum
+{
+ public static function staticMethod(): void {
+ $closure = function() {
+ echo $this->name; // error
+ };
+ $staticClosure = static function() {
+ echo $this->name; // error
+ $anon = new class() {
+ private int $test = 1;
+ public function method() {
+ $closure = function() {
+ $this->test; // ok
+ };
+ $staticClosure = static function() {
+ $this->test; // error
+ $closure2 = function() {
+ $this->test; // error
+ };
+ };
+ $staticClosure();
+ return $this; // ok
+ }
+ public static function staticMethod() {
+ $this->method(); // error
+ }
+ };
+ };
+ $staticClosure();
+ $arrowFunction = fn() => $this; // error
+ $staticArrowFunction = static fn() => $this; // error
+ $staticArrowFunction()->name;
+ }
+
+ public function method(): void {
+ $this->name; // ok
+ $closure = function() {
+ $this->name; // ok
+ $nestedStaticClosure = static function() {
+ $this->name; // error
+ };
+ };
+ $staticClosure = static function() {
+ echo $this->name; // error
+ $closuer2 = function() {
+ $this->name; // error
+ };
+ $anon = new class() {
+ private int $test = 1;
+ public function method() {
+ $closure = function() {
+ $this->test; // ok
+ };
+ $staticClosure = static function() {
+ $this->test; // error
+ $closure2 = function() {
+ $this->test; // error
+ };
+ };
+ $staticClosure();
+ return $this; // ok
+ }
+ public static function staticMethod() {
+ $this->method(); // error
+ }
+ };
+ $nestedArrow = fn() => $this; // error
+ };
+ $staticClosure();
+ $arrowFunction = fn() => $this; // ok
+ $staticArrowFunction = static fn() => $this; // error
+ $staticArrowFunction = static fn() => $anon = new class() {
+ private int $test = 1;
+ public function method() {
+ $closure = function () {
+ $this->test; // ok
+ };
+ $staticClosure = static function () {
+ $this->test; // error
+ $closure2 = function () {
+ $this->test; // error
+ };
+ };
+ $staticClosure();
+ return $this; // ok
+ }
+
+ public static function staticMethod() {
+ $this->method(); // error
+ }
+ };
+ $staticArrowFunction()->name;
+ $this->name; // ok
+ }
+}
diff --git
a/php/php.editor/test/unit/data/testfiles/verification/IncorrectStaticContextHintError/testStaticClosureAndArrowFunctionInEnum.php.testStaticClosureAndArrowFunctionInEnum.hints
b/php/php.editor/test/unit/data/testfiles/verification/IncorrectStaticContextHintError/testStaticClosureAndArrowFunctionInEnum.php.testStaticClosureAndArrowFunctionInEnum.hints
new file mode 100644
index 0000000000..e958500948
--- /dev/null
+++
b/php/php.editor/test/unit/data/testfiles/verification/IncorrectStaticContextHintError/testStaticClosureAndArrowFunctionInEnum.php.testStaticClosureAndArrowFunctionInEnum.hints
@@ -0,0 +1,54 @@
+ echo $this->name; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ echo $this->name; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $this->test; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $this->test; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $this->method(); // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $arrowFunction = fn() => $this; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $staticArrowFunction = static fn() => $this; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $this->name; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ echo $this->name; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $this->name; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $this->test; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $this->test; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $this->method(); // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $nestedArrow = fn() => $this; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $staticArrowFunction = static fn() => $this; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $this->test; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $this->test; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $this->method(); // error
+ -----
+HINT:Cannot use "$this" in static context.
diff --git
a/php/php.editor/test/unit/data/testfiles/verification/IncorrectStaticContextHintError/testStaticClosureAndArrowFunctionInTrait.php
b/php/php.editor/test/unit/data/testfiles/verification/IncorrectStaticContextHintError/testStaticClosureAndArrowFunctionInTrait.php
new file mode 100644
index 0000000000..cd4e752d83
--- /dev/null
+++
b/php/php.editor/test/unit/data/testfiles/verification/IncorrectStaticContextHintError/testStaticClosureAndArrowFunctionInTrait.php
@@ -0,0 +1,117 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+trait ThisInStaticContextTrait
+{
+ private int $test = 1;
+
+ public static function staticMethod(): void {
+ $closure = function() {
+ echo $this->test; // error
+ };
+ $staticClosure = static function() {
+ echo $this->test; // error
+ $anon = new class() {
+ private int $test = 1;
+ public function method() {
+ $closure = function() {
+ $this->test; // ok
+ };
+ $staticClosure = static function() {
+ $this->test; // error
+ $closure2 = function() {
+ $this->test; // error
+ };
+ };
+ $staticClosure();
+ return $this; // ok
+ }
+ public static function staticMethod() {
+ $this->method(); // error
+ }
+ };
+ };
+ $staticClosure();
+ $arrowFunction = fn() => $this; // error
+ $staticArrowFunction = static fn() => $this; // error
+ $staticArrowFunction()->test;
+ }
+
+ public function method(): void {
+ $this->test; // ok
+ $closure = function() {
+ $this->test; // ok
+ $nestedStaticClosure = static function() {
+ $this->test; // error
+ };
+ };
+ $staticClosure = static function() {
+ echo $this->test; // error
+ $closuer2 = function() {
+ $this->test; // error
+ };
+ $anon = new class() {
+ private int $test = 1;
+ public function method() {
+ $closure = function() {
+ $this->test; // ok
+ };
+ $staticClosure = static function() {
+ $this->test; // error
+ $closure2 = function() {
+ $this->test; // error
+ };
+ };
+ $staticClosure();
+ return $this; // ok
+ }
+ public static function staticMethod() {
+ $this->method(); // error
+ }
+ };
+ $nestedArrow = fn() => $this; // error
+ };
+ $staticClosure();
+ $arrowFunction = fn() => $this; // ok
+ $staticArrowFunction = static fn() => $this; // error
+ $staticArrowFunction = static fn() => $anon = new class() {
+ private int $test = 1;
+ public function method() {
+ $closure = function () {
+ $this->test; // ok
+ };
+ $staticClosure = static function () {
+ $this->test; // error
+ $closure2 = function () {
+ $this->test; // error
+ };
+ };
+ $staticClosure();
+ return $this; // ok
+ }
+
+ public static function staticMethod() {
+ $this->method(); // error
+ }
+ };
+ $staticArrowFunction()->test;
+ $this->test; // ok
+ }
+}
diff --git
a/php/php.editor/test/unit/data/testfiles/verification/IncorrectStaticContextHintError/testStaticClosureAndArrowFunctionInTrait.php.testStaticClosureAndArrowFunctionInTrait.hints
b/php/php.editor/test/unit/data/testfiles/verification/IncorrectStaticContextHintError/testStaticClosureAndArrowFunctionInTrait.php.testStaticClosureAndArrowFunctionInTrait.hints
new file mode 100644
index 0000000000..49e91bf486
--- /dev/null
+++
b/php/php.editor/test/unit/data/testfiles/verification/IncorrectStaticContextHintError/testStaticClosureAndArrowFunctionInTrait.php.testStaticClosureAndArrowFunctionInTrait.hints
@@ -0,0 +1,54 @@
+ echo $this->test; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ echo $this->test; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $this->test; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $this->test; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $this->method(); // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $arrowFunction = fn() => $this; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $staticArrowFunction = static fn() => $this; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $this->test; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ echo $this->test; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $this->test; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $this->test; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $this->test; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $this->method(); // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $nestedArrow = fn() => $this; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $staticArrowFunction = static fn() => $this; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $this->test; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $this->test; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $this->method(); // error
+ -----
+HINT:Cannot use "$this" in static context.
diff --git
a/php/php.editor/test/unit/data/testfiles/verification/IncorrectStaticContextHintError/testThisVariables.php
b/php/php.editor/test/unit/data/testfiles/verification/IncorrectStaticContextHintError/testThisVariables.php
new file mode 100644
index 0000000000..04a2b4c036
--- /dev/null
+++
b/php/php.editor/test/unit/data/testfiles/verification/IncorrectStaticContextHintError/testThisVariables.php
@@ -0,0 +1,76 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+class ThisInStaticContext
+{
+ private int $test = 1;
+
+ public static function getTest(): int {
+ self::staticMethod($this); // error
+ $anon = new class() {
+ private int $field = 1;
+
+ public function nestedMethod(): void {
+ echo $this->field; // ok
+ }
+
+ public static function nestedStaticMethod(): void {
+ echo $this->field; // error
+ }
+
+ };
+ return $this->test; // error
+ }
+
+
+ public static function staticMethod(ThisInStaticContext $test): void {
+ }
+
+ public function method(): void {
+ $this->test; // ok
+ $anon = new class() {
+ private int $field = 1;
+
+ public static function nestedStaticMethod(): void {
+ echo $this->field; // error
+ }
+
+ public function nestedMethod(): void {
+ echo $this->field; // ok
+ }
+ };
+ }
+}
+echo ThisInStaticContext::getTest();
+
+// anonymous class
+$anon = new class() {
+ private int $test = 1;
+
+ public static function test(): void {
+ $this->test; // error
+ }
+
+ public function method(): void {
+ $this->test(); // ok
+ }
+};
+
+$this->HTML->something(); // ok(ignore)
diff --git
a/php/php.editor/test/unit/data/testfiles/verification/IncorrectStaticContextHintError/testThisVariables.php.testThisVariables.hints
b/php/php.editor/test/unit/data/testfiles/verification/IncorrectStaticContextHintError/testThisVariables.php.testThisVariables.hints
new file mode 100644
index 0000000000..64a42b3d3a
--- /dev/null
+++
b/php/php.editor/test/unit/data/testfiles/verification/IncorrectStaticContextHintError/testThisVariables.php.testThisVariables.hints
@@ -0,0 +1,15 @@
+ self::staticMethod($this); // error
+ -----
+HINT:Cannot use "$this" in static context.
+ echo $this->field; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ return $this->test; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ echo $this->field; // error
+ -----
+HINT:Cannot use "$this" in static context.
+ $this->test; // error
+ -----
+HINT:Cannot use "$this" in static context.
diff --git
a/php/php.editor/test/unit/src/org/netbeans/modules/php/editor/verification/IncorrectStaticContextHintErrorTest.java
b/php/php.editor/test/unit/src/org/netbeans/modules/php/editor/verification/IncorrectStaticContextHintErrorTest.java
new file mode 100644
index 0000000000..6113004314
--- /dev/null
+++
b/php/php.editor/test/unit/src/org/netbeans/modules/php/editor/verification/IncorrectStaticContextHintErrorTest.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.php.editor.verification;
+
+public class IncorrectStaticContextHintErrorTest extends PHPHintsTestBase {
+
+ public IncorrectStaticContextHintErrorTest(String testName) {
+ super(testName);
+ }
+
+ @Override
+ protected String getTestDirectory() {
+ return TEST_DIRECTORY + "IncorrectStaticContextHintError/";
+ }
+
+ public void testThisVariables() throws Exception {
+ checkHints("testThisVariables.php");
+ }
+
+ public void testStaticClosureAndArrowFunctionInClass() throws Exception {
+ checkHints("testStaticClosureAndArrowFunctionInClass.php");
+ }
+
+ public void testStaticClosureAndArrowFunctionInTrait() throws Exception {
+ checkHints("testStaticClosureAndArrowFunctionInTrait.php");
+ }
+
+ public void testStaticClosureAndArrowFunctionInEnum() throws Exception {
+ checkHints("testStaticClosureAndArrowFunctionInEnum.php");
+ }
+
+ private void checkHints(String fileName) throws Exception {
+ checkHints(new IncorrectStaticContextHintError(), fileName);
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists