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 <junich...@apache.org>
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: commits-unsubscr...@netbeans.apache.org
For additional commands, e-mail: commits-h...@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


Reply via email to