Repository: elm-tooling/elm-language-server
Pull request #326 (2020-10-02 16:00:25)

Fix more definition and inference bugs
Fix some bugs due to type vs union constructor definitions, if else expr parsing, and fix/add/enable more tests. 


Patch:
diff --git a/src/util/treeUtils.ts b/src/util/treeUtils.ts
index 3c889d2..92d9ffd 100644
--- a/src/util/treeUtils.ts
+++ b/src/util/treeUtils.ts
@@ -690,21 +690,27 @@ export class TreeUtils {
     tree: Tree,
     nodeAtPosition: SyntaxNode,
   ): { node: SyntaxNode; nodeType: NodeType } | undefined {
-    let definitionNode = this.findTypeDeclaration(tree, nodeAtPosition.text);
-    if (
-      definitionNode &&
-      nodeAtPosition.parent?.type !== "value_expr" &&
-      nodeAtPosition.parent?.type !== "union_pattern"
-    ) {
-      return { node: definitionNode, nodeType: "Type" };
-    }
-    definitionNode = this.findTypeAliasDeclaration(tree, nodeAtPosition.text);
+    let definitionNode = this.findTypeAliasDeclaration(
+      tree,
+      nodeAtPosition.text,
+    );
     if (definitionNode) {
       return { node: definitionNode, nodeType: "TypeAlias" };
     }
-    definitionNode = this.findUnionConstructor(tree, nodeAtPosition.text);
-    if (definitionNode) {
-      return { node: definitionNode, nodeType: "UnionConstructor" };
+    if (
+      TreeUtils.findParentOfType("type_ref", nodeAtPosition) ||
+      nodeAtPosition.parent?.type === "type_declaration" ||
+      nodeAtPosition.parent?.type === "exposed_type"
+    ) {
+      definitionNode = this.findTypeDeclaration(tree, nodeAtPosition.text);
+      if (definitionNode) {
+        return { node: definitionNode, nodeType: "Type" };
+      }
+    } else {
+      definitionNode = this.findUnionConstructor(tree, nodeAtPosition.text);
+      if (definitionNode) {
+        return { node: definitionNode, nodeType: "UnionConstructor" };
+      }
     }
   }
 
@@ -937,18 +943,37 @@ export class TreeUtils {
           }
         }
 
