Repository: elm-tooling/elm-language-server
Pull request #547 (2021-03-04 18:41:59)

Add basic function rename test
Closes https://github.com/elm-tooling/elm-language-server/issues/543

Only the test for now, need to actually fix it :)


Patch:
diff --git a/src/compiler/references.ts b/src/compiler/references.ts
index ea3bea7..49f91da 100644
--- a/src/compiler/references.ts
+++ b/src/compiler/references.ts
@@ -45,9 +45,10 @@ export class References {
               const functionNameNode =
                 TreeUtils.getFunctionNameNodeFromDefinition(
                   definitionNode.node,
-                ) ?? definitionNode.node.type === "lower_pattern"
+                ) ??
+                (definitionNode.node.type === "lower_pattern"
                   ? definitionNode.node
-                  : undefined;
+                  : undefined);
               if (functionNameNode) {
                 const functionName = functionNameNode.text;
                 if (refSourceTree.writeable) {
@@ -106,64 +107,63 @@ export class References {
                         continue;
                       }
 
-                      const otherTreeContainer = forest.getByUri(uri);
+                      const otherSourceFile = forest.getByUri(uri);
 
-                      if (!otherTreeContainer) {
+                      if (!otherSourceFile) {
                         continue;
                       }
 
                       const importedModuleAlias =
                         TreeUtils.findImportAliasOfModule(
                           moduleName,
-                          otherTreeContainer.tree,
+                          otherSourceFile.tree,
                         ) ?? moduleName;
 
                       const allImports = imports[uri];
 
                       // Find the function in the other module's imports
-                      const found =
-                        allImports.getVar(functionName, moduleName)[0] ??
-                        allImports.getVar(
+                      const found = [
+                        ...allImports.getVar(functionName, moduleName),
+                        ...allImports.getVar(
                           `${importedModuleAlias}.${functionName}`,
                           moduleName,
-                        )[0];
-
-                      if (found) {
-                        if (
-                          otherTreeContainer &&
-                          otherTreeContainer.writeable
-                        ) {
-                          const importClause = otherTreeContainer.symbolLinks
-                            ?.get(otherTreeContainer.tree.rootNode)
-                            ?.get(importedModuleAlias);
-
-                          // Add node from exposing list
-                          if (importClause?.type === "Import") {
-                            const exposedNode = TreeUtils.findExposedFunctionNode(
-                              importClause.node,
-                              functionName,
-                            );
-
-                            if (exposedNode) {
-                              references.push({
-                                node: exposedNode,
-                                uri,
-                              });
-                            }
+                        ),
+                      ];
+
+                      if (found.length > 0 && otherSourceFile.writeable) {
+                        const importClause = otherSourceFile.symbolLinks
+                          ?.get(otherSourceFile.tree.rootNode)
+                          ?.get(importedModuleAlias);
+
+                        // Add node from exposing list
+                        if (importClause?.type === "Import") {
+                          const exposedNode = TreeUtils.findExposedFunctionNode(
+                            importClause.node,
+                            functionName,
+                          );
+
+                          if (exposedNode) {
+                            references.push({
+                              node: exposedNode,
+                              uri,
+                            });
                           }
+                        }
 
-                          // Find all function calls in the other tree
-                          const functions = this.findFunctionCalls(
-                            otherTreeContainer.tree.rootNode,
-                            found?.name,
+                        // Find all function calls in the other tree
+                        const functions = found.flatMap(
+                          (imp) =>
+                            this.findFunctionCalls(
+                              otherSourceFile.tree.rootNode,
+                              imp.name,
+                            ) ?? [],
+                        );
+                        if (functions.length > 0) {
+                          references.push(
+                            ...functions.map((node) => {
+                              return { node, uri };
+                            }),
                           );
-                          if (functions) {
-                            references.push(
-                              ...functions.map((node) => {
-                                return { node, uri };
-                              }),
-                            );
-                          }
                         }
                       }
                     }
@@ -230,64 +230,63 @@ export class References {
                         continue;
                       }
 
-                      const otherTreeContainer = forest.getByUri(uri);
+                      const otherSourceFile = forest.getByUri(uri);
 
-                      if (!otherTreeContainer) {
+                      if (!otherSourceFile) {
                         continue;
                       }
 
                       const importedModuleAlias =
                         TreeUtils.findImportAliasOfModule(
                           moduleName,
-                          otherTreeContainer.tree,
+                          otherSourceFile.tree,
                         ) ?? moduleName;
 
                       const allImports = imports[uri];
 
                       // Find the function in the other module's imports
-                      const found =
-                        allImports.getVar(portName, moduleName)[0] ??
-                        allImports.getVar(
+                      const found = [
+                        ...allImports.getVar(portName, moduleName),
+                        ...allImports.getVar(
                           `${importedModuleAlias}.${portName}`,
                           moduleName,
-                        )[0];
-
-                      if (found) {
-                        if (
-                          otherTreeContainer &&
-                          otherTreeContainer.writeable
-                        ) {
-                          const importClause = otherTreeContainer.symbolLinks
-                            ?.get(otherTreeContainer.tree.rootNode)
-                            ?.get(importedModuleAlias);
-
-                          // Add node from exposing list
-                          if (importClause?.type === "Import") {
-                            const exposedNode = TreeUtils.findExposedFunctionNode(
-                              importClause.node,
-                              portName,
-                            );
-
-                            if (exposedNode) {
-                              references.push({
-                                node: exposedNode,
-                                uri,
-                              });
-                            }
+                        ),
+                      ];
+
+                      if (found.length > 0 && otherSourceFile.writeable) {
+                        const importClause = otherSourceFile.symbolLinks
+                          ?.get(otherSourceFile.tree.rootNode)
+                          ?.get(importedModuleAlias);
+
+                        // Add node from exposing list
+                        if (importClause?.type === "Import") {
+                          const exposedNode = TreeUtils.findExposedFunctionNode(
+                            importClause.node,
+                            portName,
+                          );
+
+                          if (exposedNode) {
+                            references.push({
+                              node: exposedNode,
+                              uri,
+                            });
                           }
+                        }
 
-                          // Find all function calls in the other tree
-                          const functions = this.findFunctionCalls(
-                            otherTreeContainer.tree.rootNode,
-                            found?.name,
+                        // Find all function calls in the other tree
+                        const functions = found.flatMap(
+                          (imp) =>
+                            this.findFunctionCalls(
+                              otherSourceFile.tree.rootNode,
+                              imp.name,
+                            ) ?? [],
+                        );
+                        if (functions.length > 0) {
+                          references.push(
+                            ...functions.map((node) => {
+                              return { node, uri };
+                            }),
                           );
-                          if (functions) {
-                            references.push(
-                              ...functions.map((node) => {
-                                return { node, uri };
-                              }),
-                            );
-                          }
                         }
                       }
                     }
@@ -354,66 +353,62 @@ export class References {
                         continue;
                       }
 
-                      const otherTreeContainer = forest.getByUri(uri);
+                      const otherSourceFile = forest.getByUri(uri);
 
-                      if (!otherTreeContainer) {
+                      if (!otherSourceFile) {
                         continue;
                       }
 
                       const importedModuleAlias =
                         TreeUtils.findImportAliasOfModule(
                           moduleName,
-                          otherTreeContainer.tree,
+                          otherSourceFile.tree,
                         ) ?? moduleName;
 
                       const allImports = imports[uri];
 
                       // Find the type or type alias in the other module's imports
-                      const found =
-                        allImports.getType(
-                          typeOrTypeAliasName,
-                          moduleName,
-                        )[0] ??
-                        allImports.getType(
+                      const found = [
+                        ...allImports.getType(typeOrTypeAliasName, moduleName),
+                        ...allImports.getType(
                           `${importedModuleAlias}.${typeOrTypeAliasName}`,
                           moduleName,
-                        )[0];
-
-                      if (found) {
-                        if (
-                          otherTreeContainer &&
-                          otherTreeContainer.writeable
-                        ) {
-                          const importClause = otherTreeContainer.symbolLinks
-                            ?.get(otherTreeContainer.tree.rootNode)
-                            ?.get(importedModuleAlias);
-
-                          if (importClause?.type === "Import") {
-                            const exposedNode = TreeUtils.findExposedTypeOrTypeAliasNode(
-                              importClause.node,
-                              typeOrTypeAliasNameNode.text,
-                            );
-
-                            if (exposedNode) {
-                              references.push({
-                                node: exposedNode,
-                                uri,
-                              });
-                            }
-                          }
+                        ),
+                      ];
+
+                      if (found.length > 0 && otherSourceFile.writeable) {
+                        const importClause = otherSourceFile.symbolLinks
+                          ?.get(otherSourceFile.tree.rootNode)
+                          ?.get(importedModuleAlias);
 
-                          const typeOrTypeAliasCalls = TreeUtils.findTypeOrTypeAliasCalls(
-                            otherTreeContainer.tree,
-                            found.name,
+                        if (importClause?.type === "Import") {
+                          const exposedNode = TreeUtils.findExposedTypeOrTypeAliasNode(
+                            importClause.node,
+                            typeOrTypeAliasNameNode.text,
                           );
-                          if (typeOrTypeAliasCalls) {
-                            references.push(
-                              ...typeOrTypeAliasCalls.map((node) => {
-                                return { node, uri };
-                              }),
-                            );
+
+                          if (exposedNode) {
+                            references.push({
+                              node: exposedNode,
+                              uri,
+                            });
                           }
                         }
+
+                        const typeOrTypeAliasCalls = found.flatMap(
+                          (imp) =>
+                            TreeUtils.findTypeOrTypeAliasCalls(
+                              otherSourceFile.tree,
+                              imp.name,
+                            ) ?? [],
+                        );
+                        if (typeOrTypeAliasCalls.length > 0) {
+                          references.push(
+                            ...typeOrTypeAliasCalls.map((node) => {
+                              return { node, uri };
+                            }),
+                          );
+                        }
                       }
                     }
                   }


Test patch:
diff --git a/test/referencesProviderTests/functionReferences.test.ts b/test/referencesProviderTests/functionReferences.test.ts
index afcb5ec..4b7ea66 100644
--- a/test/referencesProviderTests/functionReferences.test.ts
+++ b/test/referencesProviderTests/functionReferences.test.ts
@@ -32,6 +32,35 @@ func = foo
     await testBase.testReferences(source);
   });
 
+  it(`function with args references in other files`, async () => {
+    const source = `
+--@ Module.elm
+module Module exposing (foo)
+                       --X
+foo a b = 42
+--^
+
+--@ Bar.elm
+module Bar exposing (..)
+
+import Module exposing (foo)
+                       --X
+
+bar = foo 1 2
+     --X
+
+--@ FooExtra.elm
+module FooExtra exposing (..)
+
+import Module exposing (foo)
+                       --X
+
+func = foo 1 2
+      --X
+`;
+    await testBase.testReferences(source);
+  });
+
   it(`function references used with module prefix in other files`, async () => {
     const source = `
 --@ Module.elm
@@ -59,6 +88,33 @@ func = Module.foo
     await testBase.testReferences(source);
   });
 
+  it(`function with arg references used with module prefix in other files`, async () => {
+    const source = `
+--@ Module.elm
+module Module exposing (foo)
+                       --X
+foo a b = 42
+--^
+
+--@ Bar.elm
+module Bar exposing (..)
+
+import Module
+
+bar = Module.foo 1 2
+           --X
+
+--@ FooExtra.elm
+module FooExtra exposing (..)
+
+import Module
+
+func = Module.foo 1 2
+             --X
+`;
+    await testBase.testReferences(source);
+  });
+
   it(`function references used with module alias prefix in other files`, async () => {
     const source = `
 --@ Module.elm
@@ -100,6 +156,20 @@ bar = foo
     await testBase.testReferences(source);
   });
 
+  it(`local calls to a function with args`, async () => {
+    const source = `
+--@ Module.elm
+module Module exposing (foo)
+                        --X
+foo a = 42
+--^
+
+bar = foo 1
+     --X
+`;
+    await testBase.testReferences(source);
+  });
+
   it(`function annotation gets a reference`, async () => {
     const source = `
 --@ Module.elm
@@ -155,6 +225,37 @@ foo =
 test = 
     foo.field
    --X
+`;
+    await testBase.testReferences(source);
+  });
+
+  it(`function references used both with and without module prefix in other files`, async () => {
+    const source = `
+--@ Module.elm
+module Module exposing (foo)
+                       --X
+foo = 42
+--^
+
+--@ Bar.elm
+module Bar exposing (..)
+
+import Module
+
+bar = Module.foo
+           --X
+
+--@ FooExtra.elm
+module FooExtra exposing (..)
+
+import Module exposing (foo)
+                       --X
+
+func = Module.foo
+             --X
+
+func2 = foo
+       --X
 `;
     await testBase.testReferences(source);
   });
diff --git a/test/renameProvider.test.ts b/test/renameProvider.test.ts
index 7789631..d637cca 100644
--- a/test/renameProvider.test.ts
+++ b/test/renameProvider.test.ts
@@ -117,6 +117,50 @@ describe("renameProvider", () => {
     });
   }
 
+  it("renaming a function", async () => {
+    const source = `
+--@ Test.elm
+module Test exposing (func)
+
+func a =
+--^
+    15
+
+--@ Module/App.elm
+module Module.App exposing (foo)
+
+import Test exposing (func)
+
+foo : a -> Int
+foo =
+    func
+`;
+
+    const expectedResult = `
+--@ Test.elm
+module Test exposing (bar)
+
+bar a =
+    15
+
+--@ Module/App.elm
+module Module.App exposing (foo)
+
+import Test exposing (bar)
+
+foo : a -> Int
+foo =
+    bar
+`;
+
+    const renameRange = Range.create(
+      Position.create(2, 0),
+      Position.create(2, 4),
+    );
+    await testPrepareRename(source, renameRange);
+    await testRename(source, "bar", expectedResult);
+  });
+
   it("renaming a qualified value function part", async () => {
     const source = `
 --@ Test.elm