Repository: elm-tooling/elm-language-server (ts)

Issue:
Type inference gets confused by aliases
Consider these two modules:

```
module LocalizedHtml exposing (Html, Language(..), div, text)

import Html


type Language
    = Italian
    | English


type alias Html a =
    Language -> Html.Html a


type alias Attribute a =
    Language -> Html.Attribute a


div : List (Attribute msg) -> List (Html msg) -> Html msg
div attributes children language =
    Html.div (List.map (\a -> a language) attributes) (List.map (\c -> c language) children)


text : String -> String -> Html msg
text en it language =
    Html.text <|
        case language of
            English ->
                en

            Italian ->
                it

```

and

```
module Main exposing (main)

import LocalizedHtml exposing (Html, Language(..), div, text)


main =
    div [] [ hello, world ] English


hello : Html msg
hello =
    text "Hello" "Ciao"


world =
    text "World" "Mondo"
```

notice how `Main` does *not* import `Html`.

## Expected Behavior
The type of `world` is inferred to be `Html msg`.

## Current Behavior
The type of `world` is inferred to be `Language -> Html msg`.

## Context
I'm wrapping `elm-ui` to add translations.

Incorrect unused_value_contructor diagnostics
Italian and English are both incorrectly marked as unused.

```elm
type Language
    = Italian
    | English


text : String -> String -> Html msg
text en it language =
    Html.text <|
        case language of
            English ->
                en

            Italian ->
                it
```

Pull request:
Bug fixes
Fixes #369, fixes #362. Added test for both bugs.

Patch:
diff --git a/src/elmWorkspace.ts b/src/elmWorkspace.ts
index 807ffe5..3938ddd 100644
--- a/src/elmWorkspace.ts
+++ b/src/elmWorkspace.ts
@@ -8,11 +8,8 @@ import { IConnection } from "vscode-languageserver";
 import { URI } from "vscode-uri";
 import Parser, { Tree } from "web-tree-sitter";
 import { Forest, IForest } from "./forest";
+import { IPossibleImportsCache, PossibleImportsCache } from "./providers";
 import * as utils from "./util/elmUtils";