-        definitionFromOtherFile = this.findImportFromImportList(
-          uri,
-          upperCaseQid.text,
-          "Type",
-          imports,
-        );
-        if (definitionFromOtherFile) {
-          return {
-            node: definitionFromOtherFile.node,
-            nodeType: "Type",
-            uri: definitionFromOtherFile.fromUri,
-          };
+        if (
+          TreeUtils.findParentOfType("type_ref", upperCaseQid) ||
+          upperCaseQid.parent?.type === "exposed_type"
+        ) {
+          definitionFromOtherFile = this.findImportFromImportList(
+            uri,
+            upperCaseQid.text,
+            "Type",
+            imports,
+          );
+          if (definitionFromOtherFile) {
+            return {
+              node: definitionFromOtherFile.node,
+              nodeType: "Type",
+              uri: definitionFromOtherFile.fromUri,
+            };
+          }
+        } else {
+          definitionFromOtherFile = this.findImportFromImportList(
+            uri,
+            upperCaseQid.text,
+            "UnionConstructor",
+            imports,
+          );
+          if (definitionFromOtherFile) {
+            return {
+              node: definitionFromOtherFile.node,
+              nodeType: "UnionConstructor",
+              uri: definitionFromOtherFile.fromUri,
+            };
+          }
         }
 
         definitionFromOtherFile = this.findImportFromImportList(
@@ -965,20 +990,6 @@ export class TreeUtils {
           };
         }
 
-        definitionFromOtherFile = this.findImportFromImportList(
-          uri,
-          upperCaseQid.text,
-          "UnionConstructor",
-          imports,
-        );
-        if (definitionFromOtherFile) {
-          return {
-            node: definitionFromOtherFile.node,
-            nodeType: "UnionConstructor",
-            uri: definitionFromOtherFile.fromUri,
-          };
-        }
-
         definitionFromOtherFile = this.findImportFromImportList(
           uri,
           TreeUtils.findImportNameNode(tree, upperCaseQid.text)?.text ??
@@ -2139,17 +2150,12 @@ export class TreeUtils {
     imports: IImports,
   ): string | undefined {
     if (imports.imports) {
-      const moduleImport = TreeUtils.findImportClauseByName(tree, module);
-
-      if (!moduleImport) {
-        return;
-      }
-
       if (
         imports.imports[uri]
           .filter(
             (imp) =>
               imp.fromModuleName === module &&
+              imp.explicitlyExposed &&
               (imp.type === "Type" ||
                 imp.type === "TypeAlias" ||
                 imp.type === "UnionConstructor"),
@@ -2159,6 +2165,12 @@ export class TreeUtils {
         return "";
       }
 
+      const moduleImport = TreeUtils.findImportClauseByName(tree, module);
+
+      if (!moduleImport) {
+        return;
+      }
+
       const asClause = TreeUtils.findFirstNamedChildOfType(
         "as_clause",
         moduleImport,
diff --git a/src/util/types/expressionTree.ts b/src/util/types/expressionTree.ts
index cbadb83..d105fd9 100644
--- a/src/util/types/expressionTree.ts
+++ b/src/util/types/expressionTree.ts
@@ -11,7 +11,7 @@ export type Expression =
   | EBinOpExpr
   | ECaseOfBranch
   | ECaseOfExpr
-  | ECharConstant
+  | ECharConstantExpr
   | EConsPattern
   | EField
   | EFieldAccessExpr
@@ -234,8 +234,8 @@ export interface EPortAnnotation extends SyntaxNode {
   name: string;
   typeExpression: ETypeExpression;
 }
-export interface ECharConstant extends SyntaxNode {
-  nodeType: "CharConstant";
+export interface ECharConstantExpr extends SyntaxNode {
+  nodeType: "CharConstantExpr";
 }
 export interface EGlslCodeExpr extends SyntaxNode {
   nodeType: "GlslCodeExpr";
@@ -425,7 +425,9 @@ export function mapSyntaxNodeToExpression(
     case "if_else_expr":
       return Object.assign(node, {
         nodeType: "IfElseExpr",
-        exprList: node.namedChildren.map((n) => mapSyntaxNodeToExpression(n)),
+        exprList: node.namedChildren
+          .map((n) => mapSyntaxNodeToExpression(n))
+          .filter(Utils.notUndefined),
       } as EIfElseExpr);
 
     case "let_in_expr":
@@ -628,10 +630,10 @@ export function mapSyntaxNodeToExpression(
           TreeUtils.findFirstNamedChildOfType("type_expression", node),
         ),
       } as EPortAnnotation);
-    case "char_constant":
+    case "char_constant_expr":
       return Object.assign(node, {
-        nodeType: "CharConstant",
-      } as ECharConstant);
+        nodeType: "CharConstantExpr",
+      } as ECharConstantExpr);
     case "glsl_code_expr":
       return Object.assign(node, {
         nodeType: "GlslCodeExpr",
diff --git a/src/util/types/typeInference.ts b/src/util/types/typeInference.ts
index 6363bbb..1934097 100644
--- a/src/util/types/typeInference.ts
+++ b/src/util/types/typeInference.ts
@@ -506,6 +506,14 @@ function fieldAccessOnNonRecordError(node: SyntaxNode, type: Type): Diagnostic {
   };
 }
 
+function missingFunctionError(node: SyntaxNode): Diagnostic {
+  return {
+    node,
+    endNode: node,
+    message: `No function definition found for \`${node.text}\``,
+  };
+}
+
 interface RecordDiff {
   extra: Map<string, Type>;
   missing: Map<string, Type>;
@@ -738,7 +746,7 @@ export class InferenceScope {
       case "CaseOfExpr":
         type = this.inferCase(e);
         break;
-      case "CharConstant":
+      case "CharConstantExpr":
         type = TChar;
         break;
       case "FieldAccessExpr":
@@ -1142,7 +1150,15 @@ export class InferenceScope {
     }
 
     if (targetType.nodeType === "Unknown") {
-      return TUnknown;
+      this.diagnostics.push(missingFunctionError(e.target));
+      const type = TFunction(argTypes, TVar("a"));
+
+      if (this.isAssignable(e.target, targetType, type)) {
+        this.expressionTypes.set(e.target, type);
+        return type.return;
+      } else {
+        return TUnknown;
+      }
     }
 
     if (targetType.nodeType !== "Function") {
@@ -1736,7 +1752,7 @@ export class InferenceScope {
         break;
       case "StringConstant":
       case "NumberConstant":
-      case "CharConstant":
+      case "CharConstantExpr":
         if (isParameter) {
           this.diagnostics.push(partialPatternError(pattern));
         }


Test patch:
diff --git a/test/definitionProviderTests/aliasedImportDefinition.test.ts b/test/definitionProviderTests/aliasedImportDefinition.test.ts
index aa63c7c..43c514f 100644
--- a/test/definitionProviderTests/aliasedImportDefinition.test.ts
+++ b/test/definitionProviderTests/aliasedImportDefinition.test.ts
@@ -12,6 +12,7 @@ main = F.bar
 --@ Foo.elm
 module Foo exposing (bar)
 bar = 42
+--X
 `;
     await testBase.testDefinition(source);
   });
@@ -25,6 +26,7 @@ type alias Model = A.Page
 --@ App.elm
 module App exposing (Page)
 type Page = Home
+--X
 `;
     await testBase.testDefinition(source);
   });
@@ -38,6 +40,7 @@ defaultPage = A.Home
 --@ App.elm
 module App exposing (Page(Home))
 type Page = Home
+           --X
 `;
     await testBase.testDefinition(source);
   });
@@ -51,6 +54,7 @@ type Entity = PersonEntity A.Person
 --@ App.elm
 module App exposing (Person)
 type alias Person = { name : String, age: Int }
+--X
 `;
     await testBase.testDefinition(source);
   });
@@ -64,6 +68,7 @@ defaultPerson = A.Person "George" 42
 --@ App.elm
 module App exposing (Person)
 type alias Person = { name : String, age: Int }
+--X
 `;
     await testBase.testDefinition(source);
   });
@@ -89,6 +94,7 @@ import Foo as F
        --^Foo.elm
 --@ Foo.elm
 module Foo exposing (bar)
+--X
 bar = 42
 `;
     await testBase.testDefinition(source);
diff --git a/test/definitionProviderTests/definitionProviderTestBase.ts b/test/definitionProviderTests/definitionProviderTestBase.ts
index aa58e67..32eb854 100644
--- a/test/definitionProviderTests/definitionProviderTestBase.ts
+++ b/test/definitionProviderTests/definitionProviderTestBase.ts
@@ -28,8 +28,8 @@ export class DefinitionProviderTestBase {
     await this.treeParser.init();
 
     const determinedTestType = getInvokeAndTargetPositionFromSource(source);
-    const targetUri = URI.file(
-      baseUri + determinedTestType.fileWithTarget,
+    const invokeUri = URI.file(
+      baseUri + determinedTestType.invokeFile,
     ).toString();
 
     switch (determinedTestType.kind) {
@@ -38,7 +38,7 @@ export class DefinitionProviderTestBase {
           const definition = this.definitionProvider.handleDefinition(
             {
               textDocument: {
-                uri: targetUri,
+                uri: invokeUri,
               },
               position: determinedTestType.invokePosition,
             },
@@ -54,7 +54,7 @@ export class DefinitionProviderTestBase {
           const definition = this.definitionProvider.handleDefinition(
             {
               textDocument: {
-                uri: targetUri,
+                uri: invokeUri,
               },
               position: determinedTestType.invokePosition,
             },
@@ -67,6 +67,10 @@ export class DefinitionProviderTestBase {
           );
 
           if (determinedTestType.targetPosition) {
+            const targetUri = URI.file(
+              baseUri + determinedTestType.targetFile,
+            ).toString();
+
             const rootNode = this.treeParser
               .getWorkspace(determinedTestType.sources)
               .getForest()
@@ -97,7 +101,7 @@ export class DefinitionProviderTestBase {
           const definition = this.definitionProvider.handleDefinition(
             {
               textDocument: {
-                uri: targetUri,
+                uri: invokeUri,
               },
               position: determinedTestType.invokePosition,
             },
@@ -107,7 +111,7 @@ export class DefinitionProviderTestBase {
           const rootNode = this.treeParser
             .getWorkspace(determinedTestType.sources)
             .getForest()
-            .treeIndex.find((a) => a.uri === targetUri)!.tree.rootNode;
+            .treeIndex.find((a) => a.uri === invokeUri)!.tree.rootNode;
           const nodeAtPosition = TreeUtils.getNamedDescendantForPosition(
             rootNode,
             determinedTestType.targetPosition,
@@ -115,7 +119,7 @@ export class DefinitionProviderTestBase {
 
           expect(definition).toEqual(
             expect.objectContaining({
-              uri: targetUri,
+              uri: invokeUri,
               range: {
                 start: {
                   line: determinedTestType.targetPosition.line,
diff --git a/test/definitionProviderTests/importDefinition.test.ts b/test/definitionProviderTests/importDefinition.test.ts
index cec668a..a727188 100644
--- a/test/definitionProviderTests/importDefinition.test.ts
+++ b/test/definitionProviderTests/importDefinition.test.ts
@@ -11,6 +11,7 @@ import Foo exposing (bar)
 --@ Foo.elm
 module Foo exposing (bar)
 bar = 42
+--X
 `;
     await testBase.testDefinition(source);
   });
@@ -24,6 +25,7 @@ main = bar
 --@ Foo.elm
 module Foo exposing (bar)
 bar = 42
+--X
 `;
     await testBase.testDefinition(source);
   });
@@ -62,6 +64,7 @@ import Foo.Bar exposing (bar)
 --@ Foo/Bar.elm
 module Foo.Bar exposing (bar)
 bar = 42
+--X
 `;
     await testBase.testDefinition(source);
   });
@@ -75,6 +78,7 @@ f = bar
 --@ Foo.elm
 module Foo exposing (bar)
 bar = 42
+--X
 `;
     await testBase.testDefinition(source);
   });
@@ -88,6 +92,7 @@ f = bar
 --@ Foo.elm
 module Foo exposing (..)
 bar = 42
+--X
 `;
     await testBase.testDefinition(source);
   });
@@ -113,6 +118,7 @@ import App exposing (Page)
 --@ App.elm
 module App exposing (Page)
 type Page = Home
+--X
 `;
     await testBase.testDefinition(source);
   });
@@ -126,6 +132,7 @@ type alias Model = Page
 --@ App.elm
 module App exposing (Page)
 type Page = Home
+--X
 `;
     await testBase.testDefinition(source);
   });
@@ -164,6 +171,7 @@ import App exposing (Page(Home))
 --@ App.elm
 module App exposing (Page(Home))
 type Page = Home
+           --X
 `;
     await testBase.testDefinition(source);
   });
@@ -177,6 +185,7 @@ defaultPage = Home
 --@ App.elm
 module App exposing (Page(Home))
 type Page = Home
+           --X
 `;
     await testBase.testDefinition(source);
   });
@@ -190,6 +199,7 @@ defaultPage = Home
 --@ App.elm
 module App exposing (Page(Home))
 type Page = Home
+           --X
 `;
     await testBase.testDefinition(source);
   });
@@ -203,6 +213,7 @@ defaultPage = Home
 --@ App.elm
 module App exposing (Page(..))
 type Page = Home
+           --X
 `;
     await testBase.testDefinition(source);
   });
@@ -216,6 +227,7 @@ defaultPage = Home
 --@ App.elm
 module App exposing (..)
 type Page = Home
+           --X
 `;
     await testBase.testDefinition(source);
   });
@@ -243,6 +255,7 @@ defaultPage = Home
 --@ App.elmtest union constructor ref in
 module App exposing (Page(Home))
 type Page = Home
+           --X
 --@Foo.elm
 module Foo exposing(..)
 `;
@@ -260,6 +273,7 @@ title page =
 --@ App.elm
 module App exposing (Page(Home))
 type Page = Home
+           --X
 `;
     await testBase.testDefinition(source);
   });
@@ -272,6 +286,7 @@ import App exposing (Person)
 --@ App.elm
 module App exposing (Person)
 type alias Person = { name : String, age: Int }
+--X
 `;
     await testBase.testDefinition(source);
   });
@@ -285,6 +300,7 @@ type Entity = PersonEntity Person
 --@ App.elm
 module App exposing (Person)
 type alias Person = { name : String, age: Int }
+--X
 `;
     await testBase.testDefinition(source);
   });
@@ -298,6 +314,7 @@ type Entity = PersonEntity Person
 --@ App.elm
 module App exposing (..)
 type alias Person = { name : String, age: Int }
+--X
 `;
     await testBase.testDefinition(source);
   });
@@ -311,6 +328,7 @@ defaultPerson = Person "George" 42
 --@ App.elm
 module App exposing (Person)
 type alias Person = { name : String, age: Int }
+--X
 `;
     await testBase.testDefinition(source);
   });
@@ -324,6 +342,7 @@ defaultPerson = Person "George" 42
 --@ App.elm
 module App exposing (..)
 type alias Person = { name : String, age: Int }
+--X
 `;
     await testBase.testDefinition(source);
   });
@@ -337,6 +356,7 @@ type alias Model = Page
 --@ App.elm
 module App exposing (Page)
 type Page = Home
+--X
 `;
     await testBase.testDefinition(source);
   });
@@ -350,6 +370,7 @@ type alias Model = Page
 --@ App.elm
 module App exposing (..)
 type Page = Home
+--X
 `;
     await testBase.testDefinition(source);
   });
@@ -374,6 +395,7 @@ import App
        --^App.elm
 --@ App.elm
 module App exposing (..)
+--X
 `;
     await testBase.testDefinition(source);
   });
@@ -385,6 +407,7 @@ import Data.User
        --^Data/User.elm
 --@ Data/User.elm
 module Data.User exposing (..)
+--X
 `;
     await testBase.testDefinition(source);
   });
@@ -397,6 +420,7 @@ import Ports exposing (foo)
 --@ Ports.elm
 port module Ports exposing (foo)
 port foo : String -> Cmd msg
+    --X
 `;
     await testBase.testDefinition(source);
   });
@@ -412,6 +436,7 @@ import Math exposing ((**))
 module Math exposing ((**))
 infix left 5 (**) = power
 power a b = 42
+--X
 `;
     await testBase.testDefinition(source);
   });
@@ -426,6 +451,7 @@ f = 2 ** 3
 module Math exposing ((**))
 infix left 5 (**) = power
 power a b = 42
+--X
 `;
     await testBase.testDefinition(source);
   });
