You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by tm...@apache.org on 2019/08/16 15:33:00 UTC

[netbeans] branch master updated: [NETBEANS-2992] Code completion does not work well inside Closures

This is an automated email from the ASF dual-hosted git repository.

tmysik 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 acdaf52  [NETBEANS-2992] Code completion does not work well inside Closures
     new 2f16e2e  Merge pull request #1434 from junichi11/netbeans-2992
acdaf52 is described below

commit acdaf52d880133c6c9153c6caecb00f22cde47ff
Author: Junichi Yamamoto <ju...@apache.org>
AuthorDate: Fri Aug 16 21:35:45 2019 +0900

    [NETBEANS-2992] Code completion does not work well inside Closures
    
    - Change the current scope when `$this` is used in anonymous functions
---
 .../modules/php/editor/model/ModelUtils.java       | 12 ++++
 .../php/editor/model/impl/ModelVisitor.java        | 14 ++++-
 .../php/editor/model/impl/VariousUtils.java        | 21 ++++++-
 .../testfiles/completion/lib/nb2992/nb2992.php     | 70 ++++++++++++++++++++++
 .../lib/nb2992/nb2992.php.testNb2992_01.completion |  4 ++
 .../lib/nb2992/nb2992.php.testNb2992_02.completion |  4 ++
 .../lib/nb2992/nb2992.php.testNb2992_03.completion |  4 ++
 .../issue247082.php.testForKeywords.completion     |  1 -
 .../completion/PHPCodeCompletionNb2992Test.java    | 58 ++++++++++++++++++
 9 files changed, 185 insertions(+), 3 deletions(-)

diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/model/ModelUtils.java b/php/php.editor/src/org/netbeans/modules/php/editor/model/ModelUtils.java
index 869eced..d79558f 100644
--- a/php/php.editor/src/org/netbeans/modules/php/editor/model/ModelUtils.java
+++ b/php/php.editor/src/org/netbeans/modules/php/editor/model/ModelUtils.java
@@ -631,4 +631,16 @@ public final class ModelUtils {
         }
         return result;
     }
+
+    /**
+     * Check whether the scope is anonymous function scope.
+     *
+     * @param scope the scope
+     * @return {@code true} if the scope is anonymous function scope,
+     * {@code false} otherwise
+     */
+    public static boolean isAnonymousFunction(Scope scope) {
+        return scope instanceof FunctionScope
+                && ((FunctionScope) scope).isAnonymous();
+    }
 }
diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/model/impl/ModelVisitor.java b/php/php.editor/src/org/netbeans/modules/php/editor/model/impl/ModelVisitor.java
index 90d6a7a..c9e38ae 100644
--- a/php/php.editor/src/org/netbeans/modules/php/editor/model/impl/ModelVisitor.java
+++ b/php/php.editor/src/org/netbeans/modules/php/editor/model/impl/ModelVisitor.java
@@ -760,9 +760,21 @@ public final class ModelVisitor extends DefaultTreePathVisitor {
             return;
         }
         Scope scope = modelBuilder.getCurrentScope();
