Repository: elm-tooling/elm-language-server
Pull request #958 (2023-05-01 23:56:44)

Bump @typescript-eslint/parser from 5.59.1 to 5.59.2
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 5.59.1 to 5.59.2.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/typescript-eslint/typescript-eslint/releases"><code>@​typescript-eslint/parser</code>'s releases</a>.</em></p>
<blockquote>
<h2>v5.59.2</h2>
<h2><a href="https://github.com/typescript-eslint/typescript-eslint/compare/v5.59.1...v5.59.2">5.59.2</a> (2023-05-01)</h2>
<p><strong>Note:</strong> Version bump only for package <code>@​typescript-eslint/typescript-eslint</code></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md"><code>@​typescript-eslint/parser</code>'s changelog</a>.</em></p>
<blockquote>
<h2><a href="https://github.com/typescript-eslint/typescript-eslint/compare/v5.59.1...v5.59.2">5.59.2</a> (2023-05-01)</h2>
<p><strong>Note:</strong> Version bump only for package <code>@​typescript-eslint/parser</code></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/typescript-eslint/typescript-eslint/commit/ce5f5165c9d4c5843c86d48b6e6e9a488eb06b0a"><code>ce5f516</code></a> chore: publish v5.59.2</li>
<li>See full diff in <a href="https://github.com/typescript-eslint/typescript-eslint/commits/v5.59.2/packages/parser">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@typescript-eslint/parser&package-manager=npm_and_yarn&previous-version=5.59.1&new-version=5.59.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c8b16b1..0fbb3a6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,18 @@
 # Change Log
 
+## 2.7.0
+
+- Suggest auto import completions and code actions for module aliases
+- Add auto import completions for ports
+- Show module aliases in hover and completions type strings
+- Improve sorting of completions for exposed values
+- Fix for field references when the module is not imported
+- Fix the extract function code action incorrectly computing parameters
+- Fix union constructor type inference when there are comments between the arguments
+- Fix some type inference bugs related to extensible records
+- Support node 18
+- Update dependencies
+
 ## 2.6.0
 
 - Start using completion item label description for module name
diff --git a/package-lock.json b/package-lock.json
index 0c6345a..f1f66e9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "@elm-tooling/elm-language-server",
-  "version": "2.6.0",
+  "version": "2.7.0",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "@elm-tooling/elm-language-server",
-      "version": "2.6.0",
+      "version": "2.7.0",
       "license": "MIT",
       "dependencies": {
         "chokidar": "^3.5.3",
@@ -1449,14 +1449,14 @@
       }
     },
     "node_modules/@typescript-eslint/parser": {
-      "version": "5.59.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.1.tgz",
-      "integrity": "sha512-nzjFAN8WEu6yPRDizIFyzAfgK7nybPodMNFGNH0M9tei2gYnYszRDqVA0xlnRjkl7Hkx2vYrEdb6fP2a21cG1g==",
+      "version": "5.59.2",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.2.tgz",
+      "integrity": "sha512-uq0sKyw6ao1iFOZZGk9F8Nro/8+gfB5ezl1cA06SrqbgJAt0SRoFhb9pXaHvkrxUpZaoLxt8KlovHNk8Gp6/HQ==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/scope-manager": "5.59.1",
-        "@typescript-eslint/types": "5.59.1",
-        "@typescript-eslint/typescript-estree": "5.59.1",
+        "@typescript-eslint/scope-manager": "5.59.2",
+        "@typescript-eslint/types": "5.59.2",
+        "@typescript-eslint/typescript-estree": "5.59.2",
         "debug": "^4.3.4"
       },
       "engines": {
@@ -1475,80 +1475,6 @@
         }
       }
     },