@@ -440,6 +466,7 @@ f = 2 ** 3
 module Math exposing ((**))
 infix left 5 (**) = power
 power a b = 42
+--X
 `;
     await testBase.testDefinition(source);
   });
@@ -454,6 +481,7 @@ f = (**) 2 3
 module Math exposing ((**))
 infix left 5 (**) = power
 power a b = 42
+--X
 `;
     await testBase.testDefinition(source);
   });
@@ -471,6 +499,7 @@ bar = 42
 --@ FooExtra.elm
 module FooExtra exposing (..)
 quux = 99
+--X
 `;
     await testBase.testDefinition(source);
   });
diff --git a/test/definitionProviderTests/layeredImportDefinition.test.ts b/test/definitionProviderTests/layeredImportDefinition.test.ts
index ce364f6..054ae9d 100644
--- a/test/definitionProviderTests/layeredImportDefinition.test.ts
+++ b/test/definitionProviderTests/layeredImportDefinition.test.ts
@@ -18,6 +18,7 @@ main = F.bar
 --@ Foo.elm
 module Foo exposing (..)
 bar = 42
+--X
 --@ FooExtra.elm
 module FooExtra exposing (..)
 quux = 99
@@ -38,6 +39,7 @@ bar = 42
 --@ FooExtra.elm
 module FooExtra exposing (..)
 quux = 99
+--X
 `;
     await testBase.testDefinition(source);
   });