-import {
-  IPossibleImportsCache,
-  PossibleImportsCache,
-} from "./util/possibleImportsCache";
 import { Settings } from "./util/settings";
 import { TypeCache } from "./util/types/typeCache";
 import {
@@ -140,12 +137,11 @@ export class ElmWorkspace implements IElmWorkspace {
   private async initWorkspace(
     progressCallback: (percent: number) => void,
   ): Promise<void> {
-    const clientSettings = await this.settings.getClientSettings();
     let progress = 0;
     let elmVersion;
     try {
       elmVersion = await utils.getElmVersion(
-        clientSettings,
+        await this.settings.getClientSettings(),
         this.rootPath,
         this.connection,
       );
@@ -154,7 +150,6 @@ export class ElmWorkspace implements IElmWorkspace {
         `Could not figure out elm version, this will impact how good the server works. \n ${e.stack}`,
       );
     }
-
     const pathToElmJson = path.join(this.rootPath.fsPath, "elm.json");
     this.connection.console.info(`Reading elm.json from ${pathToElmJson}`);
     try {
diff --git a/src/providers/completionProvider.ts b/src/providers/completionProvider.ts
index 5edf359..6b52d8f 100644
--- a/src/providers/completionProvider.ts
+++ b/src/providers/completionProvider.ts
@@ -43,6 +43,33 @@ interface ICompletionOptions {
   filterText?: string;
 }
 
+export interface IPossibleImportsCache {
+  get(uri: string): IPossibleImport[] | undefined;
+  set(uri: string, possibleImports: IPossibleImport[]): void;
+  clear(): void;
+}
+
+export class PossibleImportsCache implements IPossibleImportsCache {
+  private uri: string | undefined;
+  private possibleImports: IPossibleImport[] | undefined;
+
+  public get(uri: string): IPossibleImport[] | undefined {
+    if (uri === this.uri) {
+      return this.possibleImports;
+    }
+  }
+
+  public set(uri: string, possibleImports: IPossibleImport[]): void {
+    this.uri = uri;
+    this.possibleImports = possibleImports;
+  }
+
+  public clear(): void {
+    this.uri = undefined;
+    this.possibleImports = undefined;
+  }
+}
+
 export class CompletionProvider {
   private qidRegex = /[a-zA-Z0-9.]+/;
   private connection: IConnection;
@@ -516,7 +543,7 @@ export class CompletionProvider {
   ): CompletionItem[] {
     const completions: CompletionItem[] = [];
 
-    checker.getAllImports(treeContainer).forEach((element): void => {
+    checker.getAllImports(treeContainer).forEach((element) => {
       const markdownDocumentation = HintHelper.createHint(element.node);
       let sortPrefix = "d";
       if (element.maintainerAndPackageName) {
@@ -1051,7 +1078,7 @@ export class CompletionProvider {
       // Then sort by startsWith filter text, then matches filter text
       return possibleImports
         .filter(
-          (possibleImport): boolean =>
+          (possibleImport) =>
             !allImportedValues.get(
               possibleImport.valueToImport ?? possibleImport.value,
               (imp) => imp.fromModuleName === possibleImport.module,
diff --git a/src/providers/diagnostics/elmLsDiagnostics.ts b/src/providers/diagnostics/elmLsDiagnostics.ts
index 11db214..91782d0 100644
--- a/src/providers/diagnostics/elmLsDiagnostics.ts
+++ b/src/providers/diagnostics/elmLsDiagnostics.ts
@@ -42,6 +42,7 @@ export class ElmLsDiagnostics {
   private readonly operatorFunctionsQuery: Query;
   private readonly typeAliasesQuery: Query;
   private readonly unionVariantsQuery: Query;
+  private readonly patternReferencesQuery: Query;
 
   constructor() {
     this.language = container.resolve<Parser>("Parser").getLanguage();
@@ -64,10 +65,10 @@ export class ElmLsDiagnostics {
 
     this.moduleImportsQuery = this.language.query(
       `
-          (import_clause
-            (upper_case_qid) @moduleName
-          )
-          `,
+        (import_clause
+          (upper_case_qid) @moduleName
+        )
+    `,
     );
 
     this.moduleReferencesQuery = this.language.query(
@@ -84,226 +85,245 @@ export class ElmLsDiagnostics {
             (dot)
           )* @module.reference
         )
-        `,
+      `,
     );
 
     this.importModuleAliasesQuery = this.language.query(
       `
-          (import_clause
-            (as_clause
-              (upper_case_identifier) @moduleAlias
-            )
+        (import_clause
+          (as_clause
+            (upper_case_identifier) @moduleAlias
           )
-          `,
+        )
+      `,
     );
 
     this.moduleAliasReferencesQuery = this.language.query(
       `
-            (value_qid
-              (
-                (upper_case_identifier)
-                (dot)
-              )* @module.reference
-            )
-            (upper_case_qid
-              (
-                (upper_case_identifier)
-                (dot)
-              )* @module.reference
-            )
-            `,
+        (value_qid
+          (
+            (upper_case_identifier)
+            (dot)
+          )* @module.reference
+        )
+        (upper_case_qid
+          (
+            (upper_case_identifier)
+            (dot)
+          )* @module.reference
+        )
+      `,
     );
 
     this.patternsQuery = this.language.query(
       `
-          (value_declaration
-            (function_declaration_left
-              [
-                (pattern)
-                (record_pattern)
-                (lower_pattern)
-              ] @pattern
-            )
-          ) @patternScope
-  
-          ; For some reason, we can match on the let_in_expr
-          (value_declaration
+        (value_declaration
+          (function_declaration_left
             [
               (pattern)
               (record_pattern)
+              (lower_pattern)
             ] @pattern
-          ) @patternScope
-  
-          ; For let expr variables
-          (value_declaration
-            (function_declaration_left
-              (lower_case_identifier) @pattern
-            )
-          ) @patternScope
-  
-          (case_of_branch
-            (pattern) @pattern
-          ) @patternScope
-  
-          (anonymous_function_expr
-            (pattern) @pattern
-          ) @patternScope
-          `,
+          )
+        ) @patternScope
+
+        ; For some reason, we can match on the let_in_expr
+        (value_declaration
+          [
+            (pattern)
+            (record_pattern)
+          ] @pattern
+        ) @patternScope
+
+        ; For let expr variables
+        (value_declaration
+          (function_declaration_left
+            (lower_case_identifier) @pattern
+          )
+        ) @patternScope
+
+        (case_of_branch
+          (pattern) @pattern
+        ) @patternScope
+
+        (anonymous_function_expr
+          (pattern) @pattern
+        ) @patternScope
+      `,
     );
 
     this.caseBranchesQuery = this.language.query(
       `
-          (
-            (case_of_branch
-              (pattern) @casePattern
-              (value_expr) @caseValue
-            ) @caseBranch
-            (#eq? @casePattern "Nothing")
-            (#eq? @caseValue "Nothing")
-          )
-        `,
+        (
+          (case_of_branch
+            (pattern) @casePattern
+            (value_expr) @caseValue
+          ) @caseBranch
+          (#eq? @casePattern "Nothing")
+          (#eq? @caseValue "Nothing")
+        )
+      `,
     );
 
     this.booleanCaseExpressionsQuery = this.language.query(
       `
-          (
-            (case_of_branch
-              pattern: (pattern) @casePattern1
-              (#match? @casePattern1 "^(True|False)$")
-            ) @caseBranch
-            (case_of_branch
-              pattern: (pattern) @casePattern2
-              (#match? @casePattern2 "^(True|False|_)$")
-            )
+        (
+          (case_of_branch
+            pattern: (pattern) @casePattern1
+            (#match? @casePattern1 "^(True|False)$")
+          ) @caseBranch
+          (case_of_branch
+            pattern: (pattern) @casePattern2
+            (#match? @casePattern2 "^(True|False|_)$")
           )
-          `,
+        )
+      `,
     );
 
     this.concatOfListsQuery = this.language.query(
       `
-          (
-            (list_expr) @startList
-            .
-            (operator
-              (operator_identifier
-                "++"
-              )
+        (
+          (list_expr) @startList
+          .
+          (operator
+            (operator_identifier
+              "++"
             )
-            .
-            (list_expr) @endList
           )
-          `,
+          .
+          (list_expr) @endList
+        )
+      `,
     );
 
     this.consOfItemAndListQuery = this.language.query(
       `
-          (bin_op_expr
-            (_) @itemExpr
-            .
-            (operator
-              (operator_identifier
-                "::"
-              )
+        (bin_op_expr
+          (_) @itemExpr
+          .
+          (operator
+            (operator_identifier
+              "::"
             )
-            .
-            (list_expr) @listExpr
           )
-        `,
+          .
+          (list_expr) @listExpr
+        )
+      `,
     );
 
     this.useConsOverConcatQuery = this.language.query(
       `
-          (bin_op_expr
-            (list_expr
-              (left_square_bracket)
-              .
-              (_)
-              .
-              (right_square_bracket)
-            ) @firstPart
+        (bin_op_expr
+          (list_expr
+            (left_square_bracket)
             .
-            (operator
-              (operator_identifier
-                "++"
-              )
-            )
+            (_)
             .
-            (_) @lastPart
+            (right_square_bracket)
+          ) @firstPart
+          .
+          (operator
+            (operator_identifier
+              "++"
+            )
           )
-        `,
+          .
+          (_) @lastPart
+        )
+      `,
     );
 
     this.singleFieldRecordTypesQuery = this.language.query(
       `
-          (record_type
-            (left_brace)
-            .
-            (_)
-            .
-            (right_brace)
-          ) @recordType
-        `,
+        (record_type
+          (left_brace)
+          .
+          (_)
+          .
+          (right_brace)
+        ) @recordType
+      `,
     );
 
     this.unnecessaryListConcatQuery = this.language.query(
       `
-          (
-            (function_call_expr
-              target: (_) @target
-              arg: (list_expr
-                (left_square_bracket)
-                .
-                (list_expr)
-                .
-                ((comma) . (list_expr))*
-                .
-                (right_square_bracket)
-              )
-            ) @functionCall
-            (#eq? @target "List.concat")
-          )
-          `,
+        (
+          (function_call_expr
+            target: (_) @target
+            arg: (list_expr
+              (left_square_bracket)
+              .
+              (list_expr)
+              .
+              ((comma) . (list_expr))*
+              .
+              (right_square_bracket)
+            )
+          ) @functionCall
+          (#eq? @target "List.concat")
+        )
+      `,
     );
 
     this.unusedPortModuleQuery = this.language.query(
       `
-          (module_declaration
-            (port)
-          ) @portModule
-  
-          (port_annotation) @portAnnotation
-          `,
+        (module_declaration
+          (port)
+        ) @portModule
+
+        (port_annotation) @portAnnotation
+        `,
     );
 
     this.operatorFunctionsQuery = this.language.query(
       `
-          (function_call_expr
-            target: (operator_as_function_expr)
-            .
-            (_) @arg1
-            .
-            (_) @arg2
-          ) @functionCall
-          `,
+        (function_call_expr
+          target: (operator_as_function_expr)
+          .
+          (_) @arg1
+          .
+          (_) @arg2
+        ) @functionCall
+        `,
     );
 
     this.typeAliasesQuery = this.language.query(
       `
-          (type_alias_declaration
-            (upper_case_identifier) @typeAlias
-          )
-          `,
+        (type_alias_declaration
+          (upper_case_identifier) @typeAlias
+        )
+        `,
     );
 
     this.unionVariantsQuery = this.language.query(
       `
-          (type_declaration
-            (upper_case_identifier) @typeName
-            (union_variant
-              (upper_case_identifier) @unionVariant
-            )
+        (type_declaration
+          (upper_case_identifier) @typeName
+          (union_variant
+            (upper_case_identifier) @unionVariant
           )
-          `,
+        )
+        `,
+    );
+
+    this.patternReferencesQuery = this.language.query(
+      `
+        (
+          [
+            (value_expr)
+            (record_base_identifier)
+            (exposed_value)
+          ] @patternVariable.reference
+        )
+        (
+          (module_declaration
+            (exposing_list
+              (double_dot)
+            ) @exposingAll
+          )
+        )
+        `,
     );
   }
 
@@ -599,28 +619,14 @@ export class ElmLsDiagnostics {
       })
       .reduce((a, b) => a.concat(b), [])
       .forEach(({ scope, pattern }) => {
-        const references = this.language
-          .query(
-            `
-            (
-              [
-                (value_expr)
-                (record_base_identifier)
-                (exposed_value)
-              ] @patternVariable.reference
-              (#eq? @patternVariable.reference "${pattern.text}")
-            )
-            (
-              (module_declaration
-                (exposing_list
-                  (double_dot)
-                ) @exposingAll
-              )
-            )
-            `,
-          )
+        const references = this.patternReferencesQuery
           .matches(scope)
-          .filter(Utils.notUndefined.bind(this));
+          .filter(Utils.notUndefined.bind(this))
+          .filter(
+            (result) =>
+              result.captures[0].name !== "patternVariable.reference" ||
+              result.captures[0].node.text === pattern.text,
+          );
 
         if (scope.type === "file") {
           let outsideRef = false;
@@ -961,6 +967,10 @@ export class ElmLsDiagnostics {
             (upper_case_qid) @type.reference)
             (#eq? @type.reference "${unionVariant.text}")
           )
+          ((case_of_branch
+            (pattern) @variant.reference)
+            (#eq? @variant.reference "${unionVariant.text}")
+          )
           `,
         )
         .matches(tree.rootNode)
diff --git a/src/providers/foldingProvider.ts b/src/providers/foldingProvider.ts
index 293ef89..e42c0fe 100644
--- a/src/providers/foldingProvider.ts
+++ b/src/providers/foldingProvider.ts
@@ -52,96 +52,85 @@ export class FoldingRangeProvider {
       }
     };
 
-    if (tree) {
-      const treeCursor = tree.walk();
-
-      const traverse: () => void = (): void => {
-        const node = treeCursor.currentNode();
-        if (node.parent?.lastChild && node.isNamed) {
-          if ("import_clause" === node.type) {
-            const previousSibling = node.previousNamedSibling;
-            if (
-              previousSibling === null ||
-              previousSibling.type !== "import_clause"
-            ) {
-              const lastNode = findLastIdenticalNamedSibling(node);
-              folds.push({
-                endCharacter: lastNode.endPosition.column,
-                endLine: lastNode.endPosition.row,
-                kind: FoldingRangeKind.Imports,
-                startCharacter: node.startPosition.column,
-                startLine: node.startPosition.row,
-              });
-            }
-          } else if (node.type === "let_in_expr") {
-            // Use fields in the future
-            const valueDeclarations = node.namedChildren.filter(
-              (n) => n.type === "value_declaration",
-            );
-            const lastValueDeclaration =
-              valueDeclarations[valueDeclarations.length - 1];
-            const letBody = node.lastNamedChild;
-
-            if (lastValueDeclaration) {
-              folds.push({
-                endCharacter: lastValueDeclaration.endPosition.column,
-                endLine: lastValueDeclaration.endPosition.row,
-                kind: FoldingRangeKind.Region,
-                startCharacter: node.startPosition.column,
-                startLine: node.startPosition.row,
-              });
-            }
-
-            if (letBody) {
-              folds.push({
-                endCharacter: node.endPosition.column,
-                endLine: node.endPosition.row,
-                kind: FoldingRangeKind.Region,
-                startCharacter: letBody.startPosition.column,
-                startLine: letBody.startPosition.row - 1,
-              });
-            }
-          } else if (node.type === "if_else_expr") {
-            node.namedChildren.slice(1).forEach((child) => {
-              folds.push({
-                endCharacter: child.endPosition.column,
-                endLine: child.endPosition.row,
-                kind: FoldingRangeKind.Region,
-                startCharacter: child.startPosition.column,
-                startLine: child.startPosition.row - 1,
-              });
-            });
-          } else if (this.REGION_CONSTRUCTS.has(node.type)) {
+    const traverse: (node: SyntaxNode) => void = (node: SyntaxNode): void => {
+      if (node.parent?.lastChild && node.isNamed) {
+        if ("import_clause" === node.type) {
+          const previousSibling = node.previousNamedSibling;
+          if (
+            previousSibling === null ||
+            previousSibling.type !== "import_clause"
+          ) {
+            const lastNode = findLastIdenticalNamedSibling(node);
             folds.push({
-              endCharacter: node.endPosition.column,
-              endLine: node.endPosition.row,
-              kind: FoldingRangeKind.Region,
+              endCharacter: lastNode.endPosition.column,
+              endLine: lastNode.endPosition.row,
+              kind: FoldingRangeKind.Imports,
               startCharacter: node.startPosition.column,
               startLine: node.startPosition.row,
             });
-          } else if ("block_comment" === node.type) {
+          }
+        } else if (node.type === "let_in_expr") {
+          // Use fields in the future
+          const valueDeclarations = node.namedChildren.filter(
+            (n) => n.type === "value_declaration",
+          );
+          const lastValueDeclaration =
+            valueDeclarations[valueDeclarations.length - 1];
+          const letBody = node.lastNamedChild;
+
+          if (lastValueDeclaration) {
             folds.push({
-              endCharacter: node.endPosition.column,
-              endLine: node.endPosition.row,
-              kind: FoldingRangeKind.Comment,
+              endCharacter: lastValueDeclaration.endPosition.column,
+              endLine: lastValueDeclaration.endPosition.row,
+              kind: FoldingRangeKind.Region,
               startCharacter: node.startPosition.column,
               startLine: node.startPosition.row,
             });
           }
-        }
-
-        if (treeCursor.gotoFirstChild()) {
-          traverse();
 
-          while (treeCursor.gotoNextSibling()) {
-            traverse();
+          if (letBody) {
+            folds.push({
+              endCharacter: node.endPosition.column,
+              endLine: node.endPosition.row,
+              kind: FoldingRangeKind.Region,
+              startCharacter: letBody.startPosition.column,
+              startLine: letBody.startPosition.row - 1,
+            });
           }
-
-          treeCursor.gotoParent();
+        } else if (node.type === "if_else_expr") {
+          node.namedChildren.slice(1).forEach((child) => {
+            folds.push({
+              endCharacter: child.endPosition.column,
+              endLine: child.endPosition.row,
+              kind: FoldingRangeKind.Region,
+              startCharacter: child.startPosition.column,
+              startLine: child.startPosition.row - 1,
+            });
+          });
+        } else if (this.REGION_CONSTRUCTS.has(node.type)) {
+          folds.push({
+            endCharacter: node.endPosition.column,
+            endLine: node.endPosition.row,
+            kind: FoldingRangeKind.Region,
+            startCharacter: node.startPosition.column,
+            startLine: node.startPosition.row,
+          });
+        } else if ("block_comment" === node.type) {
+          folds.push({
+            endCharacter: node.endPosition.column,
+            endLine: node.endPosition.row,
+            kind: FoldingRangeKind.Comment,
+            startCharacter: node.startPosition.column,
+            startLine: node.startPosition.row,
+          });
         }
-      };
-
-      traverse();
+      }
+      for (const childNode of node.children) {
+        traverse(childNode);
+      }
+    };
+    if (tree) {
+      traverse(tree.rootNode);
     }
 
     this.connection.console.info(`Returned ${folds.length} folding ranges`);
diff --git a/src/util/possibleImportsCache.ts b/src/util/possibleImportsCache.ts
deleted file mode 100644
index c4bba49..0000000
--- a/src/util/possibleImportsCache.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import { IPossibleImport } from "./importUtils";
-
-export interface IPossibleImportsCache {
-  get(uri: string): IPossibleImport[] | undefined;
-  set(uri: string, possibleImports: IPossibleImport[]): void;
-  clear(): void;
-}
-
-export class PossibleImportsCache implements IPossibleImportsCache {
-  private uri: string | undefined;
-  private possibleImports: IPossibleImport[] | undefined;
-
-  public get(uri: string): IPossibleImport[] | undefined {
-    if (uri === this.uri) {
-      return this.possibleImports;
-    }
-  }
-
-  public set(uri: string, possibleImports: IPossibleImport[]): void {
-    this.uri = uri;
-    this.possibleImports = possibleImports;
-  }
-
-  public clear(): void {
-    this.uri = undefined;
-    this.possibleImports = undefined;
-  }
-}
diff --git a/src/util/treeUtils.ts b/src/util/treeUtils.ts
index 5eabdd2..02d97f2 100644
--- a/src/util/treeUtils.ts
+++ b/src/util/treeUtils.ts
@@ -447,7 +447,7 @@ export class TreeUtils {
   }
 
   /**
-   * @deprecated Should not be used due to performance. Use bindings instead
+   * @deprecated Should not use do to performance. Use bindings instead
    */
   public static findImportClauseByName(
     tree: Tree,
@@ -989,7 +989,7 @@ export class TreeUtils {
   }
 
   /**
-   * @deprecated Should not be used due to performance. Use bindings instead
+   * @deprecated Should not use do to performance. Use bindings instead
    */
   public static findAllImportClauseNodes(tree: Tree): SyntaxNode[] | undefined {
     const result = tree.rootNode.children.filter(
diff --git a/src/util/types/typeChecker.ts b/src/util/types/typeChecker.ts
index 3f60994..fc6f541 100644
--- a/src/util/types/typeChecker.ts
+++ b/src/util/types/typeChecker.ts
@@ -281,19 +281,12 @@ export function createTypeChecker(workspace: IElmWorkspace): TypeChecker {
     const uri = treeContainer.uri;
     const nodeText = nodeAtPosition.text;
     const nodeParent = nodeAtPosition.parent;
-
-    if (!nodeParent) {
-      return;
-    }
-
-    const nodeParentType = nodeParent.type;
-
     const rootSymbols = treeContainer.symbolLinks?.get(
       treeContainer.tree.rootNode,
     );
 
     if (
-      nodeParentType === "upper_case_qid" &&
+      nodeParent?.type === "upper_case_qid" &&
       nodeParent.previousNamedSibling?.type === "module"
     ) {
       const moduleNode = nodeParent.parent;
@@ -306,16 +299,16 @@ export function createTypeChecker(workspace: IElmWorkspace): TypeChecker {
         };
       }
     } else if (
-      nodeParentType === "upper_case_qid" &&
+      nodeParent?.type === "upper_case_qid" &&
       nodeParent.previousNamedSibling?.type === "import"
     ) {
       const upperCaseQid = nodeParent;
       const upperCaseQidText = upperCaseQid.text;
       return findImportOfType(treeContainer, upperCaseQidText, "Module");
     } else if (
-      (nodeParentType === "exposed_value" &&
+      (nodeParent?.type === "exposed_value" &&
         nodeParent.parent?.parent?.type === "module_declaration") ||
-      nodeParentType === "type_annotation"
+      nodeParent?.type === "type_annotation"
     ) {
       const definitionNode = rootSymbols?.get(nodeText);
 
@@ -327,7 +320,7 @@ export function createTypeChecker(workspace: IElmWorkspace): TypeChecker {
         };
       }
     } else if (
-      (nodeParentType === "exposed_type" &&
+      (nodeParent?.type === "exposed_type" &&
         nodeParent.parent?.parent?.type === "module_declaration") ||
       nodeAtPosition.previousNamedSibling?.type === "type" ||
       nodeAtPosition.previousNamedSibling?.type === "alias"
@@ -342,12 +335,12 @@ export function createTypeChecker(workspace: IElmWorkspace): TypeChecker {
         };
       }
     } else if (
-      (nodeParentType === "exposed_value" ||
-        nodeParentType === "exposed_type") &&
+      (nodeParent?.type === "exposed_value" ||
+        nodeParent?.type === "exposed_type") &&
       nodeParent.parent?.parent?.type === "import_clause"
     ) {
       return findImport(treeContainer, nodeText);
-    } else if (nodeParentType === "union_variant") {
+    } else if (nodeParent?.type === "union_variant") {
       const definitionNode = nodeParent;
       return {
         node: definitionNode,
@@ -444,18 +437,18 @@ export function createTypeChecker(workspace: IElmWorkspace): TypeChecker {
         return definitionFromOtherFile;
       }
     } else if (
-      nodeParentType === "lower_pattern" &&
+      nodeParent?.type === "lower_pattern" &&
       nodeParent.parent?.type === "record_pattern"
     ) {
       const type = findType(nodeParent.parent, uri);
       return TreeUtils.findFieldReference(type, nodeText);
     } else if (
-      nodeParentType === "value_qid" ||
-      nodeParentType === "lower_pattern" ||
-      nodeParentType === "record_base_identifier"
+      nodeParent?.type === "value_qid" ||
+      nodeParent?.type === "lower_pattern" ||
+      nodeParent?.type === "record_base_identifier"
     ) {
       let nodeAtPositionText = nodeText;
-      if (nodeParentType === "value_qid") {
+      if (nodeParent.type === "value_qid") {
         nodeAtPositionText = nodeParent.text;
       }
 
@@ -550,7 +543,7 @@ export function createTypeChecker(workspace: IElmWorkspace): TypeChecker {
           return definitionFromOtherFile;
         }
       }
-    } else if (nodeParentType === "field_access_expr") {
+    } else if (nodeParent?.type === "field_access_expr") {
       let target = nodeParent?.childForFieldName("target");
 
       // Adjust for parenthesis expr. Will need to change when we handle it better in inference
@@ -564,14 +557,14 @@ export function createTypeChecker(workspace: IElmWorkspace): TypeChecker {
       }
     } else if (
       nodeAtPosition.type === "lower_case_identifier" &&
-      nodeParentType === "field" &&
+      nodeParent?.type === "field" &&
       nodeParent.parent?.type === "record_expr"
     ) {
       const type = findType(nodeParent.parent, uri);
       return TreeUtils.findFieldReference(type, nodeText);
     } else if (
       nodeAtPosition.type === "lower_case_identifier" &&
-      nodeParentType === "field_accessor_function_expr"
+      nodeParent?.type === "field_accessor_function_expr"
     ) {
       const type = findType(nodeParent, uri);
 
@@ -582,7 +575,7 @@ export function createTypeChecker(workspace: IElmWorkspace): TypeChecker {
       }
     } else if (
       nodeAtPosition.type === "lower_case_identifier" &&
-      nodeParentType === "field_type"
+      nodeParent?.type === "field_type"
     ) {
       return {
         node: nodeParent,
@@ -591,7 +584,7 @@ export function createTypeChecker(workspace: IElmWorkspace): TypeChecker {
       };
     } else if (
       nodeAtPosition.type === "upper_case_identifier" &&
-      nodeParentType === "ERROR"
+      nodeParent?.type === "ERROR"
     ) {
       let fullModuleName = nodeText;
 
diff --git a/src/util/types/typeInference.ts b/src/util/types/typeInference.ts
index a45c8e2..bdc28d7 100644
--- a/src/util/types/typeInference.ts
+++ b/src/util/types/typeInference.ts
@@ -318,7 +318,7 @@ export function curryFunction(func: TFunction, count: number): Type {
  * @param func The function type to uncurry
  */
 export function uncurryFunction(func: TFunction): TFunction {
-  if (func.return.nodeType === "Function") {
+  if (func.return.nodeType === "Function" && !func.return.alias) {
     return TFunction(
       [...func.params, ...func.return.params],
       func.return.return,
diff --git a/src/util/types/typeReplacement.ts b/src/util/types/typeReplacement.ts
index 261d1b8..01a699a 100644
--- a/src/util/types/typeReplacement.ts
+++ b/src/util/types/typeReplacement.ts
@@ -1,4 +1,3 @@
-import { performance } from "perf_hooks";
 import { RecordFieldReferenceTable } from "./recordFieldReferenceTable";
 import {
   TVar,
@@ -12,18 +11,20 @@ import {
   TMutableRecord,
 } from "./typeInference";
 
-export let replaceTime = 0;
-export function resetReplaceTime(): void {
-  replaceTime = 0;
-}
-
 export class TypeReplacement {
+  private replacements: Map<TVar, [boolean, Type]>;
+
   constructor(
-    private replacements: Map<TVar, Type | [boolean, Type]>,
+    replacements: Map<TVar, Type>,
     private freshen: boolean,
     private keepRecordsMutable: boolean,
     private varsToRemainRigid?: TVar[],
-  ) {}
+  ) {
+    this.replacements = new Map<TVar, [boolean, Type]>();
+    replacements.forEach((value, key) => {
+      this.replacements.set(key, [false, value]);
+    });
+  }
 
   public static replace(
     type: Type,
@@ -31,21 +32,16 @@ export class TypeReplacement {
     keepRecordsMutable = false,
     varsToRemainRigid?: TVar[],
   ): Type {
-    const start = performance.now();
-    try {
-      if (!varsToRemainRigid && replacements.size === 0) {
-        return type;
-      }
-
-      return new TypeReplacement(
-        replacements,
-        false,
-        keepRecordsMutable,
-        varsToRemainRigid,
-      ).replace(type);
-    } finally {
-      replaceTime += performance.now() - start;
+    if (!varsToRemainRigid && replacements.size === 0) {
+      return type;
     }
+
+    return new TypeReplacement(
+      replacements,
+      false,
+      keepRecordsMutable,
+      varsToRemainRigid,
+    ).replace(type);
   }
 
   public static freshenVars(type: Type): Type {
@@ -238,14 +234,8 @@ export class TypeReplacement {
       return undefined;
     }
 
-    let hasBeenAccessed = false;
-    let storedType;
-    if (Array.isArray(replacement)) {
-      hasBeenAccessed = replacement[0];
-      storedType = replacement[1];
-    } else {
-      storedType = replacement;
-    }
+    const hasBeenAccessed = replacement[0];
+    const storedType = replacement[1];
 
     if (hasBeenAccessed) {
       return storedType;


Test patch:
diff --git a/.github/workflows/test-performance.yaml b/.github/workflows/test-performance.yaml
deleted file mode 100644
index dde18fa..0000000
--- a/.github/workflows/test-performance.yaml
+++ /dev/null
@@ -1,44 +0,0 @@
-name: Test Performance
-
-on: [push, pull_request]
-
-jobs:
-  build:
-    runs-on: ${{ matrix.os }}
-    strategy:
-      fail-fast: false
-      matrix:
-        os: [ubuntu-latest, windows-latest, macOS-latest]
-        node-version: [10, 12, 14]
-
-    steps:
-      - uses: actions/checkout@v2
-      - name: Checkout test target
-        uses: actions/checkout@v2
-        with:
-          # Repository name with owner. For example, actions/checkout
-          # Default: ${{ github.repository }}
-          repository: "ianmackenzie/elm-geometry"
-
-          # Relative path under $GITHUB_WORKSPACE to place the repository
-          path: "ianmackenzie/elm-geometry"
-      - name: Setup for ${{ matrix.node-version }}
-        uses: actions/setup-node@v1
-        with:
-          node-version: ${{ matrix.node-version }}
-      - name: npm ci
-        run: |
-          npm ci
-      - name: Compile
-        run: |
-          npm run compile
-      - name: Install Elm
-        run: |
-          npm install -g elm
-      - name: Elm make
-        run: |
-          cd ./ianmackenzie/elm-geometry
-          elm make ./src/Arc2d.elm
-      - name: Run performance test
-        run: |
-          npx ts-node ./test/performance.ts ./ianmackenzie/elm-geometry
diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index 77a8c69..db13803 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -9,7 +9,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [ubuntu-latest, windows-latest, macOS-latest]
-        node-version: [10, 12, 14]
+        node-version: [10, 12]
 
     steps:
       - uses: actions/checkout@v2
diff --git a/test/diagnosticTests/elmLsDiagnostics.test.ts b/test/diagnosticTests/elmLsDiagnostics.test.ts
index 96b5a84..a700406 100644
--- a/test/diagnosticTests/elmLsDiagnostics.test.ts
+++ b/test/diagnosticTests/elmLsDiagnostics.test.ts
@@ -1528,5 +1528,28 @@ foo = 1
 
       await testDiagnostics(source, "unused_value_constructor", []);
     });
+
+    it("used in case expr", async () => {
+      const source = `
+module Bar exposing (text)
+
+type Language
+    = Italian
+    | English
+
+
+text : String -> String -> Html msg
+text en it language =
+    Html.text <|
+        case language of
+            English ->
+                en
+
+            Italian ->
+                it
+			`;
+
+      await testDiagnostics(source, "unused_value_constructor", []);
+    });
   });
 });
diff --git a/test/performance.ts b/test/performance.ts
index ed588a6..8da3703 100644
--- a/test/performance.ts
+++ b/test/performance.ts
@@ -19,27 +19,18 @@ import * as path from "path";
 import { argv } from "process";
 import { Settings } from "../src/util/settings";
 import Parser from "web-tree-sitter";
-import {
-  replaceTime,
-  resetReplaceTime,
-} from "../src/util/types/typeReplacement";
 
 container.register("Connection", {
   useValue: {
     console: {
-      info: (a: string): void => {
-        // console.log(a);
-      },
-      warn: (a: string): void => {
-        // console.log(a);
+      info: (): void => {
+        //
       },
-      error: (a: string): void => {
-        // console.log(a);
+      warn: (): void => {
+        //
       },
-    },
-    window: {
-      showErrorMessage: (a: string): void => {
-        console.log(a);
+      error: (): void => {
+        //
       },
     },
   },
@@ -59,9 +50,6 @@ async function initParser(): Promise<void> {
 }
 
 export async function runPerformanceTests(uri: string): Promise<void> {
-  const pathUri = URI.file(uri);
-  console.log(`Running with uri: ${pathUri.fsPath}`);
-
   await initParser();
 
   const times: { [name: string]: number } = {};
@@ -72,7 +60,7 @@ export async function runPerformanceTests(uri: string): Promise<void> {
   const numTimes = 10;
 
   for (let i = 0; i < numTimes; i++) {
-    const elmWorkspace = new ElmWorkspace(pathUri);
+    const elmWorkspace = new ElmWorkspace(URI.file(uri));
     await elmWorkspace.init(() => {
       //
     });
@@ -86,14 +74,14 @@ export async function runPerformanceTests(uri: string): Promise<void> {
     addTime("IMPORTS   :", importsTime);
     addTime("MAPPING   :", mappingTime);
     addTime("DEFINITION:", definitionTime);
-    addTime("REPLACE   :", replaceTime);
 
     resetBindTime();
     resetInferTime();
     resetImportsTime();
     resetDefinitionAndMappingTime();
-    resetReplaceTime();
 
+    process.stdout.clearLine(-1);
+    process.stdout.cursorTo(0);
     process.stdout.write(`Finished run ${i + 1}/${numTimes}\n`);
   }
 
diff --git a/test/typeInference.test.ts b/test/typeInference.test.ts
index fe9bd6c..75468fd 100644
--- a/test/typeInference.test.ts
+++ b/test/typeInference.test.ts
@@ -566,4 +566,37 @@ func =
 `;
     await testTypeInference(basicsSources + source, "String");
   });
+
+  test("function return with aliased type", async () => {
+    const source = `
+--@ Module.elm
+module Module exposing (..)
+
+type Language
+    = Italian
+    | English
+
+type alias Html a =
+    Language -> number
+
+text : number -> number -> Html msg
+text en it language =
+    case language of
+        English ->
+            en
+
+        Italian ->
+            it
+
+--@ Test.elm
+module Test exposing (..)
+
+import Module exposing (Html, text)
+
+world =
+--^
+    text 1 2
+`;
+    await testTypeInference(basicsSources + source, "Html msg");
+  });
 });
diff --git a/test/utils/mockElmWorkspace.ts b/test/utils/mockElmWorkspace.ts
index 5eaae33..d5b148f 100644
--- a/test/utils/mockElmWorkspace.ts
+++ b/test/utils/mockElmWorkspace.ts
@@ -15,7 +15,7 @@ import { TreeUtils } from "../../src/util/treeUtils";
 import {
   IPossibleImportsCache,
   PossibleImportsCache,
-} from "../../src/util/possibleImportsCache";
+} from "../../src/providers";
 
 export const baseUri = Path.join(__dirname, "../sources/src/");