-    "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": {
-      "version": "5.59.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.1.tgz",
-      "integrity": "sha512-mau0waO5frJctPuAzcxiNWqJR5Z8V0190FTSqRw1Q4Euop6+zTwHAf8YIXNwDOT29tyUDrQ65jSg9aTU/H0omA==",
-      "dev": true,
-      "dependencies": {
-        "@typescript-eslint/types": "5.59.1",
-        "@typescript-eslint/visitor-keys": "5.59.1"
-      },
-      "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/typescript-eslint"
-      }
-    },
-    "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": {
-      "version": "5.59.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.1.tgz",
-      "integrity": "sha512-dg0ICB+RZwHlysIy/Dh1SP+gnXNzwd/KS0JprD3Lmgmdq+dJAJnUPe1gNG34p0U19HvRlGX733d/KqscrGC1Pg==",
-      "dev": true,
-      "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/typescript-eslint"
-      }
-    },
-    "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": {
-      "version": "5.59.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.1.tgz",
-      "integrity": "sha512-lYLBBOCsFltFy7XVqzX0Ju+Lh3WPIAWxYpmH/Q7ZoqzbscLiCW00LeYCdsUnnfnj29/s1WovXKh2gwCoinHNGA==",
-      "dev": true,
-      "dependencies": {
-        "@typescript-eslint/types": "5.59.1",
-        "@typescript-eslint/visitor-keys": "5.59.1",
-        "debug": "^4.3.4",
-        "globby": "^11.1.0",
-        "is-glob": "^4.0.3",
-        "semver": "^7.3.7",
-        "tsutils": "^3.21.0"
-      },
-      "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/typescript-eslint"
-      },
-      "peerDependenciesMeta": {
-        "typescript": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": {
-      "version": "5.59.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.1.tgz",
-      "integrity": "sha512-6waEYwBTCWryx0VJmP7JaM4FpipLsFl9CvYf2foAE8Qh/Y0s+bxWysciwOs0LTBED4JCaNxTZ5rGadB14M6dwA==",
-      "dev": true,
-      "dependencies": {
-        "@typescript-eslint/types": "5.59.1",
-        "eslint-visitor-keys": "^3.3.0"
-      },
-      "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/typescript-eslint"
-      }
-    },
     "node_modules/@typescript-eslint/scope-manager": {
       "version": "5.59.2",
       "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.2.tgz",
@@ -7265,58 +7191,15 @@
       }
     },
     "@typescript-eslint/parser": {
-      "version": "5.59.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.1.tgz",
-      "integrity": "sha512-nzjFAN8WEu6yPRDizIFyzAfgK7nybPodMNFGNH0M9tei2gYnYszRDqVA0xlnRjkl7Hkx2vYrEdb6fP2a21cG1g==",
+      "version": "5.59.2",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.2.tgz",
+      "integrity": "sha512-uq0sKyw6ao1iFOZZGk9F8Nro/8+gfB5ezl1cA06SrqbgJAt0SRoFhb9pXaHvkrxUpZaoLxt8KlovHNk8Gp6/HQ==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/scope-manager": "5.59.1",
-        "@typescript-eslint/types": "5.59.1",
-        "@typescript-eslint/typescript-estree": "5.59.1",
+        "@typescript-eslint/scope-manager": "5.59.2",
+        "@typescript-eslint/types": "5.59.2",
+        "@typescript-eslint/typescript-estree": "5.59.2",
         "debug": "^4.3.4"
-      },
-      "dependencies": {
-        "@typescript-eslint/scope-manager": {
-          "version": "5.59.1",
-          "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.1.tgz",
-          "integrity": "sha512-mau0waO5frJctPuAzcxiNWqJR5Z8V0190FTSqRw1Q4Euop6+zTwHAf8YIXNwDOT29tyUDrQ65jSg9aTU/H0omA==",
-          "dev": true,
-          "requires": {
-            "@typescript-eslint/types": "5.59.1",
-            "@typescript-eslint/visitor-keys": "5.59.1"
-          }
-        },
-        "@typescript-eslint/types": {
-          "version": "5.59.1",
-          "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.1.tgz",
-          "integrity": "sha512-dg0ICB+RZwHlysIy/Dh1SP+gnXNzwd/KS0JprD3Lmgmdq+dJAJnUPe1gNG34p0U19HvRlGX733d/KqscrGC1Pg==",
-          "dev": true
-        },
-        "@typescript-eslint/typescript-estree": {
-          "version": "5.59.1",
-          "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.1.tgz",
-          "integrity": "sha512-lYLBBOCsFltFy7XVqzX0Ju+Lh3WPIAWxYpmH/Q7ZoqzbscLiCW00LeYCdsUnnfnj29/s1WovXKh2gwCoinHNGA==",
-          "dev": true,
-          "requires": {
-            "@typescript-eslint/types": "5.59.1",
-            "@typescript-eslint/visitor-keys": "5.59.1",
-            "debug": "^4.3.4",
-            "globby": "^11.1.0",
-            "is-glob": "^4.0.3",
-            "semver": "^7.3.7",
-            "tsutils": "^3.21.0"
-          }
-        },
-        "@typescript-eslint/visitor-keys": {
-          "version": "5.59.1",
-          "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.1.tgz",
-          "integrity": "sha512-6waEYwBTCWryx0VJmP7JaM4FpipLsFl9CvYf2foAE8Qh/Y0s+bxWysciwOs0LTBED4JCaNxTZ5rGadB14M6dwA==",
-          "dev": true,
-          "requires": {
-            "@typescript-eslint/types": "5.59.1",
-            "eslint-visitor-keys": "^3.3.0"
-          }
-        }
       }
     },
     "@typescript-eslint/scope-manager": {
diff --git a/package.json b/package.json
index b6b31c4..2466efa 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "@elm-tooling/elm-language-server",
   "description": "Implementation of an elm language server in node.",
-  "version": "2.6.0",
+  "version": "2.7.0",
   "author": "Kolja Lampe",
   "license": "MIT",
   "main": "./out/module.js",
@@ -77,4 +77,4 @@
     "language-server",
     "lsp"
   ]