diff --git a/test/definitionProviderTests/qualifiedImportDefinition.test.ts b/test/definitionProviderTests/qualifiedImportDefinition.test.ts
index 18e91a7..98ef755 100644
--- a/test/definitionProviderTests/qualifiedImportDefinition.test.ts
+++ b/test/definitionProviderTests/qualifiedImportDefinition.test.ts
@@ -12,6 +12,7 @@ main = Foo.bar
 --@ Foo.elm
 module Foo exposing (bar)
 bar = 42
+--X
 `;
     await testBase.testDefinition(source);
   });
@@ -24,6 +25,7 @@ main = Foo.bar
       --^Foo.elm
 --@ Foo.elm
 module Foo exposing (bar)
+--X
 bar = 42
 `;
     await testBase.testDefinition(source);
@@ -38,6 +40,7 @@ type alias Model = App.Page
 --@ App.elm
 module App exposing (Page)
 type Page = Home
+--X
 `;
     await testBase.testDefinition(source);
   });
@@ -51,6 +54,7 @@ defaultPage = App.Home
 --@ App.elm
 module App exposing (Page(Home))
 type Page = Home
+           --X
 `;
     await testBase.testDefinition(source);
   });
@@ -66,6 +70,7 @@ title page =
 --@ App.elm
 module App exposing (Page(Home))
 type Page = Home