+        ASTNodeInfo<Variable> varInfo = ASTNodeInfo.create(node);
+        // NETBEANS-2992
+        // when $this is used in anonymous function, change the current scope
+        if (ModelUtils.isAnonymousFunction(scope)
+                && "$this".equals(varInfo.getName())) { // NOI18N
+            Scope inScope = scope.getInScope();
+            while (!(inScope instanceof MethodScope) && inScope instanceof FunctionScope) {
+                inScope = inScope.getInScope();
+            }
+            if (inScope instanceof MethodScope) {
+                scope = inScope;
+            }
+        }
         prepareVariable(node, scope);
         if (scope instanceof VariableNameFactory) {
-            ASTNodeInfo<Variable> varInfo = ASTNodeInfo.create(node);
             if (scope instanceof MethodScope && "$this".equals(varInfo.getName())) { //NOI18N
                 scope = scope.getInScope();
             }
diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/model/impl/VariousUtils.java b/php/php.editor/src/org/netbeans/modules/php/editor/model/impl/VariousUtils.java
index 432dce8..a6dd8c9 100644
--- a/php/php.editor/src/org/netbeans/modules/php/editor/model/impl/VariousUtils.java
+++ b/php/php.editor/src/org/netbeans/modules/php/editor/model/impl/VariousUtils.java
@@ -593,7 +593,7 @@ public final class VariousUtils {
                             || (operation.startsWith(VariousUtils.ARRAY_TYPE_PREFIX))) {
                         Set<TypeScope> newRecentTypes = new HashSet<>();
                         String varName = frag;
-                        VariableName var = ModelUtils.getFirst(varScope.getDeclaredVariables(), varName);
+                        VariableName var = getVariableName(varScope, varName);
                         if (var != null) {
                             if (i + 2 < len && VariousUtils.FIELD_TYPE_PREFIX.startsWith(fragments[i + 1])) {
                                 fldVarStack.push(var);
@@ -684,6 +684,25 @@ public final class VariousUtils {
         return recentTypes;
     }
 
+    @CheckForNull
+    private static VariableName getVariableName(final VariableScope varScope, String varName) {
+        VariableName var = ModelUtils.getFirst(varScope.getDeclaredVariables(), varName);
+        // NETBEANS-2992
+        // when $this is used in anonymous function, check the parent scope
+        if (var == null
+                && ModelUtils.isAnonymousFunction(varScope)
+                && varName.equals("$this")) { // NOI18N
+            Scope inScope = varScope.getInScope();
+            while (ModelUtils.isAnonymousFunction(inScope)) {
+                inScope = inScope.getInScope();
+            }
+            if (inScope instanceof VariableScope) {
+                var = ModelUtils.getFirst(((VariableScope) inScope).getDeclaredVariables(), varName);
+            }
+        }
+        return var;
+    }
+
     private static Collection<TypeScope> filterSuperTypes(final Collection<? extends TypeScope> typeScopes) {
         final Collection<TypeScope> result = new HashSet<>();
         if (typeScopes.size() > 1) {
diff --git a/php/php.editor/test/unit/data/testfiles/completion/lib/nb2992/nb2992.php b/php/php.editor/test/unit/data/testfiles/completion/lib/nb2992/nb2992.php
new file mode 100644
index 0000000..e6ff4c0
--- /dev/null
+++ b/php/php.editor/test/unit/data/testfiles/completion/lib/nb2992/nb2992.php
@@ -0,0 +1,70 @@
+<?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 Validator
+{
+    public function validate(\Closure $closure): void
+    {
+        $closure();
+    }
+}
+
+class Test
+{
+    public function testMethod(): void
+    {
+        echo "test" . PHP_EOL;
+    }
+}
+
+class Example
+{
+    /**
+     * @var Validator
+     */
+    private $validator;
+    /**
+     * @var Test
+     */
+    private $test;
+    public function __construct()
+    {
+        $this->validator = new Validator;
+        $this->test = new Test;
+    }
+    public function main(): void
+    {
+        $this->validator->validate(
+            function () {
+                $this->test-> // test1
+            },
+        );
+        $func = function () {
+            $this->test->testMethod(); // test2
+        };
+        $func();
+        // PHP 7.4 Arrow Function
+        $func2 = fn() => $this->test->testMethod(); //test3
+        $func2();
+    }
+
+}
+
+$example = new Example();
+$example->main();
diff --git a/php/php.editor/test/unit/data/testfiles/completion/lib/nb2992/nb2992.php.testNb2992_01.completion b/php/php.editor/test/unit/data/testfiles/completion/lib/nb2992/nb2992.php.testNb2992_01.completion
new file mode 100644
index 0000000..b7524c9
--- /dev/null
+++ b/php/php.editor/test/unit/data/testfiles/completion/lib/nb2992/nb2992.php.testNb2992_01.completion
@@ -0,0 +1,4 @@
+Code completion result for source line:
+$this->test->| // test1
+(QueryType=COMPLETION, prefixSearch=true, caseSensitive=true)
+METHOD     testMethod()                    [PUBLIC]   Test
diff --git a/php/php.editor/test/unit/data/testfiles/completion/lib/nb2992/nb2992.php.testNb2992_02.completion b/php/php.editor/test/unit/data/testfiles/completion/lib/nb2992/nb2992.php.testNb2992_02.completion
new file mode 100644
index 0000000..a244787
--- /dev/null
+++ b/php/php.editor/test/unit/data/testfiles/completion/lib/nb2992/nb2992.php.testNb2992_02.completion
@@ -0,0 +1,4 @@
+Code completion result for source line:
+$this->test->|testMethod(); // test2
+(QueryType=COMPLETION, prefixSearch=true, caseSensitive=true)
+METHOD     testMethod()                    [PUBLIC]   Test
diff --git a/php/php.editor/test/unit/data/testfiles/completion/lib/nb2992/nb2992.php.testNb2992_03.completion b/php/php.editor/test/unit/data/testfiles/completion/lib/nb2992/nb2992.php.testNb2992_03.completion
new file mode 100644
index 0000000..3dc5ebf
--- /dev/null
+++ b/php/php.editor/test/unit/data/testfiles/completion/lib/nb2992/nb2992.php.testNb2992_03.completion
@@ -0,0 +1,4 @@
+Code completion result for source line:
+$func2 = fn() => $this->test->test|Method(); //test3
+(QueryType=COMPLETION, prefixSearch=true, caseSensitive=true)
+METHOD     testMethod()                    [PUBLIC]   Test
diff --git a/php/php.editor/test/unit/data/testfiles/completion/lib/tests247082/issue247082.php.testForKeywords.completion b/php/php.editor/test/unit/data/testfiles/completion/lib/tests247082/issue247082.php.testForKeywords.completion
index 4114956..408116d 100644
--- a/php/php.editor/test/unit/data/testfiles/completion/lib/tests247082/issue247082.php.testForKeywords.completion
+++ b/php/php.editor/test/unit/data/testfiles/completion/lib/tests247082/issue247082.php.testForKeywords.completion
@@ -3,7 +3,6 @@ echo |self::MY_CONST . PHP_EOL;
 (QueryType=COMPLETION, prefixSearch=true, caseSensitive=true)
 CLASS      A                               [PUBLIC]   issue247082.php
 CLASS      ParentA                         [PUBLIC]   issue247082.php
-VARIABLE   ? $this                         [PUBLIC]   issue247082.php
 VARIABLE   ? $val                          [PUBLIC]   issue247082.php
 KEYWORD    A $this->                                  null
 KEYWORD    parent::                                   null
diff --git a/php/php.editor/test/unit/src/org/netbeans/modules/php/editor/completion/PHPCodeCompletionNb2992Test.java b/php/php.editor/test/unit/src/org/netbeans/modules/php/editor/completion/PHPCodeCompletionNb2992Test.java
new file mode 100644
index 0000000..478d6e2
--- /dev/null
+++ b/php/php.editor/test/unit/src/org/netbeans/modules/php/editor/completion/PHPCodeCompletionNb2992Test.java
@@ -0,0 +1,58 @@
+/*
+ * 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.completion;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.Map;
+import org.netbeans.api.java.classpath.ClassPath;
+import org.netbeans.modules.php.project.api.PhpSourcePath;
+import org.netbeans.spi.java.classpath.support.ClassPathSupport;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+
+
+public class PHPCodeCompletionNb2992Test extends PHPCodeCompletionTestBase {
+
+    public PHPCodeCompletionNb2992Test(String testName) {
+        super(testName);
+    }
+
+    public void testNb2992_01() throws Exception {
+        checkCompletion("testfiles/completion/lib/nb2992/nb2992.php", "$this->test->^ // test1", false);
+    }
+
+    public void testNb2992_02() throws Exception {
+        checkCompletion("testfiles/completion/lib/nb2992/nb2992.php", "$this->test->^testMethod(); // test2", false);
+    }
+
+    public void testNb2992_03() throws Exception {
+        checkCompletion("testfiles/completion/lib/nb2992/nb2992.php", "$func2 = fn() => $this->test->test^Method(); //test3", false);
+    }
+
+    @Override
+    protected Map<String, ClassPath> createClassPathsForTest() {
+        return Collections.singletonMap(
+            PhpSourcePath.SOURCE_CP,
+            ClassPathSupport.createClassPath(new FileObject[] {
+                FileUtil.toFileObject(new File(getDataDir(), "/testfiles/completion/lib/nb2992"))
+            })
+        );
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org

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