diff --git a/.eslintrc.json b/.eslintrc.json index a637dd08..360fb087 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -23,6 +23,7 @@ "@typescript-eslint/no-unused-vars": 0, "@typescript-eslint/no-explicit-any": 0, "@typescript-eslint/explicit-module-boundary-types": 0, - "@typescript-eslint/no-non-null-assertion": 0 + "@typescript-eslint/no-non-null-assertion": 0, + "no-inner-declarations": "off" } } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ff2afc2..1178a7a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # UnrealScript Language Service +## 0.6.4 (Feb 16, 2023) + +- Fixed [Class-function field hint](https://github.com/EliotVU/UnrealScript-Language-Service/issues/161). +- Fixed [Type checking error when passing an Enum object to a function as argument](https://github.com/EliotVU/UnrealScript-Language-Service/issues/166). +- Fixed [Enum tag reference in a condition expression produces an error when type checking is enabled](https://github.com/EliotVU/UnrealScript-Language-Service/issues/167). +- Fixed [Request textDocument/documentSymbol failed](https://github.com/EliotVU/UnrealScript-Language-Service/issues/169). +- [Add a new diagnostic for UC3 enum based element dimensions; and fix the misplaced range for array dimension diagnostics.](https://github.com/EliotVU/UnrealScript-Language-Service/commit/97e7b1ec9dbd62ae98c81f473a79f20826f18ac5). +- [Fail auto detection when no Object.uc document is present](https://github.com/EliotVU/UnrealScript-Language-Service/commit/1d64bc3771c5e23fa34f9624962e6567d197e879). + +## 0.6.3 (Feb 10, 2023) + +- Fixed an issue with skipLine() failing on UnrealScript directives i.e. "#exec obj load ..." + ## 0.6.2 (Feb 9, 2023) - Implemented an option to enable auto-detection of the UnrealScript language generation that's being used by the workspace. @@ -11,9 +24,6 @@ - Fixed type ```Pointer``` will be no longer recognized if the language is set to generation 3 (this has been displaced by the Core.Object.Pointer struct). - Fixed [No symbols found](https://github.com/EliotVU/UnrealScript-Language-Service/issues/157) -- Known Issues: - - - ## 0.6.1 (Jan 29, 2023) - The service will now register .u/.upk (the extensions are configurable) files as known package symbols, this means such packages will be included in the auto-completion and indexing of references. diff --git a/grammars/UCParser.g4 b/grammars/UCParser.g4 index 1f90dbfe..8df72483 100644 --- a/grammars/UCParser.g4 +++ b/grammars/UCParser.g4 @@ -13,7 +13,13 @@ options { let token; do { token = this._input.get(i++); - } while (token.type !== UCParser.NEWLINE && token.type !== UCParser.EOF) + // We cannot consume an EOF token. + if (token.type === UCParser.EOF) { + break; + } + // We need to consume, incase the stream is not filled yet. + this._input.consume(); + } while (token.type !== UCParser.NEWLINE) this._input.seek(i); } diff --git a/grammars/test/Grammar.uc b/grammars/test/Grammar.uc index 078e3087..969e8c4c 100644 --- a/grammars/test/Grammar.uc +++ b/grammars/test/Grammar.uc @@ -1,5 +1,7 @@ class Grammar; +#exec obj load package=filepath + const C1 = 0; enum E1 { diff --git a/package-lock.json b/package-lock.json index 08750efb..9d1d8f28 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "uc", - "version": "0.6.2", + "version": "0.6.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "uc", - "version": "0.6.2", + "version": "0.6.4", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -16,11 +16,11 @@ "devDependencies": { "@tsconfig/recommended": "^1.0.2", "@types/node": "^18.13.0", - "@typescript-eslint/eslint-plugin": "^5.51.0", - "@typescript-eslint/parser": "^5.51.0", + "@typescript-eslint/eslint-plugin": "^5.52.0", + "@typescript-eslint/parser": "^5.52.0", "antlr4ts-cli": "0.5.0-alpha.4", "copy-webpack-plugin": "^11.0.0", - "eslint": "^8.33.0", + "eslint": "^8.34.0", "gulp": "^4.0.2", "js-yaml": "^4.1.0", "merge-options": "^3.0.4", @@ -305,14 +305,14 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.51.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.51.0.tgz", - "integrity": "sha512-wcAwhEWm1RgNd7dxD/o+nnLW8oH+6RK1OGnmbmkj/GGoDPV1WWMVP0FXYQBivKHdwM1pwii3bt//RC62EriIUQ==", + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.52.0.tgz", + "integrity": "sha512-lHazYdvYVsBokwCdKOppvYJKaJ4S41CgKBcPvyd0xjZNbvQdhn/pnJlGtQksQ/NhInzdaeaSarlBjDXHuclEbg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.51.0", - "@typescript-eslint/type-utils": "5.51.0", - "@typescript-eslint/utils": "5.51.0", + "@typescript-eslint/scope-manager": "5.52.0", + "@typescript-eslint/type-utils": "5.52.0", + "@typescript-eslint/utils": "5.52.0", "debug": "^4.3.4", "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", @@ -339,14 +339,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.51.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.51.0.tgz", - "integrity": "sha512-fEV0R9gGmfpDeRzJXn+fGQKcl0inIeYobmmUWijZh9zA7bxJ8clPhV9up2ZQzATxAiFAECqPQyMDB4o4B81AaA==", + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.52.0.tgz", + "integrity": "sha512-e2KiLQOZRo4Y0D/b+3y08i3jsekoSkOYStROYmPUnGMEoA0h+k2qOH5H6tcjIc68WDvGwH+PaOrP1XRzLJ6QlA==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.51.0", - "@typescript-eslint/types": "5.51.0", - "@typescript-eslint/typescript-estree": "5.51.0", + "@typescript-eslint/scope-manager": "5.52.0", + "@typescript-eslint/types": "5.52.0", + "@typescript-eslint/typescript-estree": "5.52.0", "debug": "^4.3.4" }, "engines": { @@ -366,13 +366,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.51.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.51.0.tgz", - "integrity": "sha512-gNpxRdlx5qw3yaHA0SFuTjW4rxeYhpHxt491PEcKF8Z6zpq0kMhe0Tolxt0qjlojS+/wArSDlj/LtE69xUJphQ==", + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.52.0.tgz", + "integrity": "sha512-AR7sxxfBKiNV0FWBSARxM8DmNxrwgnYMPwmpkC1Pl1n+eT8/I2NAUPuwDy/FmDcC6F8pBfmOcaxcxRHspgOBMw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.51.0", - "@typescript-eslint/visitor-keys": "5.51.0" + "@typescript-eslint/types": "5.52.0", + "@typescript-eslint/visitor-keys": "5.52.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -383,13 +383,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.51.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.51.0.tgz", - "integrity": "sha512-QHC5KKyfV8sNSyHqfNa0UbTbJ6caB8uhcx2hYcWVvJAZYJRBo5HyyZfzMdRx8nvS+GyMg56fugMzzWnojREuQQ==", + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.52.0.tgz", + "integrity": "sha512-tEKuUHfDOv852QGlpPtB3lHOoig5pyFQN/cUiZtpw99D93nEBjexRLre5sQZlkMoHry/lZr8qDAt2oAHLKA6Jw==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.51.0", - "@typescript-eslint/utils": "5.51.0", + "@typescript-eslint/typescript-estree": "5.52.0", + "@typescript-eslint/utils": "5.52.0", "debug": "^4.3.4", "tsutils": "^3.21.0" }, @@ -410,9 +410,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.51.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.51.0.tgz", - "integrity": "sha512-SqOn0ANn/v6hFn0kjvLwiDi4AzR++CBZz0NV5AnusT2/3y32jdc0G4woXPWHCumWtUXZKPAS27/9vziSsC9jnw==", + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.52.0.tgz", + "integrity": "sha512-oV7XU4CHYfBhk78fS7tkum+/Dpgsfi91IIDy7fjCyq2k6KB63M6gMC0YIvy+iABzmXThCRI6xpCEyVObBdWSDQ==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -423,13 +423,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.51.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.51.0.tgz", - "integrity": "sha512-TSkNupHvNRkoH9FMA3w7TazVFcBPveAAmb7Sz+kArY6sLT86PA5Vx80cKlYmd8m3Ha2SwofM1KwraF24lM9FvA==", + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.52.0.tgz", + "integrity": "sha512-WeWnjanyEwt6+fVrSR0MYgEpUAuROxuAH516WPjUblIrClzYJj0kBbjdnbQXLpgAN8qbEuGywiQsXUVDiAoEuQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.51.0", - "@typescript-eslint/visitor-keys": "5.51.0", + "@typescript-eslint/types": "5.52.0", + "@typescript-eslint/visitor-keys": "5.52.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -450,16 +450,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.51.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.51.0.tgz", - "integrity": "sha512-76qs+5KWcaatmwtwsDJvBk4H76RJQBFe+Gext0EfJdC3Vd2kpY2Pf//OHHzHp84Ciw0/rYoGTDnIAr3uWhhJYw==", + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.52.0.tgz", + "integrity": "sha512-As3lChhrbwWQLNk2HC8Ree96hldKIqk98EYvypd3It8Q1f8d5zWyIoaZEp2va5667M4ZyE7X8UUR+azXrFl+NA==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.51.0", - "@typescript-eslint/types": "5.51.0", - "@typescript-eslint/typescript-estree": "5.51.0", + "@typescript-eslint/scope-manager": "5.52.0", + "@typescript-eslint/types": "5.52.0", + "@typescript-eslint/typescript-estree": "5.52.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0", "semver": "^7.3.7" @@ -476,12 +476,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.51.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.51.0.tgz", - "integrity": "sha512-Oh2+eTdjHjOFjKA27sxESlA87YPSOJafGCR0md5oeMdh1ZcCfAGCIOL216uTBAkAIptvLIfKQhl7lHxMJet4GQ==", + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.52.0.tgz", + "integrity": "sha512-qMwpw6SU5VHCPr99y274xhbm+PRViK/NATY6qzt+Et7+mThGuFSl/ompj2/hrBlRP/kq+BFdgagnOSgw9TB0eA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.51.0", + "@typescript-eslint/types": "5.52.0", "eslint-visitor-keys": "^3.3.0" }, "engines": { @@ -2118,9 +2118,9 @@ } }, "node_modules/eslint": { - "version": "8.33.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.33.0.tgz", - "integrity": "sha512-WjOpFQgKK8VrCnAtl8We0SUOy/oVZ5NHykyMiagV1M9r8IFpIJX7DduK6n1mpfhlG7T1NLWm2SuD8QB7KFySaA==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.34.0.tgz", + "integrity": "sha512-1Z8iFsucw+7kSqXNZVslXS8Ioa4u2KM7GPwuKtkTFAqZ/cHMcEaR+1+Br0wLlot49cNxIiZk5wp8EAbPcYZxTg==", "dev": true, "dependencies": { "@eslint/eslintrc": "^1.4.1", @@ -7282,14 +7282,14 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "5.51.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.51.0.tgz", - "integrity": "sha512-wcAwhEWm1RgNd7dxD/o+nnLW8oH+6RK1OGnmbmkj/GGoDPV1WWMVP0FXYQBivKHdwM1pwii3bt//RC62EriIUQ==", + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.52.0.tgz", + "integrity": "sha512-lHazYdvYVsBokwCdKOppvYJKaJ4S41CgKBcPvyd0xjZNbvQdhn/pnJlGtQksQ/NhInzdaeaSarlBjDXHuclEbg==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.51.0", - "@typescript-eslint/type-utils": "5.51.0", - "@typescript-eslint/utils": "5.51.0", + "@typescript-eslint/scope-manager": "5.52.0", + "@typescript-eslint/type-utils": "5.52.0", + "@typescript-eslint/utils": "5.52.0", "debug": "^4.3.4", "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", @@ -7300,53 +7300,53 @@ } }, "@typescript-eslint/parser": { - "version": "5.51.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.51.0.tgz", - "integrity": "sha512-fEV0R9gGmfpDeRzJXn+fGQKcl0inIeYobmmUWijZh9zA7bxJ8clPhV9up2ZQzATxAiFAECqPQyMDB4o4B81AaA==", + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.52.0.tgz", + "integrity": "sha512-e2KiLQOZRo4Y0D/b+3y08i3jsekoSkOYStROYmPUnGMEoA0h+k2qOH5H6tcjIc68WDvGwH+PaOrP1XRzLJ6QlA==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.51.0", - "@typescript-eslint/types": "5.51.0", - "@typescript-eslint/typescript-estree": "5.51.0", + "@typescript-eslint/scope-manager": "5.52.0", + "@typescript-eslint/types": "5.52.0", + "@typescript-eslint/typescript-estree": "5.52.0", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "5.51.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.51.0.tgz", - "integrity": "sha512-gNpxRdlx5qw3yaHA0SFuTjW4rxeYhpHxt491PEcKF8Z6zpq0kMhe0Tolxt0qjlojS+/wArSDlj/LtE69xUJphQ==", + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.52.0.tgz", + "integrity": "sha512-AR7sxxfBKiNV0FWBSARxM8DmNxrwgnYMPwmpkC1Pl1n+eT8/I2NAUPuwDy/FmDcC6F8pBfmOcaxcxRHspgOBMw==", "dev": true, "requires": { - "@typescript-eslint/types": "5.51.0", - "@typescript-eslint/visitor-keys": "5.51.0" + "@typescript-eslint/types": "5.52.0", + "@typescript-eslint/visitor-keys": "5.52.0" } }, "@typescript-eslint/type-utils": { - "version": "5.51.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.51.0.tgz", - "integrity": "sha512-QHC5KKyfV8sNSyHqfNa0UbTbJ6caB8uhcx2hYcWVvJAZYJRBo5HyyZfzMdRx8nvS+GyMg56fugMzzWnojREuQQ==", + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.52.0.tgz", + "integrity": "sha512-tEKuUHfDOv852QGlpPtB3lHOoig5pyFQN/cUiZtpw99D93nEBjexRLre5sQZlkMoHry/lZr8qDAt2oAHLKA6Jw==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "5.51.0", - "@typescript-eslint/utils": "5.51.0", + "@typescript-eslint/typescript-estree": "5.52.0", + "@typescript-eslint/utils": "5.52.0", "debug": "^4.3.4", "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "5.51.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.51.0.tgz", - "integrity": "sha512-SqOn0ANn/v6hFn0kjvLwiDi4AzR++CBZz0NV5AnusT2/3y32jdc0G4woXPWHCumWtUXZKPAS27/9vziSsC9jnw==", + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.52.0.tgz", + "integrity": "sha512-oV7XU4CHYfBhk78fS7tkum+/Dpgsfi91IIDy7fjCyq2k6KB63M6gMC0YIvy+iABzmXThCRI6xpCEyVObBdWSDQ==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.51.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.51.0.tgz", - "integrity": "sha512-TSkNupHvNRkoH9FMA3w7TazVFcBPveAAmb7Sz+kArY6sLT86PA5Vx80cKlYmd8m3Ha2SwofM1KwraF24lM9FvA==", + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.52.0.tgz", + "integrity": "sha512-WeWnjanyEwt6+fVrSR0MYgEpUAuROxuAH516WPjUblIrClzYJj0kBbjdnbQXLpgAN8qbEuGywiQsXUVDiAoEuQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.51.0", - "@typescript-eslint/visitor-keys": "5.51.0", + "@typescript-eslint/types": "5.52.0", + "@typescript-eslint/visitor-keys": "5.52.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -7355,28 +7355,28 @@ } }, "@typescript-eslint/utils": { - "version": "5.51.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.51.0.tgz", - "integrity": "sha512-76qs+5KWcaatmwtwsDJvBk4H76RJQBFe+Gext0EfJdC3Vd2kpY2Pf//OHHzHp84Ciw0/rYoGTDnIAr3uWhhJYw==", + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.52.0.tgz", + "integrity": "sha512-As3lChhrbwWQLNk2HC8Ree96hldKIqk98EYvypd3It8Q1f8d5zWyIoaZEp2va5667M4ZyE7X8UUR+azXrFl+NA==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.51.0", - "@typescript-eslint/types": "5.51.0", - "@typescript-eslint/typescript-estree": "5.51.0", + "@typescript-eslint/scope-manager": "5.52.0", + "@typescript-eslint/types": "5.52.0", + "@typescript-eslint/typescript-estree": "5.52.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0", "semver": "^7.3.7" } }, "@typescript-eslint/visitor-keys": { - "version": "5.51.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.51.0.tgz", - "integrity": "sha512-Oh2+eTdjHjOFjKA27sxESlA87YPSOJafGCR0md5oeMdh1ZcCfAGCIOL216uTBAkAIptvLIfKQhl7lHxMJet4GQ==", + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.52.0.tgz", + "integrity": "sha512-qMwpw6SU5VHCPr99y274xhbm+PRViK/NATY6qzt+Et7+mThGuFSl/ompj2/hrBlRP/kq+BFdgagnOSgw9TB0eA==", "dev": true, "requires": { - "@typescript-eslint/types": "5.51.0", + "@typescript-eslint/types": "5.52.0", "eslint-visitor-keys": "^3.3.0" } }, @@ -8655,9 +8655,9 @@ "dev": true }, "eslint": { - "version": "8.33.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.33.0.tgz", - "integrity": "sha512-WjOpFQgKK8VrCnAtl8We0SUOy/oVZ5NHykyMiagV1M9r8IFpIJX7DduK6n1mpfhlG7T1NLWm2SuD8QB7KFySaA==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.34.0.tgz", + "integrity": "sha512-1Z8iFsucw+7kSqXNZVslXS8Ioa4u2KM7GPwuKtkTFAqZ/cHMcEaR+1+Br0wLlot49cNxIiZk5wp8EAbPcYZxTg==", "dev": true, "requires": { "@eslint/eslintrc": "^1.4.1", diff --git a/package.json b/package.json index bba19a7f..6e6317a6 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "uc", "description": "UnrealScript Language support", "displayName": "UnrealScript", - "version": "0.6.2", + "version": "0.6.4", "author": { "name": "Eliot van Uytfanghe", "url": "https://EliotVU.com" @@ -50,11 +50,11 @@ "devDependencies": { "@tsconfig/recommended": "^1.0.2", "@types/node": "^18.13.0", - "@typescript-eslint/eslint-plugin": "^5.51.0", - "@typescript-eslint/parser": "^5.51.0", + "@typescript-eslint/eslint-plugin": "^5.52.0", + "@typescript-eslint/parser": "^5.52.0", "antlr4ts-cli": "0.5.0-alpha.4", "copy-webpack-plugin": "^11.0.0", - "eslint": "^8.33.0", + "eslint": "^8.34.0", "gulp": "^4.0.2", "js-yaml": "^4.1.0", "merge-options": "^3.0.4", diff --git a/server/package-lock.json b/server/package-lock.json index a745b3f7..c30052d9 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -1,19 +1,19 @@ { "name": "server", - "version": "0.6.2", + "version": "0.6.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "server", - "version": "0.6.2", + "version": "0.6.4", "license": "MIT", "dependencies": { "antlr4-c3": "^2.2.1", "crc-32": "^1.2.2", "glob": "^8.1.0", "rxjs": "^7.8.0", - "vscode-languageserver": "^8.0.2", + "vscode-languageserver": "^8.1.0", "vscode-languageserver-textdocument": "^1.0.8" }, "devDependencies": { @@ -1181,31 +1181,31 @@ } }, "node_modules/vscode-jsonrpc": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.0.2.tgz", - "integrity": "sha512-RY7HwI/ydoC1Wwg4gJ3y6LpU9FJRZAUnTYMXthqhFXXu77ErDd/xkREpGuk4MyYkk4a+XDWAMqe0S3KkelYQEQ==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.1.0.tgz", + "integrity": "sha512-6TDy/abTQk+zDGYazgbIPc+4JoXdwC8NHU9Pbn4UJP1fehUyZmM4RHp5IthX7A6L5KS30PRui+j+tbbMMMafdw==", "engines": { "node": ">=14.0.0" } }, "node_modules/vscode-languageserver": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-8.0.2.tgz", - "integrity": "sha512-bpEt2ggPxKzsAOZlXmCJ50bV7VrxwCS5BI4+egUmure/oI/t4OlFzi/YNtVvY24A2UDOZAgwFGgnZPwqSJubkA==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-8.1.0.tgz", + "integrity": "sha512-eUt8f1z2N2IEUDBsKaNapkz7jl5QpskN2Y0G01T/ItMxBxw1fJwvtySGB9QMecatne8jFIWJGWI61dWjyTLQsw==", "dependencies": { - "vscode-languageserver-protocol": "3.17.2" + "vscode-languageserver-protocol": "3.17.3" }, "bin": { "installServerIntoExtension": "bin/installServerIntoExtension" } }, "node_modules/vscode-languageserver-protocol": { - "version": "3.17.2", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.2.tgz", - "integrity": "sha512-8kYisQ3z/SQ2kyjlNeQxbkkTNmVFoQCqkmGrzLH6A9ecPlgTbp3wDTnUNqaUxYr4vlAcloxx8zwy7G5WdguYNg==", + "version": "3.17.3", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.3.tgz", + "integrity": "sha512-924/h0AqsMtA5yK22GgMtCYiMdCOtWTSGgUOkgEDX+wk2b0x4sAfLiO4NxBxqbiVtz7K7/1/RgVrVI0NClZwqA==", "dependencies": { - "vscode-jsonrpc": "8.0.2", - "vscode-languageserver-types": "3.17.2" + "vscode-jsonrpc": "8.1.0", + "vscode-languageserver-types": "3.17.3" } }, "node_modules/vscode-languageserver-textdocument": { @@ -1214,9 +1214,9 @@ "integrity": "sha512-1bonkGqQs5/fxGT5UchTgjGVnfysL0O8v1AYMBjqTbWQTFn721zaPGDYFkOKtfDgFiSgXM3KwaG3FMGfW4Ed9Q==" }, "node_modules/vscode-languageserver-types": { - "version": "3.17.2", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.2.tgz", - "integrity": "sha512-zHhCWatviizPIq9B7Vh9uvrH6x3sK8itC84HkamnBWoDFJtzBf7SWlpLCZUit72b3os45h6RWQNC9xHRDF8dRA==" + "version": "3.17.3", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.3.tgz", + "integrity": "sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA==" }, "node_modules/vscode-uri": { "version": "3.0.7", @@ -2173,25 +2173,25 @@ "dev": true }, "vscode-jsonrpc": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.0.2.tgz", - "integrity": "sha512-RY7HwI/ydoC1Wwg4gJ3y6LpU9FJRZAUnTYMXthqhFXXu77ErDd/xkREpGuk4MyYkk4a+XDWAMqe0S3KkelYQEQ==" + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.1.0.tgz", + "integrity": "sha512-6TDy/abTQk+zDGYazgbIPc+4JoXdwC8NHU9Pbn4UJP1fehUyZmM4RHp5IthX7A6L5KS30PRui+j+tbbMMMafdw==" }, "vscode-languageserver": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-8.0.2.tgz", - "integrity": "sha512-bpEt2ggPxKzsAOZlXmCJ50bV7VrxwCS5BI4+egUmure/oI/t4OlFzi/YNtVvY24A2UDOZAgwFGgnZPwqSJubkA==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-8.1.0.tgz", + "integrity": "sha512-eUt8f1z2N2IEUDBsKaNapkz7jl5QpskN2Y0G01T/ItMxBxw1fJwvtySGB9QMecatne8jFIWJGWI61dWjyTLQsw==", "requires": { - "vscode-languageserver-protocol": "3.17.2" + "vscode-languageserver-protocol": "3.17.3" } }, "vscode-languageserver-protocol": { - "version": "3.17.2", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.2.tgz", - "integrity": "sha512-8kYisQ3z/SQ2kyjlNeQxbkkTNmVFoQCqkmGrzLH6A9ecPlgTbp3wDTnUNqaUxYr4vlAcloxx8zwy7G5WdguYNg==", + "version": "3.17.3", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.3.tgz", + "integrity": "sha512-924/h0AqsMtA5yK22GgMtCYiMdCOtWTSGgUOkgEDX+wk2b0x4sAfLiO4NxBxqbiVtz7K7/1/RgVrVI0NClZwqA==", "requires": { - "vscode-jsonrpc": "8.0.2", - "vscode-languageserver-types": "3.17.2" + "vscode-jsonrpc": "8.1.0", + "vscode-languageserver-types": "3.17.3" } }, "vscode-languageserver-textdocument": { @@ -2200,9 +2200,9 @@ "integrity": "sha512-1bonkGqQs5/fxGT5UchTgjGVnfysL0O8v1AYMBjqTbWQTFn721zaPGDYFkOKtfDgFiSgXM3KwaG3FMGfW4Ed9Q==" }, "vscode-languageserver-types": { - "version": "3.17.2", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.2.tgz", - "integrity": "sha512-zHhCWatviizPIq9B7Vh9uvrH6x3sK8itC84HkamnBWoDFJtzBf7SWlpLCZUit72b3os45h6RWQNC9xHRDF8dRA==" + "version": "3.17.3", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.3.tgz", + "integrity": "sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA==" }, "vscode-uri": { "version": "3.0.7", diff --git a/server/package.json b/server/package.json index 5eab2757..78ac7038 100644 --- a/server/package.json +++ b/server/package.json @@ -5,7 +5,7 @@ "author": "Eliot van Uytfanghe", "publisher": "EliotVU", "license": "MIT", - "version": "0.6.2", + "version": "0.6.4", "repository": { "type": "git", "url": "https://github.com/EliotVU/UnrealScript-Language-Service" @@ -22,7 +22,7 @@ "crc-32": "^1.2.2", "glob": "^8.1.0", "rxjs": "^7.8.0", - "vscode-languageserver": "^8.0.2", + "vscode-languageserver": "^8.1.0", "vscode-languageserver-textdocument": "^1.0.8" }, "devDependencies": { diff --git a/server/src/UC/Parser/Parser.test.ts b/server/src/UC/Parser/Parser.test.ts index f76ff391..ecca9a52 100644 --- a/server/src/UC/Parser/Parser.test.ts +++ b/server/src/UC/Parser/Parser.test.ts @@ -21,7 +21,7 @@ function parseText(text: string): UCParser { const inputStream = UCInputStream.fromString(text); const lexer = new UCLexer(inputStream); const tokens = new UCTokenStream(lexer); - tokens.fill(); + // tokens.fill(); const parser = new UCParser(tokens); return parser; @@ -51,7 +51,7 @@ function parseExec(parser: UCParser): { errors: string[] } { describe('Grammar', () => { it('should have no syntax errors', () => { - const tests = ['parser.constDecl.uc', 'parser.defaultPropertiesBlock.uc']; + const tests = ['Grammar.uc', 'parser.constDecl.uc', 'parser.defaultPropertiesBlock.uc']; for (const testFileName of tests) { const text = getText(resolveExampleFileName(testFileName)); const p = parseText(text); diff --git a/server/src/UC/Symbols/ArrayOperations.ts b/server/src/UC/Symbols/ArrayOperations.ts index 4168ab6f..df7e0553 100644 --- a/server/src/UC/Symbols/ArrayOperations.ts +++ b/server/src/UC/Symbols/ArrayOperations.ts @@ -1,8 +1,13 @@ import { toName } from '../name'; import { NAME_ARRAY } from '../names'; import { - DEFAULT_RANGE, ModifierFlags, StaticIntType, StaticMetaType, UCMethodLikeSymbol, UCParamSymbol, - UCStructSymbol + DEFAULT_RANGE, + ModifierFlags, + StaticIntType, + StaticMetaType, + UCMethodLikeSymbol, + UCParamSymbol, + UCStructSymbol, } from './'; /** (defaultproperties) Acts as a template for array operations such as MyArray.Replace(item1, item2) etc. */ @@ -15,34 +20,29 @@ DefaultArray.addSymbol(EmptyOperation); const AddOperation = new UCMethodLikeSymbol(toName('Add')); AddOperation.modifiers |= ModifierFlags.Intrinsic | ModifierFlags.Keyword; -const AddElementParam = new UCParamSymbol({ name: toName('Element'), range: DEFAULT_RANGE }); -AddElementParam.type = StaticMetaType; +const AddElementParam = new UCParamSymbol({ name: toName('Element'), range: DEFAULT_RANGE }, DEFAULT_RANGE, StaticMetaType); AddOperation.addSymbol(AddElementParam); AddOperation.params = [AddElementParam]; DefaultArray.addSymbol(AddOperation); const RemoveOperation = new UCMethodLikeSymbol(toName('Remove')); RemoveOperation.modifiers |= ModifierFlags.Intrinsic | ModifierFlags.Keyword; -const RemoveElementParam = new UCParamSymbol({ name: toName('Element'), range: DEFAULT_RANGE }); -RemoveElementParam.type = StaticMetaType; +const RemoveElementParam = new UCParamSymbol({ name: toName('Element'), range: DEFAULT_RANGE }, DEFAULT_RANGE, StaticMetaType); RemoveOperation.addSymbol(RemoveElementParam); RemoveOperation.params = [RemoveElementParam]; DefaultArray.addSymbol(RemoveOperation); const RemoveIndexOperation = new UCMethodLikeSymbol(toName('RemoveIndex')); RemoveIndexOperation.modifiers |= ModifierFlags.Intrinsic | ModifierFlags.Keyword; -const RemoveIndexParam = new UCParamSymbol({ name: toName('Index'), range: DEFAULT_RANGE }); -RemoveIndexParam.type = StaticIntType; +const RemoveIndexParam = new UCParamSymbol({ name: toName('Index'), range: DEFAULT_RANGE }, DEFAULT_RANGE, StaticIntType); RemoveIndexOperation.addSymbol(RemoveIndexParam); RemoveIndexOperation.params = [RemoveIndexParam]; DefaultArray.addSymbol(RemoveIndexOperation); const ReplaceOperation = new UCMethodLikeSymbol(toName('Replace')); ReplaceOperation.modifiers |= ModifierFlags.Intrinsic | ModifierFlags.Keyword; -const ReplaceElement1Param = new UCParamSymbol({ name: toName('Element1'), range: DEFAULT_RANGE }); -ReplaceElement1Param.type = StaticMetaType; -const ReplaceElement2Param = new UCParamSymbol({ name: toName('Element2'), range: DEFAULT_RANGE }); -ReplaceElement2Param.type = StaticMetaType; +const ReplaceElement1Param = new UCParamSymbol({ name: toName('Element1'), range: DEFAULT_RANGE }, DEFAULT_RANGE, StaticMetaType); +const ReplaceElement2Param = new UCParamSymbol({ name: toName('Element2'), range: DEFAULT_RANGE }, DEFAULT_RANGE, StaticMetaType); ReplaceOperation.addSymbol(ReplaceElement1Param); ReplaceOperation.addSymbol(ReplaceElement2Param); ReplaceOperation.params = [ReplaceElement1Param, ReplaceElement2Param]; diff --git a/server/src/UC/Symbols/ClassSymbol.ts b/server/src/UC/Symbols/ClassSymbol.ts index 36d73d21..3e0bc15f 100644 --- a/server/src/UC/Symbols/ClassSymbol.ts +++ b/server/src/UC/Symbols/ClassSymbol.ts @@ -28,7 +28,9 @@ export class UCClassSymbol extends UCStructSymbol { | 1 << UCSymbolKind.Enum | 1 << UCSymbolKind.ScriptStruct | 1 << UCSymbolKind.Property - | 1 << UCSymbolKind.Function; + | 1 << UCSymbolKind.Function + | 1 << UCSymbolKind.Event + | 1 << UCSymbolKind.Delegate; declare outer: UCPackage; declare super?: UCClassSymbol; diff --git a/server/src/UC/Symbols/CoreSymbols.ts b/server/src/UC/Symbols/CoreSymbols.ts index 3eb63156..77af616f 100644 --- a/server/src/UC/Symbols/CoreSymbols.ts +++ b/server/src/UC/Symbols/CoreSymbols.ts @@ -24,12 +24,14 @@ import { NAME_PACKAGE, NAME_POINTERPROPERTY, NAME_PROPERTY, + NAME_ROTATOR, NAME_SCRIPTSTRUCT, NAME_STATE, NAME_STRINGPROPERTY, NAME_STRPROPERTY, NAME_STRUCT, NAME_STRUCTPROPERTY, + NAME_VECTOR, } from '../names'; import { addHashedSymbol, @@ -40,6 +42,7 @@ import { UCClassSymbol, UCPackage, UCPropertySymbol, + UCScriptStructSymbol, } from './'; export const CORE_PACKAGE = new UCPackage(NAME_CORE); @@ -49,20 +52,32 @@ export const IntrinsicObject = new UCClassSymbol({ name: NAME_OBJECT, range: DEF IntrinsicObject.modifiers |= ModifierFlags.Native | ModifierFlags.Abstract; IntrinsicObject.outer = CORE_PACKAGE; -export const Object_OuterProperty = new UCPropertySymbol({ name: NAME_OUTER, range: DEFAULT_RANGE }); +export const IntrinsicVector = new UCScriptStructSymbol({ name: NAME_VECTOR, range: DEFAULT_RANGE }); +IntrinsicVector.outer = IntrinsicObject; + +export const IntrinsicRotator = new UCScriptStructSymbol({ name: NAME_ROTATOR, range: DEFAULT_RANGE }); +IntrinsicRotator.outer = IntrinsicObject; + +export const Object_OuterProperty = new UCPropertySymbol( + { name: NAME_OUTER, range: DEFAULT_RANGE }, + DEFAULT_RANGE, StaticObjectType); Object_OuterProperty.modifiers |= ModifierFlags.Native; -Object_OuterProperty.type = StaticObjectType; Object_OuterProperty.outer = IntrinsicObject; +IntrinsicObject.addSymbol(Object_OuterProperty); -export const Object_NameProperty = new UCPropertySymbol({ name: NAME_NAME, range: DEFAULT_RANGE }); +export const Object_NameProperty = new UCPropertySymbol( + { name: NAME_NAME, range: DEFAULT_RANGE }, + DEFAULT_RANGE, StaticNameType); Object_NameProperty.modifiers |= ModifierFlags.Native; -Object_NameProperty.type = StaticNameType; Object_NameProperty.outer = IntrinsicObject; +IntrinsicObject.addSymbol(Object_NameProperty); -export const Object_ClassProperty = new UCPropertySymbol({ name: NAME_CLASS, range: DEFAULT_RANGE }); +export const Object_ClassProperty = new UCPropertySymbol( + { name: NAME_CLASS, range: DEFAULT_RANGE }, + DEFAULT_RANGE, StaticObjectType); Object_ClassProperty.modifiers |= ModifierFlags.Native; -Object_ClassProperty.type = StaticObjectType; Object_ClassProperty.outer = IntrinsicObject; +IntrinsicObject.addSymbol(Object_ClassProperty); export const IntrinsicField = new UCClassSymbol({ name: NAME_FIELD, range: DEFAULT_RANGE }); IntrinsicField.modifiers |= ModifierFlags.Intrinsic; diff --git a/server/src/UC/Symbols/EnumSymbol.ts b/server/src/UC/Symbols/EnumSymbol.ts index 525746ca..49baf134 100644 --- a/server/src/UC/Symbols/EnumSymbol.ts +++ b/server/src/UC/Symbols/EnumSymbol.ts @@ -1,12 +1,16 @@ - - import { UCDocument } from '../document'; import { Name } from '../name'; import { NAME_ENUMCOUNT } from '../names'; import { SymbolWalker } from '../symbolWalker'; import { - ContextKind, ISymbol, UCEnumMemberSymbol, UCFieldSymbol, UCStructSymbol, UCSymbolKind, - UCTypeKind + ContextKind, + ISymbol, + StaticEnumType, + UCEnumMemberSymbol, + UCFieldSymbol, + UCStructSymbol, + UCSymbolKind, + UCTypeKind, } from './'; export class UCEnumSymbol extends UCStructSymbol { @@ -18,37 +22,41 @@ export class UCEnumSymbol extends UCStructSymbol { public maxValue: number; public enumCountMember: UCEnumMemberSymbol; - override getTypeKind() { - return UCTypeKind.Byte; - } + override getTypeKind() { + return UCTypeKind.Enum; + } + + override getType() { + return StaticEnumType; + } protected override getTypeKeyword(): string { - return 'enum'; - } - - override getTooltip(): string { - return `${this.getTypeKeyword()} ${this.getPath()}`; - } - - override getCompletionSymbols(document: UCDocument, _context: ContextKind, _kinds?: UCSymbolKind): C[] { - const symbols: ISymbol[] = []; - for (let child = this.children; child; child = child.next) { - if (child.acceptCompletion(document, this)) { - symbols.push(child); - } - } + return 'enum'; + } + + override getTooltip(): string { + return `${this.getTypeKeyword()} ${this.getPath()}`; + } + + override getCompletionSymbols(document: UCDocument, _context: ContextKind, _kinds?: UCSymbolKind): C[] { + const symbols: ISymbol[] = []; + for (let child = this.children; child; child = child.next) { + if (child.acceptCompletion(document, this)) { + symbols.push(child); + } + } symbols.push(this.enumCountMember); - return symbols as C[]; - } + return symbols as C[]; + } override getSymbol(id: Name, kind?: UCSymbolKind): T | undefined { if (id === NAME_ENUMCOUNT) { return this.enumCountMember as unknown as T; } return super.getSymbol(id, kind); - } + } - override accept(visitor: SymbolWalker): Result | void { - return visitor.visitEnum(this); - } + override accept(visitor: SymbolWalker): Result | void { + return visitor.visitEnum(this); + } } \ No newline at end of file diff --git a/server/src/UC/Symbols/ISymbol.test.ts b/server/src/UC/Symbols/ISymbol.test.ts index 5791899a..1311aba1 100644 --- a/server/src/UC/Symbols/ISymbol.test.ts +++ b/server/src/UC/Symbols/ISymbol.test.ts @@ -2,9 +2,7 @@ import { expect } from 'chai'; import { toName } from '../name'; import { NAME_CLASS, NAME_PACKAGE } from '../names'; -import { - DEFAULT_RANGE, getOuter, UCClassSymbol, UCPackage, UCPropertySymbol, UCSymbolKind -} from './'; +import { DEFAULT_RANGE, getOuter, StaticNoneType, UCClassSymbol, UCPackage, UCPropertySymbol, UCSymbolKind } from './'; describe('Test ISymbol utilities', () => { const packageSymbol = new UCPackage(NAME_PACKAGE); @@ -12,11 +10,16 @@ describe('Test ISymbol utilities', () => { const classSymbol = new UCClassSymbol({ name: NAME_CLASS, range: DEFAULT_RANGE }); classSymbol.outer = packageSymbol; - const propertySymbol = new UCPropertySymbol({ name: toName('MyProperty'), range: DEFAULT_RANGE }); + const propertySymbol = new UCPropertySymbol({ + name: toName('MyProperty'), + range: DEFAULT_RANGE + }, DEFAULT_RANGE, StaticNoneType); classSymbol.addSymbol(propertySymbol); it('getOuter()', () => { - expect(getOuter(propertySymbol, UCSymbolKind.Class)).to.equal(classSymbol); - expect(getOuter(propertySymbol, UCSymbolKind.Package)).to.equal(packageSymbol); + expect(getOuter(propertySymbol, UCSymbolKind.Class)) + .to.equal(classSymbol); + expect(getOuter(propertySymbol, UCSymbolKind.Package)) + .to.equal(packageSymbol); }); }); diff --git a/server/src/UC/Symbols/ISymbol.ts b/server/src/UC/Symbols/ISymbol.ts index 3fc61183..8c9d316f 100644 --- a/server/src/UC/Symbols/ISymbol.ts +++ b/server/src/UC/Symbols/ISymbol.ts @@ -56,7 +56,6 @@ export interface ISymbolContainer { export type ContextInfo = { contextType?: ITypeSymbol; inAssignment?: boolean; - isOptional?: boolean; hasArguments?: boolean; isQualified?: boolean; }; diff --git a/server/src/UC/Symbols/IntrinsicArrayIterator.ts b/server/src/UC/Symbols/IntrinsicArrayIterator.ts index b241bbff..5705aa7b 100644 --- a/server/src/UC/Symbols/IntrinsicArrayIterator.ts +++ b/server/src/UC/Symbols/IntrinsicArrayIterator.ts @@ -1,15 +1,21 @@ +import { + DEFAULT_RANGE, + MethodFlags, + ModifierFlags, + StaticIntType, + StaticMetaType, + UCMethodLikeSymbol, + UCParamSymbol, +} from '.'; import { toName } from '../name'; -import { DEFAULT_RANGE, MethodFlags, ModifierFlags, StaticIntType, StaticMetaType, UCMethodLikeSymbol, UCParamSymbol } from '.'; export const ArrayIterator = new UCMethodLikeSymbol(toName('Iterator')); ArrayIterator.modifiers |= ModifierFlags.Intrinsic | ModifierFlags.ReadOnly | ModifierFlags.NoDeclaration; -ArrayIterator.specifiers |= MethodFlags.Iterator | MethodFlags.Static || MethodFlags.Final; -const OutParam = new UCParamSymbol({ name: toName('Element'), range: DEFAULT_RANGE }); -OutParam.type = StaticMetaType; +ArrayIterator.specifiers |= MethodFlags.Iterator | MethodFlags.Static | MethodFlags.Final; +const OutParam = new UCParamSymbol({ name: toName('Element'), range: DEFAULT_RANGE }, DEFAULT_RANGE, StaticMetaType); OutParam.modifiers |= ModifierFlags.Out; ArrayIterator.addSymbol(OutParam); -const IndexParam = new UCParamSymbol({ name: toName('Index'), range: DEFAULT_RANGE }); -IndexParam.type = StaticIntType; +const IndexParam = new UCParamSymbol({ name: toName('Index'), range: DEFAULT_RANGE }, DEFAULT_RANGE, StaticIntType); IndexParam.modifiers |= ModifierFlags.Out; ArrayIterator.addSymbol(IndexParam); ArrayIterator.params = [OutParam, IndexParam]; diff --git a/server/src/UC/Symbols/IntrinsicSymbols.ts b/server/src/UC/Symbols/IntrinsicSymbols.ts index eeef82ea..a13d3468 100644 --- a/server/src/UC/Symbols/IntrinsicSymbols.ts +++ b/server/src/UC/Symbols/IntrinsicSymbols.ts @@ -6,6 +6,7 @@ import { StaticDelegateType, StaticFloatType, StaticIntType, + StaticMetaType, StaticNameType, StaticRangeType, StaticRotatorType, @@ -24,17 +25,14 @@ export * from './EngineSymbols'; export const IntrinsicArray = new UCStructSymbol({ name: NAME_ARRAY, range: DEFAULT_RANGE }); IntrinsicArray.modifiers |= ModifierFlags.Intrinsic; -export const Array_LengthProperty = new UCPropertySymbol({ name: toName('Length'), range: DEFAULT_RANGE }); -Array_LengthProperty.type = StaticIntType; +export const Array_LengthProperty = new UCPropertySymbol({ name: toName('Length'), range: DEFAULT_RANGE }, DEFAULT_RANGE, StaticIntType); Array_LengthProperty.modifiers |= ModifierFlags.Intrinsic; IntrinsicArray.addSymbol(Array_LengthProperty); const Array_InsertFunction = new UCMethodSymbol({ name: toName('Insert'), range: DEFAULT_RANGE }); Array_InsertFunction.modifiers |= ModifierFlags.Intrinsic; -const IndexParam = new UCParamSymbol({ name: toName('Index'), range: DEFAULT_RANGE }); -IndexParam.type = StaticIntType; -const CountParam = new UCParamSymbol({ name: toName('Count'), range: DEFAULT_RANGE }); -CountParam.type = StaticIntType; +const IndexParam = new UCParamSymbol({ name: toName('Index'), range: DEFAULT_RANGE }, DEFAULT_RANGE, StaticIntType); +const CountParam = new UCParamSymbol({ name: toName('Count'), range: DEFAULT_RANGE }, DEFAULT_RANGE, StaticIntType); Array_InsertFunction.addSymbol(IndexParam); Array_InsertFunction.addSymbol(CountParam); Array_InsertFunction.params = [IndexParam, CountParam]; @@ -42,10 +40,8 @@ IntrinsicArray.addSymbol(Array_InsertFunction); const Array_RemoveFunction = new UCMethodSymbol({ name: toName('Remove'), range: DEFAULT_RANGE }); Array_RemoveFunction.modifiers |= ModifierFlags.Intrinsic; -const IndexParam2 = new UCParamSymbol({ name: toName('Index'), range: DEFAULT_RANGE }); -IndexParam2.type = StaticIntType; -const CountParam2 = new UCParamSymbol({ name: toName('Count'), range: DEFAULT_RANGE }); -CountParam2.type = StaticIntType; +const IndexParam2 = new UCParamSymbol({ name: toName('Index'), range: DEFAULT_RANGE }, DEFAULT_RANGE, StaticIntType); +const CountParam2 = new UCParamSymbol({ name: toName('Count'), range: DEFAULT_RANGE }, DEFAULT_RANGE, StaticIntType); Array_RemoveFunction.addSymbol(IndexParam2); Array_RemoveFunction.addSymbol(CountParam2); Array_RemoveFunction.params = [IndexParam2, CountParam2]; @@ -53,24 +49,22 @@ IntrinsicArray.addSymbol(Array_RemoveFunction); const Array_AddFunction = new UCMethodSymbol({ name: toName('Add'), range: DEFAULT_RANGE }); Array_AddFunction.modifiers |= ModifierFlags.Intrinsic; -const CountParam3 = new UCParamSymbol({ name: toName('Count'), range: DEFAULT_RANGE }); -CountParam3.type = StaticIntType; +const CountParam3 = new UCParamSymbol({ name: toName('Count'), range: DEFAULT_RANGE }, DEFAULT_RANGE, StaticIntType); Array_AddFunction.addSymbol(CountParam3); Array_AddFunction.params = [CountParam3]; IntrinsicArray.addSymbol(Array_AddFunction); const Array_AddItemFunction = new UCMethodSymbol({ name: toName('AddItem'), range: DEFAULT_RANGE }); Array_AddItemFunction.modifiers |= ModifierFlags.Intrinsic; -const ItemParam = new UCParamSymbol({ name: toName('Item'), range: DEFAULT_RANGE }); +const ItemParam = new UCParamSymbol({ name: toName('Item'), range: DEFAULT_RANGE }, DEFAULT_RANGE, StaticMetaType); Array_AddItemFunction.addSymbol(ItemParam); Array_AddItemFunction.params = [ItemParam]; IntrinsicArray.addSymbol(Array_AddItemFunction); const Array_InsertItemFunction = new UCMethodSymbol({ name: toName('InsertItem'), range: DEFAULT_RANGE }); Array_InsertItemFunction.modifiers |= ModifierFlags.Intrinsic; -const IndexParam3 = new UCParamSymbol({ name: toName('Index'), range: DEFAULT_RANGE }); -IndexParam3.type = StaticIntType; -const ItemParam2 = new UCParamSymbol({ name: toName('Item'), range: DEFAULT_RANGE }); +const IndexParam3 = new UCParamSymbol({ name: toName('Index'), range: DEFAULT_RANGE }, DEFAULT_RANGE, StaticIntType); +const ItemParam2 = new UCParamSymbol({ name: toName('Item'), range: DEFAULT_RANGE }, DEFAULT_RANGE, StaticMetaType); Array_InsertItemFunction.addSymbol(IndexParam3); Array_InsertItemFunction.addSymbol(ItemParam2); Array_InsertItemFunction.params = [IndexParam3, ItemParam2]; @@ -78,53 +72,43 @@ IntrinsicArray.addSymbol(Array_InsertItemFunction); const Array_RemoveItemFunction = new UCMethodSymbol({ name: toName('RemoveItem'), range: DEFAULT_RANGE }); Array_RemoveItemFunction.modifiers |= ModifierFlags.Intrinsic; -const ItemParam3 = new UCParamSymbol({ name: toName('Item'), range: DEFAULT_RANGE }); +const ItemParam3 = new UCParamSymbol({ name: toName('Item'), range: DEFAULT_RANGE }, DEFAULT_RANGE, StaticMetaType); Array_RemoveItemFunction.addSymbol(ItemParam3); Array_RemoveItemFunction.params = [ItemParam3]; IntrinsicArray.addSymbol(Array_RemoveItemFunction); const Array_FindFunction = new UCMethodSymbol({ name: toName('Find'), range: DEFAULT_RANGE }); Array_FindFunction.modifiers |= ModifierFlags.Intrinsic | ModifierFlags.ReadOnly; -const ItemParam4 = new UCParamSymbol({ name: toName('Value|PropertyName'), range: DEFAULT_RANGE }); +const ItemParam4 = new UCParamSymbol({ name: toName('Value|PropertyName'), range: DEFAULT_RANGE }, DEFAULT_RANGE, StaticMetaType); Array_FindFunction.addSymbol(ItemParam4); -const ItemParam5 = new UCParamSymbol({ name: toName('Value'), range: DEFAULT_RANGE }); -ItemParam5.type = StaticNameType; +const ItemParam5 = new UCParamSymbol({ name: toName('Value'), range: DEFAULT_RANGE }, DEFAULT_RANGE, StaticNameType); Array_FindFunction.addSymbol(ItemParam5); Array_FindFunction.params = [ItemParam4, ItemParam5]; IntrinsicArray.addSymbol(Array_FindFunction); const Array_SortFunction = new UCMethodSymbol({ name: toName('Sort'), range: DEFAULT_RANGE }); Array_SortFunction.modifiers |= ModifierFlags.Intrinsic; -const PredicateParam = new UCParamSymbol({ name: toName('Predicate'), range: DEFAULT_RANGE }); -PredicateParam.type = StaticDelegateType; +const PredicateParam = new UCParamSymbol({ name: toName('Predicate'), range: DEFAULT_RANGE }, DEFAULT_RANGE, StaticDelegateType); Array_SortFunction.addSymbol(PredicateParam); Array_SortFunction.params = [PredicateParam]; IntrinsicArray.addSymbol(Array_SortFunction); const ReturnValueIdentifier = { name: NAME_RETURNVALUE, range: DEFAULT_RANGE }; -const VectorReturnValue = new UCParamSymbol(ReturnValueIdentifier); -VectorReturnValue.type = StaticVectorType; - -const RotatorReturnValue = new UCParamSymbol(ReturnValueIdentifier); -RotatorReturnValue.type = StaticRotatorType; - -const RangeReturnValue = new UCParamSymbol(ReturnValueIdentifier); -RangeReturnValue.type = StaticRangeType; +const VectorReturnValue = new UCParamSymbol(ReturnValueIdentifier, DEFAULT_RANGE, StaticVectorType); +const RotatorReturnValue = new UCParamSymbol(ReturnValueIdentifier, DEFAULT_RANGE, StaticRotatorType); +const RangeReturnValue = new UCParamSymbol(ReturnValueIdentifier, DEFAULT_RANGE, StaticRangeType); export const IntrinsicVectLiteral = new UCMethodLikeSymbol(toName('Vect')); IntrinsicVectLiteral.returnValue = VectorReturnValue; -const XParam = new UCParamSymbol({ name: toName('X'), range: DEFAULT_RANGE }); -XParam.type = StaticFloatType; +const XParam = new UCParamSymbol({ name: toName('X'), range: DEFAULT_RANGE }, DEFAULT_RANGE, StaticFloatType); IntrinsicVectLiteral.addSymbol(XParam); -const YParam = new UCParamSymbol({ name: toName('Y'), range: DEFAULT_RANGE }); -YParam.type = StaticFloatType; +const YParam = new UCParamSymbol({ name: toName('Y'), range: DEFAULT_RANGE }, DEFAULT_RANGE, StaticFloatType); IntrinsicVectLiteral.addSymbol(YParam); -const ZParam = new UCParamSymbol({ name: toName('Z'), range: DEFAULT_RANGE }); -ZParam.type = StaticFloatType; +const ZParam = new UCParamSymbol({ name: toName('Z'), range: DEFAULT_RANGE }, DEFAULT_RANGE, StaticFloatType); IntrinsicVectLiteral.addSymbol(ZParam); IntrinsicVectLiteral.params = [XParam, YParam, ZParam]; @@ -132,16 +116,13 @@ IntrinsicVectLiteral.params = [XParam, YParam, ZParam]; export const IntrinsicRotLiteral = new UCMethodLikeSymbol(toName('Rot')); IntrinsicRotLiteral.returnValue = RotatorReturnValue; -const PitchParam = new UCParamSymbol({ name: toName('Pitch'), range: DEFAULT_RANGE }); -PitchParam.type = StaticIntType; +const PitchParam = new UCParamSymbol({ name: toName('Pitch'), range: DEFAULT_RANGE }, DEFAULT_RANGE, StaticIntType); IntrinsicRotLiteral.addSymbol(PitchParam); -const YawParam = new UCParamSymbol({ name: toName('Yaw'), range: DEFAULT_RANGE }); -YawParam.type = StaticIntType; +const YawParam = new UCParamSymbol({ name: toName('Yaw'), range: DEFAULT_RANGE }, DEFAULT_RANGE, StaticIntType); IntrinsicRotLiteral.addSymbol(YawParam); -const RollParam = new UCParamSymbol({ name: toName('Roll'), range: DEFAULT_RANGE }); -RollParam.type = StaticIntType; +const RollParam = new UCParamSymbol({ name: toName('Roll'), range: DEFAULT_RANGE }, DEFAULT_RANGE, StaticIntType); IntrinsicRotLiteral.addSymbol(RollParam); IntrinsicRotLiteral.params = [PitchParam, YawParam, RollParam]; @@ -149,12 +130,10 @@ IntrinsicRotLiteral.params = [PitchParam, YawParam, RollParam]; export const IntrinsicRngLiteral = new UCMethodLikeSymbol(toName('Rng')); IntrinsicRngLiteral.returnValue = RangeReturnValue; -const MinParam = new UCParamSymbol({ name: toName('Min'), range: DEFAULT_RANGE }); -MinParam.type = StaticFloatType; +const MinParam = new UCParamSymbol({ name: toName('Min'), range: DEFAULT_RANGE }, DEFAULT_RANGE, StaticFloatType); IntrinsicRngLiteral.addSymbol(MinParam); -const MaxParam = new UCParamSymbol({ name: toName('Max'), range: DEFAULT_RANGE }); -MaxParam.type = StaticFloatType; +const MaxParam = new UCParamSymbol({ name: toName('Max'), range: DEFAULT_RANGE }, DEFAULT_RANGE, StaticFloatType); IntrinsicRngLiteral.addSymbol(MaxParam); IntrinsicRngLiteral.params = [MinParam, MaxParam]; diff --git a/server/src/UC/Symbols/MethodSymbol.ts b/server/src/UC/Symbols/MethodSymbol.ts index 6036d509..c5055c70 100644 --- a/server/src/UC/Symbols/MethodSymbol.ts +++ b/server/src/UC/Symbols/MethodSymbol.ts @@ -5,8 +5,17 @@ import { Name } from '../name'; import { NAME_ACTOR, NAME_ENGINE, NAME_SPAWN } from '../names'; import { SymbolWalker } from '../symbolWalker'; import { - ContextKind, DEFAULT_RANGE, isFunction, ISymbol, ModifierFlags, UCFieldSymbol, UCObjectSymbol, - UCParamSymbol, UCStructSymbol, UCSymbolKind, UCTypeKind + ContextKind, + DEFAULT_RANGE, + isFunction, + ISymbol, + ModifierFlags, + UCFieldSymbol, + UCObjectSymbol, + UCParamSymbol, + UCStructSymbol, + UCSymbolKind, + UCTypeKind, } from './'; export enum MethodFlags { @@ -35,20 +44,20 @@ export class UCMethodSymbol extends UCStructSymbol { | 1 << UCSymbolKind.Property/** params and locals */; override kind = UCSymbolKind.Function; - override modifiers = ModifierFlags.ReadOnly; + override modifiers = ModifierFlags.ReadOnly; - public specifiers: MethodFlags = MethodFlags.None; + public specifiers: MethodFlags = MethodFlags.None; - public returnValue?: UCParamSymbol; - public overriddenMethod?: UCMethodSymbol; + public returnValue?: UCParamSymbol; + public overriddenMethod?: UCMethodSymbol; - public params?: UCParamSymbol[]; + public params?: UCParamSymbol[]; - /** - * Required count for a proper function call, this is exclusive of optional params. - * This value is initialized when analyze() is called on this symbol; remains undefined if no params. - */ - public requiredParamsCount?: number; + /** + * Required count for a proper function call, this is exclusive of optional params. + * This value is initialized when analyze() is called on this symbol; remains undefined if no params. + */ + public requiredParamsCount?: number; hasAnySpecifierFlags(flags: MethodFlags): boolean { return (this.specifiers & flags) !== 0; @@ -58,9 +67,9 @@ export class UCMethodSymbol extends UCStructSymbol { return (this.specifiers & MethodFlags.Delegate) !== 0; } - isOperatorKind(): this is UCBaseOperatorSymbol { + isOperatorKind(): this is UCBaseOperatorSymbol { return (this.specifiers & MethodFlags.OperatorKind) !== 0; - } + } /** * Returns true if this method is marked as a (binary) operator. @@ -70,154 +79,154 @@ export class UCMethodSymbol extends UCStructSymbol { return (this.specifiers & MethodFlags.Operator) !== 0; } - isPreOperator(): this is UCPreOperatorSymbol { - return (this.specifiers & MethodFlags.PreOperator) !== 0; - } + isPreOperator(): this is UCPreOperatorSymbol { + return (this.specifiers & MethodFlags.PreOperator) !== 0; + } - isPostOperator(): this is UCPostOperatorSymbol { - return (this.specifiers & MethodFlags.PostOperator) !== 0; - } + isPostOperator(): this is UCPostOperatorSymbol { + return (this.specifiers & MethodFlags.PostOperator) !== 0; + } - override getTypeKind() { - return UCTypeKind.Object; - } + override getTypeKind() { + return UCTypeKind.Object; + } - override getType() { - return this.returnValue?.getType(); - } + override getType() { + return this.returnValue?.getType(); + } - override getDocumentation(): string | undefined { - const doc = super.getDocumentation(); - if (doc) { - return doc; - } + override getDocumentation(): string | undefined { + const doc = super.getDocumentation(); + if (doc) { + return doc; + } - if (this.overriddenMethod) { - return this.overriddenMethod.getDocumentation(); - } - } + if (this.overriddenMethod) { + return this.overriddenMethod.getDocumentation(); + } + } - override getContainedSymbolAtPos(position: Position) { - return this.returnValue?.getSymbolAtPos(position) ?? super.getContainedSymbolAtPos(position); - } + override getContainedSymbolAtPos(position: Position) { + return this.returnValue?.getSymbolAtPos(position) ?? super.getContainedSymbolAtPos(position); + } - override getCompletionSymbols(document: UCDocument, context: ContextKind, kinds?: UCSymbolKind) { - if (context === ContextKind.DOT) { - const resolvedType = this.returnValue?.getType()?.getRef(); - if (resolvedType instanceof UCObjectSymbol) { - return resolvedType.getCompletionSymbols(document, context, kinds); - } - } + override getCompletionSymbols(document: UCDocument, context: ContextKind, kinds?: UCSymbolKind) { + if (context === ContextKind.DOT) { + const resolvedType = this.returnValue?.getType()?.getRef(); + if (resolvedType instanceof UCObjectSymbol) { + return resolvedType.getCompletionSymbols(document, context, kinds); + } + } - const symbols: ISymbol[] = []; + const symbols: ISymbol[] = []; for (let child = this.children; child; child = child.next) { - if (typeof kinds !== 'undefined' && ((1 << child.kind) & kinds) === 0) { - continue; - } - if (child.acceptCompletion(document, this)) { - symbols.push(child); - } - } + if (typeof kinds !== 'undefined' && ((1 << child.kind) & kinds) === 0) { + continue; + } + if (child.acceptCompletion(document, this)) { + symbols.push(child); + } + } const outerSymbols = this.outer.getCompletionSymbols(document, context, kinds); if (outerSymbols) { return outerSymbols.concat(symbols as C[]); } return symbols as C[]; - } - - override findSuperSymbol(id: Name, kind?: UCSymbolKind) { - return this.getSymbol(id, kind) ?? ((this.outer)).findSuperSymbol(id, kind); - } - - override index(document: UCDocument, context: UCStructSymbol) { - if (this.returnValue) { - this.returnValue.index(document, context); - - // For UC1 and UC2 we have to hardcode the "coerce" modifier for the Spawn's return type. - if (this.getName() === NAME_SPAWN - && this.outer?.getName() === NAME_ACTOR - && this.outer.outer?.getName() === NAME_ENGINE) { - this.returnValue.modifiers |= ModifierFlags.Coerce; - } - } - super.index(document, context); - } + } + + override findSuperSymbol(id: Name, kind?: UCSymbolKind) { + return this.getSymbol(id, kind) ?? ((this.outer)).findSuperSymbol(id, kind); + } + + override index(document: UCDocument, context: UCStructSymbol) { + if (this.returnValue) { + this.returnValue.index(document, context); + + // For UC1 and UC2 we have to hardcode the "coerce" modifier for the Spawn's return type. + if (this.getName() === NAME_SPAWN + && this.outer?.getName() === NAME_ACTOR + && this.outer.outer?.getName() === NAME_ENGINE) { + this.returnValue.modifiers |= ModifierFlags.Coerce; + } + } + super.index(document, context); + } protected override indexSuper(document: UCDocument, context: UCStructSymbol) { if (context.super) { - const symbolOverride = context.super.findSuperSymbol(this.getName()); - if (symbolOverride + const symbolOverride = context.super.findSuperSymbol(this.getName()); + if (symbolOverride && isFunction(symbolOverride) // Never override a private method && !symbolOverride.hasAnyModifierFlags(ModifierFlags.Private)) { - document.indexReference(symbolOverride, { - location: Location.create(document.uri, this.id.range) - }); - this.overriddenMethod = symbolOverride; + document.indexReference(symbolOverride, { + location: Location.create(document.uri, this.id.range) + }); + this.overriddenMethod = symbolOverride; this.super = symbolOverride; - } - } + } + } } protected override getTypeHint(): string | undefined { if (this.modifiers & ModifierFlags.Intrinsic) { - return '(intrinsic)'; - } + return '(intrinsic)'; + } - if (this.overriddenMethod) { + if (this.overriddenMethod) { return '(override)'; - } + } } - protected override getTypeKeyword(): string { - return 'function'; - } + protected override getTypeKeyword(): string { + return 'function'; + } public buildTypeKeyword(): string { return this.getTypeKeyword(); } - override getTooltip(): string { - const text: Array = []; + override getTooltip(): string { + const text: Array = []; - text.push(this.getTypeHint()); - const modifiers = this.buildModifiers(); + text.push(this.getTypeHint()); + const modifiers = this.buildModifiers(); if (modifiers.length > 0) { text.push(modifiers.join(' ')); } - text.push(this.getTypeKeyword()); - if (this.returnValue) { - text.push(this.returnValue.getTextForReturnValue()); - } - text.push(this.getPath() + this.buildParameters()); + text.push(this.getTypeKeyword()); + if (this.returnValue) { + text.push(this.returnValue.getTextForReturnValue()); + } + text.push(this.getPath() + this.buildParameters()); - return text.filter(s => s).join(' '); - } + return text.filter(s => s).join(' '); + } - override buildModifiers(): string[] { - const text = super.buildModifiers(); + override buildModifiers(): string[] { + const text = super.buildModifiers(); if (this.specifiers & MethodFlags.Final) { text.push('final'); } - if (this.specifiers & MethodFlags.Static) { - text.push('static'); - } + if (this.specifiers & MethodFlags.Static) { + text.push('static'); + } - return text; - } + return text; + } - public buildParameters(): string { - return this.params - ? `(${this.params.map(f => f.getTextForSignature()).join(', ')})` - : '()'; - } + public buildParameters(): string { + return this.params + ? `(${this.params.map(f => f.getTextForSignature()).join(', ')})` + : '()'; + } override accept(visitor: SymbolWalker): Result | void { - return visitor.visitMethod(this); - } + return visitor.visitMethod(this); + } } /** @@ -225,62 +234,66 @@ export class UCMethodSymbol extends UCStructSymbol { * For those particular 'methods' we want to pass back the @returnValue as our symbol's type. */ export class UCMethodLikeSymbol extends UCMethodSymbol { - override modifiers = ModifierFlags.ReadOnly | ModifierFlags.Intrinsic; + override modifiers = ModifierFlags.ReadOnly | ModifierFlags.Intrinsic; override specifiers = MethodFlags.Static | MethodFlags.Final; - constructor(name: Name) { - super({ name, range: DEFAULT_RANGE }); - } + constructor(name: Name) { + super({ name, range: DEFAULT_RANGE }); + } + + override getTypeKind() { + return UCTypeKind.Error; + } } export class UCEventSymbol extends UCMethodSymbol { override kind = UCSymbolKind.Event; - protected override getTypeKeyword(): string { - return 'event'; - } + protected override getTypeKeyword(): string { + return 'event'; + } } export class UCDelegateSymbol extends UCMethodSymbol { override kind = UCSymbolKind.Delegate; - override modifiers = ModifierFlags.None; + override modifiers = ModifierFlags.None; override getTypeKind() { - return UCTypeKind.Delegate; - } + return UCTypeKind.Delegate; + } - protected override getTypeKeyword(): string { - return 'delegate'; - } + protected override getTypeKeyword(): string { + return 'delegate'; + } } export abstract class UCBaseOperatorSymbol extends UCMethodSymbol { override kind = UCSymbolKind.Operator; - override acceptCompletion(_document: UCDocument, _context: ISymbol): boolean { - // TODO: Perhaps only list operators with a custom Identifier? i.e. "Dot" and "Cross". - return false; - } + override acceptCompletion(_document: UCDocument, _context: ISymbol): boolean { + // TODO: Perhaps only list operators with a custom Identifier? i.e. "Dot" and "Cross". + return false; + } } export class UCBinaryOperatorSymbol extends UCBaseOperatorSymbol { - precedence?: number; + precedence?: number; - protected override getTypeKeyword(): string { - return `operator(${this.precedence})`; - } + protected override getTypeKeyword(): string { + return `operator(${this.precedence})`; + } } export class UCPreOperatorSymbol extends UCBaseOperatorSymbol { - protected override getTypeKeyword(): string { - return 'preoperator'; - } + protected override getTypeKeyword(): string { + return 'preoperator'; + } } export class UCPostOperatorSymbol extends UCBaseOperatorSymbol { - protected override getTypeKeyword(): string { - return 'postoperator'; - } + protected override getTypeKeyword(): string { + return 'postoperator'; + } } export function areMethodsCompatibleWith(a: UCMethodSymbol, b: UCMethodSymbol): boolean { diff --git a/server/src/UC/Symbols/Package.ts b/server/src/UC/Symbols/Package.ts index 88b0e1f7..275628ac 100644 --- a/server/src/UC/Symbols/Package.ts +++ b/server/src/UC/Symbols/Package.ts @@ -21,6 +21,10 @@ export class SymbolsTable implements ISymbolContainer { return this.symbols.size; } + enumerate(): IterableIterator { + return this.symbols.values(); + } + *enumerateAll(): Generator { for (const symbol of this.symbols.values()) { for (let next = symbol.nextInHash; next; next = next.nextInHash as C) { @@ -36,7 +40,7 @@ export class SymbolsTable implements ISymbolContainer { if (((1 << symbol.kind) & kinds) !== 0) { yield symbol as C; } - + for (let next = symbol.nextInHash; next; next = next.nextInHash as C) { if (((1 << next.kind) & kinds) !== 0) { yield next as C; @@ -229,4 +233,16 @@ export function tryFindSymbolInPackage(id: Name, pkg: export function tryFindClassSymbol(id: Name, kind: UCSymbolKind = UCSymbolKind.Class): UCClassSymbol | undefined { const symbol = ObjectsTable.getSymbol(id.hash, kind) ?? findOrIndexClassSymbol(id); return symbol; +} + +/** Enumerates all global objects, including the chain linked objects. */ +export function* enumerateObjects(): Generator { + for (const symbol of ObjectsTable.enumerate()) { + for (let next = symbol.nextInHash; next; next = next.nextInHash) { + yield next; + } + yield symbol; + } + + return []; } \ No newline at end of file diff --git a/server/src/UC/Symbols/PropertySymbol.ts b/server/src/UC/Symbols/PropertySymbol.ts index cb07a595..0fcfa6c4 100644 --- a/server/src/UC/Symbols/PropertySymbol.ts +++ b/server/src/UC/Symbols/PropertySymbol.ts @@ -5,6 +5,7 @@ import { config, UCGeneration } from '../indexer'; import { SymbolWalker } from '../symbolWalker'; import { ContextKind, + Identifier, isConstSymbol, isEnumSymbol, isEnumTagSymbol, @@ -31,6 +32,11 @@ export class UCPropertySymbol extends UCFieldSymbol { // Array dimension is statically based on a declared symbol, such as a const or enum member. public arrayDimRef?: ITypeSymbol; public arrayDimRange?: Range; + + constructor(id: Identifier, range: Range, type: ITypeSymbol) { + super(id, range); + this.type = type; + } isDynamicArray(): this is { type: UCArrayTypeSymbol } { return (this.type?.getTypeKind() === UCTypeKind.Array); diff --git a/server/src/UC/Symbols/ScriptStructSymbol.ts b/server/src/UC/Symbols/ScriptStructSymbol.ts index 4d5be50d..2bd99dd0 100644 --- a/server/src/UC/Symbols/ScriptStructSymbol.ts +++ b/server/src/UC/Symbols/ScriptStructSymbol.ts @@ -1,9 +1,6 @@ import { UCDocument } from '../document'; import { SymbolWalker } from '../symbolWalker'; -import { - ContextKind, isFunction, isProperty, ISymbol, ModifierFlags, UCObjectSymbol, UCStructSymbol, - UCSymbolKind, UCTypeKind -} from './'; +import { ContextKind, ISymbol, ModifierFlags, UCObjectSymbol, UCStructSymbol, UCSymbolKind, UCTypeKind } from './'; export class UCScriptStructSymbol extends UCStructSymbol { static readonly allowedKindsMask = 1 << UCSymbolKind.Const diff --git a/server/src/UC/Symbols/Symbol.ts b/server/src/UC/Symbols/Symbol.ts index 67988228..0ce6b75e 100644 --- a/server/src/UC/Symbols/Symbol.ts +++ b/server/src/UC/Symbols/Symbol.ts @@ -23,7 +23,7 @@ export const DEFAULT_RANGE = Range.create(DEFAULT_POSITION, DEFAULT_POSITION); export const DEFAULT_IDENTIFIER: Identifier = { name: NAME_NONE, range: DEFAULT_RANGE -} ; +}; export const enum ContextKind { None, @@ -36,75 +36,77 @@ export const enum ContextKind { export abstract class UCObjectSymbol implements ISymbol, IWithInnerSymbols, IWithIndex { readonly kind: UCSymbolKind = UCSymbolKind.None; - public outer?: UCObjectSymbol; - public description?: Token[]; + public outer?: UCObjectSymbol = undefined; + public nextInHash?: UCObjectSymbol | undefined = undefined; + + public description?: Token[] = undefined; // TODO: Clarify id - constructor(public readonly id: Identifier) { + constructor(public readonly id: Identifier) { - } + } - /** - * Returns the whole range this symbol encompasses i.e. for a struct this should be inclusive of the entire block. - */ - getRange(): Range { - return this.id.range; - } + /** + * Returns the whole range this symbol encompasses i.e. for a struct this should be inclusive of the entire block. + */ + getRange(): Range { + return this.id.range; + } getName(): Name { - return this.id.name; - } + return this.id.name; + } // Particular use case to index symbol references by outer. - getHash(): number { - return getSymbolPathHash(this); - } + getHash(): number { + return getSymbolPathHash(this); + } - getPath(): string { + getPath(): string { const names: string[] = [this.id.name.text]; - for (let outer = this.outer; outer; outer = outer.outer) { + for (let outer = this.outer; outer; outer = outer.outer) { names.unshift(outer.id.name.text); - } - return names.join('.'); - } + } + return names.join('.'); + } - getTypeKind() { - return UCTypeKind.Error; - } + getTypeKind() { + return UCTypeKind.Error; + } - /** Returns a tooltip for this symbol, usually mirroring the written code, but minimalized and formatted. */ - getTooltip(): string { - return this.getPath(); - } + /** Returns a tooltip for this symbol, usually mirroring the written code, but minimalized and formatted. */ + getTooltip(): string { + return this.getPath(); + } - getSymbolAtPos(position: Position): ISymbol | undefined { - return intersectsWithRange(position, this.getRange()) && this.getContainedSymbolAtPos(position) || this; - } + getSymbolAtPos(position: Position): ISymbol | undefined { + return intersectsWithRange(position, this.getRange()) && this.getContainedSymbolAtPos(position) || this; + } - protected getContainedSymbolAtPos(_position: Position): ISymbol | undefined { - return undefined; - } + protected getContainedSymbolAtPos(_position: Position): ISymbol | undefined { + return undefined; + } - // TODO: Refactor ISymbol to CompletionItem. - getCompletionSymbols(_document: UCDocument, _context: ContextKind, _kinds?: UCSymbolKind): C[] { - return []; - } + // TODO: Refactor ISymbol to CompletionItem. + getCompletionSymbols(_document: UCDocument, _context: ContextKind, _kinds?: UCSymbolKind): C[] { + return []; + } - acceptCompletion(_document: UCDocument, _context: ISymbol): boolean { - return true; - } + acceptCompletion(_document: UCDocument, _context: ISymbol): boolean { + return true; + } - index(_document: UCDocument, _context: UCStructSymbol, _info?: ContextInfo) { + index(_document: UCDocument, _context: UCStructSymbol, _info?: ContextInfo) { // } - getDocumentation(): string | undefined { - return this.description?.map(t => t.text!).join('\n'); - } + getDocumentation(): string | undefined { + return this.description?.map(t => t.text!).join('\n'); + } - accept(visitor: SymbolWalker): Result | void { - return visitor.visit(this); - } + accept(visitor: SymbolWalker): Result | void { + return visitor.visit(this); + } toString(): string { return this.getPath(); diff --git a/server/src/UC/Symbols/TypeSymbol.ts b/server/src/UC/Symbols/TypeSymbol.ts index 8301be7b..1b5846d8 100644 --- a/server/src/UC/Symbols/TypeSymbol.ts +++ b/server/src/UC/Symbols/TypeSymbol.ts @@ -11,8 +11,11 @@ import { NAME_BUTTON, NAME_BYTE, NAME_DELEGATE, + NAME_ENUM, + NAME_ERROR, NAME_FLOAT, NAME_INT, + NAME_INTERFACE, NAME_MAP, NAME_NAME, NAME_NONE, @@ -21,6 +24,7 @@ import { NAME_RANGE, NAME_ROTATOR, NAME_STRING, + NAME_STRUCT, NAME_TYPE, NAME_VECTOR, } from '../names'; @@ -33,6 +37,8 @@ import { Identifier, INode, IntrinsicArray, + IntrinsicRotator, + IntrinsicVector, ISymbol, IWithIndex, IWithInnerSymbols, @@ -91,12 +97,13 @@ export const enum UCSymbolKind { Macro } -export enum UCTypeKind { +export const enum UCTypeKind { /** An unrecognized type */ Error, None, - Byte, // Also true for an enum member. - Int, // Also true for a pointer + Byte, + Enum, + Int, Bool, Float, Object, @@ -118,6 +125,28 @@ export enum UCTypeKind { Button } +export const TypeKindToName: Readonly> = new Map([ + [UCTypeKind.Error, NAME_ERROR], + [UCTypeKind.None, NAME_NONE], + [UCTypeKind.Byte, NAME_BYTE], + [UCTypeKind.Enum, NAME_ENUM], + [UCTypeKind.Int, NAME_INT], + [UCTypeKind.Bool, NAME_BOOL], + [UCTypeKind.Float, NAME_FLOAT], + [UCTypeKind.Object, NAME_OBJECT], + [UCTypeKind.Name, NAME_NAME], + [UCTypeKind.Delegate, NAME_DELEGATE], + [UCTypeKind.Interface, NAME_INTERFACE], + [UCTypeKind.Range, NAME_RANGE], + [UCTypeKind.Struct, NAME_STRUCT], + [UCTypeKind.Rotator, NAME_ROTATOR], + [UCTypeKind.String, NAME_STRING], + [UCTypeKind.Map, NAME_MAP], + [UCTypeKind.Array, NAME_ARRAY], + [UCTypeKind.Pointer, NAME_POINTER], + [UCTypeKind.Button, NAME_BUTTON], +]); + export interface ITypeSymbol extends ISymbol, IWithReference, IWithInnerSymbols, IWithIndex { getTypeText(): string; getTypeKind(): UCTypeKind; @@ -137,11 +166,11 @@ export class UCTypeSymbol implements ITypeSymbol { constructor( readonly type: UCTypeKind, - readonly range?: Range + readonly range: Range = DEFAULT_RANGE ) { } getName(): Name { - return TypeKindToName.get(this.type) ?? NAME_NONE; + return TypeKindToName.get(this.type)!; } getHash(): number { @@ -149,7 +178,7 @@ export class UCTypeSymbol implements ITypeSymbol { } getRange(): Range { - return this.range ?? DEFAULT_RANGE; + return this.range; } getPath(): string { @@ -496,7 +525,10 @@ export class UCQualifiedTypeSymbol implements ITypeSymbol { } } +export const StaticErrorType = new UCTypeSymbol(UCTypeKind.Error); +export const StaticNoneType = new UCTypeSymbol(UCTypeKind.None); export const StaticByteType = new UCTypeSymbol(UCTypeKind.Byte); +export const StaticEnumType = new UCTypeSymbol(UCTypeKind.Enum); export const StaticIntType = new UCTypeSymbol(UCTypeKind.Int); export const StaticBoolType = new UCTypeSymbol(UCTypeKind.Bool); export const StaticFloatType = new UCTypeSymbol(UCTypeKind.Float); @@ -504,7 +536,6 @@ export const StaticNameType = new UCTypeSymbol(UCTypeKind.Name); export const StaticStringType = new UCTypeSymbol(UCTypeKind.String); export const StaticPointerType = new UCTypeSymbol(UCTypeKind.Pointer); export const StaticButtonType = new UCTypeSymbol(UCTypeKind.Button); -export const StaticNoneType = new UCTypeSymbol(UCTypeKind.None); export const StaticObjectType = new UCObjectTypeSymbol({ name: NAME_OBJECT, range: DEFAULT_RANGE }, DEFAULT_RANGE); export const StaticArrayType = new UCArrayTypeSymbol({ name: NAME_ARRAY, range: DEFAULT_RANGE }); @@ -515,6 +546,9 @@ export const StaticRotatorType = new UCObjectTypeSymbol({ name: NAME_ROTATOR, ra export const StaticRangeType = new UCObjectTypeSymbol({ name: NAME_RANGE, range: DEFAULT_RANGE }); export const StaticMetaType = new UCMetaTypeSymbol({ name: NAME_TYPE, range: DEFAULT_RANGE }); +StaticVectorType.setRefNoIndex(IntrinsicVector); +StaticRotatorType.setRefNoIndex(IntrinsicRotator); + export const CastTypeSymbolMap: Readonly> = new WeakMap([ [NAME_BYTE, StaticByteType], [NAME_INT, StaticIntType], @@ -526,30 +560,6 @@ export const CastTypeSymbolMap: Readonly> = new WeakM [NAME_BUTTON, StaticBoolType] ]); -export const TypeKindToName: Readonly> = new Map([ - [UCTypeKind.None, NAME_NONE], - [UCTypeKind.Byte, NAME_BYTE], - [UCTypeKind.Int, NAME_INT], - [UCTypeKind.Bool, NAME_BOOL], - [UCTypeKind.Float, NAME_FLOAT], - [UCTypeKind.String, NAME_STRING], - [UCTypeKind.Name, NAME_NAME], - // <= UC2 - [UCTypeKind.Pointer, NAME_POINTER], - // UC2 - [UCTypeKind.Button, NAME_BUTTON], -]); - -export function quoteTypeFlags(kind: UCTypeKind): string { - const type = UCTypeKind[kind]; - if (process.env.NODE_ENV === 'development') { - if (typeof type === 'undefined') { - throw new Error(`Unknown type index '${kind}'.`); - } - } - return type.toString(); -} - /** No conversion allowed */ const N = 0x00; /** Conversion is allowed */ @@ -570,56 +580,57 @@ const D = 0x04; **/ /** @formatter:off */ const TypeConversionFlagsTable: Readonly<{ [key: number]: number[] }> = [ -/* From Error None Byte Int Bool Float Object Name Delegate Interface Range Struct Vector Rotator String Map Array Pointer +/* From Error None Byte Enum Int Bool Float Object Name Delegate Interface Range Struct Vector Rotator String Map Array Pointer /* To */ -/* Error */[N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N], -/* None */[N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N], -/* Byte */[N, N, N, Y | A, Y, Y | A, N, N, N, N, N, N, N, N, Y, N, N, N], -/* Int */[N, N, Y | A, N, Y, Y | A, N, N, N, N, N, N, N, N, Y, N, N, N], -/* Bool */[N, N, Y, Y | D, N, Y, Y, Y, N, Y, N, N, Y, Y, Y, N, N, N], -/* Float */[N, N, Y | A, Y | A, Y, N, N, N, N, N, N, N, N, N, Y, N, N, N], -/* Object */[N, Y | A, N, N, N, N, N, N, N, A, N, N, N, N, N, N, N, N], -/* Name */[N, Y | A, N, N, N | D, N, N, N, N, N, N, N, N, N, Y | D, N, N, N], -/* Delegate */[N, Y | A, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N], -/* Interface*/[N, Y | A, N, N, N, N, Y | A, N, N, N, N, N, N, N, N, N, N, N], -/* Range */[N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N], -/* Struct */[N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N], -/* Vector */[N, N, N, N, N, N, N, N, N, N, N, N, N, Y, Y, N, N, N], -/* Rotator */[N, N, N, N, N, N, N, N, N, N, N, N, Y, N, Y, N, N, N], -/* String */[N, N, Y, Y, Y, Y, Y, Y, Y, Y, N, N, Y, Y, N, N, N, N], -/* Map */[N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N], -/* Array */[N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N], -/* Pointer */[N, N, N, N | D, N, N, N, N, N, N, N, N, N, N, N, N, N, N], +/* Error */[N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N], +/* None */[N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N], +/* Byte */[N, N, N, Y | A, Y | A, Y, Y | A, N, N, N, N, N, N, N, N, Y, N, N, N], +/* Enum */[N, N, Y | A, N, Y | A, N, N, Y | A, N, N, N, N, N, N, N, Y, N, N, N], +/* Int */[N, N, Y | A, Y | A, N, Y, Y | A, N, N, N, N, N, N, N, N, Y, N, N, N], +/* Bool */[N, N, Y, N, Y | D, N, Y, Y, Y, N, Y, N, N, Y, Y, Y, N, N, N], +/* Float */[N, N, Y | A, N, Y | A, Y, N, N, N, N, N, N, N, N, N, Y, N, N, N], +/* Object */[N, Y | A, N, Y | A, N, N, N, N, N, N, A, N, N, N, N, N, N, N, N], +/* Name */[N, Y | A, N, N, N, N | D, N, N, N, N, N, N, N, N, N, Y | D, N, N, N], +/* Delegate */[N, Y | A, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N], +/* Interface*/[N, Y | A, N, N, N, N, N, Y | A, N, N, N, N, N, N, N, N, N, N, N], +/* Range */[N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N], +/* Struct */[N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N], +/* Vector */[N, N, N, N, N, N, N, N, N, N, N, N, N, N, Y, Y, N, N, N], +/* Rotator */[N, N, N, N, N, N, N, N, N, N, N, N, N, Y, N, Y, N, N, N], +/* String */[N, N, Y, N, Y, Y, Y, Y, Y, Y, Y, N, N, Y, Y, N, N, N, N], +/* Map */[N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N], +/* Array */[N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N], +/* Pointer */[N, N, N, N, N | D, N, N, N, N, N, N, N, N, N, N, N, N, N, N], ]; /** @formatter:on */ -export function getTypeConversionFlags(input: UCTypeKind, dest: UCTypeKind): number { - return TypeConversionFlagsTable[dest][input]; +export function getTypeConversionFlags(inputTypeKInd: UCTypeKind, destTypeKind: UCTypeKind): number { + return TypeConversionFlagsTable[destTypeKind][inputTypeKInd]; } -export function getConversionCost(input: ITypeSymbol, dest: ITypeSymbol): number { - let inputKind = input.getTypeKind(); - if (inputKind === UCTypeKind.Struct) { - if (input.getName() === NAME_VECTOR) { - inputKind = UCTypeKind.Vector; - } else if (input.getName() === NAME_ROTATOR) { - inputKind = UCTypeKind.Rotator; +export function getConversionCost(inputType: ITypeSymbol, destType: ITypeSymbol): number { + let inputTypeKind = inputType.getTypeKind(); + if (inputTypeKind === UCTypeKind.Struct) { + if (inputType.getName() === NAME_VECTOR) { + inputTypeKind = UCTypeKind.Vector; + } else if (inputType.getName() === NAME_ROTATOR) { + inputTypeKind = UCTypeKind.Rotator; } } - let destKind = dest.getTypeKind(); - if (destKind === UCTypeKind.Struct) { - if (dest.getName() === NAME_VECTOR) { - destKind = UCTypeKind.Vector; - } else if (dest.getName() === NAME_ROTATOR) { - destKind = UCTypeKind.Rotator; + let destTypeKind = destType.getTypeKind(); + if (destTypeKind === UCTypeKind.Struct) { + if (destType.getName() === NAME_VECTOR) { + destTypeKind = UCTypeKind.Vector; + } else if (destType.getName() === NAME_ROTATOR) { + destTypeKind = UCTypeKind.Rotator; } } - if (inputKind === destKind) { + if (inputTypeKind === destTypeKind) { return 1; } - const flags = getTypeConversionFlags(inputKind, destKind); + const flags = getTypeConversionFlags(inputTypeKind, destTypeKind); if (flags === N) { return -1; } @@ -640,43 +651,46 @@ export const enum UCMatchFlags { /** * (dest) SomeObject = (src) none; */ -export function typesMatch(input: ITypeSymbol, dest: ITypeSymbol, matchFlags: UCMatchFlags = UCMatchFlags.None): boolean { +export function typesMatch(inputType: ITypeSymbol, destType: ITypeSymbol, matchFlags: UCMatchFlags = UCMatchFlags.None): boolean { // Ignore types with no reference (Error) - let inputKind = input.getTypeKind(); - if (inputKind === UCTypeKind.Error) { + let inputTypeKind = inputType.getTypeKind(); + if (inputTypeKind === UCTypeKind.Error) { return true; } - let destKind = dest.getTypeKind(); - if (destKind === UCTypeKind.Error) { + let destTypeKind = destType.getTypeKind(); + if (destTypeKind === UCTypeKind.Error) { return true; } - if (inputKind === UCTypeKind.Struct) { - if (input.getName() === NAME_VECTOR) { - inputKind = UCTypeKind.Vector; - } else if (input.getName() === NAME_ROTATOR) { - inputKind = UCTypeKind.Rotator; + if (inputTypeKind === UCTypeKind.Struct) { + if (inputType.getName() === NAME_VECTOR) { + inputTypeKind = UCTypeKind.Vector; + } else if (inputType.getName() === NAME_ROTATOR) { + inputTypeKind = UCTypeKind.Rotator; } } - if (destKind === UCTypeKind.Struct) { - if (dest.getName() === NAME_VECTOR) { - destKind = UCTypeKind.Vector; - } else if (dest.getName() === NAME_ROTATOR) { - destKind = UCTypeKind.Rotator; + if (destTypeKind === UCTypeKind.Struct) { + if (destType.getName() === NAME_VECTOR) { + destTypeKind = UCTypeKind.Vector; + } else if (destType.getName() === NAME_ROTATOR) { + destTypeKind = UCTypeKind.Rotator; } } - if (inputKind === destKind) { + if (inputTypeKind === destTypeKind) { // TODO: Return a distinguisable return type return true; } - const c = getTypeConversionFlags(inputKind, destKind); + const c = getTypeConversionFlags(inputTypeKind, destTypeKind); if (c === N) { - if (destKind === UCTypeKind.Delegate) { - return input.getRef()?.kind === UCSymbolKind.Function; + if (destTypeKind === UCTypeKind.Delegate) { + return inputType.getRef()?.kind === UCSymbolKind.Function; + } + if (destTypeKind === UCTypeKind.Object) { + // TODO: Class hierarchy } return false; } @@ -685,7 +699,6 @@ export function typesMatch(input: ITypeSymbol, dest: ITypeSymbol, matchFlags: UC return true; } - // TODO: Class hierarchy // TODO: Struct (vector, rotator, range) conversions return (c & A) !== 0 || ((matchFlags & UCMatchFlags.Coerce) != 0 && (c & Y) !== 0); } diff --git a/server/src/UC/diagnostics/diagnostic.ts b/server/src/UC/diagnostics/diagnostic.ts index b155a456..b85adeed 100644 --- a/server/src/UC/diagnostics/diagnostic.ts +++ b/server/src/UC/diagnostics/diagnostic.ts @@ -1,59 +1,63 @@ import { Diagnostic, DiagnosticSeverity, Range } from 'vscode-languageserver-types'; export interface IDiagnosticNode { - getRange(): Range; + getRange(): Range; } export class ErrorDiagnostic implements IDiagnosticNode { - constructor(private range: Range, private error: string) { - } + constructor(private range: Range, private error: string) { + } - getRange(): Range { - return this.range; - } + getRange(): Range { + return this.range; + } - toString(): string { - return this.error; - } + toString(): string { + return this.error; + } } export interface IDiagnosticMessage { - text: string; - code?: string; - severity?: DiagnosticSeverity | number + text: string; + code?: string; + severity?: DiagnosticSeverity | number } export interface IDiagnosticTemplate { - range: Range; - message: IDiagnosticMessage; - args?: string[]; + range: Range; + message: IDiagnosticMessage; + args?: string[]; - custom?: { [key: string]: any }; + custom?: { [key: string]: any }; } export class DiagnosticCollection { - private items: IDiagnosticTemplate[] = []; + private items: IDiagnosticTemplate[] = []; - add(template: IDiagnosticTemplate) { - this.items.push(template); - } + add(template: IDiagnosticTemplate) { + this.items.push(template); + } count(): number { return this.items.length; } - toDiagnostic(): Diagnostic[] { - return this.items.map(template => { - const diagnostic: Diagnostic = { - range: template.range, - message: template.args - ? template.message.text.replace(/\{(\d)\}/g, (_match, index) => template.args![index]) - : template.message.text, - severity: template.message.severity as DiagnosticSeverity, - code: template.message.code, - source: 'unrealscript' - }; - return Object.assign(diagnostic, template.custom); - }); - } + toDiagnostic(): Diagnostic[] { + return this.items.map(template => { + const diagnostic: Diagnostic = { + range: template.range, + message: template.args + ? template.message.text.replace(/\{(\d)\}/g, (_match, index) => template.args![index]) + : template.message.text, + severity: template.message.severity as DiagnosticSeverity, + code: template.message.code, + source: 'unrealscript' + }; + return Object.assign(diagnostic, template.custom); + }); + } +} + +export function rangeToString(range: Range): string { + return `(${range.start.line + 1}:${range.start.character + 1})`; } \ No newline at end of file diff --git a/server/src/UC/diagnostics/documentAnalyzer.ts b/server/src/UC/diagnostics/documentAnalyzer.ts index 34f817cc..66155faf 100644 --- a/server/src/UC/diagnostics/documentAnalyzer.ts +++ b/server/src/UC/diagnostics/documentAnalyzer.ts @@ -1,31 +1,96 @@ -import { toName } from '../name'; import { DiagnosticSeverity, Range } from 'vscode-languageserver'; import { UCDocument } from '../document'; import { - IExpression, UCArrayCountExpression, UCAssignmentOperatorExpression, UCBaseOperatorExpression, - UCBinaryOperatorExpression, UCCallExpression, UCConditionalExpression, - UCDefaultAssignmentExpression, UCDefaultMemberCallExpression, UCDefaultStructLiteral, - UCElementAccessExpression, UCEmptyArgument, UCIdentifierLiteralExpression, UCMemberExpression, - UCMetaClassExpression, UCNameOfExpression, UCObjectLiteral, UCParenthesizedExpression, - UCPropertyAccessExpression, UCSizeOfLiteral, UCSuperExpression + IExpression, + UCArrayCountExpression, + UCAssignmentOperatorExpression, + UCBaseOperatorExpression, + UCBinaryOperatorExpression, + UCCallExpression, + UCConditionalExpression, + UCDefaultAssignmentExpression, + UCDefaultMemberCallExpression, + UCDefaultStructLiteral, + UCElementAccessExpression, + UCEmptyArgument, + UCIdentifierLiteralExpression, + UCMemberExpression, + UCMetaClassExpression, + UCNameOfExpression, + UCObjectLiteral, + UCParenthesizedExpression, + UCPropertyAccessExpression, + UCSizeOfLiteral, + UCSuperExpression, } from '../expressions'; import { config, getDocumentById, UCGeneration } from '../indexer'; +import { toName } from '../name'; import { NAME_ENUMCOUNT, NAME_NONE, NAME_STATE, NAME_STRUCT } from '../names'; import { - UCAssertStatement, UCBlock, UCCaseClause, UCDoUntilStatement, UCExpressionStatement, - UCForEachStatement, UCForStatement, UCGotoStatement, UCIfStatement, UCRepIfStatement, - UCReturnStatement, UCSwitchStatement, UCWhileStatement + UCAssertStatement, + UCBlock, + UCCaseClause, + UCDoUntilStatement, + UCExpressionStatement, + UCForEachStatement, + UCForStatement, + UCGotoStatement, + UCIfStatement, + UCRepIfStatement, + UCReturnStatement, + UCSwitchStatement, + UCWhileStatement, } from '../statements'; import { - areMethodsCompatibleWith, ArrayIterator, Array_LengthProperty, ClassModifierFlags, ContextInfo, IntrinsicClass, - IntrinsicEnum, isClass, isDelegateSymbol, isEnumSymbol, isField, isFunction, isMethodSymbol, - isStateSymbol, isTypeSymbol, ITypeSymbol, MethodFlags, ModifierFlags, quoteTypeFlags, - resolveType, StaticBoolType, StaticDelegateType, StaticIntType, StaticMetaType, StaticNameType, typesMatch, - UCArchetypeSymbol, UCArrayTypeSymbol, UCClassSymbol, UCConstSymbol, UCDelegateSymbol, - UCDelegateTypeSymbol, UCEnumMemberSymbol, UCEnumSymbol, UCFieldSymbol, UCInterfaceSymbol, UCMatchFlags, - UCMethodSymbol, UCObjectTypeSymbol, UCParamSymbol, UCPropertySymbol, UCQualifiedTypeSymbol, - UCScriptStructSymbol, UCStateSymbol, UCStructSymbol, UCSymbolKind, UCTypeKind + areMethodsCompatibleWith, + Array_LengthProperty, + ArrayIterator, + ClassModifierFlags, + ContextInfo, + IntrinsicClass, + IntrinsicEnum, + isClass, + isDelegateSymbol, + isEnumSymbol, + isEnumTagSymbol, + isField, + isFunction, + isMethodSymbol, + isStateSymbol, + isTypeSymbol, + ITypeSymbol, + MethodFlags, + ModifierFlags, + resolveType, + StaticBoolType, + StaticDelegateType, + StaticIntType, + StaticMetaType, + StaticNameType, + TypeKindToName, + typesMatch, + UCArchetypeSymbol, + UCArrayTypeSymbol, + UCClassSymbol, + UCConstSymbol, + UCDelegateSymbol, + UCDelegateTypeSymbol, + UCEnumMemberSymbol, + UCEnumSymbol, + UCFieldSymbol, + UCInterfaceSymbol, + UCMatchFlags, + UCMethodSymbol, + UCObjectTypeSymbol, + UCParamSymbol, + UCPropertySymbol, + UCQualifiedTypeSymbol, + UCScriptStructSymbol, + UCStateSymbol, + UCStructSymbol, + UCSymbolKind, + UCTypeKind, } from '../Symbols'; import { DefaultSymbolWalker } from '../symbolWalker'; import { DiagnosticCollection, IDiagnosticMessage } from './diagnostic'; @@ -34,12 +99,12 @@ import * as diagnosticMessages from './diagnosticMessages.json'; const OBJECT_DOCUMENT_ID = toName('Object'); // TODO: Check if a UField is obscuring another UField -export class DocumentAnalyzer extends DefaultSymbolWalker { - private scopes: UCStructSymbol[] = []; +export class DocumentAnalyzer extends DefaultSymbolWalker { + private readonly diagnostics = new DiagnosticCollection(); + private readonly scopes: UCStructSymbol[] = []; private context?: UCStructSymbol; private state: ContextInfo = {}; private cachedState: ContextInfo = {}; - private diagnostics = new DiagnosticCollection(); private allowedKindsMask: UCSymbolKind = 1 << UCClassSymbol.allowedKindsMask | 1 << UCSymbolKind.Class | 1 << UCSymbolKind.Interface; @@ -58,6 +123,10 @@ export class DocumentAnalyzer extends DefaultSymbolWalker { expect(grammarDocument.hasBeenIndexed).to.be.true; }); - it('Diagnostics free?', () => { + it('should have no problems', () => { const diagnoser = new DocumentAnalyzer(grammarDocument); - const diagnostics = diagnoser.visitDocument(grammarDocument); + grammarDocument.accept(diagnoser); + const diagnostics = diagnoser.getDiagnostics(); const msg = diagnostics.toDiagnostic() .map(d => d.message) .join(';'); diff --git a/server/src/UC/document.ts b/server/src/UC/document.ts index dfd50e51..45ef7daf 100644 --- a/server/src/UC/document.ts +++ b/server/src/UC/document.ts @@ -56,7 +56,7 @@ export class UCDocument { public readonly uri: DocumentUri; /** The current indexed TextDocument's version as reported by the client. */ - public indexedVersion: number = 0; + public indexedVersion = 0; // TODO: Displace this with a DiagnosticCollection visitor. public nodes: IDiagnosticNode[] = []; @@ -69,7 +69,7 @@ export class UCDocument { private readonly indexReferencesMade = new Map>(); // List of symbols, including macro declarations. - private scope = new SymbolsTable(); + private readonly scope = new SymbolsTable(); constructor(readonly filePath: string, public readonly classPackage: UCPackage) { this.fileName = path.basename(filePath, '.uc'); @@ -78,7 +78,7 @@ export class UCDocument { } public enumerateSymbols() { - return this.scope.enumerateAll(); + return this.scope.enumerate(); } public addSymbol(symbol: UCObjectSymbol): void { @@ -90,7 +90,7 @@ export class UCDocument { } public parse(text: string): DocumentParseData { - console.log('parsing document ' + this.fileName); + console.log(`parsing document "${this.fileName}"`); const inputStream = UCInputStream.fromString(text); const lexer = new UCLexer(inputStream); @@ -110,7 +110,7 @@ export class UCDocument { } catch (err) { console.error(err); } finally { - console.info(this.fileName + ': preprocessing time ' + (performance.now() - startPreprocressing)); + console.info(`${this.fileName}: preprocessing time ${performance.now() - startPreprocressing}`); } } } @@ -144,7 +144,7 @@ export class UCDocument { } public build(text: string = this.readText()): DocumentParseData { - console.log('building document ' + this.fileName); + console.log(`building document "${this.fileName}"`); const inputStream = UCInputStream.fromString(text); const lexer = new UCLexer(inputStream); @@ -166,7 +166,7 @@ export class UCDocument { } catch (err) { console.error(err); } finally { - console.info(this.fileName + ': preprocessing time ' + (performance.now() - startPreprocressing)); + console.info(`${this.fileName}: preprocessing time ${performance.now() - startPreprocressing}`); } } } @@ -208,7 +208,7 @@ export class UCDocument { err ); } - console.info(this.fileName + ': transforming time ' + (performance.now() - startWalking)); + console.info(`${this.fileName}: transforming time ${performance.now() - startWalking}`); } tokenStream.release(tokenStream.mark()); this.nodes = this.nodes.concat(errorListener.nodes); @@ -241,7 +241,9 @@ export class UCDocument { for (const [key, value] of this.indexReferencesMade) { const refs = IndexedReferencesMap.get(key); if (refs) { - value.forEach(ref => refs.delete(ref)); + for (const ref of value) { + refs.delete(ref); + } } } this.indexReferencesMade.clear(); diff --git a/server/src/UC/documentASTWalker.ts b/server/src/UC/documentASTWalker.ts index 298932ce..91827980 100644 --- a/server/src/UC/documentASTWalker.ts +++ b/server/src/UC/documentASTWalker.ts @@ -93,6 +93,7 @@ import { ITypeSymbol, MethodFlags, ModifierFlags, + StaticErrorType, UCArchetypeSymbol, UCArrayTypeSymbol, UCBinaryOperatorSymbol, @@ -789,8 +790,7 @@ export class DocumentASTWalker extends AbstractParseTreeVisitor implements name: NAME_RETURNVALUE, range: typeSymbol.getRange() }; - const returnValue = new UCParamSymbol(returnValueId); - returnValue.type = typeSymbol; + const returnValue = new UCParamSymbol(returnValueId, returnValueId.range, typeSymbol); returnValue.modifiers |= paramModifiers; this.declare(returnValue); @@ -860,8 +860,7 @@ export class DocumentASTWalker extends AbstractParseTreeVisitor implements const varNode = ctx.variable(); const identifier: Identifier = createIdentifier(varNode.identifier()); - const symbol = new UCParamSymbol(identifier, rangeFromBounds(ctx.start, ctx.stop)); - symbol.type = typeSymbol; + const symbol = new UCParamSymbol(identifier, rangeFromBounds(ctx.start, ctx.stop), typeSymbol); if (ctx._expr) { symbol.defaultExpression = ctx._expr.accept(this); modifiers |= ModifierFlags.Optional; @@ -968,7 +967,8 @@ export class DocumentASTWalker extends AbstractParseTreeVisitor implements const symbol: UCPropertySymbol = new type( identifier, // Stop at varCtx instead of localCtx for multiple variable declarations. - rangeFromBounds(ctx.parent!.start, ctx.stop) + rangeFromBounds(ctx.parent!.start, ctx.stop), + StaticErrorType ); this.initVariable(symbol, ctx); return symbol; diff --git a/server/src/UC/expressions.ts b/server/src/UC/expressions.ts index 365257ac..ccace630 100644 --- a/server/src/UC/expressions.ts +++ b/server/src/UC/expressions.ts @@ -4,22 +4,70 @@ import { UCDocument } from './document'; import { intersectsWith } from './helpers'; import { config, getEnumMember } from './indexer'; import { - CastTypeSymbolMap, ContextInfo, CORE_PACKAGE, DefaultArray, findOrIndexClassSymbol, - findOverloadedOperator, findSuperStruct, getConversionCost, getOuter, getSymbolHash, - getSymbolOuterHash, hasDefinedBaseType, Identifier, INode, IntrinsicClass, IntrinsicRngLiteral, - IntrinsicRotLiteral, IntrinsicVectLiteral, isConstSymbol, isFunction, isOperator, isProperty, - isStateSymbol, isStruct, ISymbol, ITypeSymbol, IWithIndex, IWithInnerSymbols, ModifierFlags, - ObjectsTable, OuterObjectsTable, resolveType, StaticBoolType, StaticByteType, StaticFloatType, - StaticIntType, StaticMetaType, StaticNameType, StaticNoneType, StaticRangeType, - StaticRotatorType, StaticStringType, StaticVectorType, tryFindClassSymbol, UCArrayTypeSymbol, - UCClassSymbol, UCMethodSymbol, UCNodeKind, UCObjectSymbol, UCObjectTypeSymbol, UCPackage, - UCQualifiedTypeSymbol, UCStructSymbol, UCSymbolKind, UCTypeKind, UCTypeSymbol + CastTypeSymbolMap, + ContextInfo, + CORE_PACKAGE, + DefaultArray, + findOrIndexClassSymbol, + findOverloadedOperator, + findSuperStruct, + getConversionCost, + getOuter, + getSymbolHash, + getSymbolOuterHash, + hasDefinedBaseType, + Identifier, + INode, + IntrinsicClass, + IntrinsicRngLiteral, + IntrinsicRotLiteral, + IntrinsicVectLiteral, + isConstSymbol, + isFunction, + isOperator, + isProperty, + isStateSymbol, + isStruct, + ISymbol, + ITypeSymbol, + IWithIndex, + IWithInnerSymbols, + ModifierFlags, + ObjectsTable, + OuterObjectsTable, + resolveType, + StaticBoolType, + StaticByteType, + StaticErrorType, + StaticFloatType, + StaticIntType, + StaticMetaType, + StaticNameType, + StaticNoneType, + StaticRangeType, + StaticRotatorType, + StaticStringType, + StaticVectorType, + tryFindClassSymbol, + UCArrayTypeSymbol, + UCClassSymbol, + UCMethodSymbol, + UCNodeKind, + UCObjectSymbol, + UCObjectTypeSymbol, + UCPackage, + UCQualifiedTypeSymbol, + UCStructSymbol, + UCSymbolKind, + UCTypeKind, + UCTypeSymbol, } from './Symbols'; import { SymbolWalker } from './symbolWalker'; export interface IExpression extends INode, IWithIndex, IWithInnerSymbols { getRange(): Range; getMemberSymbol(): ISymbol | undefined; + // TODO: Refactor to never return undefined, always return a StaticErrorType instead. getType(): ITypeSymbol | undefined; getSymbolAtPos(position: Position): ISymbol | undefined; getValue(): number | undefined; @@ -44,7 +92,7 @@ export abstract class UCExpression implements IExpression { } getType(): ITypeSymbol | undefined { - return undefined; + return StaticErrorType; } getSymbolAtPos(position: Position): ISymbol | undefined { @@ -216,6 +264,11 @@ export class UCCallExpression extends UCExpression { } index(document: UCDocument, context?: UCStructSymbol, info?: ContextInfo) { + if (!this.arguments?.length) { + this.expression.index(document, context); + return; + } + /** * The compiler has a hacky implementation for casts/function calls. * @@ -223,7 +276,7 @@ export class UCCallExpression extends UCExpression { * If it's a match, then the first argument is compiled, * if the argument is not an Object type or hit another argument, then it assumes it's a function call. */ - if (this.arguments?.length == 1 && this.expression instanceof UCMemberExpression) { + if (this.arguments.length == 1 && this.expression instanceof UCMemberExpression) { const member = this.expression; const name = member.id.name; @@ -253,23 +306,45 @@ export class UCCallExpression extends UCExpression { this.expression.index(document, context); } - const type = this.expression.getType(); - const symbol = type?.getRef(); - if (symbol && isFunction(symbol)) { - if (this.arguments) for (let i = 0; i < this.arguments.length; ++i) { - const arg = this.arguments[i]; - const param = symbol.params?.[i]; - const destType = param?.getType(); - arg.index(document, context, { - contextType: destType, - inAssignment: param ? param.hasAnyModifierFlags(ModifierFlags.Out) : undefined - }); + const methodType = this.expression.getType(); + const methodSymbol = methodType?.getRef(); + if (methodSymbol && isFunction(methodSymbol)) { + if (methodSymbol.params) { + const argInfo: ContextInfo = { + contextType: StaticErrorType, + inAssignment: false, + }; + for (let i = 0; i < this.arguments.length; ++i) { + if (i < methodSymbol.params.length) { + const param = methodSymbol.params[i]; + argInfo.contextType = param.getType(); + argInfo.inAssignment = param.hasAnyModifierFlags(ModifierFlags.Out); + this.arguments[i].index(document, context, argInfo); + } else { + // * Excessive arguments, we will still index the arguements. + this.arguments[i].index(document, context, { + contextType: StaticErrorType, + inAssignment: false + }); + } + } + } else { + // * Excessive arguments, we will still index the arguements. + for (let i = 0; i < this.arguments.length; ++i) { + this.arguments[i].index(document, context, { + contextType: StaticErrorType, + inAssignment: false + }); + } } } else { // TODO: Handle call on array?? - if (this.arguments) for (let i = 0; i < this.arguments.length; ++i) { + for (let i = 0; i < this.arguments.length; ++i) { const arg = this.arguments[i]; - arg.index(document, context, info); + arg.index(document, context, { + contextType: StaticErrorType, + inAssignment: false + }); } } } @@ -483,43 +558,55 @@ export class UCBinaryOperatorExpression extends UCExpression { return this.left?.getSymbolAtPos(position) ?? this.right?.getSymbolAtPos(position); } - index(document: UCDocument, context: UCStructSymbol, info: ContextInfo = {}) { + index(document: UCDocument, context: UCStructSymbol, info?: ContextInfo) { + let leftType: ITypeSymbol; if (this.left) { this.left.index(document, context, info); + leftType = getExpressionTypeSafe(this.left); + } else { + leftType = StaticErrorType; + } - const type = this.left.getType(); - info.contextType = type; + let rightType: ITypeSymbol; + if (this.right) { + this.right.index(document, context, { + contextType: leftType + }); + rightType = getExpressionTypeSafe(this.right); + } else { + rightType = StaticErrorType; } - this.right?.index(document, context, info); if (this.operator) { - const leftType = this.left?.getType(); - const rightType = this.right?.getType(); - if (leftType && rightType) { - const opId = this.operator.getName(); - const operatorSymbol = findOverloadedOperator(context, opId, ( - symbol => symbol.isBinaryOperator() - && symbol.params?.length === 2 - && getConversionCost(leftType, symbol.params[0].getType()) === 1 - && getConversionCost(rightType, symbol.params[1].getType()) === 1 - )); - if (operatorSymbol) { - this.operator.setRef(operatorSymbol, document); - } + const opId = this.operator.getName(); + const operatorSymbol = findOverloadedOperator(context, opId, ( + symbol => symbol.isBinaryOperator() + && symbol.params?.length === 2 + && getConversionCost(leftType, symbol.params[0].getType()) === 1 + && getConversionCost(rightType, symbol.params[1].getType()) === 1 + )); + if (operatorSymbol) { + this.operator.setRef(operatorSymbol, document); } } } } export class UCAssignmentOperatorExpression extends UCBinaryOperatorExpression { - index(document: UCDocument, context: UCStructSymbol, info: ContextInfo = {}) { + index(document: UCDocument, context: UCStructSymbol, info?: ContextInfo) { + let leftType: ITypeSymbol; if (this.left) { this.left.index(document, context, { inAssignment: true }); + leftType = getExpressionTypeSafe(this.left); + } else { + leftType = StaticErrorType; + } - const type = this.left.getType(); - info.contextType = type; + if (this.right) { + this.right.index(document, context, { + contextType: leftType + }); } - this.right?.index(document, context, info); } } @@ -537,8 +624,14 @@ export class UCMemberExpression extends UCExpression { getType() { const symbol = this.type?.getRef(); // We resolve UCMethodSymbols in UCCallExpression, because we don't want to return the function's type in assignment expressions... - if (symbol && (isProperty(symbol) || isConstSymbol(symbol))) { - return symbol.getType(); + if (symbol) { + if (isProperty(symbol)) { + return symbol.getType(); + } + + if (isConstSymbol(symbol)) { + return symbol.getType(); + } } return this.type; } @@ -551,7 +644,8 @@ export class UCMemberExpression extends UCExpression { index(document: UCDocument, context: UCStructSymbol, info?: ContextInfo) { const id = this.id.name; - const contextTypeKind = info?.contextType?.getTypeKind(); + const contextType = info?.contextType; + const contextTypeKind = contextType?.getTypeKind(); if (contextTypeKind === UCTypeKind.Name) { const label = context.labels?.[id.hash]; if (label) { @@ -567,11 +661,15 @@ export class UCMemberExpression extends UCExpression { let member = isStruct(context) && context.findSuperSymbol(id); if (!member) { // Look for a context-less enum tag reference, e.g. (myLocalByte === ET_EnumTag) - const doCheckForEnumTag = !config.checkTypes || ( - contextTypeKind === UCTypeKind.Byte || - contextTypeKind === UCTypeKind.Int - ); - if (doCheckForEnumTag) { + // Follow the compilers behavior: + // - Tags are only visible when we have a context hint that is compatible with an enum. + if (config.checkTypes) { + if (contextTypeKind === UCTypeKind.Byte || + contextTypeKind === UCTypeKind.Int || + contextTypeKind === UCTypeKind.Enum) { + member = getEnumMember(id); + } + } else { member = getEnumMember(id); } } @@ -638,27 +736,50 @@ export class UCDefaultMemberCallExpression extends UCExpression { this.propertyMember.index(document, context, info); const type = this.propertyMember.getType(); - if (type && UCArrayTypeSymbol.is(type)) { - const symbol = DefaultArray.findSuperSymbol(this.operationMember.id.name); - if (symbol) { - this.operationMember.setRef(symbol, document); - - const innerType = type.baseType; - if (innerType) { - const info: ContextInfo = { - inAssignment: true - }; - - if (this.arguments) for (let i = 0; i < this.arguments.length; ++i) { - const arg = this.arguments[i]; - const param = symbol.params && symbol.params.length > i && symbol.params[i]; - if (param && param.type === StaticMetaType) { - info.contextType = innerType; - } - arg.index(document, context, info); - } + const isArrayType = type && UCArrayTypeSymbol.is(type); + const methodSymbol = isArrayType + ? DefaultArray.findSuperSymbol(this.operationMember.id.name) + : undefined; + if (methodSymbol) { + this.operationMember.setRef(methodSymbol, document); + } + + if (!this.arguments) { + return; + } + + const typeParameter = isArrayType + ? type.baseType + : StaticErrorType; + if (methodSymbol?.params) { + const argInfo: ContextInfo = { + contextType: StaticErrorType, + inAssignment: false, + }; + for (let i = 0; i < this.arguments.length; ++i) { + if (i < methodSymbol.params.length) { + const param = methodSymbol.params[i]; + const paramType = param.getType(); + argInfo.contextType = paramType === StaticMetaType && typeParameter + ? typeParameter + : paramType; + argInfo.inAssignment = param.hasAnyModifierFlags(ModifierFlags.Out); + this.arguments[i].index(document, context, argInfo); + } else { + // * Excessive arguments, we will still index the arguements. + this.arguments[i].index(document, context, { + contextType: typeParameter, + inAssignment: false + }); } } + } else { + if (this.arguments) for (let i = 0; i < this.arguments.length; ++i) { + this.arguments[i].index(document, context, { + contextType: typeParameter, + inAssignment: false + }); + } } } } @@ -689,6 +810,7 @@ export class UCIdentifierLiteralExpression extends UCMemberExpression { switch (kind) { case UCTypeKind.Byte: case UCTypeKind.Int: + case UCTypeKind.Enum: // FIXME: Constants should take precedence member = getEnumMember(name); break; @@ -842,7 +964,7 @@ export abstract class UCLiteral implements IExpression { return undefined; } - getType(): ITypeSymbol | undefined{ + getType(): ITypeSymbol | undefined { return undefined; } @@ -1108,4 +1230,8 @@ export class UCMetaClassExpression extends UCExpression { this.classRef?.index(document, context!); this.expression?.index(document, context, info); } +} + +function getExpressionTypeSafe(expr: IExpression): ITypeSymbol { + return expr.getType() ?? StaticErrorType; } \ No newline at end of file diff --git a/server/src/UC/indexer.ts b/server/src/UC/indexer.ts index 89b41a60..ddb35d34 100644 --- a/server/src/UC/indexer.ts +++ b/server/src/UC/indexer.ts @@ -57,7 +57,7 @@ export const defaultSettings: UCLanguageServerSettings = { generation: UCGeneration.UC3, licensee: UELicensee.Epic, analyzeDocuments: EAnalyzeOption.OnlyActive, - checkTypes: false, + checkTypes: true, macroSymbols: { 'debug': '' }, @@ -112,7 +112,7 @@ export function indexDocument(document: UCDocument, text?: string): void { // We set this here to prevent any re-triggering within the following indexing process. document.hasBeenIndexed = true; if (document.class) { - for (let symbol of document.enumerateSymbols()) { + for (const symbol of document.enumerateSymbols()) { symbol.index(document, document.class); } } diff --git a/server/src/UC/names.ts b/server/src/UC/names.ts index f55b451c..0a5285e0 100644 --- a/server/src/UC/names.ts +++ b/server/src/UC/names.ts @@ -1,5 +1,6 @@ import { toName } from './name'; +export const NAME_ERROR = toName('Error'); export const NAME_NONE = toName('None'); export const NAME_BYTE = toName('Byte'); export const NAME_FLOAT = toName('Float'); diff --git a/server/src/UC/test/enum/EnumTest.uc b/server/src/UC/test/enum/EnumTest.uc index 533de8b7..ccf08ae6 100644 --- a/server/src/UC/test/enum/EnumTest.uc +++ b/server/src/UC/test/enum/EnumTest.uc @@ -1,33 +1,80 @@ class EnumTest; -enum EEnumTest { +enum EEnumTest +{ ET_None, ET_Other }; var EEnumTest MyEnumProperty; +/*UC3*/ var EEnumTest MyEnumBasedDimProperty[EEnumTest]; - // FIXME + +// FIXME // var EEnumTest MyQualifiedEnumBasedDimProperty[EEnumTest.EnumCount]; -function EEnumTest EnumTestMethod(EEnumTest p1 = ET_None, EEnumTest p2 = EEnumTest.ET_None) { +var array MyEnumArrayProperty; + +function EnumObjectTest(Enum object); +function EnumObjectTest2(Object object); +function EnumByteTest(int b); +function EnumIntTest(int i); + +/* Test all kind of situations where we need a byte/int hint to resolve an identifier to an enum tag. */ +function EEnumTest EnumHintTest(EEnumTest p1 = ET_Max) +{ + local byte b1; + + // in assignments p1 = EEnumTest.EnumCount; - p2 = ET_None; - p2 = ET_Other; + p1 = EEnumTest.ET_None; + p1 = ET_Other; + p1 = 0; + p1 = byte(0); + p1 = EnumHintTest(p1); + b1 = p1; + b1 = EnumHintTest(p1); + + // in switch cases switch (p1) { - case EEnumTest.EnumCount: - case ET_None: + case EEnumTest.EnumCount: return ET_Other; + case EEnumTest.ET_None: case ET_Other: + case 0: + case byte(0): break; } - return ET_None; - // FIXME: Missing enum hint, and type coercing is broken. - return String(self) != "none" + // in binary operators + if (EnumHintTest(0) != ET_Other) { + return 0; + } + + // in literals + EnumObjectTest(Enum'EEnumTest'); + EnumObjectTest2(Enum'EEnumTest'); + + // in arguments + EnumHintTest(p1); + EnumHintTest(ET_Other); + EnumHintTest(0); + EnumHintTest(byte(0)); + + // coerce + EnumIntTest(p1); // as a byte + EnumByteTest(EEnumTest.EnumCount); + EnumIntTest(ET_Other); + + // in returns + return ET_Max; +} + +// FIXME: Missing enum hint, and type coercing is broken. +function EEnumTest EnumConditionalHintTest(bool bOther) +{ + return bOther == true ? ET_Other : ET_None; - - Enum'EEnumTest'; } defaultproperties @@ -38,4 +85,7 @@ defaultproperties // MyEnumBasedDimProperty(EEnumTest.ET_None)=ET_None // FIXME // MyEnumProperty=EEnumTest.ET_None + + // Verify that the enum hint is picked up. + MyEnumArrayProperty.Add(ET_None) } \ No newline at end of file diff --git a/server/src/UC/test/enum/enum.test.ts b/server/src/UC/test/enum/enum.test.ts index ed585f4f..621c62af 100644 --- a/server/src/UC/test/enum/enum.test.ts +++ b/server/src/UC/test/enum/enum.test.ts @@ -1,31 +1,33 @@ import { expect } from 'chai'; -import { - UCAssignmentOperatorExpression, - UCDefaultAssignmentExpression, - UCDefaultElementAccessExpression, - UCObjectLiteral, -} from '../../expressions'; +import { rangeToString } from '../../diagnostics/diagnostic'; +import { DocumentAnalyzer } from '../../diagnostics/documentAnalyzer'; +import { UCDefaultAssignmentExpression, UCDefaultElementAccessExpression } from '../../expressions'; import { queueIndexDocument } from '../../indexer'; import { toName } from '../../name'; -import { NAME_ENUMCOUNT } from '../../names'; -import { UCExpressionStatement, UCSwitchStatement } from '../../statements'; +import { NAME_DEFAULTPROPERTIES, NAME_ENUMCOUNT } from '../../names'; import { - IntrinsicEnum, + addHashedSymbol, + IntrinsicObject, + removeHashedSymbol, UCDefaultPropertiesBlock, UCEnumMemberSymbol, + UCEnumSymbol, UCMethodSymbol, UCPropertySymbol, UCSymbolKind, } from '../../Symbols'; +import { assertBinaryOperatorExpressionMemberSymbol, assertExpressionStatement } from '../utils/codeAsserts'; import { usingDocuments } from '../utils/utils'; -describe('Enum', () => { +describe('Enum usage', () => { usingDocuments(__dirname, ['EnumTest.uc'], ([testDocument]) => { + addHashedSymbol(IntrinsicObject); queueIndexDocument(testDocument); + removeHashedSymbol(IntrinsicObject); const documentClass = testDocument.class; - const enumTestSymbol = documentClass.getSymbol(toName('EEnumTest')); + const enumTestSymbol = documentClass.getSymbol(toName('EEnumTest')); expect(enumTestSymbol) .to.not.be.undefined; @@ -48,7 +50,7 @@ describe('Enum', () => { // Not yet globally indexed // TODO: Implement globally to enable support for Enum'EEnumTest'; // it('Enum EEnumTest is indexed', () => { - // const globalSymbol = ObjectsTable.getSymbol(toName('EEnumTest'), UCTypeFlags.Enum); + // const globalSymbol = ObjectsTable.getSymbol(toName('EEnumTest'), UCSymbolKind.Enum); // expect(globalSymbol).to.not.be.undefined; // }); @@ -64,77 +66,57 @@ describe('Enum', () => { }); it('Usage in Methods', () => { - const symbol = documentClass.getSymbol(toName('EnumTestMethod')); + const symbol = documentClass.getSymbol(toName('EnumHintTest')); expect(symbol, 'symbol') .to.not.be.undefined; expect(symbol.returnValue.getType().getRef()) + .is.equal(enumTestSymbol); + + expect(symbol.params[0].getType().getRef()) .to.equal(enumTestSymbol); - for (const param of symbol.params) { - expect(param.getType().getRef()) - .to.equal(enumTestSymbol); - expect(param.defaultExpression.getType().getRef().outer) - .to.equal(enumTestSymbol); - } - - expect(symbol.block, 'method block') - .to.not.be.undefined; - for (const stm of symbol.block.statements) { - if (stm instanceof UCSwitchStatement) { - const expr = stm.expression; - expect(expr.getType().getRef()) - .to.equal(enumTestSymbol); - for (const stm2 of stm.then.statements) { - if (stm2 instanceof UCExpressionStatement) { - expect(stm2.expression.getType().getRef().outer) - .to.equal(enumTestSymbol); - } - } - } else if (stm instanceof UCExpressionStatement) { - const expr = stm.expression; - if (expr instanceof UCAssignmentOperatorExpression) { - expect(expr.left.getType().getRef()) - .to.equal(enumTestSymbol); - expect(expr.right.getType().getRef().outer) - .to.equal(enumTestSymbol); - } else if (expr instanceof UCObjectLiteral) { - expect(expr.castRef.getRef(), 'castRef') - .to.equal(IntrinsicEnum, 'enum class'); - expect(expr.objectRef.getRef(), 'objectRef') - .to.equal(enumTestSymbol, 'enum object'); - } else { - expect(stm.expression.getType().getRef().outer) - .to.equal(enumTestSymbol, 'enum object'); - } - } - } + expect(symbol.params[0].defaultExpression.getType().getRef()) + .to.equal(enumTestSymbol.getSymbol(toName('ET_Max'))); }); it('Usage in DefaultProperties', () => { const symbol = documentClass.getSymbol( - toName('Default'), + NAME_DEFAULTPROPERTIES, UCSymbolKind.DefaultPropertiesBlock); expect(symbol, 'symbol') .to.not.be.undefined; + expect(symbol.block, 'symbol block') .to.not.be.undefined; + const block = symbol.block; + // MyEnumProperty=ET_None - { - const stm = symbol.block.statements[0] as UCDefaultAssignmentExpression; - expect(stm.left.getType().getRef(), 'MyEnumProperty') - .to.equal(enumTestSymbol); - expect(stm.right.getType().getRef().outer, 'ET_None') - .to.equal(enumTestSymbol); - } + assertBinaryOperatorExpressionMemberSymbol( + assertExpressionStatement(block.statements[0]).expression, + documentClass.getSymbol(toName('MyEnumProperty')), + enumTestSymbol.getSymbol(toName('ET_None'))); + // MyEnumBasedDimProperty(ET_None)=ET_None - { - const stm = symbol.block.statements[1] as UCDefaultElementAccessExpression; - expect(stm.expression.getType().getRef(), 'MyEnumBasedDimProperty') - .to.equal(enumTestSymbol); - expect(stm.argument.getType().getRef().outer, '(ET_None)') - .to.equal(enumTestSymbol); - } + assertBinaryOperatorExpressionMemberSymbol( + assertExpressionStatement(block.statements[1]).expression, + documentClass.getSymbol(toName('MyEnumBasedDimProperty')), + enumTestSymbol.getSymbol(toName('ET_None'))); + + expect(((assertExpressionStatement(block.statements[1]).expression as UCDefaultAssignmentExpression).left as UCDefaultElementAccessExpression) + .argument.getMemberSymbol()) + .to.equal(enumTestSymbol.getSymbol(toName('ET_None'))); + }); + + it('should have no problems', () => { + const diagnoser = new DocumentAnalyzer(testDocument); + documentClass.accept(diagnoser); + const diagnostics = diagnoser.getDiagnostics(); + const msg = diagnostics.toDiagnostic() + .map(d => `${rangeToString(d.range)}: ${d.message}`) + .join('\n'); + expect(diagnostics.count(), msg) + .is.equal(0); }); }); -}); \ No newline at end of file +}); diff --git a/server/src/UC/test/struct/StructTest.uc b/server/src/UC/test/struct/StructTest.uc new file mode 100644 index 00000000..1356a391 --- /dev/null +++ b/server/src/UC/test/struct/StructTest.uc @@ -0,0 +1,22 @@ +class StructTest; + +struct Vector +{ + var float X, Y, Z; +}; + +static final operator(16) Vector * (float A, Vector B); +static final operator(16) Vector * (Vector A, float B); + +function TypeOperatorsTest() +{ + local Vector vector; + + // intrinsics + vector = vector; + vector = vect(1, 1, 0); + + // operators + vector = 1.0 * vect(1, 1, 0); + vector = vect(1, 1, 0) * 1.0; +} \ No newline at end of file diff --git a/server/src/UC/test/struct/struct.test.ts b/server/src/UC/test/struct/struct.test.ts new file mode 100644 index 00000000..bdbb6312 --- /dev/null +++ b/server/src/UC/test/struct/struct.test.ts @@ -0,0 +1,28 @@ +import { expect } from 'chai'; + +import { rangeToString } from '../../diagnostics/diagnostic'; +import { DocumentAnalyzer } from '../../diagnostics/documentAnalyzer'; +import { queueIndexDocument } from '../../indexer'; +import { addHashedSymbol, IntrinsicObject, removeHashedSymbol } from '../../Symbols'; +import { usingDocuments } from '../utils/utils'; + +describe('Struct usage', () => { + usingDocuments(__dirname, ['StructTest.uc'], ([testDocument]) => { + addHashedSymbol(IntrinsicObject); + queueIndexDocument(testDocument); + removeHashedSymbol(IntrinsicObject); + + const documentClass = testDocument.class; + + it('should have no problems', () => { + const diagnoser = new DocumentAnalyzer(testDocument); + documentClass.accept(diagnoser); + const diagnostics = diagnoser.getDiagnostics(); + const msg = diagnostics.toDiagnostic() + .map(d => `${rangeToString(d.range)}: ${d.message}`) + .join('\n'); + expect(diagnostics.count(), msg) + .is.equal(0); + }); + }); +}); diff --git a/server/src/UC/test/tsconfig.json b/server/src/UC/test/tsconfig.json index bf432987..4e1e2374 100644 --- a/server/src/UC/test/tsconfig.json +++ b/server/src/UC/test/tsconfig.json @@ -4,7 +4,4 @@ "strictNullChecks": false, "sourceMap": true }, - "include": [ - "**/*.test.ts" - ] } \ No newline at end of file diff --git a/server/src/UC/test/utils/codeAsserts.ts b/server/src/UC/test/utils/codeAsserts.ts new file mode 100644 index 00000000..daf9a484 --- /dev/null +++ b/server/src/UC/test/utils/codeAsserts.ts @@ -0,0 +1,37 @@ +import { fail } from 'assert'; +import { expect } from 'chai'; + +import { IExpression, UCBinaryOperatorExpression } from '../../expressions'; +import { IStatement, UCExpressionStatement } from '../../statements'; +import { ISymbol } from '../../Symbols'; + +export function assertExpressionStatement(stm: IStatement | undefined): UCExpressionStatement { + if (!(stm instanceof UCExpressionStatement)) { + return fail('invalid instance'); + } + + return stm as UCExpressionStatement; +} + +export function assertExpressionMemberSymbol(expr: IExpression, expected: ISymbol): ISymbol { + const symbol = expr.getMemberSymbol(); + expect(symbol) + .to.equal(expected); + return symbol; +} + +export function assertBinaryOperatorExpressionMemberSymbol( + expr: IExpression | undefined, + expectedLeft: ISymbol, + expectedRight: ISymbol): UCBinaryOperatorExpression { + if (!(expr instanceof UCBinaryOperatorExpression)) { + return fail('invalid instance'); + } + + expect(expr.left.getMemberSymbol(), 'left') + .to.equal(expectedLeft); + expect(expr.right.getMemberSymbol(), 'right') + .to.equal(expectedRight); + + return expr as UCBinaryOperatorExpression; +} \ No newline at end of file diff --git a/server/src/completion.ts b/server/src/completion.ts index 17c793ea..fe408d9a 100644 --- a/server/src/completion.ts +++ b/server/src/completion.ts @@ -192,7 +192,7 @@ export async function getCompletableSymbolItems(uri: DocumentUri, position: Posi if (typeof data.context === 'undefined') { throw new Error('No parse context!'); } - return buildCompletableSymbolItems(document, position, { context: data.context, parser: data.parser }) + return buildCompletableSymbolItems(document, position, { context: data.context, parser: data.parser }); } function insertTextForFunction(symbol: UCMethodSymbol): string { @@ -281,7 +281,7 @@ async function buildCompletableSymbolItems( cc.preferredRules = PreferredRulesSet; const stream = data.parser.inputStream; - let carretToken = getCaretTokenFromStream(stream, position); + const carretToken = getCaretTokenFromStream(stream, position); if (!carretToken) { console.warn(`No carret token at ${position.line}:${position.character}`); // throw new Error(`No carret token at ${position}`); @@ -397,7 +397,7 @@ async function buildCompletableSymbolItems( const items: CompletionItem[] = []; const symbols: ISymbol[] = []; let globalTypes: UCSymbolKind = UCSymbolKind.None; - let shouldIncludeTokenKeywords: boolean = true; + let shouldIncludeTokenKeywords = true; if (candidates.rules.has(UCParser.RULE_member) || carretRuleContext?.ruleIndex == UCParser.RULE_program) { if (isStruct(scopeSymbol)) { @@ -563,7 +563,7 @@ async function buildCompletableSymbolItems( } } else if (isWithin(UCParser.RULE_defaultValue)) { - let shouldIncludeConstants: Boolean = true; + let shouldIncludeConstants = true; switch (rule) { case UCParser.RULE_defaultIdentifierRef: { if (carretContextSymbol && isTypeSymbol(carretContextSymbol)) { @@ -605,6 +605,7 @@ async function buildCompletableSymbolItems( shouldIncludeConstants = false; break; + case UCTypeKind.Enum: case UCTypeKind.Byte: { candidates.tokens.delete(UCParser.NONE_LITERAL); @@ -643,7 +644,7 @@ async function buildCompletableSymbolItems( let i = 0; const expressions = properties.map(symbol => `${symbol.getName().text}=$${++i}`); - const structLiteralText: string = `(${expressions.join(',')})`; + const structLiteralText = `(${expressions.join(',')})`; const snippet: CompletionItem = buildSnippetSymbol(structLiteralText); items.push(snippet); break; @@ -723,19 +724,23 @@ async function buildCompletableSymbolItems( case UCParser.RULE_qualifiedIdentifier: { switch (contextRule) { case UCParser.RULE_qualifiedIdentifierArguments: { - globalTypes |= 1 << UCSymbolKind.Interface | 1 << UCSymbolKind.Package; + globalTypes |= 1 << UCSymbolKind.Interface + | 1 << UCSymbolKind.Package; break; } case UCParser.RULE_extendsClause: { switch (scopeSymbol?.kind) { case UCSymbolKind.Class: { - globalTypes |= 1 << UCSymbolKind.Class | 1 << UCSymbolKind.Package; + globalTypes |= 1 << UCSymbolKind.Class + | 1 << UCSymbolKind.Package; break; } case UCSymbolKind.ScriptStruct: { - globalTypes |= 1 << UCSymbolKind.ScriptStruct | 1 << UCSymbolKind.Class | 1 << UCSymbolKind.Package; + globalTypes |= 1 << UCSymbolKind.ScriptStruct + | 1 << UCSymbolKind.Class + | 1 << UCSymbolKind.Package; break; } } @@ -756,7 +761,8 @@ async function buildCompletableSymbolItems( } case UCParser.RULE_typeDecl: { - globalTypes |= 1 << UCSymbolKind.Enum | 1 << UCSymbolKind.ScriptStruct; + globalTypes |= 1 << UCSymbolKind.Enum + | 1 << UCSymbolKind.ScriptStruct; break; } } @@ -783,7 +789,7 @@ async function buildCompletableSymbolItems( if (contextSymbol) { if (isPackage(contextSymbol)) { - for (let symbol of ObjectsTable.enumerateKinds(PackageTypeContextSymbolKinds)) { + for (const symbol of ObjectsTable.enumerateKinds(PackageTypeContextSymbolKinds)) { if (symbol.outer !== contextSymbol) { continue; } diff --git a/server/src/documentDiagnostics.ts b/server/src/documentDiagnostics.ts index d07190cf..3702813e 100644 --- a/server/src/documentDiagnostics.ts +++ b/server/src/documentDiagnostics.ts @@ -18,7 +18,8 @@ function diagnosticsFromNodes(nodes: IDiagnosticNode[]) { } export function getDocumentDiagnostics(document: UCDocument): Diagnostic[] { - const documentAnalyzer = new DocumentAnalyzer(document); - const diagnostics = documentAnalyzer.visitDocument(document); + const diagnoser = new DocumentAnalyzer(document); + document.accept(diagnoser); + const diagnostics = diagnoser.getDiagnostics(); return diagnosticsFromNodes(document.nodes).concat(diagnostics.toDiagnostic()); } diff --git a/server/src/documentSymbol.ts b/server/src/documentSymbol.ts index bbed29eb..6e09ff6d 100644 --- a/server/src/documentSymbol.ts +++ b/server/src/documentSymbol.ts @@ -32,7 +32,7 @@ export function getDocumentSymbols(document: UCDocument): DocumentSymbol[] | und // Little hack, lend a hand and push all the class's children to the top. const symbols = document.enumerateSymbols(); - for (let symbol of symbols) { + for (const symbol of symbols) { if (isSymbolDefined(symbol)) { const documentSymbol = toDocumentSymbol(symbol); documentSymbols.push(documentSymbol); diff --git a/server/src/server.ts b/server/src/server.ts index e97fa6b4..3aae3aa9 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -2,7 +2,7 @@ import * as fs from 'fs'; import glob from 'glob'; import * as path from 'path'; import { performance } from 'perf_hooks'; -import { BehaviorSubject, firstValueFrom, interval, Subject, Subscription } from 'rxjs'; +import { BehaviorSubject, firstValueFrom, interval, of, Subject, Subscription } from 'rxjs'; import { debounce, delay, filter, map, switchMap, tap, timeout } from 'rxjs/operators'; import * as url from 'url'; import { DocumentUri, TextDocument } from 'vscode-languageserver-textdocument'; @@ -134,7 +134,7 @@ function getFiles(fsPath: string, pattern: string): Promise { } resolve(matches); }); - }) + }); } type WorkspaceFiles = { @@ -146,7 +146,7 @@ async function getWorkspaceFiles(folders: WorkspaceFolder[], reason: string): Pr let documentFiles: string[] = []; let packageFiles: string[] = []; - for (let folder of folders) { + for (const folder of folders) { const folderFSPath = URI.parse(folder.uri).fsPath; connection.console.info(`Scanning folder '${folderFSPath}' using pattern '${packageFileGlobPattern}', '${documentFileGlobPattern}'`); await Promise.all([ @@ -244,7 +244,7 @@ function invalidatePendingDocuments() { } } -async function awaitDocumentDelivery(uri: DocumentUri): Promise { +async function awaitDocumentDelivery(uri: DocumentUri, timeoutEach = 1000 * 60): Promise { const document = getDocumentByURI(uri); if (document && document.hasBeenIndexed) { return document; @@ -260,12 +260,15 @@ async function awaitDocumentDelivery(uri: DocumentUri): Promise { + return of(undefined); + } }) )); } -async function awaitDocumentBuilt(uri: DocumentUri): Promise { +async function awaitDocumentBuilt(uri: DocumentUri, timeoutEach = 1000 * 60): Promise { const document = getDocumentByURI(uri); if (document && document.hasBeenBuilt) { return document; @@ -275,7 +278,10 @@ async function awaitDocumentBuilt(uri: DocumentUri): Promise doc.uri === uri), timeout({ - each: 1000 * 60 + each: timeoutEach, + with: () => { + return of(undefined); + } }) )); } @@ -429,7 +435,7 @@ connection.onInitialized((params) => { if (document.hasBeenIndexed) { if (process.env.NODE_ENV === 'development') { - connection.console.log(`Document "${document.fileName}" is already indexed.`) + connection.console.log(`Document "${document.fileName}" is already indexed.`); } return; } @@ -479,9 +485,9 @@ connection.onInitialized((params) => { const newGeneration = tryAutoDetectGeneration(); if (newGeneration) { config.generation = newGeneration; - connection.console.info(`Auto-detected generation ${config.generation}.`) + connection.console.info(`Auto-detected generation ${config.generation}.`); } else { - connection.console.warn(`Auto-detection failed, resorting to UC3.`) + connection.console.warn(`Auto-detection failed, resorting to UC3.`); } } @@ -506,14 +512,14 @@ connection.onInitialized((params) => { .filter(Boolean) as UCDocument[]; for (let i = activeDocuments.length - 1; i >= 0; i--) { - connection.console.log(`Queueing active document "${activeDocuments[i].fileName}".`) + connection.console.log(`Queueing active document "${activeDocuments[i].fileName}".`); work.report(activeDocuments.length / i - 1.0, `${activeDocuments[i].fileName}`); // if (documents[i].hasBeenIndexed) { // continue; // } if (work.token.isCancellationRequested) { - connection.console.warn(`The workspace indexing has been cancelled.`) + connection.console.warn(`The workspace indexing has been cancelled.`); break; } @@ -529,7 +535,7 @@ connection.onInitialized((params) => { } if (work.token.isCancellationRequested) { - connection.console.warn(`The workspace indexing has been cancelled.`) + connection.console.warn(`The workspace indexing has been cancelled.`); break; } @@ -645,7 +651,7 @@ connection.onDidChangeConfiguration((params: { settings: { unrealscript: UCLangu setConfiguration(params.settings.unrealscript); initializeConfiguration(); - connection.console.info(`Re-indexing workspace due configuration changes.`) + connection.console.info(`Re-indexing workspace due configuration changes.`); isIndexReady$.next(false); }); @@ -672,8 +678,13 @@ function applyConfiguration(settings: UCLanguageServerSettings) { * The code should assume that no UC symbols do exist other than packages. */ function tryAutoDetectGeneration(): UCGeneration | undefined { + let document = getDocumentById(toName('Object')); + if (!document || document.classPackage !== CORE_PACKAGE) { + return undefined; + } + // UE3 has Component.uc we can use to determine the generation. - let document = getDocumentById(toName('Component')); + document = getDocumentById(toName('Component')); if (document?.classPackage === CORE_PACKAGE) { return UCGeneration.UC3; } @@ -797,14 +808,14 @@ function setupFilePatterns(settings: UCLanguageServerSettings) { } connection.onHover(async (e) => { - const document = await awaitDocumentDelivery(e.textDocument.uri); + const document = await awaitDocumentDelivery(e.textDocument.uri, 5000); if (document) { return getDocumentTooltip(document, e.position); } }); connection.onDefinition(async (e) => { - const document = await awaitDocumentDelivery(e.textDocument.uri); + const document = await awaitDocumentDelivery(e.textDocument.uri, 5000); if (document) { return getDocumentDefinition(document, e.position); } @@ -881,7 +892,7 @@ connection.onRenameRequest(async (e) => { return undefined; } - const references = getSymbolReferences(symbol) + const references = getSymbolReferences(symbol); if (!references) { return undefined; } diff --git a/server/src/workspaceSymbol.ts b/server/src/workspaceSymbol.ts index 3787ba08..61ac9bdf 100644 --- a/server/src/workspaceSymbol.ts +++ b/server/src/workspaceSymbol.ts @@ -8,8 +8,8 @@ import { isParamSymbol, isStruct, UCObjectSymbol } from './UC/Symbols'; export function getWorkspaceSymbols(query: string): WorkspaceSymbol[] | undefined { const workspaceSymbols: WorkspaceSymbol[] = []; - for (let document of enumerateDocuments()) { - for (let symbol of document.enumerateSymbols()) { + for (const document of enumerateDocuments()) { + for (const symbol of document.enumerateSymbols()) { if (isSymbolDefined(symbol)) { buildWorkSpaceSymbols(symbol); } diff --git a/server/test/server.test.ts b/server/test/server.test.ts new file mode 100644 index 00000000..25be1481 --- /dev/null +++ b/server/test/server.test.ts @@ -0,0 +1,27 @@ +import { expect } from 'chai'; +import path = require('path'); + +import { getDocumentSymbol, getDocumentTooltip } from '../src/UC/helpers'; +import { createDocumentByPath, createPackageByDir, indexDocument } from '../src/UC/indexer'; + +// TODO: E2E tests, write VSCode client tests instead. +describe('getDocumentTooltip', () => { + const pathToObject = path.resolve(__dirname, 'workspace', 'TestPackage', 'Classes', 'TestPackage.uc'); + const testPackageDocument = createDocumentByPath(pathToObject, createPackageByDir(pathToObject)); + indexDocument(testPackageDocument); + + const classSymbol = testPackageDocument.class!; + + it('should match the class symbol', () => { + const position = classSymbol.id.range.start; + const symbol = getDocumentSymbol(testPackageDocument, position); + expect(symbol) + .to.equal(classSymbol); + }); + + it('should retrieve the class symbol display info', async () => { + const hoverInfo = await getDocumentTooltip(testPackageDocument, classSymbol.id.range.start); + expect(hoverInfo) + .to.not.be.undefined; + }); +}); \ No newline at end of file diff --git a/server/test/workspace/TestPackage/Classes/TestPackage.uc b/server/test/workspace/TestPackage/Classes/TestPackage.uc new file mode 100644 index 00000000..764350c8 --- /dev/null +++ b/server/test/workspace/TestPackage/Classes/TestPackage.uc @@ -0,0 +1 @@ +class TestPackage extends Core.Object; \ No newline at end of file diff --git a/server/test/workspace/workspace.test.ts b/server/test/workspace/workspace.test.ts index 8baa03df..ff4548cc 100644 --- a/server/test/workspace/workspace.test.ts +++ b/server/test/workspace/workspace.test.ts @@ -3,6 +3,7 @@ import path = require('path'); import { createDocumentByPath } from '../../src/UC/indexer'; import { CORE_PACKAGE } from '../../src/UC/Symbols'; +// TODO: Initialize entire workspace describe('Initialize workspace', () => { const pathToObject = path.resolve(__dirname, '..', 'Core', 'Classes', 'Object.uc'); createDocumentByPath(pathToObject, CORE_PACKAGE); diff --git a/server/tsconfig.build.json b/server/tsconfig.build.json index 728a5d42..8e4cf64d 100644 --- a/server/tsconfig.build.json +++ b/server/tsconfig.build.json @@ -1,5 +1,5 @@ { "extends": "./tsconfig.json", "include": ["src/**/*.ts"], - "exclude": ["src/**/*.test.ts"] + "exclude": ["src/**/*.test.ts", "src/**/test/**/*.ts"] } \ No newline at end of file