+           --X
 `;
     await testBase.testDefinition(source);
   });
@@ -79,6 +84,7 @@ type Entity = PersonEntity App.Person
 --@ App.elm
 module App exposing (Person)
 type alias Person = { name : String, age: Int }
+--X
 `;
     await testBase.testDefinition(source);
   });
@@ -92,6 +98,7 @@ defaultPerson = App.Person "George" 42
 --@ App.elm
 module App exposing (Person)
 type alias Person = { name : String, age: Int }
+--X
 `;
     await testBase.testDefinition(source);
   });
@@ -105,6 +112,45 @@ update msg model = (model, Ports.foo "blah")
 --@ Ports.elm
 port module Ports exposing (foo)
 port foo : String -> Cmd msg
+--X
+`;
+    await testBase.testDefinition(source);
+  });
+
+  it(`test union constructor should not resolve to type declaration from other file`, async () => {
+    const source = `
+--@ main.elm
+import App
+
+func: App.User
+func =
+    App.User { data = "" }
+        --^App.elm
+
+--@ App.elm
+module App exposing (..)
+
+type User = User { data : String }
+           --X
+`;
+    await testBase.testDefinition(source);
+  });
+
+  it(`test type declaration should not resolve to union constructor from other file`, async () => {
+    const source = `
+--@ main.elm
+import App
+
+func: App.User
+         --^App.elm
+func =
+    App.User { data = "" }
+
+--@ App.elm
+module App exposing (..)
+
+type User = User { data : String }
+--X
 `;
     await testBase.testDefinition(source);
   });