-}
\ No newline at end of file
+}
diff --git a/src/compiler/typeCache.ts b/src/compiler/typeCache.ts
index 1a610f9..35ece79 100644
--- a/src/compiler/typeCache.ts
+++ b/src/compiler/typeCache.ts
@@ -8,18 +8,23 @@ type CacheKey =
   | "PACKAGE_TYPE_ANNOTATION"
   | "PACKAGE_TYPE_AND_TYPE_ALIAS"
   | "PACKAGE_VALUE_DECLARATION"
+  | "PACKAGE_UNION_VARIANT"
   | "PROJECT_TYPE_ANNOTATION"
   | "PROJECT_TYPE_AND_TYPE_ALIAS"
-  | "PROJECT_VALUE_DECLARATION";
+  | "PROJECT_VALUE_DECLARATION"
+  | "PROJECT_UNION_VARIANT";
 
 export class TypeCache {
   private packageTypeAnnotation: SyntaxNodeMap<SyntaxNode, InferenceResult>;
   private packageTypeAndTypeAlias: SyntaxNodeMap<SyntaxNode, InferenceResult>;
   private packageValueDeclaration: SyntaxNodeMap<SyntaxNode, InferenceResult>;
+  private packageUnionVariant: SyntaxNodeMap<SyntaxNode, InferenceResult>;
   private projectTypeAnnotation: SyntaxNodeMap<SyntaxNode, InferenceResult>;
   private projectTypeAndTypeAlias: SyntaxNodeMap<SyntaxNode, InferenceResult>;
   private projectValueDeclaration: SyntaxNodeMap<SyntaxNode, InferenceResult>;
+  private projectUnionVariant: SyntaxNodeMap<SyntaxNode, InferenceResult>;
   private declarationAnnotations: MultiMap<number, SyntaxNode>;
+  private typeUnionVariants: MultiMap<number, SyntaxNode>;
 
   constructor() {
     this.packageTypeAnnotation = new SyntaxNodeMap<
@@ -34,6 +39,7 @@ export class TypeCache {
       SyntaxNode,
       InferenceResult
     >();
+    this.packageUnionVariant = new SyntaxNodeMap<SyntaxNode, InferenceResult>();
     this.projectTypeAnnotation = new SyntaxNodeMap<
       SyntaxNode,
       InferenceResult
@@ -46,7 +52,10 @@ export class TypeCache {
       SyntaxNode,
       InferenceResult
     >();
+    this.projectUnionVariant = new SyntaxNodeMap<SyntaxNode, InferenceResult>();
+
     this.declarationAnnotations = new MultiMap();
+    this.typeUnionVariants = new MultiMap();
   }
 
   public getOrSet(
@@ -61,12 +70,16 @@ export class TypeCache {
         return this.packageTypeAndTypeAlias.getOrSet(node, setter);
       case "PACKAGE_VALUE_DECLARATION":
         return this.packageValueDeclaration.getOrSet(node, setter);
+      case "PACKAGE_UNION_VARIANT":
+        return this.packageUnionVariant.getOrSet(node, setter);
       case "PROJECT_TYPE_ANNOTATION":
         return this.projectTypeAnnotation.getOrSet(node, setter);
       case "PROJECT_TYPE_AND_TYPE_ALIAS":
         return this.projectTypeAndTypeAlias.getOrSet(node, setter);
       case "PROJECT_VALUE_DECLARATION":
         return this.projectValueDeclaration.getOrSet(node, setter);
+      case "PROJECT_UNION_VARIANT":
+        return this.projectUnionVariant.getOrSet(node, setter);
     }
   }
 
@@ -74,6 +87,7 @@ export class TypeCache {
     this.projectTypeAnnotation.clear();
     this.projectTypeAndTypeAlias.clear();
     this.projectValueDeclaration.clear();
+    this.projectUnionVariant.clear();
   }
 
   public invalidateValueDeclaration(node: SyntaxNode): void {
@@ -89,6 +103,9 @@ export class TypeCache {
 
   public invalidateTypeOrTypeAlias(node: SyntaxNode): void {
     this.projectTypeAndTypeAlias.delete(node);
+    this.typeUnionVariants
+      .getAll(node.id)
+      ?.forEach((variant) => this.projectUnionVariant.delete(variant));
   }
 
   /**
@@ -111,4 +128,15 @@ export class TypeCache {
       this.declarationAnnotations.set(declaration.id, annotation);
     }
   }
+
+  public trackUnionVariant(unionVariant: SyntaxNode): void {
+    const typeDeclaration = TreeUtils.findParentOfType(
+      "type_declaration",
+      unionVariant,
+    );
+
+    if (typeDeclaration) {
+      this.typeUnionVariants.set(typeDeclaration.id, unionVariant);
+    }
+  }
 }
diff --git a/src/compiler/typeExpression.ts b/src/compiler/typeExpression.ts
index 3261d60..ee51acc 100644
--- a/src/compiler/typeExpression.ts
+++ b/src/compiler/typeExpression.ts
@@ -37,6 +37,28 @@ import { RecordFieldReferenceTable } from "./utils/recordFieldReferenceTable";
 import { IProgram } from "./program";
 import { Diagnostic, Diagnostics, error } from "./diagnostics";
 
+export class PerformanceTimer {
+  public total = 0;
+
+  public track<T>(func: () => T, track = true): T {
+    if (!track) {
+      return func();
+    }
+
+    const start = performance.now();
+
+    try {
+      return func();
+    } finally {
+      this.total += performance.now() - start;
+    }
+  }
+
+  public reset(): void {
+    this.total = 0;
+  }
+}
+
 export class TypeExpression {
   // All the type variables we've seen
   private varsByExpression: SyntaxNodeMap<Expression, TVar> = new SyntaxNodeMap<
@@ -72,21 +94,21 @@ export class TypeExpression {
 
       TypeReplacement.freeze(inferenceResult.type);
 
-      return {
-        ...inferenceResult,
-        type: TypeReplacement.freshenVars(inferenceResult.type),
-      };
+      return inferenceResult;
     };
 
-    if (!program.getForest().getByUri(e.tree.uri)?.writeable) {
-      return program
-        .getTypeCache()
-        .getOrSet("PACKAGE_TYPE_AND_TYPE_ALIAS", e, setter);
-    } else {
-      return program
-        .getTypeCache()
-        .getOrSet("PROJECT_TYPE_AND_TYPE_ALIAS", e, setter);
-    }
+    const result = !program.getForest().getByUri(e.tree.uri)?.writeable
+      ? program
+          .getTypeCache()
+          .getOrSet("PACKAGE_TYPE_AND_TYPE_ALIAS", e, setter)
+      : program
+          .getTypeCache()
+          .getOrSet("PROJECT_TYPE_AND_TYPE_ALIAS", e, setter);
+
+    return {
+      ...result,
+      type: TypeReplacement.freshenVars(result.type),
+    };
   }
 
   public static typeAliasDeclarationInference(
@@ -105,21 +127,21 @@ export class TypeExpression {
 
       TypeReplacement.freeze(inferenceResult.type);
 
-      return {
-        ...inferenceResult,
-        type: TypeReplacement.freshenVars(inferenceResult.type),
-      };
+      return inferenceResult;
     };
 
-    if (!program.getForest().getByUri(e.tree.uri)?.writeable) {
-      return program
-        .getTypeCache()
-        .getOrSet("PACKAGE_TYPE_AND_TYPE_ALIAS", e, setter);
-    } else {
-      return program
-        .getTypeCache()
-        .getOrSet("PROJECT_TYPE_AND_TYPE_ALIAS", e, setter);
-    }
+    const result = !program.getForest().getByUri(e.tree.uri)?.writeable
+      ? program
+          .getTypeCache()
+          .getOrSet("PACKAGE_TYPE_AND_TYPE_ALIAS", e, setter)
+      : program
+          .getTypeCache()
+          .getOrSet("PROJECT_TYPE_AND_TYPE_ALIAS", e, setter);
+
+    return {
+      ...result,
+      type: TypeReplacement.freshenVars(result.type),
+    };
   }
 
   public static typeAnnotationInference(
@@ -158,16 +180,27 @@ export class TypeExpression {
     e: EUnionVariant,
     program: IProgram,
   ): InferenceResult {
-    const inferenceResult = new TypeExpression(
-      e,
-      program,
-      /* rigidVars */ false,
-    ).inferUnionConstructor(e);
-    TypeReplacement.freeze(inferenceResult.type);
+    const setter = (): InferenceResult => {
+      const inferenceResult = new TypeExpression(
+        e,
+        program,
+        /* rigidVars */ false,
+      ).inferUnionConstructor(e);
+
+      TypeReplacement.freeze(inferenceResult.type);
+
+      program.getTypeCache().trackUnionVariant(e);
+
+      return inferenceResult;
+    };
+
+    const result = !program.getForest().getByUri(e.tree.uri)?.writeable
+      ? program.getTypeCache().getOrSet("PACKAGE_UNION_VARIANT", e, setter)
+      : program.getTypeCache().getOrSet("PROJECT_UNION_VARIANT", e, setter);
 
     return {
-      ...inferenceResult,
-      type: TypeReplacement.freshenVars(inferenceResult.type),
+      ...result,
+      type: TypeReplacement.freshenVars(result.type),
     };
   }
 
diff --git a/src/compiler/typeInference.ts b/src/compiler/typeInference.ts
index 06aec61..9b4ef24 100644
--- a/src/compiler/typeInference.ts
+++ b/src/compiler/typeInference.ts
@@ -337,8 +337,11 @@ function getParentPatternDeclaration(
   return parentPattern.pattern ? parentPattern : undefined;
 }
 
-export function getTypeclassName(type: TVar): string | undefined {
-  if (type.name.length < 6) {
+type TypeClass = "number" | "appendable" | "comparable" | "compappend";
+
+export function getTypeclassName(type: TVar): TypeClass | undefined {
+  // Alias typeclasses aren't respected: https://github.com/elm/compiler/issues/2225
+  if (type.name.length < 6 || type.alias) {
     return;
   } else if (type.name.startsWith("number")) {
     return "number";
@@ -924,16 +927,17 @@ export class InferenceScope {
 
   private inferList(expr: EListExpr): Type {
     const exprTypes = expr.exprList.map((e) => this.infer(e));
+    const elementType = TVar("a");
 
-    for (let i = 1; i < expr.exprList.length; i++) {
-      if (this.isAssignable(expr.exprList[i], exprTypes[i], exprTypes[0])) {
-        this.expressionTypes.set(expr.exprList[i], exprTypes[0]);
+    for (let i = 0; i < expr.exprList.length; i++) {
+      if (this.isAssignable(expr.exprList[i], exprTypes[i], elementType)) {
+        this.expressionTypes.set(expr.exprList[i], elementType);
       } else {
         break;
       }
     }
 
-    return TList(exprTypes[0] ?? TVar("a"));
+    return TList(elementType);
   }
 
   private inferIfElse(ifElseExpr: EIfElseExpr): Type {
@@ -2158,13 +2162,9 @@ export class InferenceScope {
           case "MutableRecord":
             {
               result =
-                (ty2?.nodeType === "Record" &&
-                  this.mutableRecordAssignable(ty1, ty2)) ||
-                (ty2?.nodeType === "MutableRecord" &&
-                  this.mutableRecordAssignable(
-                    ty1,
-                    mutableRecordAsRecord(ty2),
-                  ));
+                (ty2?.nodeType === "Record" ||
+                  ty2?.nodeType === "MutableRecord") &&
+                this.mutableRecordAssignable(ty1, ty2);
             }
             break;
           case "Unit":
@@ -2189,24 +2189,60 @@ export class InferenceScope {
 
   private mutableRecordAssignable(
     type1: TMutableRecord,
-    type2: TRecord,
+    type2: TRecord | TMutableRecord,
   ): boolean {
-    if (!this.recordAssignable(mutableRecordAsRecord(type1), type2)) {
+    if (
+      !this.recordAssignable(
+        mutableRecordAsRecord(type1),
+        type2.nodeType === "MutableRecord"
+          ? mutableRecordAsRecord(type2)
+          : type2,
+      )
+    ) {
       return false;
     }
 
     type1.fields = { ...type1.fields, ...type2.fields };
+
+    if (type2.nodeType === "MutableRecord") {
+      type2.fields = { ...type2.fields, ...type1.fields };
+    }
+
     return true;
   }
 
   private recordAssignable(type1: TRecord, type2: TRecord): boolean {
     const result = this.calculateRecordDiff(type1, type2).isEmpty;
     if (result) {
+      const getReplacementRecordDiff = (
+        record: TRecord,
+        recordWithBaseType: TRecord,
+      ): TRecord => {
+        const baseTypeFields = Object.keys(recordWithBaseType.fields);
+        return TRecord(
+          Object.fromEntries(
+            Object.entries(record.fields).filter(
+              ([k]) => !baseTypeFields.includes(k),
+            ),
+          ),
+          record.baseType,
+          record.alias,
+          record.fieldReferences,
+        );
+      };
+
       if (!type1.baseType && type2.baseType?.nodeType === "Var") {
-        this.trackReplacement(type1, type2.baseType);
+        this.trackReplacement(
+          getReplacementRecordDiff(type1, type2),
+          type2.baseType,
+        );
       }
+
       if (!type2.baseType && type1.baseType?.nodeType === "Var") {
-        this.trackReplacement(type1.baseType, type2);
+        this.trackReplacement(
+          type1.baseType,
+          getReplacementRecordDiff(type2, type1),
+        );
       }
 
       type1.fieldReferences.addAll(type2.fieldReferences);
@@ -2321,19 +2357,20 @@ export class InferenceScope {
     type: Type | undefined,
     typeVar: TVar,
   ): boolean {
-    const allAssignableTo = (types: Type[], typeClass: string): boolean => {
+    const allAssignableTo = (types: Type[], typeClass: TypeClass): boolean => {
       return types.every((t) => this.assignable(t, TVar(typeClass)));
     };
 
-    if (typeVar.name.startsWith("number")) {
+    const typeVarClass = getTypeclassName(typeVar);
+    if (typeVarClass === "number") {
       return (
         type?.nodeType === "Union" && (typeIsFloat(type) || typeIsInt(type))
       );
-    } else if (typeVar.name.startsWith("appendable")) {
+    } else if (typeVarClass === "appendable") {
       return (
         type?.nodeType === "Union" && (typeIsString(type) || typeIsList(type))
       );
-    } else if (typeVar.name.startsWith("comparable")) {
+    } else if (typeVarClass === "comparable") {
       if (type?.nodeType === "Tuple") {
         return allAssignableTo(type.types, "comparable");
       } else if (type?.nodeType === "Union") {
@@ -2349,7 +2386,7 @@ export class InferenceScope {
       } else {
         return false;
       }
-    } else if (typeVar.name.startsWith("compappend")) {
+    } else if (typeVarClass === "compappend") {
       return (
         type?.nodeType === "Union" &&
         (typeIsString(type) ||
@@ -2363,8 +2400,8 @@ export class InferenceScope {
   }
 
   private typeclassesCompatible(
-    name1: string,
-    name2?: string,
+    name1: TypeClass,
+    name2?: TypeClass,
     unconstrainedAllowed = true,
   ): boolean {
     if (!name2) {
@@ -2395,8 +2432,8 @@ export class InferenceScope {
   }
 
   private typeclassesConstrainToCompappend(
-    tc1?: string,
-    tc2?: string,
+    tc1?: TypeClass,
+    tc2?: TypeClass,
   ): boolean {
     if (tc1 === "comparable") {
       return tc2 === "appendable" || tc2 === "compappend";
@@ -2412,7 +2449,7 @@ export class InferenceScope {
       return;
     }
 
-    // Assign k to be of type v
+    // Assign typeVar to be of type
     const assign = (typeVar: TVar, type: Type): void => {
       if (anyTypeVar(type, (tVar) => tVar === typeVar)) {
         throw Error("InfiniteTypeException");


Test patch:
diff --git a/test/diagnosticTests/elmDiagnostics.test.ts b/test/diagnosticTests/elmDiagnostics.test.ts
index 08cc183..7b69470 100644
--- a/test/diagnosticTests/elmDiagnostics.test.ts
+++ b/test/diagnosticTests/elmDiagnostics.test.ts
@@ -1462,7 +1462,8 @@ f {} =
     });
   });
 
-  it.skip("should not have an error for a generic type alias used in different branches", async () => {
+  // https://github.com/elm-tooling/elm-language-server/issues/689
+  it("should not have an error for a generic type alias used in different branches", async () => {
     const source = `
 --@ Test.elm
 module Test exposing (..)
@@ -1487,6 +1488,153 @@ type DataType
     await testTypeInference(basicsSources + source, []);
   });
 
+  // https://github.com/elm-tooling/elm-language-server/issues/678
+  it("should not have an error for a generic type record", async () => {
+    const source = `
+--@ Test.elm
+module Test exposing (..)
+
+type Example a = Example { a | foo : Float }
+
+example : Example {}
+example = Example { foo = 0 }
+`;
+    await testTypeInference(basicsSources + source, []);
+
+    const source2 = `
+--@ Test.elm
+module Test exposing (..)
+
+type Example a = Example { a | foo : Float }
+
+example : Example { bar : Int }
+example = Example { foo = 0, bar = 1 }
+    `;
+    await testTypeInference(basicsSources + source2, []);
+  });
+
+  // https://github.com/elm-tooling/elm-language-server/issues/634
+  it("should not enforce type variable contraints for aliases", async () => {
+    const source = `
+--@ Test.elm
+module Test exposing (..)
+
+type alias A appendable =
+    appendable
+
+a : A Int
+a =
+    1
+`;
+    await testTypeInference(basicsSources + source, []);
+
+    const source2 = `
+--@ Test.elm
+module Test exposing (..)
+
+type alias A number =
+  number
+
+a : A String
+a =
+    ""
+    `;
+    await testTypeInference(basicsSources + stringSources + source2, []);
+
+    const source3 = `
+--@ Test.elm
+module Test exposing (..)
+
+type alias A appendable = 
+    { a : appendable }
+
+a : A Int
+a = 
+    { a = 0 }
+    `;
+    await testTypeInference(basicsSources + source3, []);
+  });
+
+  it("should enforce type variable contraints for record contructors", async () => {
+    const source = `
+--@ Test.elm
+module Test exposing (..)
+
+type alias A appendable = 
+    { a : appendable }
+
+a = 
+    A 0
+    --^
+`;
+    await testTypeInference(basicsSources + source, [
+      { message: Diagnostics.TypeMismatch, args: ["appendable", "number"] },
+    ]);
+
+    const source2 = `
+--@ Test.elm
+module Test exposing (..)
+
+type B appendable = 
+    With appendable | Without
+
+b : B Int
+b = With 0
+       --^
+    `;
+    await testTypeInference(basicsSources + source2, [
+      { message: Diagnostics.TypeMismatch, args: ["appendable", "number"] },
+    ]);
+
+    const source3 = `
+--@ Test.elm
+module Test exposing (..)
+
+type B appendable = 
+    With appendable | Without
+
+b : B Int
+b = Without
+     --^
+        `;
+    await testTypeInference(basicsSources + source3, [
+      { message: Diagnostics.TypeMismatch, args: ["B Int", "B appendable"] },
+    ]);
+  });
+
+  // https://github.com/elm-tooling/elm-language-server/issues/494
+  it("should work with type alias of function with type variable", async () => {
+    const source = `
+--@ Test.elm
+module Test exposing (..)
+
+type Config data
+    = Config (RequiredConfig data)
+
+type alias RequiredConfig data =
+    Data data -> String
+
+type alias Data data =
+    { data
+        | windowWidth : Int
+        , windowHeight : Int
+    }
+
+-- PUBLIC CONFIG
+
+type alias PublicConfig =
+    Config {}
+
+type alias PublicRequiredConfig =
+    RequiredConfig {}
+
+publicConfig : PublicRequiredConfig -> PublicConfig
+publicConfig =
+    Config
+`;
+    await testTypeInference(basicsSources + stringSources + source, []);
+  });
+
   it("should not have an error when converting a number to Float or Int", async () => {
     const source = `
 --@ Test.elm
diff --git a/test/diagnosticTests/sources.ts b/test/diagnosticTests/sources.ts
index 3341347..d526615 100644
--- a/test/diagnosticTests/sources.ts
+++ b/test/diagnosticTests/sources.ts
@@ -1908,7 +1908,7 @@ isChar char =
 
 export const listSources = `
 --@ List.elm
-module List exposing ((::), reverse, foldl)
+module List exposing ((::), reverse, foldl, foldr, map)
 
 import Basics exposing (..)
 import Elm.Kernel.List
@@ -1933,4 +1933,46 @@ foldl func acc list =
 
     x :: xs ->
       foldl func (func x acc) xs
+
+
+map : (a -> b) -> List a -> List b
+map f xs =
+  foldr (\\x acc -> cons (f x) acc) [] xs
+
+
+foldr : (a -> b -> b) -> b -> List a -> b
+foldr fn acc ls =
+    foldrHelper fn acc 0 ls
+
+
+foldrHelper : (a -> b -> b) -> b -> Int -> List a -> b
+foldrHelper fn acc ctr ls =
+    case ls of
+        [] ->
+            acc
+
+        a :: r1 ->
+            case r1 of
+                [] ->
+                    fn a acc
+
+                b :: r2 ->
+                    case r2 of
+                        [] ->
+                            fn a (fn b acc)
+
+                        c :: r3 ->
+                            case r3 of
+                                [] ->
+                                    fn a (fn b (fn c acc))
+
+                                d :: r4 ->
+                                    let
+                                        res =
+                                            if ctr > 500 then
+                                                foldl fn acc (reverse r4)
+                                            else
+                                                foldrHelper fn acc (ctr + 1) r4
+                                    in
+                                        fn a (fn b (fn c (fn d res)))
 `;
diff --git a/test/performance.ts b/test/performance.ts
index b90eea4..841f34c 100644
--- a/test/performance.ts
+++ b/test/performance.ts
@@ -23,6 +23,7 @@ import {
 } from "../src/cancellation";
 import { randomBytes } from "crypto";
 import { createProgramHost } from "./utils/sourceTreeParser";
+import { PerformanceTimer } from "../src/compiler/typeExpression";
 
 container.register("Connection", {
   useValue: {
@@ -69,6 +70,11 @@ export async function runPerformanceTests(uri: string): Promise<void> {
     times[name] = (times[name] ?? 0) + time;
   }
 
+  // function addPerfTime(name: string, time: PerformanceTimer): void {
+  //   times[name] = (times[name] ?? 0) + time.total;
+  //   time.reset();
+  // }
+
   const numTimes = 10;
 
   for (let i = 0; i < numTimes; i++) {
diff --git a/test/typeInference.test.ts b/test/typeInference.test.ts
index 3d99620..92ece9b 100644
--- a/test/typeInference.test.ts
+++ b/test/typeInference.test.ts
@@ -4,14 +4,16 @@ import { getVarNames } from "../src/compiler/typeInference";
 import { TreeUtils } from "../src/util/treeUtils";
 import { getTargetPositionFromSource } from "./utils/sourceParser";
 import { SourceTreeParser, srcUri } from "./utils/sourceTreeParser";
+import { listSources } from "./diagnosticTests/sources";
 
 const basicsSources = `
 --@ Basics.elm
-module Basics exposing ((+), (|>), (==), Int, Float, Bool(..), (>>))
+module Basics exposing ((+), (/), (|>), (==), Int, Float, Bool(..), (>>))
 
 infix left  0 (|>) = apR
 infix non   4 (==) = eq
 infix left  6 (+)  = add
+infix left  7 (/)  = fdiv
 infix right 9 (>>) = composeR
 
 type Int = Int
@@ -36,6 +38,10 @@ composeR : (a -> b) -> (b -> c) -> (a -> c)
 composeR f g x =
   g (f x)
 
+fdiv : Float -> Float -> Float
+fdiv =
+  Elm.Kernel.Basics.fdiv
+
 --@ String.elm
 module String exposing (String)
 
@@ -583,7 +589,7 @@ func =
     await testTypeInference(basicsSources + source, "String");
   });
 
-  xtest("function return with aliased type", async () => {
+  test.skip("function return with aliased type", async () => {
     const source = `
 --@ Module.elm
 module Module exposing (..)
@@ -712,4 +718,49 @@ bug =
       }
     }
   });
+
+  // https://github.com/elm-tooling/elm-language-server/issues/620
+  test("records applied to lists of functions", async () => {
+    const source = `
+--@ Test.elm
+module Test exposing (..)
+
+func r =
+--^
+    List.map ((|>) r) [ \\{ foo } -> foo, \\{ bar } -> bar ]
+`;
+    await testTypeInference(
+      basicsSources + listSources + source,
+      "{ a | foo : b, bar : b } -> List b",
+    );
+
+    const source2 = `
+--@ Test.elm
+module Test exposing (..)
+
+func r =
+--^
+    List.map ((|>) r) [ \\{ foo } -> foo, \\{ bar } -> bar, \\{ baz } -> baz ]
+`;
+    await testTypeInference(
+      basicsSources + listSources + source2,
+      "{ a | foo : b, bar : b, baz : b } -> List b",
+    );
+
+    const source3 = `
+--@ Test.elm
+module Test exposing (..)
+
+number i =
+--^
+    List.map ((|>) i)
+        [ (+) 1
+        , (/) 2
+        ]
+`;
+    await testTypeInference(
+      basicsSources + listSources + source3,
+      "Float -> List Float",
+    );
+  });
 });