diff --git a/test/definitionProviderTests/typeResolveDefinition.test.ts b/test/definitionProviderTests/typeResolveDefinition.test.ts
index d922ea7..e2a260a 100644
--- a/test/definitionProviderTests/typeResolveDefinition.test.ts
+++ b/test/definitionProviderTests/typeResolveDefinition.test.ts
@@ -3,10 +3,10 @@ import { DefinitionProviderTestBase } from "./definitionProviderTestBase";
 describe("typeResolveDefinition", () => {
   const testBase = new DefinitionProviderTestBase();
 
-  xit(`test union type ref`, async () => {
+  it(`test union type ref`, async () => {
     const source = `
 type Page = Home
-     --X
+--X
 title : Page -> String
         --^
 `;
@@ -55,50 +55,50 @@ type Page = Home
     await testBase.testDefinition(source);
   });
 
-  xit(`test type alias ref from module exposing list`, async () => {
+  it(`test type alias ref from module exposing list`, async () => {
     const source = `
 module Main exposing (Person)
                       --^
 type alias Person = { name : String, age: Int }
-           --X
+--X
 `;
     await testBase.testDefinition(source);
   });
 
-  xit(`test type alias ref in type annotation`, async () => {
+  it(`test type alias ref in type annotation`, async () => {
     const source = `
 type alias Person = { name : String, age: Int }
-           --X
+--X
 personToString : Person -> String
                  --^
 `;
     await testBase.testDefinition(source);
   });
 
-  xit(`test type alias record constructor ref`, async () => {
+  it(`test type alias record constructor ref`, async () => {
     const source = `
 type alias Person = { name : String, age: Int }
-           --X
+--X
 defaultPerson = Person "George" 42
                 --^
 `;
     await testBase.testDefinition(source);
   });
 
-  xit(`test parametric union type ref `, async () => {
+  it(`test parametric union type ref `, async () => {
     const source = `
 type Page a = Home a
-     --X
+--X
 title : Page a -> String
         --^
 `;
     await testBase.testDefinition(source);
   });
 
-  xit(`test parametric type alias ref `, async () => {
+  it(`test parametric type alias ref `, async () => {
     const source = `
 type alias Person a = { name : String, extra : a }
-           --X
+--X
 title : Person a -> String
         --^
 `;
@@ -107,9 +107,10 @@ title : Person a -> String
 
   xit(`test union constructor ref should not resolve to a record constructor`, async () => {
     const source = `
-    type alias User = { name : String, age : Int }
-    foo user =
-    case user of
+type alias User = { name : String, age : Int }
+
+foo user =
+  case user of
     User -> "foo"
     --^unresolved
     `;
@@ -128,6 +129,49 @@ type Page a = Home a
     const source = `
 type alias User details = { name : String, extra : details }
                 --X                                --^
+`;
+    await testBase.testDefinition(source);
+  });
+
+  it(`test union constructor should not resolve to type declaration`, async () => {
+    const source = `
+type User = User { data : String }
+            --X
+func: User
+func =
+    User { data = "" }
+   --^
+`;
+    await testBase.testDefinition(source);
+  });
+
+  it(`test type declaration should not resolve to union constructor`, async () => {
+    const source = `
+type User = User { data : String }
+--X
+
+func: User
+    --^
+func =
+User { data = "" }
+`;
+await testBase.testDefinition(source);
+});
+
+it(`test type declaration resolves to itself`, async () => {
+  const source = `
+  type UnitlessFloat
+  --X   --^
+    = UnitlessFloat
+`;
+    await testBase.testDefinition(source);
+  });
+
+it(`test union contructor resolves to itself`, async () => {
+  const source = `
+  type UnitlessFloat
+  = UnitlessFloat
+    --X   --^
 `;
     await testBase.testDefinition(source);
   });
diff --git a/test/definitionProviderTests/wildcardImportDefinition.test.ts b/test/definitionProviderTests/wildcardImportDefinition.test.ts
index a9f39c7..0a40106 100644
--- a/test/definitionProviderTests/wildcardImportDefinition.test.ts
+++ b/test/definitionProviderTests/wildcardImportDefinition.test.ts
@@ -16,6 +16,7 @@ bar = 42
 --@ Bar.elm
 module Bar exposing (..)
 bar = 99
+--X
 `;
     await testBase.testDefinition(source);
   });
@@ -30,6 +31,7 @@ main = bar
 --@ Foo.elm
 module Foo exposing (..)
 bar = 42
+--X
 --@ Bar.elm
 module Bar exposing (..)
 bar = 99
diff --git a/test/utils/sourceParser.ts b/test/utils/sourceParser.ts
index be162c0..e799ee4 100644
--- a/test/utils/sourceParser.ts
+++ b/test/utils/sourceParser.ts
@@ -44,21 +44,21 @@ interface IUnresolvedTest {
   kind: "unresolved";
   invokePosition: Position;
   sources: { [K: string]: string };
-  fileWithTarget: string;
+  invokeFile: string;
 }
 interface IResolvedTest {
   kind: "resolves";
   invokePosition: Position;
   targetPosition: Position;
   sources: { [K: string]: string };
-  fileWithTarget: string;
+  invokeFile: string;
 }
 interface IResolvesToDifferentFileTest {
   kind: "resolvesToDifferentFile";
   invokePosition: Position;
   targetFile: string;
   sources: { [K: string]: string };
-  fileWithTarget: string;
+  invokeFile: string;
   targetPosition?: Position;
 }
 
@@ -69,7 +69,7 @@ export function getInvokeAndTargetPositionFromSource(source: string): TestType {
   let invokePosition;
   let targetPosition;
   let targetFile;
-  let fileWithTarget = "";
+  let invokeFile = "";
 
   for (const fileName in sources) {
     sources[fileName].split("\n").forEach((s, line) => {
@@ -86,7 +86,7 @@ export function getInvokeAndTargetPositionFromSource(source: string): TestType {
         };
         unresolved = true;
 
-        fileWithTarget = fileName;
+        invokeFile = fileName;
       } else if (invokeFileCharacter >= 0) {
         targetFile = /--\^([A-Z][a-zA-Z0-9_/]*\.elm)/.exec(s)?.[1];
 
@@ -94,13 +94,13 @@ export function getInvokeAndTargetPositionFromSource(source: string): TestType {
           line: line - 1,
           character: invokeFileCharacter + 2,
         };
-        fileWithTarget = fileName;
+        invokeFile = fileName;
       } else if (invokeCharacter >= 0) {
         invokePosition = {
           line: line - 1,
           character: invokeCharacter + 2,
         };
-        fileWithTarget = fileName;
+        invokeFile = fileName;
       }
       if (targetCharacter >= 0) {
         targetPosition = {
@@ -115,7 +115,12 @@ export function getInvokeAndTargetPositionFromSource(source: string): TestType {
     if (!invokePosition) {
       fail();
     }
-    return { kind: "unresolved", invokePosition, sources, fileWithTarget };
+    return {
+      kind: "unresolved",
+      invokePosition,
+      sources,
+      invokeFile,
+    };
   } else if (targetFile) {
     if (!targetFile || !invokePosition) {
       fail();
@@ -125,7 +130,7 @@ export function getInvokeAndTargetPositionFromSource(source: string): TestType {
       invokePosition,
       sources,
       targetFile,
-      fileWithTarget,
+      invokeFile,
       targetPosition,
     };
   } else {
@@ -137,7 +142,7 @@ export function getInvokeAndTargetPositionFromSource(source: string): TestType {
       invokePosition,
       targetPosition,
       sources,
-      fileWithTarget,
+      invokeFile,
     };
   }
 }