diff --git a/.gitignore b/.gitignore index 2a23da7872b255928df7d4d3d547cc6a41e8326f..c8ed6fcdfeec7e6cba537e35c399575a5a839322 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ ets2panda/linter*/**/.vscode ets2panda/linter*/**/.cache ets2panda/linter*/test/**/results ets2panda/linter*/test_rules/**/results +ets2panda/linter*/test_regression/**/results ets2panda/linter*/**/package-lock.json **/compile_commands.json .cache diff --git a/ets2panda/linter-4.2/README.md b/ets2panda/linter-4.2/README.md index 3983b1f55123e84c069cdba99b25d334aaae64ed..9c637a534ebb9f839d09fc10853aaf72337a5fbe 100644 --- a/ets2panda/linter-4.2/README.md +++ b/ets2panda/linter-4.2/README.md @@ -1,14 +1,17 @@ # TypeScript linter -Typescript linter ( further mentioned as 'linter' ) is a tool to check typescript sources and find language elements -and constructions which are deprecated to use in a purpose to migrate sources to STS. + +Typescript linter ( further mentioned as 'linter' ) is a tool to check typescript sources and find language elements +and constructions which are deprecated to use in a purpose to migrate sources to STS. The linter is currently under development. ## Prerequisits ### Visual Studio Code + For development, it's recommended to use `VS Code`, as it has a full built-in support for TypeScript language. ### NodeJS and NPM + Install the latest stable version of `NodeJS` and `NPM`. It is recommended using a `Node version manager` to install Node and NPM ([nvm](https://github.com/nvm-sh/nvm) for Linux; [nvm-windows](https://github.com/coreybutler/nvm-windows) for windows - v1.1.9 is the most stable). You can also follow the [official guide](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm). ## Building @@ -26,7 +29,9 @@ npm run build ``` ## Running + Run the following command from the same directory: + ```bash node dist/tslinter.js [options] [input files] ``` @@ -56,6 +61,7 @@ To prevent command line buffer overflow, response file may be used. It is specif ## Running tests Run the following command: + ```bash npm test ``` diff --git a/ets2panda/linter-4.2/cookbook_convertor/README.md b/ets2panda/linter-4.2/cookbook_convertor/README.md index d54c35d4a7d0515d8a2809dcfd0daee54d366b31..3a63ff481687d542e91f5f944037a3af1e909b64 100644 --- a/ets2panda/linter-4.2/cookbook_convertor/README.md +++ b/ets2panda/linter-4.2/cookbook_convertor/README.md @@ -1,4 +1,5 @@ # Cookbook Convertor + Cookbook convertor is a tool that generates rule descriptions for TS Linter from Cookbook receipts. Convertor accepts RST file that contains the sources for Cookbook receipts. ## Prerequisits @@ -20,7 +21,9 @@ npm run build ``` ## Running + Run the following command from the same directory: + ```bash node build/cookbook_convertor.js [rst_file] ``` diff --git a/ets2panda/linter-4.2/cookbook_convertor/res/recipes.rst b/ets2panda/linter-4.2/cookbook_convertor/res/recipes.rst index 3c1856c0cc472cdf0d01d8a21a758482c206fb85..9f4bbd13d580b1017a73fff43c8e32cb4fea2e19 100644 --- a/ets2panda/linter-4.2/cookbook_convertor/res/recipes.rst +++ b/ets2panda/linter-4.2/cookbook_convertor/res/recipes.rst @@ -685,49 +685,6 @@ All types must be specified explicitly. let a: A let b: A -.. _R024: - -|CB_R| #24: Optional arguments are not supported ------------------------------------------------- - -|CB_RULE| -~~~~~~~~~ - -Currently, |LANG| does not support optional parameters. Specify an -optional parameter as a parameter of a nullable type with the -default value ``null``. Default parameter values are supported for all types. - -|CB_BAD| -~~~~~~~~ - -.. code-block:: typescript - - // x is an optional parameter: - function f(x?: number) { - console.log(x) // log undefined or number - } - - // x is a required parameter with the default value: - function g(x: number = 1) { - console.log(x) - } - -|CB_OK| -~~~~~~~ - -.. code-block:: typescript - - // Optional parameters are not supported, - // but you can assign a default value ``null`` for the parameter: - function f(x: number | null = null) { - console.log(x); // log null or number - } - - // x is a required argument with the default value: - function g(x: number = 1) { - console.log(x); - } - .. _R025: |CB_R| #25: Declaring fields in ``constructor`` is not supported @@ -1095,56 +1052,6 @@ public APIs and decide whether such types are identical. Use other mechanisms * :ref:`R031` * :ref:`R035` -.. _R033: - -|CB_R| #33: Optional properties are not supported -------------------------------------------------- - -|CB_RULE| -~~~~~~~~~ - -|LANG| does not support optional properties. Use properties with default values. -Use properties of nullable types and the default ``null`` value to distinguish -whether a value is set or not. - -|CB_BAD| -~~~~~~~~ - -.. code-block:: typescript - - interface CompilerOptions { - strict?: boolean - sourcePath?: string - targetPath?: string - } - - var options: CompilerOptions = { - strict: true, - sourcepath: "./src", - } - if option.targetPath == undefined { - // set default - } - -|CB_OK| -~~~~~~~ - -.. code:: typescript - - interface CompilerOptions { - strict: boolean = false - sourcePath: string = "" - targetPath: string | null = null - } - - let options: CompilerOptions = { - strict: true, - sourcepath: "./src", - } - if option.targetPath == null { - // set default - } - .. _R034: |CB_R| #34: Generic functions must be called with explicit type specialization @@ -2823,7 +2730,6 @@ library (for example, ``Int32Array``) are also supported. for (let n of a) { console.log(n) } - |CB_SEE| ~~~~~~~~ @@ -4180,7 +4086,6 @@ Differences are described in separate recipes. * :ref:`R120` * :ref:`R121` * :ref:`R122` -* :ref:`R123` * :ref:`R124` * :ref:`R125` * :ref:`R126` @@ -4433,64 +4338,6 @@ Use explicit ``import ... from ...`` instead. * :ref:`R120` -.. _R123: - -|CB_R| #123: Renaming in export declarations is not supported -------------------------------------------------------------- - -|CB_RULE| -~~~~~~~~~ - -|LANG| does not support renaming in export declarations. Similar effect -can be achieved through setting an alias for the exported entity. - -|CB_BAD| -~~~~~~~~ - -.. code-block:: typescript - - // file1.ts - class MyClass { - // ... - } - - export { MyClass as RenamedClass } - - // file2.ts - import { RenamedClass } from "./file1" - - function main(): void { - const myObject = new RenamedClass() - // ... - } - -|CB_OK| -~~~~~~~ - -.. code-block:: typescript - - // module1 - class MyClass { - // ... - } - - export RenamedClass = MyClass - - // module2 - import RenamedClass from "./module1" - - function main(): void { - const myObject = new RenamedClass() - // ... - } - -|CB_SEE| -~~~~~~~~ - -* :ref:`R124` -* :ref:`R125` -* :ref:`R126` - .. _R124: |CB_R| #124: Export list declaration is not supported @@ -4524,7 +4371,6 @@ entities must be explicitly annotated with the ``export`` keyword. |CB_SEE| ~~~~~~~~ -* :ref:`R123` * :ref:`R125` * :ref:`R126` @@ -4579,7 +4425,6 @@ imported explicitly from the modules that export them. |CB_SEE| ~~~~~~~~ -* :ref:`R123` * :ref:`R124` * :ref:`R126` @@ -4631,7 +4476,6 @@ Use regular ``export`` / ``import`` instead. |CB_SEE| ~~~~~~~~ -* :ref:`R123` * :ref:`R124` * :ref:`R125` diff --git a/ets2panda/linter-4.2/cookbook_convertor/src/cookbook_convertor.ts b/ets2panda/linter-4.2/cookbook_convertor/src/cookbook_convertor.ts index d6e672ebff585f03aa6bcaf4354dae59e10eaf46..58932f507750bde7b477bf56ac0aad2ff76a4306 100644 --- a/ets2panda/linter-4.2/cookbook_convertor/src/cookbook_convertor.ts +++ b/ets2panda/linter-4.2/cookbook_convertor/src/cookbook_convertor.ts @@ -16,448 +16,473 @@ import { readFileSync, writeFileSync } from 'fs'; import { join } from 'path'; - -const COPYRIGHT_HEADER = "/* \n\ +const COPYRIGHT_HEADER = + '/* \n\ * Copyright (c) 2022-2023 Huawei Device Co., Ltd. \n\ - * Licensed under the Apache License, Version 2.0 (the \"License\"); \n\ + * Licensed under the Apache License, Version 2.0 (the "License"); \n\ * you may not use this file except in compliance with the License. \n\ * You may obtain a copy of the License at \n\ * \n\ * http://www.apache.org/licenses/LICENSE-2.0 \n\ * \n\ * Unless required by applicable law or agreed to in writing, software \n\ - * distributed under the License is distributed on an \"AS IS\" BASIS, \n\ + * distributed under the License is distributed on an "AS IS" BASIS, \n\ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. \n\ * See the License for the specific language governing permissions and \n\ * limitations under the License. \n\ */ \n\ -"; +'; -const CODE_PROLOGUE = "export const cookBookMsg: string[] = [];\n\ +const CODE_PROLOGUE = + 'export const cookBookMsg: string[] = [];\n\ export const cookBookTag: string[] = [];\n\ \n\ -for( let i = 0; i < 148; i++) {\n\ - cookBookMsg[ i ] = '';\n\ +for( let i = 0; i <= 150; i++) {\n\ + cookBookMsg[ i ] = \'\';\n\ }\n\ -"; - -// HTML tegs -const T_BR = "
"; -const T_UNDERLINE = ""; -const T_END_UNDERLINE = ""; -const T_BOLD = ""; -const T_END_BOLD = ""; -const T_ITALIC = ""; -const T_END_ITALIC = ""; -const T_CODE = ""; -const T_END_CODE = ""; -const T_NBSP = " "; +'; + +// HTML tags +const T_BR = '
'; +const T_BOLD = ''; +const T_END_BOLD = ''; +const T_CODE = ''; +const T_END_CODE = ''; +const T_NBSP = ' '; const T_HR = '
'; // RST substititions -const CB_ = "|CB_"; -const CB_R = "|CB_R|"; -const CB_RULE = "|CB_RULE|"; -const CB_BAD = "|CB_BAD|"; -const CB_OK = "|CB_OK|"; -const CB_ERROR = "|CB_ERROR|"; //replace:: **Severity: error** -const CB_WARNING = "|CB_WARNING|"; //replace:: **Severity: warning** -const CB_SEE = "|CB_SEE|"; -const CB_REF = ":ref:"; -const CB_META = ".. meta"; - -const NEW_REC_HEADER = ".. _R"; -const CODE_BLOCK = ".. code"; // should be ".. code-block" but in some places there is error in doc file - -let MAKE_MD = false; // flag to generate .md files - -let doc_lines: string[]; -let _line:number +const CB_PREFIX = '|CB_PREFIX'; +const CB_R = '|CB_R|'; +const CB_RULE = '|CB_RULE|'; +const CB_BAD = '|CB_BAD|'; +const CB_OK = '|CB_OK|'; +// replace:: **Severity: error** +const CB_ERROR = '|CB_ERROR|'; +// replace:: **Severity: warning** +const CB_WARNING = '|CB_WARNING|'; +const CB_SEE = '|CB_SEE|'; +const CB_REF = ':ref:'; +const CB_META = '.. meta'; +const CB_FIX = ':fix:'; + +const NEW_REC_HEADER = /.. _R\d\d\d:/; +// should be ".. code-block" but in some places there is error in doc file +const CODE_BLOCK = '.. code'; + +let docLines: string[]; +let curLine: number; let recNum: number; -let tegs: string[] = []; -let ruleNames: string[] = []; -let cooks: string[] = []; +const tegs: string[] = []; +const ruleNames: string[] = []; let mdText: string[] = []; +const fixTitles: Map = new Map(); +// continue line +const CL = ' \\'; +const STR_DLMTR = '\''; - -const CL = " \\"; // continue line -const STR_DLMTR = "\'"; - - -function syncReadFile(filename: string) { +function syncReadFile(filename: string): string[] { const contents = readFileSync(filename, 'utf-8'); - doc_lines = contents.split(/\r?\n/); + docLines = contents.split(/\r?\n/); -// make table of rule names - _line = 0; + // make table of rule names + curLine = 0; let ruleNum = -1; - while( _line < doc_lines.length ) { - if(doc_lines[ _line ].startsWith( NEW_REC_HEADER ) ) { -console.log(">>>>>>> START RULE " + doc_lines[ _line ].split( NEW_REC_HEADER )[1]) - ruleNum = Number((doc_lines[ _line ].split( NEW_REC_HEADER )[1]).split(":")[0]); -console.log(" NUMBER: " + ruleNum) - } - if( doc_lines[ _line ].startsWith( CB_R ) ) { - let line = doc_lines[ _line ].split( CB_R )[1]; - ruleNames[ ruleNum ] = line; //line.split(':')[1]; - _line++; + while (curLine < docLines.length) { + const line = docLines[curLine]; + if (NEW_REC_HEADER.test(line)) { + ruleNum = Number(line.replace(/\D/g, '')); + console.log('>>>>>>> START RULE ' + ruleNum + ':'); + console.log(' NUMBER: ' + ruleNum); + } + if (docLines[curLine].startsWith(CB_R)) { + let line = docLines[curLine].split(CB_R)[1]; + ruleNames[ruleNum] = line; + curLine++; needHeader(); - if( doc_lines[ _line ].startsWith( CB_RULE ) ) { - line = doc_lines[ _line ].trim().replace( CB_RULE, "").trim(); - ruleNames[ ruleNum ] = ruleNames[ ruleNum ] + " (" + line + ")"; + if (docLines[curLine].startsWith(CB_RULE)) { + line = docLines[curLine].trim().replace(CB_RULE, ''). + trim(); + ruleNames[ruleNum] = ruleNames[ruleNum] + ' (' + line + ')'; } } - _line ++; + curLine++; } // scan text - _line = 0; - while( _line < doc_lines.length ) { - skipEmptyLines(); - if( doc_lines[_line].startsWith(NEW_REC_HEADER)) { - makeRecept(); - } - else - _line++; + curLine = 0; + while (curLine < docLines.length) { + skipEmptyLines(); + const line = docLines[curLine]; + if (NEW_REC_HEADER.test(line)) { + makeRecipe(); + } else { + curLine++; + } } - return doc_lines; + return docLines; } +/* + * + * utility functions + * + */ -// -// utility functions -// - -function replaceAll( s: string, from: string, to: string): string { - let ss = s.split(from); - let outStr = ""; - ss.forEach( (line) => { outStr += to + line; }); +function replaceAll(s: string, from: string, to: string): string { + const ss = s.split(from); + let outStr = ''; + ss.forEach((line) => { + outStr += to + line; + }); - return outStr.replace( to, ""); // remove 1st 'to' substring + // remove 1st 'to' substring + return outStr.replace(to, ''); } -function translateLine( s: string ) : string { - let line = s; - line = line.replace( CB_BAD, "TypeScript"); - line = line.replace( CB_OK, "ArkTS"); - //.. |CB_RULE| replace:: Rule - line = line.replace( CB_ERROR, "**Severity: error**" ); - line = line.replace( CB_WARNING, "**Severity: warning**" ); - line = line.replace(CB_SEE, "## See also" ); +function translateLine(s: string): string { + let line = s; + line = line.replace(CB_BAD, 'TypeScript'); + line = line.replace(CB_OK, 'ArkTS'); - line = line.replace( "|JS|", "JavaScript"); - line = line.replace( "|LANG|", "ArkTS"); //.. |LANG| replace:: {lang} - line = line.replace( "|TS|", "TypeScript"); - - return line; -} + line = line.replace(CB_ERROR, '**Severity: error**'); + line = line.replace(CB_WARNING, '**Severity: warning**'); + line = line.replace(CB_SEE, '## See also'); + line = replaceAll(line, '|JS|', 'JavaScript'); + line = replaceAll(line, '|LANG|', 'ArkTS'); + line = replaceAll(line, '|TS|', 'TypeScript'); -function translateTeg( s: string) :string { - return replaceAll( s, "\`\`", '"' ).trim(); + return line; } - -function makeHdr( s: string) :string { - return replaceAll( s, "\`\`", "\'" ); +function translateTeg(s: string): string { + return replaceAll(s, '``', '"').trim(); } - -function highlightCode( s: string ): string { - let ss = s.split("\`\`"); - let line = ss[0]; - for( let i = 1; i < ss.length; i++ ) { - if( (i % 2) === 0 ) - line += T_END_CODE; - else - line += T_CODE; - line += ss[i]; - } - return line; +function highlightCode(s: string): string { + const ss = s.split('``'); + let line = ss[0]; + for (let i = 1; i < ss.length; i++) { + if (i % 2 === 0) { + line += T_END_CODE; + } else { + line += T_CODE; + } + line += ss[i]; + } + return line; } -function escapeSym( s: string ): string { - let ss = replaceAll(s, "\'", "\\\'"); - return replaceAll(ss, "\"", "\\\""); +function escapeSym(s: string): string { + const ss = replaceAll(s, '\'', '\\\''); + return replaceAll(ss, '"', '\\"'); } -function setNBSP( s: string ): string { - let ss = ""; - let flag = true; - for( let ch of s ) { - if( ch !== " " && ch !== "\t" ) - flag = false; - if( flag && ch === " " ) - ss += T_NBSP; - else if( flag && ch ==="\t" ) - ss += T_NBSP + T_NBSP + T_NBSP + T_NBSP + T_NBSP + T_NBSP + T_NBSP + T_NBSP ; - else - ss += ch; - } - return ss; +function setNBSP(s: string): string { + let ss = ''; + let flag = true; + for (const ch of s) { + if (ch !== ' ' && ch !== '\t') { + flag = false; + } + if (flag && ch === ' ') { + ss += T_NBSP; + } else if (flag && ch === '\t') { + ss += T_NBSP + T_NBSP + T_NBSP + T_NBSP + T_NBSP + T_NBSP + T_NBSP + T_NBSP; + } else { + ss += ch; + } + } + return ss; } -function skipEmptyLines() { - while( _line < doc_lines.length ) { - let s = doc_lines[_line]; - s = s.trim(); - if( s !== "") - break; - _line++; - } +function skipEmptyLines(): void { + while (curLine < docLines.length) { + let s = docLines[curLine]; + s = s.trim(); + if (s !== '') { + break; + } + curLine++; + } } function isHeader(): boolean { - return doc_lines[ _line ].startsWith( CB_ ) || doc_lines[ _line ].startsWith( ".." ) ; + return docLines[curLine].startsWith(CB_PREFIX) || docLines[curLine].startsWith('..'); } -function needHeader() { - while ( _line < doc_lines.length && !isHeader() ) - _line++; +function needHeader(): void { + while (curLine < docLines.length && !isHeader()) { + curLine++; + } } - -// -// parsing functions -// - -function makeRecept() { - recNum = Number(doc_lines[_line].slice(NEW_REC_HEADER.length, NEW_REC_HEADER.length+3)) - console.log("cookBookMsg[ " + recNum + " ] = " + STR_DLMTR + CL); - _line++; - mdText = []; - makeTeg(); - makeBody(); - makeBad(); - makeOk(); - makeSee(); - - // emit .md file - let mdFileName = join("./md", "recipe" + recNum + ".md" ); - writeFileSync( mdFileName, "", { flag: 'w', }); - mdText.forEach((mdLine) => { -console.error("MD> " + mdLine); - writeFileSync(mdFileName, mdLine + '\n', { flag: "a+"} ) - }); - - console.log(STR_DLMTR + ";"); - console.log(""); +function isFixTitle(): boolean { + return docLines[curLine].trimStart().startsWith(CB_FIX); } +/* + * + * parsing functions + * + */ -function makeTeg() { - needHeader(); -console.error(">>>TEG>>>: " + _line + " -> " + doc_lines[_line]); - if( ! doc_lines[ _line ].startsWith( CB_R ) ) - return; - let line = doc_lines[ _line ].split( CB_R )[1]; - - mdText.push("# " + line); //.split(':')[1] ); - mdText.push(""); +function makeFixTitle(): void { + while (curLine < docLines.length && !isHeader() && !isFixTitle()) { + curLine++; + } - line = escapeSym( translateLine(line) ); - let teg = translateTeg( line ); - let hdr = highlightCode(line); - console.log(hdr + T_BR + CL); - tegs[ recNum ] = teg; //.split(':')[1]; - _line++; + if (isFixTitle()) { + const title = docLines[curLine].split(CB_FIX)[1].trim(); + fixTitles.set(recNum, escapeSym(title)); + } } +function makeRecipe(): void { + const line = docLines[curLine]; + recNum = Number(line.replace(/\D/g, '')); + console.log('cookBookMsg[ ' + recNum + ' ] = ' + STR_DLMTR + CL); + curLine++; + mdText = []; + makeTag(); + makeBody(); + makeBad(); + makeOk(); + makeSee(); + + // emit .md file + const mdFileName = join('./md', 'recipe' + recNum + '.md'); + writeFileSync(mdFileName, '', { flag: 'w' }); + mdText.forEach((mdLine) => { + console.error('MD> ' + mdLine); + writeFileSync(mdFileName, mdLine + '\n', { flag: 'a+' }); + }); + + console.log(STR_DLMTR + ';'); + console.log(''); +} -function makeBody(): string { - let body = ""; - - needHeader(); -console.error(">>>BODY HDR>>>: " + + _line + " -> " + doc_lines[_line]); - if( !doc_lines[ _line ].startsWith( CB_RULE ) ) - return ""; +function makeTag(): void { + needHeader(); + console.error('>>>TEG>>>: ' + curLine + ' -> ' + docLines[curLine]); + if (!docLines[curLine].startsWith(CB_R)) { + return; + } + let line = docLines[curLine].split(CB_R)[1]; - let line = doc_lines[ _line ].trim(); - let md_line = line; - line = line.replace( CB_RULE, ""); - line = escapeSym( translateLine(line) ); - tegs[ recNum ] = tegs[ recNum ].trim() + " (" + replaceAll(translateTeg(line), '"', '') + ")"; + mdText.push('# ' + translateLine(line)); + mdText.push(''); - _line++; _line++; // skip underline - console.log( T_HR + T_BOLD + "Rule" + T_END_BOLD + T_BR + CL ); + line = escapeSym(translateLine(line)); + const teg = translateTeg(line); + const hdr = highlightCode(line); + console.log(hdr + T_BR + CL); + tegs[recNum] = teg; + curLine++; +} - mdText.push( md_line.replace( CB_RULE, "Rule" ) ); //("## Rule"); - mdText.push(""); +function makeBody(): string { + let body = ''; + needHeader(); + console.error('>>>BODY HDR>>>: ' + +curLine + ' -> ' + docLines[curLine]); + if (!docLines[curLine].startsWith(CB_RULE)) { + return ''; + } + let line = docLines[curLine].trim(); + const mdLine = line; + line = line.replace(CB_RULE, ''); + line = escapeSym(translateLine(line)); + tegs[recNum] = tegs[recNum].trim() + ' (' + replaceAll(translateTeg(line), '"', '') + ')'; + curLine++; + + // skip underline + curLine++; + console.log(T_HR + T_BOLD + 'Rule' + T_END_BOLD + T_BR + CL); + + // ("## Rule"); + mdText.push(mdLine.replace(CB_RULE, 'Rule')); + mdText.push(''); + needHeader(); + console.error('>>>BODY 2 HDR>>>: ' + +curLine + ' -> ' + docLines[curLine]); + + if (docLines[curLine].startsWith(CB_META)) { + curLine++; + makeFixTitle(); needHeader(); -console.error(">>>BODY 2 HDR>>>: " + + _line + " -> " + doc_lines[_line]); - if( doc_lines[ _line ].startsWith(CB_META) ) { - _line++; - needHeader(); -console.error(">>>BODY 3 HDR>>>: " + + _line + " -> " + doc_lines[_line]); - } - //_line++; - while( !isHeader() || doc_lines[ _line ].startsWith( CB_ERROR ) || doc_lines[ _line ].startsWith( CB_WARNING ) ) { - let s = translateLine( doc_lines[_line] ); + console.error('>>>BODY 3 HDR>>>: ' + +curLine + ' -> ' + docLines[curLine]); + } - mdText.push(s); + while (!isHeader() || docLines[curLine].startsWith(CB_ERROR) || docLines[curLine].startsWith(CB_WARNING)) { + let s = translateLine(docLines[curLine]); - s = highlightCode( s ); - s = escapeSym( s ); - console.log(s + CL); - - body += s; - _line++; - } - console.log(T_BR + CL); + mdText.push(s); + s = highlightCode(s); + s = escapeSym(s); + console.log(s + CL); + body += s; + curLine++; + } - mdText.push(""); + console.log(T_BR + CL); + mdText.push(''); - return body; + return body; } +function makeBad(): string { + let badCode = ''; + needHeader(); + console.error('>>>makeBAD HDR>>>: ' + docLines[curLine]); + if (!docLines[curLine].startsWith(CB_BAD)) { + return ''; + } + curLine++; + // skip underline + curLine++; -function makeBad(): string { - let badCode =""; - - needHeader(); -console.error(">>>makeBAD HDR>>>: " + doc_lines[_line]); - if( ! doc_lines[_line].startsWith( CB_BAD ) ) { - return ""; - } - _line++; _line++; // skip underline + console.log(T_HR + T_BOLD + 'TypeScript' + T_END_BOLD + T_BR + CL); - console.log( T_HR + T_BOLD + "TypeScript" + T_END_BOLD + T_BR + CL ); + mdText.push('## TypeScript'); + mdText.push(''); - mdText.push("## TypeScript"); - mdText.push(""); + while (curLine < docLines.length && !isHeader()) { + let s = translateLine(docLines[curLine]); + mdText.push(s); - while( _line < doc_lines.length && !isHeader() ) { - let s = translateLine( doc_lines[_line] ); - mdText.push( s ); + s = highlightCode(s); + console.log(s + CL); - s = highlightCode( s ); - console.log(s + CL); + badCode += s; + curLine++; + } - badCode += s; - _line++; + skipEmptyLines(); + if (docLines[curLine++].startsWith(CODE_BLOCK)) { + mdText.push('```'); + console.log(T_CODE + CL); + while (curLine < docLines.length && !isHeader()) { + mdText.push(docLines[curLine]); + console.log(setNBSP(escapeSym(docLines[curLine])) + T_BR + CL); + curLine++; } + console.log(T_END_CODE + T_BR + CL); - skipEmptyLines(); - if( doc_lines[_line++].startsWith( CODE_BLOCK ) ) { - mdText.push("```"); - console.log( T_CODE + CL ); - while( _line < doc_lines.length && !isHeader() ) { - mdText.push( doc_lines[_line] ); - console.log( setNBSP( escapeSym(doc_lines[_line]) ) + T_BR + CL ); - _line++; - } - console.log( T_END_CODE + T_BR + CL ); - - mdText.push("```"); - } - mdText.push(""); - - return badCode; -} + mdText.push('```'); + } + mdText.push(''); + return badCode; +} function makeOk(): string { - let goodCode = ""; + let goodCode = ''; - needHeader(); -console.error( ">>>makeOK HDR>>>: " + doc_lines[ _line ] ); - if( _line >= doc_lines.length || !doc_lines[_line].startsWith(CB_OK) ) { - return ""; - } - _line++; _line++; // skip underline - console.log( T_HR + T_BOLD + "ArkTS" + T_END_BOLD + T_BR + CL ); - - mdText.push("## ArkTS"); - mdText.push(""); - - while( _line < doc_lines.length && !isHeader() ) { - let s = translateLine( doc_lines[ _line ] ); - - mdText.push( s ); - - s = highlightCode( s ); - console.log(s + CL); - - goodCode += s; - _line++; - } + needHeader(); + console.error('>>>makeOK HDR>>>: ' + docLines[curLine]); + if (curLine >= docLines.length || !docLines[curLine].startsWith(CB_OK)) { + return ''; + } + curLine++; + // skip underline + curLine++; + console.log(T_HR + T_BOLD + 'ArkTS' + T_END_BOLD + T_BR + CL); - skipEmptyLines(); - if( doc_lines[ _line++ ].startsWith( CODE_BLOCK ) ) { - console.log( T_CODE + CL ); + mdText.push('## ArkTS'); + mdText.push(''); - mdText.push("```"); + while (curLine < docLines.length && !isHeader()) { + let s = translateLine(docLines[curLine]); - while( _line < doc_lines.length && !isHeader() ) { - mdText.push( doc_lines[_line] ); - console.log( setNBSP( escapeSym(doc_lines[ _line ]) ) + T_BR + CL ); - _line++; - } - console.log( T_END_CODE + T_BR + CL); + mdText.push(s); - mdText.push("```"); - } + s = highlightCode(s); + console.log(s + CL); - mdText.push(""); + goodCode += s; + curLine++; + } - return goodCode; -} + skipEmptyLines(); + if (docLines[curLine++].startsWith(CODE_BLOCK)) { + console.log(T_CODE + CL); + mdText.push('```'); -function makeSee( ): string { - const RECIPE = "Recipe "; -console.error(">>> #" + recNum + " PASSED: " + doc_lines[_line]); - while( _line < doc_lines.length && !doc_lines[ _line ].startsWith( ".." ) ) { + while (curLine < docLines.length && !isHeader()) { + mdText.push(docLines[curLine]); + console.log(setNBSP(escapeSym(docLines[curLine])) + T_BR + CL); + curLine++; + } + console.log(T_END_CODE + T_BR + CL); - let s = translateLine( doc_lines[_line] ); + mdText.push('```'); + } - if( s.split(CB_REF)[1] ) { - s = s.replace("*", "-") - s = s.replace( CB_REF, RECIPE); - s = s.replace("`R", ""); - let ruleNum = Number( s.replace("`", "").split(RECIPE)[1]); -console.error(">>>RULE in SEE " + ruleNum + " " + s.replace("`", "") + " -> " + ruleNames[ruleNum] ); - s = s.replace("`", ":"); - s += ' ' + ruleNames[ruleNum]; - } + mdText.push(''); - mdText.push( s ); + return goodCode; +} - if( doc_lines[_line].startsWith(CB_SEE) ) - _line++; - _line++; - } +function makeSee(): string { + const RECIPE = 'Recipe '; + console.error('>>> #' + recNum + ' PASSED: ' + docLines[curLine]); + while (curLine < docLines.length && !docLines[curLine].startsWith('..')) { + let s = translateLine(docLines[curLine]); + + if (s.split(CB_REF)[1]) { + s = s.replace('*', '-'); + s = s.replace(CB_REF, RECIPE); + s = s.replace('`R', ''); + const ruleNum = Number(s.replace('`', '').split(RECIPE)[1]); + console.error('>>>RULE in SEE ' + ruleNum + ' ' + s.replace('`', '') + ' -> ' + ruleNames[ruleNum]); + s = s.replace('`', ':'); + s += ' ' + ruleNames[ruleNum]; + } - mdText.push(""); + mdText.push(s); - return ""; -} + if (docLines[curLine].startsWith(CB_SEE)) { + curLine++; + } + curLine++; + } + + mdText.push(''); + return ''; +} -// -// Main routine -// +/* + * + * Main routine + * + */ let commandLineArgs = process.argv.slice(2); if (commandLineArgs.length === 0) { - console.error(">>> Command line error: no arguments"); - process.exit(-1); + console.error('>>> Command line error: no arguments'); + process.exit(-1); } -if( commandLineArgs[0] == '-md') { - commandLineArgs = process.argv.slice(3); - MAKE_MD = true; +if (commandLineArgs[0] === '-md') { + commandLineArgs = process.argv.slice(3); } -let inFileName = commandLineArgs[0]; +const inFileName = commandLineArgs[0]; console.log(COPYRIGHT_HEADER); -console.log( CODE_PROLOGUE ); -syncReadFile( inFileName); +console.log(CODE_PROLOGUE); +syncReadFile(inFileName); -for( recNum = 1; recNum < tegs.length; recNum++ ) { - console.log( "cookBookTag[ " + recNum + " ] = " + STR_DLMTR + ( tegs[ recNum ] ? tegs[ recNum ] : "" ) + STR_DLMTR + ";" ); +for (recNum = 1; recNum < tegs.length; recNum++) { + console.log('cookBookTag[ ' + recNum + ' ] = ' + STR_DLMTR + (tegs[recNum] ? tegs[recNum] : '') + STR_DLMTR + ';'); } +console.log('\nexport const cookBookRefToFixTitle: Map = new Map(['); +for (const num of fixTitles.keys()) { + console.log(` [${num}, '${fixTitles.get(num)}'],`); +} +console.log(']);'); diff --git a/ets2panda/linter-4.2/docs/rules/recipe118.md b/ets2panda/linter-4.2/docs/rules/recipe118.md deleted file mode 100644 index f625bc3e982353617c9dd28f7c6d67ff886e1e6d..0000000000000000000000000000000000000000 --- a/ets2panda/linter-4.2/docs/rules/recipe118.md +++ /dev/null @@ -1,39 +0,0 @@ -# Special import type declarations are not supported - -Rule ``arkts-no-special-imports`` - -**Severity: error** - -ArkTS does not have a special notation for importing types. -Use ordinary import instead. - - -## TypeScript - - -``` - - // Re-using the same import - import { APIResponseType } from "api" - - // Explicitly use import type - import type { APIResponseType } from "api" - -``` - -## ArkTS - - -``` - - import { APIResponseType } from "api" - -``` - -## See also - -- Recipe 119: Importing a module for side-effects only is not supported (``arkts-no-side-effects-imports``) -- Recipe 120: ``import default as ...`` is not supported (``arkts-no-import-default-as``) -- Recipe 121: ``require`` and ``import`` assignment are not supported (``arkts-no-require``) - - diff --git a/ets2panda/linter-4.2/docs/rules/recipe127.md b/ets2panda/linter-4.2/docs/rules/recipe127.md deleted file mode 100644 index c89d24fbac4b50b1e68937ea99e7e979ae8c1452..0000000000000000000000000000000000000000 --- a/ets2panda/linter-4.2/docs/rules/recipe127.md +++ /dev/null @@ -1,48 +0,0 @@ -# Special ``export type`` declarations are not supported - -Rule ``arkts-no-special-exports`` - -**Severity: error** - -ArkTS does not have a special notation for exporting types through -``export type ...``. Use ordinary export instead. - - -## TypeScript - - -``` - - // Explicitly exported class: - export class Class1 { - // ... - } - - // Declared class later exported through export type ... - class Class2 { - // ... - } - - // This is not supported: - export type { Class2 } - -``` - -## ArkTS - - -``` - - // Explicitly exported class: - export class Class1 { - // ... - } - - // Explicitly exported class: - export class Class2 { - // ... - } - -``` - - diff --git a/ets2panda/linter-4.2/docs/rules/recipe144.md b/ets2panda/linter-4.2/docs/rules/recipe144.md index 28acf4a49c2d12c132c77f1e840a1d5de12e0f0d..ec36a4ffbf2f7a833502932ffc2010f44f6e2d24 100644 --- a/ets2panda/linter-4.2/docs/rules/recipe144.md +++ b/ets2panda/linter-4.2/docs/rules/recipe144.md @@ -29,8 +29,6 @@ Properties and functions of the global object: ``eval`` ``handler.has()``, ``handler.isExtensible()``, ``handler.ownKeys()``, ``handler.preventExtensions()``, ``handler.set()``, ``handler.setPrototypeOf()`` -``ArrayBuffer``: ``isView`` - ## See also diff --git a/ets2panda/linter-4.2/docs/rules/recipe151.md b/ets2panda/linter-4.2/docs/rules/recipe151.md index 0b00ba64328b8da460cba56c63a6dd63fd050e23..2e7fd2d46a7c1a86d1c9ac02edd38945f5cfd0b1 100644 --- a/ets2panda/linter-4.2/docs/rules/recipe151.md +++ b/ets2panda/linter-4.2/docs/rules/recipe151.md @@ -1,17 +1,18 @@ # Usage of ``ESObject`` type is restricted -Rule ``arkts-limited-esobject`` +Rule ``arkts-limited-esobj`` **Severity: warning** -ArkTS does not allow using ``ESObject`` type in some cases. The most part of limitations -are put in place in order to prevent spread of dynamic objects in the static codebase. -The only scenario where it is permited to use ``ESObject`` as type specifier is in local -variable declaration. Initialization of variables with ``ESObject`` type is also limited. -Such variables can only be initialized with values that originate from interop: -other ``ESObject`` typed variables, any, unknown, variables with anonymous type, etc. -It is prohibited to initialize ``ESObject`` typed variable with statically typed value. -Varaible of type ``ESObject`` can only be passed to interop calls and assigned to other +ArkTS does not allow using ``ESObject`` type in some cases. The most part of +limitations are put in place in order to prevent spread of dynamic objects in +the static codebase. The only scenario where it is permited to use ``ESObject`` +as type specifier is in local variable declaration. Initialization of variables +with ``ESObject`` type is also limited. Such variables can only be initialized +with values that originate from interop: other ``ESObject`` typed variables, +any, unknown, variables with anonymous type, etc. It is prohibited to +initialize ``ESObject`` typed variable with statically typed value. Varaible +of type ``ESObject`` can only be passed to interop calls and assigned to other variables of type ``ESObject``. @@ -32,12 +33,13 @@ variables of type ``ESObject``. let e3: ESObject = {}; // CTE - can't initialize ESObject with not dynamic values let e4: ESObject = []; // CTE - can't initialize ESObject with not dynamic values let e5: ESObject = ""; // CTE - can't initialize ESObject with not dynamic values + e5['prop'] // CTE - can't access dynamic properties of ESObject + e5[1] // CTE - can't access dynamic properties of ESObject + e5.prop // CTE - can't access dynamic properties of ESObject + let e6: ESObject = foo(); // OK - explicitly annotaded as ESObject let e7 = e6; // OK - initialize ESObject with ESObject - e6['prop'] // CTE - can't access dynamic properties of ESObject - e6[1] // CTE - can't access dynamic properties of ESObject - e6.prop // CTE - can't access dynamic properties of ESObject - bar(e6) // OK - ESObject is passed to interop call + bar(e7) // OK - ESObject is passed to interop call } ``` @@ -51,4 +53,3 @@ variables of type ``ESObject``. - Recipe 066: ``in`` operator is not supported (``arkts-no-in``) - Recipe 137: ``globalThis`` is not supported (``arkts-no-globalthis``) - diff --git a/ets2panda/linter-4.2/docs/rules/recipe29.md b/ets2panda/linter-4.2/docs/rules/recipe29.md index c7be978d6962ce635842df26f13c20591f59b060..7af61c43e6cc5976617e17be86598978444d8ebe 100644 --- a/ets2panda/linter-4.2/docs/rules/recipe29.md +++ b/ets2panda/linter-4.2/docs/rules/recipe29.md @@ -10,9 +10,13 @@ that are either declared in the class, or accessible via inheritance. Accessing any other fields is prohibited, and causes compile-time errors. To access a field, use ``obj.field`` syntax, indexed access (``obj["field"]``) -is not supported. An exception are all typed arrays from the standard library -(for example, ``Int32Array``), which support access to their elements through -``container[index]`` syntax. +is not supported. An exception are: + +- All typed arrays from the standard library (for example, ``Int32Array``), which +support access to their elements through ``container[index]`` syntax. +- Tuples. +- Records. +- Enums. ## TypeScript diff --git a/ets2panda/linter-4.2/package.json b/ets2panda/linter-4.2/package.json index 651fc138f3956a2c3dcb3f761dc83b75fce18deb..2db6b27776ac981d55bc9639fb34515ad4f36064 100644 --- a/ets2panda/linter-4.2/package.json +++ b/ets2panda/linter-4.2/package.json @@ -11,16 +11,17 @@ "scripts": { "tsc": "tsc", "tscv": "tsc --version", - "webpack":"webpack -t node --config webpack.config.js", + "webpack": "webpack -t node --config webpack.config.js", "clean": "rimraf build dist bundle", "compile": "npm run tsc", "build": "npm run clean && npm run compile && npm run webpack && npm run pack:linter", "postinstall": "npm run build", "pack:linter": "rimraf bundle && mkdir bundle && npm pack --pack-destination bundle", - "test": "npm run compile && rimraf test/results test_rules/results && node build/src/TestRunner.js test test_rules", + "test": "npm run test_main && npm run test_rules && npm run test_regression", "test_main": "npm run compile && rimraf test/results && node build/src/TestRunner.js test", + "test_regression": "npm run compile && rimraf test_regression/results && node build/src/TestRunner.js test_regression", "test_rules": "npm run compile && rimraf test_rules/results && node build/src/TestRunner.js test_rules", - "update-tests": "node scripts/update-test-results.mjs test test_rules" + "update-tests": "node scripts/update-test-results.mjs test test_rules test_regression" }, "devDependencies": { "@types/node": "18.11.7", @@ -39,7 +40,6 @@ "log4js": "6.4.0", "commander": "9.4.0" }, - "bundledDependencies": [ "typescript", "log4js", diff --git a/ets2panda/linter-4.2/scripts/update-test-results.mjs b/ets2panda/linter-4.2/scripts/update-test-results.mjs index dc579e42453a4416215504af7d0e3133221a4f33..45029c37eb3f2858d3a3848b5eebc2453598d268 100644 --- a/ets2panda/linter-4.2/scripts/update-test-results.mjs +++ b/ets2panda/linter-4.2/scripts/update-test-results.mjs @@ -37,7 +37,7 @@ const RESULTS_DIR = 'results' let testDirs = []; // forces to update all tests regardless of whether there was diff in a test result -const force_update = false; +let force_update = false; for (let arg of process.argv.slice(2)) { if (arg === '--force') @@ -72,33 +72,37 @@ function readTestFile(filePath) { function updateTest(testDir, testFile, mode) { let resultExt = RESULT_EXT[mode]; - let testFileWithExt = testFile + resultExt; + let resultFileWithExt = testFile + resultExt; + let resultFilePath = path.join(testDir, resultFileWithExt); // Do not update autofix result if test is skipped - if (mode === Mode.AUTOFIX && fs.existsSync(path.join(testDir, testFileWithExt + AUTOFIX_SKIP_EXT))) { + if (mode === Mode.AUTOFIX && fs.existsSync(path.join(testDir, testFile + AUTOFIX_SKIP_EXT))) { return; } - // Update test result when 'diff' exists or the 'force' option is enabled. - if (!fs.existsSync(path.join(testDir, RESULTS_DIR, testFileWithExt + DIFF_EXT)) && !force_update) { + // Update test result when: + // - '.diff' exists + // - expected '.json' doesn't exist + // - 'force' option is enabled + if (fs.existsSync(resultFilePath) && !fs.existsSync(path.join(testDir, RESULTS_DIR, resultFileWithExt + DIFF_EXT)) && !force_update) { return; } - let expectedResult = readTestFile(path.join(testDir, testFileWithExt)); + let expectedResult = readTestFile(resultFilePath); const copyright = expectedResult?.copyright ?? DEFAULT_COPYRIGHT; - let actualResult = readTestFile(path.join(testDir, RESULTS_DIR, testFileWithExt)); + let actualResult = readTestFile(path.join(testDir, RESULTS_DIR, resultFileWithExt)); if (!actualResult || !actualResult.nodes) { - console.log(`Failed to update ${testFileWithExt}: couldn't read ACTUAL result file.`); + console.log(`Failed to update ${resultFileWithExt}: couldn't read ACTUAL result file.`); return; } // Write file with actual test results. let newResultJSON = JSON.stringify({ copyright, nodes: actualResult.nodes }, null, 4); - fs.writeFileSync(path.join(testDir, testFileWithExt), newResultJSON); + fs.writeFileSync(resultFilePath, newResultJSON); - console.log(`Updated ${testFileWithExt}`); + console.log(`Updated ${resultFileWithExt}`); } for (let testDir of testDirs) { diff --git a/ets2panda/linter-4.2/src/AutofixInfo.ts b/ets2panda/linter-4.2/src/AutofixInfo.ts index a0d25dbd25e6e8c99c812ba2d08bcb6f80ed6a06..6265e48de896b3350720261bd036f9419eceb091 100644 --- a/ets2panda/linter-4.2/src/AutofixInfo.ts +++ b/ets2panda/linter-4.2/src/AutofixInfo.ts @@ -17,4 +17,4 @@ export interface AutofixInfo { problemID: string; start: number; end: number; -} \ No newline at end of file +} diff --git a/ets2panda/linter-4.2/src/Autofixer.ts b/ets2panda/linter-4.2/src/Autofixer.ts index d082f5ade8234e5feb0f20069176f9e16bd2b751..6bfe2e8c26f6549f47970f756f933e812969d283 100644 --- a/ets2panda/linter-4.2/src/Autofixer.ts +++ b/ets2panda/linter-4.2/src/Autofixer.ts @@ -14,20 +14,24 @@ */ import * as ts from 'typescript'; -import { AutofixInfo } from './AutofixInfo'; +import type { AutofixInfo } from './AutofixInfo'; import { FaultID } from './Problems'; import { isAssignmentOperator } from './Utils'; export const AUTOFIX_ALL: AutofixInfo = { - problemID: '', start: -1, end: -1 -} + problemID: '', + start: -1, + end: -1 +}; -// Some fixes are potentially risky and may break source code if fixes -// are applied separately. -// Temporary solution is to disable all risky autofixes, until the -// algorithm is improved to guarantee that fixes can be applied -// safely and won't break program code. -const UNSAFE_FIXES: FaultID[] = [ FaultID.LiteralAsPropertyName, FaultID.PropertyAccessByIndex ]; +/* + * Some fixes are potentially risky and may break source code if fixes + * are applied separately. + * Temporary solution is to disable all risky autofixes, until the + * algorithm is improved to guarantee that fixes can be applied + * safely and won't break program code. + */ +const UNSAFE_FIXES: FaultID[] = [FaultID.LiteralAsPropertyName, FaultID.PropertyAccessByIndex]; export interface Autofix { replacementText: string; @@ -36,149 +40,180 @@ export interface Autofix { } export class AutofixInfoSet { - private autofixInfo: AutofixInfo[]; + private readonly autofixInfo: AutofixInfo[]; constructor(autofixInfo: AutofixInfo[] | undefined) { this.autofixInfo = autofixInfo ? autofixInfo : []; } - public shouldAutofix(node: ts.Node, faultID: FaultID): boolean { - if (UNSAFE_FIXES.includes(faultID)) return false; - if (this.autofixInfo.length === 0) return false; + shouldAutofix(node: ts.Node, faultID: FaultID): boolean { + if (UNSAFE_FIXES.includes(faultID)) { + return false; + } + if (this.autofixInfo.length === 0) { + return false; + } if (this.autofixInfo.length === 1 && this.autofixInfo[0] === AUTOFIX_ALL) { return true; } - return this.autofixInfo.findIndex( - value => value.start === node.getStart() && value.end === node.getEnd() && value.problemID === FaultID[faultID] - ) !== -1; + return ( + this.autofixInfo.findIndex((value) => { + return value.start === node.getStart() && value.end === node.getEnd() && value.problemID === FaultID[faultID]; + }) !== -1 + ); } } export function fixLiteralAsPropertyName(node: ts.Node): Autofix[] | undefined { if (ts.isPropertyDeclaration(node) || ts.isPropertyAssignment(node)) { - let propName = (node as (ts.PropertyDeclaration | ts.PropertyAssignment)).name; - let identName = propertyName2IdentifierName(propName); - if (identName) + const propName = node.name; + const identName = propertyName2IdentifierName(propName); + if (identName) { return [{ replacementText: identName, start: propName.getStart(), end: propName.getEnd() }]; + } } return undefined; } export function fixPropertyAccessByIndex(node: ts.Node): Autofix[] | undefined { if (ts.isElementAccessExpression(node)) { - let elemAccess = node as ts.ElementAccessExpression; - let identifierName = indexExpr2IdentifierName(elemAccess.argumentExpression); - if (identifierName) - return [{ - replacementText: elemAccess.expression.getText() + '.' + identifierName, - start: elemAccess.getStart(), end: elemAccess.getEnd() - }]; + const elemAccess = node; + const identifierName = indexExpr2IdentifierName(elemAccess.argumentExpression); + if (identifierName) { + return [ + { + replacementText: elemAccess.expression.getText() + '.' + identifierName, + start: elemAccess.getStart(), + end: elemAccess.getEnd() + } + ]; + } } return undefined; } -export function fixFunctionExpression(funcExpr: ts.FunctionExpression, +export function fixFunctionExpression( + funcExpr: ts.FunctionExpression, params: ts.NodeArray = funcExpr.parameters, retType: ts.TypeNode | undefined = funcExpr.type, - modifiers: readonly ts.Modifier[] | undefined): Autofix { + modifiers: readonly ts.Modifier[] | undefined +): Autofix { let arrowFunc: ts.Expression = ts.factory.createArrowFunction( - modifiers, undefined, params, retType, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), + modifiers, + undefined, + params, + retType, + ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), funcExpr.body ); if (needsParentheses(funcExpr)) { arrowFunc = ts.factory.createParenthesizedExpression(arrowFunc); } - let text = printer.printNode(ts.EmitHint.Unspecified, arrowFunc, funcExpr.getSourceFile()); + const text = printer.printNode(ts.EmitHint.Unspecified, arrowFunc, funcExpr.getSourceFile()); return { start: funcExpr.getStart(), end: funcExpr.getEnd(), replacementText: text }; } export function fixReturnType(funcLikeDecl: ts.FunctionLikeDeclaration, typeNode: ts.TypeNode): Autofix { - let text = ': ' + printer.printNode(ts.EmitHint.Unspecified, typeNode, funcLikeDecl.getSourceFile()); - let pos = getReturnTypePosition(funcLikeDecl); + const text = ': ' + printer.printNode(ts.EmitHint.Unspecified, typeNode, funcLikeDecl.getSourceFile()); + const pos = getReturnTypePosition(funcLikeDecl); return { start: pos, end: pos, replacementText: text }; } export function dropTypeOnVarDecl(varDecl: ts.VariableDeclaration): Autofix { - let newVarDecl = ts.factory.createVariableDeclaration(varDecl.name, undefined, undefined, undefined); - let text = printer.printNode(ts.EmitHint.Unspecified, newVarDecl, varDecl.getSourceFile()); - return { start: varDecl.getStart(), end: varDecl.getEnd(), replacementText: text}; + const newVarDecl = ts.factory.createVariableDeclaration(varDecl.name, undefined, undefined, undefined); + const text = printer.printNode(ts.EmitHint.Unspecified, newVarDecl, varDecl.getSourceFile()); + return { start: varDecl.getStart(), end: varDecl.getEnd(), replacementText: text }; } -export function dropTypeOnlyFlag( - impExpNode: ts.ImportClause | ts.ExportDeclaration -): Autofix { +export function dropTypeOnlyFlag(impExpNode: ts.ImportClause | ts.ExportDeclaration): Autofix { let text: string; if (ts.isImportClause(impExpNode)) { - let newImportClause = ts.factory.createImportClause(false, impExpNode.name, impExpNode.namedBindings); + const newImportClause = ts.factory.createImportClause(false, impExpNode.name, impExpNode.namedBindings); text = printer.printNode(ts.EmitHint.Unspecified, newImportClause, impExpNode.getSourceFile()); - } - else { - let newExportDecl = ts.factory.createExportDeclaration(undefined, impExpNode.modifiers, false, - impExpNode.exportClause, impExpNode.moduleSpecifier); + } else { + const newExportDecl = ts.factory.createExportDeclaration( + undefined, + impExpNode.modifiers, + false, + impExpNode.exportClause, + impExpNode.moduleSpecifier + ); text = printer.printNode(ts.EmitHint.Unspecified, newExportDecl, impExpNode.getSourceFile()); } return { start: impExpNode.getStart(), end: impExpNode.getEnd(), replacementText: text }; } -export function fixDefaultImport(importClause: ts.ImportClause, - defaultSpec: ts.ImportSpecifier, nonDefaultSpecs: ts.ImportSpecifier[]): Autofix { - let nameBindings = nonDefaultSpecs.length > 0 ? ts.factory.createNamedImports(nonDefaultSpecs) : undefined; - let newImportClause = ts.factory.createImportClause(importClause.isTypeOnly, defaultSpec.name, nameBindings); - let text = printer.printNode(ts.EmitHint.Unspecified, newImportClause, importClause.getSourceFile()); +export function fixDefaultImport( + importClause: ts.ImportClause, + defaultSpec: ts.ImportSpecifier, + nonDefaultSpecs: ts.ImportSpecifier[] +): Autofix { + const nameBindings = nonDefaultSpecs.length > 0 ? ts.factory.createNamedImports(nonDefaultSpecs) : undefined; + const newImportClause = ts.factory.createImportClause(importClause.isTypeOnly, defaultSpec.name, nameBindings); + const text = printer.printNode(ts.EmitHint.Unspecified, newImportClause, importClause.getSourceFile()); return { start: importClause.getStart(), end: importClause.getEnd(), replacementText: text }; } export function fixTypeAssertion(typeAssertion: ts.TypeAssertion): Autofix { const asExpr = ts.factory.createAsExpression(typeAssertion.expression, typeAssertion.type); - let text = printer.printNode(ts.EmitHint.Unspecified, asExpr, typeAssertion.getSourceFile()); + const text = printer.printNode(ts.EmitHint.Unspecified, asExpr, typeAssertion.getSourceFile()); return { start: typeAssertion.getStart(), end: typeAssertion.getEnd(), replacementText: text }; } const printer: ts.Printer = ts.createPrinter(); -function numericLiteral2IdentifierName(numeric: ts.NumericLiteral) { +function numericLiteral2IdentifierName(numeric: ts.NumericLiteral): string { return '__' + numeric.getText(); } -function stringLiteral2IdentifierName(str: ts.StringLiteral) { - let text = (str as ts.StringLiteral).getText(); - return text.substring(1, text.length-1); // cut out starting and ending quoters. +function stringLiteral2IdentifierName(str: ts.StringLiteral): string { + const text = str.getText(); + // cut out starting and ending quoters. + return text.substring(1, text.length - 1); } function propertyName2IdentifierName(name: ts.PropertyName): string { - if (name.kind === ts.SyntaxKind.NumericLiteral) - return numericLiteral2IdentifierName(name as ts.NumericLiteral); + if (name.kind === ts.SyntaxKind.NumericLiteral) { + return numericLiteral2IdentifierName(name); + } - if (name.kind === ts.SyntaxKind.StringLiteral) - return stringLiteral2IdentifierName(name as ts.StringLiteral); + if (name.kind === ts.SyntaxKind.StringLiteral) { + return stringLiteral2IdentifierName(name); + } return ''; } -function indexExpr2IdentifierName(index: ts.Expression) { - if (index.kind === ts.SyntaxKind.NumericLiteral) +function indexExpr2IdentifierName(index: ts.Expression): string { + if (index.kind === ts.SyntaxKind.NumericLiteral) { return numericLiteral2IdentifierName(index as ts.NumericLiteral); + } - if (index.kind === ts.SyntaxKind.StringLiteral) + if (index.kind === ts.SyntaxKind.StringLiteral) { return stringLiteral2IdentifierName(index as ts.StringLiteral); + } return ''; } function getReturnTypePosition(funcLikeDecl: ts.FunctionLikeDeclaration): number { if (funcLikeDecl.body) { - // Find position of the first node or token that follows parameters. - // After that, iterate over child nodes in reverse order, until found - // first closing parenthesis. - let postParametersPosition = ts.isArrowFunction(funcLikeDecl) - ? funcLikeDecl.equalsGreaterThanToken.getStart() - : funcLikeDecl.body.getStart(); + + /* + * Find position of the first node or token that follows parameters. + * After that, iterate over child nodes in reverse order, until found + * first closing parenthesis. + */ + const postParametersPosition = ts.isArrowFunction(funcLikeDecl) ? + funcLikeDecl.equalsGreaterThanToken.getStart() : + funcLikeDecl.body.getStart(); const children = funcLikeDecl.getChildren(); for (let i = children.length - 1; i >= 0; i--) { const child = children[i]; - if (child.kind === ts.SyntaxKind.CloseParenToken && child.getEnd() < postParametersPosition) + if (child.kind === ts.SyntaxKind.CloseParenToken && child.getEnd() < postParametersPosition) { return child.getEnd(); + } } } @@ -188,9 +223,15 @@ function getReturnTypePosition(funcLikeDecl: ts.FunctionLikeDeclaration): number function needsParentheses(node: ts.FunctionExpression): boolean { const parent = node.parent; - return ts.isPrefixUnaryExpression(parent) || ts.isPostfixUnaryExpression(parent) || - ts.isPropertyAccessExpression(parent) || ts.isElementAccessExpression(parent) || - ts.isTypeOfExpression(parent) || ts.isVoidExpression(parent) || ts.isAwaitExpression(parent) || - (ts.isCallExpression(parent) && node === parent.expression) || - (ts.isBinaryExpression(parent) && !isAssignmentOperator(parent.operatorToken)); -} \ No newline at end of file + return ( + ts.isPrefixUnaryExpression(parent) || + ts.isPostfixUnaryExpression(parent) || + ts.isPropertyAccessExpression(parent) || + ts.isElementAccessExpression(parent) || + ts.isTypeOfExpression(parent) || + ts.isVoidExpression(parent) || + ts.isAwaitExpression(parent) || + ts.isCallExpression(parent) && node === parent.expression || + ts.isBinaryExpression(parent) && !isAssignmentOperator(parent.operatorToken) + ); +} diff --git a/ets2panda/linter-4.2/src/CommandLineOptions.ts b/ets2panda/linter-4.2/src/CommandLineOptions.ts index c9803afb790b06c472f08042ff0054a3ffdcd97e..9dd4acd716855a7e1425894afb585d4c2c015b50 100644 --- a/ets2panda/linter-4.2/src/CommandLineOptions.ts +++ b/ets2panda/linter-4.2/src/CommandLineOptions.ts @@ -13,8 +13,8 @@ * limitations under the License. */ -import * as ts from 'typescript'; -import { AutofixInfo } from './AutofixInfo'; +import type * as ts from 'typescript'; +import type { AutofixInfo } from './AutofixInfo'; export interface CommandLineOptions { testMode?: boolean; @@ -25,4 +25,4 @@ export interface CommandLineOptions { parsedConfigFile?: ts.ParsedCommandLine; inputFiles: string[]; autofixInfo?: AutofixInfo[]; -} \ No newline at end of file +} diff --git a/ets2panda/linter-4.2/src/CommandLineParser.ts b/ets2panda/linter-4.2/src/CommandLineParser.ts index 9544757307dc76c9897aaa57ee30fc7f79c45f04..007bf9a175030fd6c2af70d9bf8e65a7e30d8285 100644 --- a/ets2panda/linter-4.2/src/CommandLineParser.ts +++ b/ets2panda/linter-4.2/src/CommandLineParser.ts @@ -13,9 +13,9 @@ * limitations under the License. */ -import Logger from '../utils/logger'; +import logger from '../utils/logger'; import { logTscDiagnostic, decodeAutofixInfo } from './Utils'; -import { CommandLineOptions } from './CommandLineOptions'; +import type { CommandLineOptions } from './CommandLineOptions'; import { AUTOFIX_ALL } from './Autofixer'; import { Command, Option } from 'commander'; import * as ts from 'typescript'; @@ -27,15 +27,16 @@ const TSX_EXT = '.tsx'; const ETS_EXT = '.ets'; const JSON_EXT = '.json'; -const logger = Logger.getLogger(); +const loggerInstance = logger.getLogger(); let inputFiles: string[]; let responseFile = ''; -function addSrcFile(value: string, dummy: string) { - if(value.startsWith('@')) +function addSrcFile(value: string): void { + if (value.startsWith('@')) { responseFile = value; - else + } else { inputFiles.push(value); + } } const getFiles = (dir: string): string[] => { @@ -43,87 +44,114 @@ const getFiles = (dir: string): string[] => { const files = fs.readdirSync(dir); for (let i = 0; i < files.length; ++i) { - let name = path.join(dir, files[i]); + const name = path.join(dir, files[i]); if (fs.statSync(name).isDirectory()) { resultFiles.push(...getFiles(name)); } else { - let extension = path.extname(name); - if (extension === TS_EXT || extension === TSX_EXT || extension === ETS_EXT) + const extension = path.extname(name); + if (extension === TS_EXT || extension === TSX_EXT || extension === ETS_EXT) { resultFiles.push(name); + } } } return resultFiles; }; -function addProjectFolder(projectFolder: string, previous: any ) { +function addProjectFolder(projectFolder: string, previous: string[]): string[] { return previous.concat([projectFolder]); } -export function parseCommandLine(commandLineArgs: string[]): CommandLineOptions { +function formCommandLineOptions(program: Command): CommandLineOptions { const opts: CommandLineOptions = { inputFiles: [], warningsAsErrors: false }; + // Default mode of the linter. + opts.strictMode = true; + opts.inputFiles = inputFiles; + + const options = program.opts(); + if (options.relax) { + opts.strictMode = false; + } + if (options.TSC_Errors) { + opts.logTscErrors = true; + } + if (options.devecoPluginMode) { + opts.ideMode = true; + } + if (options.testMode) { + opts.testMode = true; + } + if (options.projectFolder) { + doProjectFolderArg(options.projectFolder, opts); + } + if (options.project) { + doProjectArg(options.project, opts); + } + if (options.autofix) { + doAutofixArg(options.autofix, opts); + } + if (options.warningsAsErrors) { + opts.warningsAsErrors = true; + } + return opts; +} +export function parseCommandLine(commandLineArgs: string[]): CommandLineOptions { const program = new Command(); - program - .name('tslinter') - .description('Linter for TypeScript sources') - .version('0.0.1'); - program - .option('-E, --TSC_Errors', 'show error messages from Tsc') - .option('--relax', 'relax mode On') - .option('--test-mode', 'run linter as if running TS test files') - .option('--deveco-plugin-mode', 'run as IDE plugin') - .option('-p, --project ', 'path to TS project config file') - .option('--project-folder ', 'path to folder containig TS files to verify', addProjectFolder, []) - .option('--autofix [autofix.json]', 'fix errors specified by JSON file (all if file is omitted)', - (val: string, prev: string|boolean) => { return val.endsWith(JSON_EXT) ? val : true; }) - .addOption(new Option('--warnings-as-errors', 'treat warnings as errors').hideHelp(true)); - program - .argument('[srcFile...]', 'files to be verified', addSrcFile); - - opts.strictMode = true; // Default mode of the linter. + program.name('tslinter').description('Linter for TypeScript sources'). + version('0.0.1'); + program. + option('-E, --TSC_Errors', 'show error messages from Tsc'). + option('--relax', 'relax mode On'). + option('--test-mode', 'run linter as if running TS test files'). + option('--deveco-plugin-mode', 'run as IDE plugin'). + option('-p, --project ', 'path to TS project config file'). + option('--project-folder ', 'path to folder containig TS files to verify', addProjectFolder, []). + option('--autofix [autofix.json]', 'fix errors specified by JSON file (all if file is omitted)', (val: string) => { + return val.endsWith(JSON_EXT) ? val : true; + }). + addOption(new Option('--warnings-as-errors', 'treat warnings as errors').hideHelp(true)); + program.argument('[srcFile...]', 'files to be verified', addSrcFile); + inputFiles = []; - let cmdArgs: string[] = ['dummy', 'dummy']; // method parse() eats two first args, so make them dummy + // method parse() eats two first args, so make them dummy + let cmdArgs: string[] = ['dummy', 'dummy']; cmdArgs.push(...commandLineArgs); program.parse(cmdArgs); if (responseFile !== '') { try { - commandLineArgs = fs.readFileSync(responseFile.slice(1)).toString().split('\n').filter((e) => e.trimEnd()); + commandLineArgs = fs. + readFileSync(responseFile.slice(1)). + toString(). + split('\n'). + filter((e) => { + return e.trimEnd(); + }); cmdArgs = ['dummy', 'dummy']; cmdArgs.push(...commandLineArgs); - program.parse( cmdArgs); - } catch (error: any) { - logger.error('Failed to read response file: ' + (error.message ?? error)); - process.exit(-1) + program.parse(cmdArgs); + } catch (error) { + loggerInstance.error('Failed to read response file: ' + error); + process.exit(-1); } } - opts.inputFiles = inputFiles; - const options = program.opts(); - if (options.relax) opts.strictMode = false; - if (options.TSC_Errors) opts.logTscErrors = true; - if (options.devecoPluginMode) opts.ideMode = true; - if (options.testMode) opts.testMode = true; - if (options.projectFolder) doProjectFolderArg(options.projectFolder, opts); - if (options.project) doProjectArg(options.project, opts); - if (options.autofix) doAutofixArg(options.autofix, opts); - if (options.warningsAsErrors) opts.warningsAsErrors = true; - return opts; + return formCommandLineOptions(program); } -function doProjectFolderArg(prjFolders: string[], opts: CommandLineOptions) { - for( let i = 0; i < prjFolders.length; i++ ) { - var prjFolderPath = prjFolders[ i ]; +function doProjectFolderArg(prjFolders: string[], opts: CommandLineOptions): void { + for (let i = 0; i < prjFolders.length; i++) { + const prjFolderPath = prjFolders[i]; try { opts.inputFiles.push(...getFiles(prjFolderPath)); - } catch (error: any) { - logger.error('Failed to read folder: ' + (error.message ?? error)); + } catch (error: unknown) { + loggerInstance.error('Failed to read folder: ' + error); process.exit(-1); } } } -function doProjectArg(cfgPath: string, opts: CommandLineOptions) { +function doProjectArg(cfgPath: string, opts: CommandLineOptions): void { // Process project file (tsconfig.json) and retrieve config arguments. const configFile = cfgPath; @@ -133,30 +161,36 @@ function doProjectArg(cfgPath: string, opts: CommandLineOptions) { try { const oldUnrecoverableDiagnostic = host.onUnRecoverableConfigFileDiagnostic; - host.onUnRecoverableConfigFileDiagnostic = (diagnostic: ts.Diagnostic) => { diagnostics.push(diagnostic); }; + host.onUnRecoverableConfigFileDiagnostic = (diagnostic: ts.Diagnostic): void => { + diagnostics.push(diagnostic); + }; opts.parsedConfigFile = ts.getParsedCommandLineOfConfigFile(configFile, {}, host); host.onUnRecoverableConfigFileDiagnostic = oldUnrecoverableDiagnostic; - if (opts.parsedConfigFile) + if (opts.parsedConfigFile) { diagnostics.push(...ts.getConfigFileParsingDiagnostics(opts.parsedConfigFile)); + } if (diagnostics.length > 0) { // Log all diagnostic messages and exit program. - logger.error('Failed to read config file.'); - logTscDiagnostic(diagnostics, logger.info); + loggerInstance.error('Failed to read config file.'); + logTscDiagnostic(diagnostics, loggerInstance.info); process.exit(-1); } - } catch (error: any) { - logger.error('Failed to read config file: ' + (error.message ?? error)); + } catch (error: unknown) { + loggerInstance.error('Failed to read config file: ' + error); process.exit(-1); } } -function doAutofixArg(autofixOptVal: string|boolean, opts: CommandLineOptions) { +function doAutofixArg(autofixOptVal: string | boolean, opts: CommandLineOptions): void { if (typeof autofixOptVal === 'string') { - let autofixInfoStr = fs.readFileSync(autofixOptVal).toString(); - let autofixInfos = JSON.parse(autofixInfoStr); - opts.autofixInfo = autofixInfos.autofixInfo.map((x: string) => decodeAutofixInfo(x)); + const autofixInfoStr = fs.readFileSync(autofixOptVal).toString(); + const autofixInfos = JSON.parse(autofixInfoStr); + opts.autofixInfo = autofixInfos.autofixInfo.map((x: string) => { + return decodeAutofixInfo(x); + }); + } else { + opts.autofixInfo = [AUTOFIX_ALL]; } - else opts.autofixInfo = [AUTOFIX_ALL]; } diff --git a/ets2panda/linter-4.2/src/CompilerWrapper.ts b/ets2panda/linter-4.2/src/CompilerWrapper.ts index 8ecfa8bbf92c8f132693d2c4faa1cba39de1c102..5788123918fdd2f9dcfae7a35c0b28f000519aeb 100644 --- a/ets2panda/linter-4.2/src/CompilerWrapper.ts +++ b/ets2panda/linter-4.2/src/CompilerWrapper.ts @@ -16,8 +16,8 @@ import * as ts from 'typescript'; import { logTscDiagnostic } from './Utils'; import { consoleLog } from './TypeScriptLinter'; -import { CommandLineOptions } from './CommandLineOptions'; -import { LintOptions } from './LintOptions'; +import type { CommandLineOptions } from './CommandLineOptions'; +import type { LintOptions } from './LintOptions'; export function compile(options: LintOptions, extraOptions?: any): ts.Program { const createProgramOptions = formTscOptions(options.cmdOptions, extraOptions); @@ -41,28 +41,28 @@ export function compile(options: LintOptions, extraOptions?: any): ts.Program { function formTscOptions(cmdOptions: CommandLineOptions, extraOptions?: any): ts.CreateProgramOptions { if (cmdOptions.parsedConfigFile) { - let options: ts.CreateProgramOptions = { + const options: ts.CreateProgramOptions = { rootNames: cmdOptions.parsedConfigFile.fileNames, options: cmdOptions.parsedConfigFile.options, projectReferences: cmdOptions.parsedConfigFile.projectReferences, - configFileParsingDiagnostics: ts.getConfigFileParsingDiagnostics(cmdOptions.parsedConfigFile), + configFileParsingDiagnostics: ts.getConfigFileParsingDiagnostics(cmdOptions.parsedConfigFile) }; if (extraOptions) { options.options = Object.assign(options.options, extraOptions); } - + return options; } - let options: ts.CreateProgramOptions = { + const options: ts.CreateProgramOptions = { rootNames: cmdOptions.inputFiles, options: { target: ts.ScriptTarget.Latest, module: ts.ModuleKind.CommonJS, allowJs: true, - checkJs: true, - }, + checkJs: true + } }; if (extraOptions) { diff --git a/ets2panda/linter-4.2/src/CookBookMsg.ts b/ets2panda/linter-4.2/src/CookBookMsg.ts index c097345da5b47ffe0f98b7215329933df4889a14..5b77b6e5206566f366c83e6c3bd728a3b4ee211a 100644 --- a/ets2panda/linter-4.2/src/CookBookMsg.ts +++ b/ets2panda/linter-4.2/src/CookBookMsg.ts @@ -1,26 +1,27 @@ -/* - * Copyright (c) 2022-2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. +/* + * Copyright (c) 2022-2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ export const cookBookMsg: string[] = []; export const cookBookTag: string[] = []; -for (let i = 0; i <= 150; i++) { - cookBookMsg[i] = ''; +for (let i = 0; i <= 151; i++) { + cookBookMsg[i] = ''; } -cookBookTag[1] = 'Objects with property names that are not identifiers are not supported (arkts-identifiers-as-prop-names)'; +cookBookTag[1] = + 'Objects with property names that are not identifiers are not supported (arkts-identifiers-as-prop-names)'; cookBookTag[2] = '"Symbol()" API is not supported (arkts-no-symbol)'; cookBookTag[3] = 'Private \'#\' identifiers are not supported (arkts-no-private-identifiers)'; cookBookTag[4] = 'Use unique names for types and namespaces. (arkts-unique-names)'; @@ -56,8 +57,9 @@ cookBookTag[33] = ''; cookBookTag[34] = 'Type inference in case of generic function calls is limited (arkts-no-inferred-generic-params)'; cookBookTag[35] = ''; cookBookTag[36] = ''; -cookBookTag[37] = 'RegExp literals are not supported (arkts-no-regexp-literals)'; -cookBookTag[38] = 'Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)'; +cookBookTag[37] = ''; +cookBookTag[38] = + 'Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)'; cookBookTag[39] = ''; cookBookTag[40] = 'Object literals cannot be used as type declarations (arkts-no-obj-literals-as-types)'; cookBookTag[41] = ''; @@ -118,7 +120,8 @@ cookBookTag[95] = ''; cookBookTag[96] = 'Type guarding is supported with "instanceof" and "as" (arkts-no-is)'; cookBookTag[97] = ''; cookBookTag[98] = ''; -cookBookTag[99] = 'It is possible to spread only arrays or classes derived from arrays into the rest parameter or array literals (arkts-no-spread)'; +cookBookTag[99] = + 'It is possible to spread only arrays or classes derived from arrays into the rest parameter or array literals (arkts-no-spread)'; cookBookTag[100] = ''; cookBookTag[101] = ''; cookBookTag[102] = 'Interface can not extend interfaces with the same method (arkts-no-extend-same-prop)'; @@ -130,12 +133,14 @@ cookBookTag[107] = ''; cookBookTag[108] = ''; cookBookTag[109] = ''; cookBookTag[110] = ''; -cookBookTag[111] = 'Enumeration members can be initialized only with compile time expressions of the same type (arkts-no-enum-mixed-types)'; +cookBookTag[111] = + 'Enumeration members can be initialized only with compile time expressions of the same type (arkts-no-enum-mixed-types)'; cookBookTag[112] = ''; cookBookTag[113] = '"enum" declaration merging is not supported (arkts-no-enum-merging)'; cookBookTag[114] = 'Namespaces cannot be used as objects (arkts-no-ns-as-obj)'; cookBookTag[115] = ''; -cookBookTag[116] = 'Non-declaration statements in namespaces are not supported (arkts-no-ns-statements)'; +cookBookTag[116] = + 'Non-declaration statements in namespaces are not supported (single semicolons are considered as empty non-declaration statements) (arkts-no-ns-statements)'; cookBookTag[117] = ''; cookBookTag[118] = 'Special import type declarations are not supported (arkts-no-special-imports)'; cookBookTag[119] = 'Importing a module for side-effects only is not supported (arkts-no-side-effects-imports)'; @@ -159,7 +164,8 @@ cookBookTag[136] = 'Prototype assignment is not supported (arkts-no-prototype-as cookBookTag[137] = '"globalThis" is not supported (arkts-no-globalthis)'; cookBookTag[138] = 'Some of utility types are not supported (arkts-no-utility-types)'; cookBookTag[139] = 'Declaring properties on functions is not supported (arkts-no-func-props)'; -cookBookTag[140] = '"Function.apply", "Function.bind", "Function.call" are not supported (arkts-no-func-apply-bind-call)'; +cookBookTag[140] = + '"Function.apply", "Function.bind", "Function.call" are not supported (arkts-no-func-apply-bind-call)'; cookBookTag[141] = ''; cookBookTag[142] = '"as const" assertions are not supported (arkts-no-as-const)'; cookBookTag[143] = 'Import assertions are not supported (arkts-no-import-assertions)'; @@ -170,4 +176,4 @@ cookBookTag[147] = 'No dependencies on TypeScript code are currently allowed (ar cookBookTag[148] = 'No decorators except ArkUI decorators are currently allowed (arkts-no-decorators-except-arkui)'; cookBookTag[149] = 'Classes cannot be used as objects (arkts-no-classes-as-obj)'; cookBookTag[150] = '"import" statements after other statements are not allowed (arkts-no-misplaced-imports)'; -cookBookTag[151] = 'Usage of "ESObject" type is restricted (arkts-limited-esobject)'; +cookBookTag[151] = 'Usage of "ESObject" type is restricted (arkts-limited-esobj)'; diff --git a/ets2panda/linter-4.2/src/DiagnosticChecker.ts b/ets2panda/linter-4.2/src/DiagnosticChecker.ts index dd327de111e711b2e96ba421aa2e4b99ef3e2f88..b64436f904739806768401678b388b84b09eea36 100644 --- a/ets2panda/linter-4.2/src/DiagnosticChecker.ts +++ b/ets2panda/linter-4.2/src/DiagnosticChecker.ts @@ -16,5 +16,5 @@ import type * as ts from 'typescript'; export interface DiagnosticChecker { - checkDiagnosticMessage(msgText: string | ts.DiagnosticMessageChain): boolean; -} \ No newline at end of file + checkDiagnosticMessage: (msgText: string | ts.DiagnosticMessageChain) => boolean; +} diff --git a/ets2panda/linter/src/utils/functions/GetScriptKind.ts b/ets2panda/linter-4.2/src/ForEachNodeInSubtree.ts similarity index 53% rename from ets2panda/linter/src/utils/functions/GetScriptKind.ts rename to ets2panda/linter-4.2/src/ForEachNodeInSubtree.ts index e1bedc1b38141603517ddf3b4dee0200b73458f0..08fd8e7148347756a44d6e2b653555b14444c54b 100644 --- a/ets2panda/linter/src/utils/functions/GetScriptKind.ts +++ b/ets2panda/linter-4.2/src/ForEachNodeInSubtree.ts @@ -14,23 +14,18 @@ */ import * as ts from 'typescript'; -import * as path from 'node:path'; -export function getScriptKind(srcFile: ts.SourceFile): ts.ScriptKind { - const fileName = srcFile.fileName - const ext = path.extname(fileName).toLowerCase() - switch (ext) { - case ts.Extension.Ts: - return ts.ScriptKind.TS; - case ts.Extension.Tsx: - return ts.ScriptKind.TSX; - case ts.Extension.Js: - return ts.ScriptKind.JS; - case ts.Extension.Jsx: - return ts.ScriptKind.JSX; - case ts.Extension.Json: - return ts.ScriptKind.JSON; - default: - return ts.ScriptKind.Unknown; +export function forEachNodeInSubtree( + node: ts.Node, + cb: (n: ts.Node) => void, + stopCond?: (n: ts.Node) => boolean +): void { + cb(node); + if (stopCond && stopCond(node)) { + return; } -} \ No newline at end of file + + ts.forEachChild(node, (child) => { + forEachNodeInSubtree(child, cb, stopCond); + }); +} diff --git a/ets2panda/linter-4.2/src/IsFileFromModuleCallback.ts b/ets2panda/linter-4.2/src/IsFileFromModuleCallback.ts new file mode 100644 index 0000000000000000000000000000000000000000..46ed92b861d4da1be3221f33ded3a68d9166a41d --- /dev/null +++ b/ets2panda/linter-4.2/src/IsFileFromModuleCallback.ts @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2023-2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export type IsFileFromModuleCallback = (fileFsPath: string) => boolean; diff --git a/ets2panda/linter-4.2/src/LibraryTypeCallDiagnosticChecker.ts b/ets2panda/linter-4.2/src/LibraryTypeCallDiagnosticChecker.ts index e80b4025a56a37011308d42eb0727c3687e73210..5f59db087dd7437ffb7d4f76b1ddef45963a6253 100644 --- a/ets2panda/linter-4.2/src/LibraryTypeCallDiagnosticChecker.ts +++ b/ets2panda/linter-4.2/src/LibraryTypeCallDiagnosticChecker.ts @@ -13,19 +13,25 @@ * limitations under the License. */ -import * as ts from 'typescript'; -import { DiagnosticChecker } from './DiagnosticChecker'; +import type * as ts from 'typescript'; +import type { DiagnosticChecker } from './DiagnosticChecker'; -// Current approach relates on error code and error message matching and it is quite fragile, -// so this place should be checked thoroughly in the case of typescript upgrade +/* + * Current approach relates on error code and error message matching and it is quite fragile, + * so this place should be checked thoroughly in the case of typescript upgrade + */ export const TYPE_0_IS_NOT_ASSIGNABLE_TO_TYPE_1_ERROR_CODE = 2322; -export const TYPE_UNKNOWN_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE = /^Type '(.*)\bunknown\b(.*)' is not assignable to type '.*'\.$/; -export const TYPE_NULL_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE = /^Type 'null' is not assignable to type '.*'\.$/; -export const TYPE_UNDEFINED_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE = /^Type 'undefined' is not assignable to type '.*'\.$/; +export const TYPE_UNKNOWN_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE = + /^Type '(.*)\bunknown\b(.*)' is not assignable to type '.*'\.$/; +export const TYPE_NULL_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE = /^Type '(.*)\bnull\b(.*)' is not assignable to type '.*'\.$/; +export const TYPE_UNDEFINED_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE = + /^Type '(.*)\bundefined\b(.*)' is not assignable to type '.*'\.$/; export const ARGUMENT_OF_TYPE_0_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_ERROR_CODE = 2345; -export const ARGUMENT_OF_TYPE_NULL_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_RE = /^Argument of type 'null' is not assignable to parameter of type '.*'\.$/; -export const ARGUMENT_OF_TYPE_UNDEFINED_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_RE = /^Argument of type 'undefined' is not assignable to parameter of type '.*'\.$/; +export const ARGUMENT_OF_TYPE_NULL_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_RE = + /^Argument of type '(.*)\bnull\b(.*)' is not assignable to parameter of type '.*'\.$/; +export const ARGUMENT_OF_TYPE_UNDEFINED_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_RE = + /^Argument of type '(.*)\bundefined\b(.*)' is not assignable to parameter of type '.*'\.$/; export class LibraryTypeCallDiagnosticChecker implements DiagnosticChecker { inLibCall: boolean = false; @@ -43,7 +49,8 @@ export class LibraryTypeCallDiagnosticChecker implements DiagnosticChecker { checkMessageText(msg: string): boolean { if (this.inLibCall) { - const match = msg.match(ARGUMENT_OF_TYPE_NULL_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_RE) || + const match = + msg.match(ARGUMENT_OF_TYPE_NULL_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_RE) || msg.match(ARGUMENT_OF_TYPE_UNDEFINED_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_RE) || msg.match(TYPE_UNDEFINED_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE) || msg.match(TYPE_NULL_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE); @@ -127,4 +134,4 @@ export class LibraryTypeCallDiagnosticChecker implements DiagnosticChecker { } return true; } -} \ No newline at end of file +} diff --git a/ets2panda/linter/src/utils/consts/LimitedStdApi.ts b/ets2panda/linter-4.2/src/LimitedStdAPI.ts similarity index 51% rename from ets2panda/linter/src/utils/consts/LimitedStdApi.ts rename to ets2panda/linter-4.2/src/LimitedStdAPI.ts index 1599fbfe9210a7b902bfffac2596ea6a87fe3a28..8e28dfa76e1bc4c88dd4b7f4d287a0a5e4e0452b 100644 --- a/ets2panda/linter/src/utils/consts/LimitedStdApi.ts +++ b/ets2panda/linter-4.2/src/LimitedStdAPI.ts @@ -13,23 +13,35 @@ * limitations under the License. */ -import { FaultID } from "./Problems"; +import { FaultID } from './Problems'; -/** - * ArrayBuffer - */ -const LIMITED_STD_ARRAYBUFFER_API = [ +const LIMITED_STD_SYMBOL_API = [ // properties + 'asyncIterator', + 'description', + 'hasInstance', + 'isConcatSpreadable', + 'match', + 'matchAll', + 'replace', + 'search', + 'species', + 'split', + 'toPrimitive', + 'toStringTag', + 'unscopables', + // methods - 'isView' + 'for', + 'keyFor', + 'toString', + 'valueOf' ]; -/** - * Object - */ const LIMITED_STD_OBJECT_API = [ // properties '__proto__', + // methods '__defineGetter__', '__defineSetter__', @@ -54,14 +66,12 @@ const LIMITED_STD_OBJECT_API = [ 'preventExtensions', 'propertyIsEnumerable', 'seal', - 'setPrototypeOf', + 'setPrototypeOf' ]; -/** - * Proxy - */ const LIMITED_STD_PROXYHANDLER_API = [ // properties + // methods 'apply', 'construct', @@ -78,11 +88,9 @@ const LIMITED_STD_PROXYHANDLER_API = [ 'setPrototypeOf' ]; -/** - * Reflect - */ const LIMITED_STD_REFLECT_API = [ // properties + // methods 'apply', 'construct', @@ -92,64 +100,51 @@ const LIMITED_STD_REFLECT_API = [ 'getPrototypeOf', 'isExtensible', 'preventExtensions', - 'setPrototypeOf', + 'setPrototypeOf' ]; -/** - * Symbol - */ -const LIMITED_STD_SYMBOL_API = [ - 'Symbol', - // properties - 'asyncIterator', - 'description', - 'hasInstance', - 'isConcatSpreadable', - 'match', - 'matchAll', - 'replace', - 'search', - 'species', - 'split', - 'toPrimitive', - 'toStringTag', - 'unscopables', - // methods - 'for', - 'keyFor', - 'toString', - 'valueOf', +const LIMITED_STD_FUNCTION_API_APPLY_CALL = [ + // properties + + // methods + 'apply', + 'call' ]; -/** - * Function - */ -const LIMITED_STD_FUNCTION_API = [ +const LIMITED_STD_FUNCTION_API_BIND = [ // properties + // methods - 'apply', - 'bind', - 'call', + 'bind' ]; -/** - * Global - */ -export const LIMITED_STD_GLOBAL_API = [ +const LIMITED_STD_GLOBAL_API = [ // properties + // methods - 'eval', + 'eval' ]; -export const LIMITED_STD_API = new Map, fault: FaultID}> ([ - ['Object', {arr: LIMITED_STD_OBJECT_API, fault: FaultID.LimitedStdLibApi}], - ['ObjectConstructor', {arr: LIMITED_STD_OBJECT_API, fault: FaultID.LimitedStdLibApi}], - ['Reflect', {arr: LIMITED_STD_REFLECT_API, fault: FaultID.LimitedStdLibApi}], - ['ProxyHandler', {arr: LIMITED_STD_PROXYHANDLER_API, fault: FaultID.LimitedStdLibApi}], - ['ArrayBuffer', {arr: LIMITED_STD_ARRAYBUFFER_API, fault: FaultID.LimitedStdLibApi}], - ['ArrayBufferConstructor', {arr: LIMITED_STD_ARRAYBUFFER_API, fault: FaultID.LimitedStdLibApi}], - ['Symbol', {arr: LIMITED_STD_SYMBOL_API, fault: FaultID.SymbolType}], - ['SymbolConstructor', {arr: LIMITED_STD_SYMBOL_API, fault: FaultID.SymbolType}], - ['Function', {arr: LIMITED_STD_FUNCTION_API, fault: FaultID.FunctionApplyBindCall}], - ['CallableFunction', {arr: LIMITED_STD_FUNCTION_API, fault: FaultID.FunctionApplyBindCall}], -]) +const STD_SYMBOL_ENTRY = [{ api: LIMITED_STD_SYMBOL_API, faultId: FaultID.SymbolType }]; +const STD_OBJECT_ENTRY = [{ api: LIMITED_STD_OBJECT_API, faultId: FaultID.LimitedStdLibApi }]; +const STD_PROXYHANDLER_ENTRY = [{ api: LIMITED_STD_PROXYHANDLER_API, faultId: FaultID.LimitedStdLibApi }]; +const STD_REFLECT_ENTRY = [{ api: LIMITED_STD_REFLECT_API, faultId: FaultID.LimitedStdLibApi }]; +const STD_FUNCTION_ENTRY = [ + { api: LIMITED_STD_FUNCTION_API_APPLY_CALL, faultId: FaultID.FunctionApplyCall }, + { api: LIMITED_STD_FUNCTION_API_BIND, faultId: FaultID.FunctionBind } +]; +const STD_GLOBAL_ENTRY = [{ api: LIMITED_STD_GLOBAL_API, faultId: FaultID.LimitedStdLibApi }]; + +export type LimitedStdLibApiEntry = { api: string[]; faultId: FaultID }; + +export const LIMITED_STD_API = new Map([ + [undefined, STD_GLOBAL_ENTRY], + ['Object', STD_OBJECT_ENTRY], + ['ObjectConstructor', STD_OBJECT_ENTRY], + ['Reflect', STD_REFLECT_ENTRY], + ['ProxyHandler', STD_PROXYHANDLER_ENTRY], + ['Symbol', STD_SYMBOL_ENTRY], + ['SymbolConstructor', STD_SYMBOL_ENTRY], + ['Function', STD_FUNCTION_ENTRY], + ['CallableFunction', STD_FUNCTION_ENTRY] +]); diff --git a/ets2panda/linter-4.2/src/LintOptions.ts b/ets2panda/linter-4.2/src/LintOptions.ts index a2d1184f59fbf5189cc5e18a23cafab7b9c7fd37..d25e3a3bd824b26bed798a0a56e500e4a8c83903 100644 --- a/ets2panda/linter-4.2/src/LintOptions.ts +++ b/ets2panda/linter-4.2/src/LintOptions.ts @@ -13,12 +13,14 @@ * limitations under the License. */ -import * as ts from 'typescript'; -import { CommandLineOptions } from './CommandLineOptions'; +import type * as ts from 'typescript'; +import type { CommandLineOptions } from './CommandLineOptions'; +import type { IsFileFromModuleCallback } from './IsFileFromModuleCallback'; // common options interface, additional fields may be used by plugins export interface LintOptions { cmdOptions: CommandLineOptions; tsProgram?: ts.Program; + isFileFromModuleCb?: IsFileFromModuleCallback; [key: string]: any; } diff --git a/ets2panda/linter-4.2/src/LintRunResult.ts b/ets2panda/linter-4.2/src/LintRunResult.ts index 565b7bf6dfb2d7a980f25519eecb08ede6d686f7..751cc8d6e80148bd6d248441440480058f680102 100644 --- a/ets2panda/linter-4.2/src/LintRunResult.ts +++ b/ets2panda/linter-4.2/src/LintRunResult.ts @@ -13,9 +13,9 @@ * limitations under the License. */ -import { ProblemInfo } from './ProblemInfo'; +import type { ProblemInfo } from './ProblemInfo'; export interface LintRunResult { errorNodes: number; problemsInfos: Map; -} \ No newline at end of file +} diff --git a/ets2panda/linter-4.2/src/LinterRunner.ts b/ets2panda/linter-4.2/src/LinterRunner.ts index 466e245285f03a5ef0f951940c25006a7820c2ed..fc0be1fc4f6ee498eb8fdcdd9967b97d675fdfd2 100644 --- a/ets2panda/linter-4.2/src/LinterRunner.ts +++ b/ets2panda/linter-4.2/src/LinterRunner.ts @@ -13,58 +13,104 @@ * limitations under the License. */ -import * as ts from 'typescript'; -import { ProblemInfo } from './ProblemInfo'; +import type * as ts from 'typescript'; +import type { ProblemInfo } from './ProblemInfo'; import { TypeScriptLinter, consoleLog } from './TypeScriptLinter'; import { FaultID, faultsAttrs } from './Problems'; import { parseCommandLine } from './CommandLineParser'; import { LinterConfig } from './TypeScriptLinterConfig'; -import { LintRunResult } from './LintRunResult'; -import Logger from '../utils/logger'; +import type { LintRunResult } from './LintRunResult'; +import logger from '../utils/logger'; import * as fs from 'node:fs'; import * as os from 'node:os'; import * as readline from 'node:readline'; import * as path from 'node:path'; import { compile } from './CompilerWrapper'; -import { CommandLineOptions } from './CommandLineOptions'; -import { LintOptions } from './LintOptions'; +import type { CommandLineOptions } from './CommandLineOptions'; +import type { LintOptions } from './LintOptions'; import { AutofixInfoSet } from './Autofixer'; import { TSCCompiledProgram, getStrictOptions, transformDiagnostic } from './ts-diagnostics/TSCCompiledProgram'; import { mergeArrayMaps, pathContainsDirectory, TsUtils } from './Utils'; +import { ProblemSeverity } from './ProblemSeverity'; -const logger = Logger.getLogger(); +const loggerInstance = logger.getLogger(); // Use static init method for Linter configuration, as TypeScript 4.2 doesn't support static blocks. LinterConfig.initStatic(); -export function lint(options: LintOptions): LintRunResult { - const cmdOptions = options.cmdOptions; - - const tscDiagnosticsLinter = createLinter(options); - const tsProgram = tscDiagnosticsLinter.getOriginalProgram(); +function toFixedPercent(part: number): string { + const percentiles = 100; + const precision = 2; + return (part * percentiles).toFixed(precision); +} - // Prepare list of input files for linter and retrieve AST for those files. - let inputFiles: string[] = cmdOptions.inputFiles; +function prepareInputFilesList(cmdOptions: CommandLineOptions): string[] { + let inputFiles = cmdOptions.inputFiles; if (cmdOptions.parsedConfigFile) { inputFiles = cmdOptions.parsedConfigFile.fileNames; if (cmdOptions.inputFiles.length > 0) { - // Apply linter only to the project source files that are specified - // as a command-line arguments. Other source files will be discarded. - const cmdInputsResolvedPaths = cmdOptions.inputFiles.map((x) => path.resolve(x)); - const configInputsResolvedPaths = inputFiles.map((x) => path.resolve(x)); - inputFiles = configInputsResolvedPaths.filter((x) => cmdInputsResolvedPaths.some((y) => x === y)); + + /* + * Apply linter only to the project source files that are specified + * as a command-line arguments. Other source files will be discarded. + */ + const cmdInputsResolvedPaths = cmdOptions.inputFiles.map((x) => { + return path.resolve(x); + }); + const configInputsResolvedPaths = inputFiles.map((x) => { + return path.resolve(x); + }); + inputFiles = configInputsResolvedPaths.filter((x) => { + return cmdInputsResolvedPaths.some((y) => { + return x === y; + }); + }); } } - // #13436: ignore-list for ArkTS projects. - inputFiles = inputFiles.filter(input => - !TsUtils.ARKTS_IGNORE_FILES.some(ignore => path.basename(input) === ignore) && - !TsUtils.ARKTS_IGNORE_DIRS.some(ignore => pathContainsDirectory(path.resolve(input), ignore))); + return inputFiles; +} + +function countProblems(linter: TypeScriptLinter): [number, number] { + let errorNodesTotal = 0; + let warningNodes = 0; + for (let i = 0; i < FaultID.LAST_ID; i++) { + // if Strict mode - count all cases + if (!linter.strictMode && faultsAttrs[i].migratable) { + // In relax mode skip migratable + continue; + } + switch (faultsAttrs[i].severity) { + case ProblemSeverity.ERROR: + errorNodesTotal += linter.nodeCounters[i]; + break; + case ProblemSeverity.WARNING: + warningNodes += linter.nodeCounters[i]; + break; + } + } + + return [errorNodesTotal, warningNodes]; +} + +export function lint(options: LintOptions): LintRunResult { + const cmdOptions = options.cmdOptions; + + const tscDiagnosticsLinter = createLinter(options); + const tsProgram = tscDiagnosticsLinter.getOriginalProgram(); + + // Prepare list of input files for linter and retrieve AST for those files. + let inputFiles = prepareInputFilesList(cmdOptions); + inputFiles = inputFiles.filter((input) => { + return shouldProcessFile(options, input); + }); const srcFiles: ts.SourceFile[] = []; for (const inputFile of inputFiles) { const srcFile = tsProgram.getSourceFile(inputFile); - if (srcFile) srcFiles.push(srcFile); + if (srcFile) { + srcFiles.push(srcFile); + } } const tscStrictDiagnostics = getTscDiagnostics(tscDiagnosticsLinter, srcFiles); @@ -81,34 +127,55 @@ export function lint(options: LintOptions): LintRunResult { consoleLog('\n\n\nFiles scanned: ', srcFiles.length); consoleLog('\nFiles with problems: ', errorNodes); - let errorNodesTotal = 0, warningNodes = 0; - for (let i = 0; i < FaultID.LAST_ID; i++) { - // if Strict mode - count all cases - if (!linter.strictMode && faultsAttrs[i].migratable) // In relax mode skip migratable - continue; + const [errorNodesTotal, warningNodes] = countProblems(linter); - if (faultsAttrs[i].warning) warningNodes += linter.nodeCounters[i]; - else errorNodesTotal += linter.nodeCounters[i]; - } logTotalProblemsInfo(errorNodesTotal, warningNodes, linter); logProblemsPercentageByFeatures(linter); + return { errorNodes: errorNodesTotal, - problemsInfos: mergeArrayMaps(problemsInfos, transformTscDiagnostics(tscStrictDiagnostics)), + problemsInfos: mergeArrayMaps(problemsInfos, transformTscDiagnostics(tscStrictDiagnostics)) }; } +/* + * We want linter to accept program with no strict options set at all + * due to them affecting type deduction. + */ +function clearStrictOptions(options: LintOptions): LintOptions { + if (!options.parserOptions) { + return options; + } + const newOptions = { ...options }; + newOptions.parserOptions.strict = false; + newOptions.parserOptions.alwaysStrict = false; + newOptions.parserOptions.noImplicitAny = false; + newOptions.parserOptions.noImplicitThis = false; + newOptions.parserOptions.strictBindCallApply = false; + newOptions.parserOptions.strictFunctionTypes = false; + newOptions.parserOptions.strictNullChecks = false; + newOptions.parserOptions.strictPropertyInitialization = false; + newOptions.parserOptions.useUnknownInCatchVariables = false; + return newOptions; +} + export function createLinter(options: LintOptions): TSCCompiledProgram { if (options.tscDiagnosticsLinter) { return options.tscDiagnosticsLinter; } - const tsProgram = options.tsProgram ?? compile(options, getStrictOptions()); - return new TSCCompiledProgram(tsProgram, options); + + /* + * if we are provided with the pre-compiled program, don't tamper with options + * otherwise, clear all strict related options to avoid differences in type deduction + */ + const newOptions = options.tsProgram ? options : clearStrictOptions(options); + const tsProgram = newOptions.tsProgram ?? compile(newOptions, getStrictOptions()); + return new TSCCompiledProgram(tsProgram, newOptions); } function lintFiles(srcFiles: ts.SourceFile[], linter: TypeScriptLinter): LintRunResult { let problemFiles = 0; - let problemsInfos: Map = new Map(); + const problemsInfos: Map = new Map(); for (const srcFile of srcFiles) { const prevVisitedNodes = linter.totalVisitedNodes; @@ -118,12 +185,13 @@ function lintFiles(srcFiles: ts.SourceFile[], linter: TypeScriptLinter): LintRun linter.warningLineNumbersString = ''; const nodeCounters: number[] = []; - for (let i = 0; i < FaultID.LAST_ID; i++) + for (let i = 0; i < FaultID.LAST_ID; i++) { nodeCounters[i] = linter.nodeCounters[i]; + } linter.lint(srcFile); // save results and clear problems array - problemsInfos.set( path.normalize(srcFile.fileName), [...linter.problemsInfos]); + problemsInfos.set(path.normalize(srcFile.fileName), [...linter.problemsInfos]); linter.problemsInfos.length = 0; // print results for current file @@ -132,13 +200,19 @@ function lintFiles(srcFiles: ts.SourceFile[], linter: TypeScriptLinter): LintRun const fileWarningLines = linter.totalWarningLines - prevWarningLines; problemFiles = countProblemFiles( - nodeCounters, problemFiles, srcFile, fileVisitedNodes, fileErrorLines, fileWarningLines, linter + nodeCounters, + problemFiles, + srcFile, + fileVisitedNodes, + fileErrorLines, + fileWarningLines, + linter ); } return { errorNodes: problemFiles, - problemsInfos: problemsInfos, + problemsInfos: problemsInfos }; } @@ -151,10 +225,10 @@ function lintFiles(srcFiles: ts.SourceFile[], linter: TypeScriptLinter): LintRun */ function getTscDiagnostics( tscDiagnosticsLinter: TSCCompiledProgram, - sourceFiles: ts.SourceFile[], + sourceFiles: ts.SourceFile[] ): Map { const strictDiagnostics = new Map(); - sourceFiles.forEach(file => { + sourceFiles.forEach((file) => { const diagnostics = tscDiagnosticsLinter.getStrictDiagnostics(file.fileName); if (diagnostics.length !== 0) { strictDiagnostics.set(path.normalize(file.fileName), diagnostics); @@ -163,68 +237,85 @@ function getTscDiagnostics( return strictDiagnostics; } -function transformTscDiagnostics( - strictDiagnostics: Map -): Map { +function transformTscDiagnostics(strictDiagnostics: Map): Map { const problemsInfos = new Map(); - strictDiagnostics.forEach((diagnostics, file, map) => { - problemsInfos.set(file, diagnostics.map(x => transformDiagnostic(x))); + strictDiagnostics.forEach((diagnostics, file) => { + problemsInfos.set( + file, + diagnostics.map((x) => { + return transformDiagnostic(x); + }) + ); }); return problemsInfos; } function countProblemFiles( - nodeCounters: number[], filesNumber: number, tsSrcFile: ts.SourceFile, - fileNodes: number, fileErrorLines: number, fileWarningLines: number, linter: TypeScriptLinter, -) { - let errorNodes = 0, warningNodes = 0; + nodeCounters: number[], + filesNumber: number, + tsSrcFile: ts.SourceFile, + fileNodes: number, + fileErrorLines: number, + fileWarningLines: number, + linter: TypeScriptLinter +): number { + let errorNodes = 0; + let warningNodes = 0; for (let i = 0; i < FaultID.LAST_ID; i++) { - let nodeCounterDiff = linter.nodeCounters[i] - nodeCounters[i]; - if (faultsAttrs[i].warning) warningNodes += nodeCounterDiff; - else errorNodes += nodeCounterDiff; + const nodeCounterDiff = linter.nodeCounters[i] - nodeCounters[i]; + switch (faultsAttrs[i].severity) { + case ProblemSeverity.ERROR: + errorNodes += nodeCounterDiff; + break; + case ProblemSeverity.WARNING: + warningNodes += nodeCounterDiff; + break; + } } - if (errorNodes > 0) { filesNumber++; - let errorRate = ((errorNodes / fileNodes) * 100).toFixed(2); - let warningRate = ((warningNodes / fileNodes) * 100).toFixed(2); + const errorRate = toFixedPercent(errorNodes / fileNodes); + const warningRate = toFixedPercent(warningNodes / fileNodes); consoleLog(tsSrcFile.fileName, ': ', '\n\tError lines: ', linter.errorLineNumbersString); consoleLog(tsSrcFile.fileName, ': ', '\n\tWarning lines: ', linter.warningLineNumbersString); - consoleLog('\n\tError constructs (%): ', errorRate, '\t[ of ', fileNodes, ' constructs ], \t', fileErrorLines, ' lines'); - consoleLog('\n\tWarning constructs (%): ', warningRate, '\t[ of ', fileNodes, ' constructs ], \t', fileWarningLines, ' lines'); + consoleLog(`\n\tError constructs (%): ${errorRate}\t[ of ${fileNodes} constructs ], \t${fileErrorLines} lines`); + consoleLog( + `\n\tWarning constructs (%): ${warningRate}\t[ of ${fileNodes} constructs ], \t${fileWarningLines} lines` + ); } - return filesNumber; } -function logTotalProblemsInfo(errorNodes: number, warningNodes: number, linter: TypeScriptLinter) { - let errorRate = ((errorNodes / linter.totalVisitedNodes) * 100).toFixed(2); - let warningRate = ((warningNodes / linter.totalVisitedNodes) * 100).toFixed(2); +function logTotalProblemsInfo(errorNodes: number, warningNodes: number, linter: TypeScriptLinter): void { + const errorRate = toFixedPercent(errorNodes / linter.totalVisitedNodes); + const warningRate = toFixedPercent(warningNodes / linter.totalVisitedNodes); consoleLog('\nTotal error constructs (%): ', errorRate); consoleLog('\nTotal warning constructs (%): ', warningRate); consoleLog('\nTotal error lines:', linter.totalErrorLines, ' lines\n'); consoleLog('\nTotal warning lines:', linter.totalWarningLines, ' lines\n'); } -function logProblemsPercentageByFeatures(linter: TypeScriptLinter) { +function logProblemsPercentageByFeatures(linter: TypeScriptLinter): void { consoleLog('\nPercent by features: '); + const paddingPercentage = 7; for (let i = 0; i < FaultID.LAST_ID; i++) { // if Strict mode - count all cases - if (!linter.strictMode && faultsAttrs[i].migratable) + if (!linter.strictMode && faultsAttrs[i].migratable) { continue; + } - let nodes = linter.nodeCounters[i]; - let lines = linter.lineCounters[i]; - let pecentage = ((nodes / linter.totalVisitedNodes) * 100).toFixed(2).padEnd(7, ' '); + const nodes = linter.nodeCounters[i]; + const lines = linter.lineCounters[i]; + const pecentage = toFixedPercent(nodes / linter.totalVisitedNodes).padEnd(paddingPercentage, ' '); consoleLog(LinterConfig.nodeDesc[i].padEnd(55, ' '), pecentage, '[', nodes, ' constructs / ', lines, ' lines]'); } } -export function run() { +export function run(): void { const commandLineArgs = process.argv.slice(2); if (commandLineArgs.length === 0) { - logger.info('Command line error: no arguments'); + loggerInstance.info('Command line error: no arguments'); process.exit(-1); } @@ -244,11 +335,12 @@ export function run() { } } -function getTempFileName() { - return path.join(os.tmpdir(), Math.floor(Math.random() * 10000000).toString() + '_linter_tmp_file.ts'); +function getTempFileName(): string { + const bigNumber = 10000000; + return path.join(os.tmpdir(), Math.floor(Math.random() * bigNumber).toString() + '_linter_tmp_file.ts'); } -function runIDEMode(cmdOptions: CommandLineOptions) { +function runIDEMode(cmdOptions: CommandLineOptions): void { TypeScriptLinter.ideMode = true; const tmpFileName = getTempFileName(); // read data from stdin @@ -256,10 +348,12 @@ function runIDEMode(cmdOptions: CommandLineOptions) { const rl = readline.createInterface({ input: process.stdin, output: writeStream, - terminal: false, + terminal: false }); - rl.on('line', (line: string) => { fs.appendFileSync(tmpFileName, line + '\n'); }); + rl.on('line', (line: string) => { + fs.appendFileSync(tmpFileName, line + '\n'); + }); rl.once('close', () => { // end of input writeStream.close(); @@ -270,22 +364,47 @@ function runIDEMode(cmdOptions: CommandLineOptions) { const result = lint({ cmdOptions: cmdOptions }); const problems = Array.from(result.problemsInfos.values()); if (problems.length === 1) { - const jsonMessage = problems[0].map((x) => ({ - line: x.line, - column: x.column, - start: x.start, - end: x.end, - type: x.type, - suggest: x.suggest, - rule: x.rule, - severity: x.severity, - autofixable: x.autofixable, - autofix: x.autofix - })); - logger.info(`{"linter messages":${JSON.stringify(jsonMessage)}}`); + const jsonMessage = problems[0].map((x) => { + return { + line: x.line, + column: x.column, + start: x.start, + end: x.end, + type: x.type, + suggest: x.suggest, + rule: x.rule, + severity: x.severity, + autofixable: x.autofixable, + autofix: x.autofix + }; + }); + loggerInstance.info(`{"linter messages":${JSON.stringify(jsonMessage)}}`); } else { - logger.error('Unexpected error: could not lint file'); + loggerInstance.error('Unexpected error: could not lint file'); } fs.unlinkSync(tmpFileName); }); } + +function shouldProcessFile(options: LintOptions, fileFsPath: string): boolean { + if ( + TsUtils.ARKTS_IGNORE_FILES.some((ignore) => { + return path.basename(fileFsPath) === ignore; + }) + ) { + return false; + } + + if ( + TsUtils.ARKTS_IGNORE_DIRS_NO_OH_MODULES.some((ignore) => { + return pathContainsDirectory(path.resolve(fileFsPath), ignore); + }) + ) { + return false; + } + + return ( + !pathContainsDirectory(path.resolve(fileFsPath), TsUtils.ARKTS_IGNORE_DIRS_OH_MODULES) || + options.isFileFromModuleCb !== undefined && options.isFileFromModuleCb(fileFsPath) + ); +} diff --git a/ets2panda/linter-4.2/src/ProblemInfo.ts b/ets2panda/linter-4.2/src/ProblemInfo.ts index 0339677419324bf539b2ee156f874fc44e3264b3..94f4cdeb86ba9ca4dd2f0081fbc9fe951720cbb3 100644 --- a/ets2panda/linter-4.2/src/ProblemInfo.ts +++ b/ets2panda/linter-4.2/src/ProblemInfo.ts @@ -13,7 +13,7 @@ * limitations under the License. */ -import { Autofix } from './Autofixer'; +import type { Autofix } from './Autofixer'; export interface ProblemInfo { line: number; diff --git a/ets2panda/linter-4.2/src/ProblemSeverity.ts b/ets2panda/linter-4.2/src/ProblemSeverity.ts index 3736c692ef46726f5ecced3be6290c18c371f039..e89dc4a78f69f1c70d9e3c4341cd7d8e8e71b917 100644 --- a/ets2panda/linter-4.2/src/ProblemSeverity.ts +++ b/ets2panda/linter-4.2/src/ProblemSeverity.ts @@ -13,4 +13,7 @@ * limitations under the License. */ -export enum ProblemSeverity { WARNING = 1, ERROR = 2 } +export enum ProblemSeverity { + WARNING = 1, + ERROR = 2 +} diff --git a/ets2panda/linter-4.2/src/Problems.ts b/ets2panda/linter-4.2/src/Problems.ts index 98bd4701b9054be916c8f229cada1cde816e8dad..e35662a38c8b8453f9af3017897757e170eedd1e 100644 --- a/ets2panda/linter-4.2/src/Problems.ts +++ b/ets2panda/linter-4.2/src/Problems.ts @@ -13,121 +13,188 @@ * limitations under the License. */ +import { ProblemSeverity } from './ProblemSeverity'; + export enum FaultID { - AnyType, SymbolType, ObjectLiteralNoContextType, ArrayLiteralNoContextType, - ComputedPropertyName, LiteralAsPropertyName, TypeQuery, RegexLiteral, IsOperator, - DestructuringParameter, YieldExpression, InterfaceMerging, EnumMerging, InterfaceExtendsClass, IndexMember, WithStatement, - ThrowStatement, IndexedAccessType, UnknownType, ForInStatement, InOperator, - ImportFromPath, FunctionExpression, IntersectionType, - ObjectTypeLiteral, CommaOperator, LimitedReturnTypeInference, - LambdaWithTypeParameters, ClassExpression, DestructuringAssignment, - DestructuringDeclaration, VarDeclaration, CatchWithUnsupportedType, DeleteOperator, - DeclWithDuplicateName, UnaryArithmNotNumber, ConstructorType, ConstructorIface, ConstructorFuncs, CallSignature, - TypeAssertion, PrivateIdentifier, LocalFunction, - ConditionalType, MappedType, NamespaceAsObject, ClassAsObject, - NonDeclarationInNamespace, GeneratorFunction, FunctionContainsThis, PropertyAccessByIndex, JsxElement, - EnumMemberNonConstInit, ImplementsClass, MethodReassignment, MultipleStaticBlocks, ThisType, - IntefaceExtendDifProps, StructuralIdentity, TypeOnlyImport, TypeOnlyExport, DefaultImport, - ExportAssignment, ImportAssignment, - GenericCallNoTypeArgs, ParameterProperties, - InstanceofUnsupported, ShorthandAmbientModuleDecl, WildcardsInModuleName, UMDModuleDefinition, - NewTarget, DefiniteAssignment, Prototype, GlobalThis, - UtilityType, PropertyDeclOnFunction, FunctionApplyBindCall, ConstAssertion, ImportAssertion, - SpreadOperator, LimitedStdLibApi, ErrorSuppression, StrictDiagnostic, UnsupportedDecorators, ImportAfterStatement, - EsObjectType, LAST_ID, // this should always be last enum + AnyType, + SymbolType, + ObjectLiteralNoContextType, + ArrayLiteralNoContextType, + ComputedPropertyName, + LiteralAsPropertyName, + TypeQuery, + IsOperator, + DestructuringParameter, + YieldExpression, + InterfaceMerging, + EnumMerging, + InterfaceExtendsClass, + IndexMember, + WithStatement, + ThrowStatement, + IndexedAccessType, + UnknownType, + ForInStatement, + InOperator, + ImportFromPath, + FunctionExpression, + IntersectionType, + ObjectTypeLiteral, + CommaOperator, + LimitedReturnTypeInference, + LambdaWithTypeParameters, + ClassExpression, + DestructuringAssignment, + DestructuringDeclaration, + VarDeclaration, + CatchWithUnsupportedType, + DeleteOperator, + DeclWithDuplicateName, + UnaryArithmNotNumber, + ConstructorType, + ConstructorIface, + ConstructorFuncs, + CallSignature, + TypeAssertion, + PrivateIdentifier, + LocalFunction, + ConditionalType, + MappedType, + NamespaceAsObject, + ClassAsObject, + NonDeclarationInNamespace, + GeneratorFunction, + FunctionContainsThis, + PropertyAccessByIndex, + JsxElement, + EnumMemberNonConstInit, + ImplementsClass, + MethodReassignment, + MultipleStaticBlocks, + ThisType, + IntefaceExtendDifProps, + StructuralIdentity, + DefaultImport, + ExportAssignment, + ImportAssignment, + GenericCallNoTypeArgs, + ParameterProperties, + InstanceofUnsupported, + ShorthandAmbientModuleDecl, + WildcardsInModuleName, + UMDModuleDefinition, + NewTarget, + DefiniteAssignment, + Prototype, + GlobalThis, + UtilityType, + PropertyDeclOnFunction, + FunctionApplyCall, + FunctionBind, + ConstAssertion, + ImportAssertion, + SpreadOperator, + LimitedStdLibApi, + ErrorSuppression, + StrictDiagnostic, + UnsupportedDecorators, + ImportAfterStatement, + EsObjectType, + // this should always be last enum + LAST_ID } -export class FaultAttributs { - migratable?: boolean; - warning?: boolean; - cookBookRef = '-1'; +export class FaultAttributes { + constructor( + public cookBookRef: number, + public migratable: boolean = false, + public severity: ProblemSeverity = ProblemSeverity.ERROR + ) {} } -export const faultsAttrs: FaultAttributs[] = []; +export const faultsAttrs: FaultAttributes[] = []; -faultsAttrs[FaultID.LiteralAsPropertyName] = {migratable: true, cookBookRef: '1',}; -faultsAttrs[FaultID.ComputedPropertyName] = {cookBookRef: '1',}; -faultsAttrs[FaultID.SymbolType] = {cookBookRef: '2',}; -faultsAttrs[FaultID.PrivateIdentifier] = {migratable: true, cookBookRef: '3',}; -faultsAttrs[FaultID.DeclWithDuplicateName] = {migratable: true, cookBookRef: '4',}; -faultsAttrs[FaultID.VarDeclaration] = {migratable: true, cookBookRef: '5',}; -faultsAttrs[FaultID.AnyType] = {cookBookRef: '8'}; -faultsAttrs[FaultID.UnknownType] = {cookBookRef: '8',}; -faultsAttrs[FaultID.CallSignature] = {cookBookRef: '14',}; -faultsAttrs[FaultID.ConstructorType] = {cookBookRef: '15',}; -faultsAttrs[FaultID.MultipleStaticBlocks] = {cookBookRef: '16',}; -faultsAttrs[FaultID.IndexMember] = {cookBookRef: '17',}; -faultsAttrs[FaultID.IntersectionType] = {cookBookRef: '19',}; -faultsAttrs[FaultID.ThisType] = {cookBookRef: '21',}; -faultsAttrs[FaultID.ConditionalType] = {cookBookRef: '22',}; -faultsAttrs[FaultID.ParameterProperties] = {migratable: true, cookBookRef: '25',}; -faultsAttrs[FaultID.ConstructorIface] = {cookBookRef: '27',}; -faultsAttrs[FaultID.IndexedAccessType] = {cookBookRef: '28',}; -faultsAttrs[FaultID.PropertyAccessByIndex] = {migratable: true, cookBookRef: '29',}; -faultsAttrs[FaultID.StructuralIdentity] = {cookBookRef: '30',}; -faultsAttrs[FaultID.GenericCallNoTypeArgs] = {cookBookRef: '34',}; -faultsAttrs[FaultID.RegexLiteral] = {cookBookRef: '37',}; -faultsAttrs[FaultID.ObjectLiteralNoContextType] = {cookBookRef: '38',}; -faultsAttrs[FaultID.ObjectTypeLiteral] = {cookBookRef: '40',}; -faultsAttrs[FaultID.ArrayLiteralNoContextType] = {cookBookRef: '43',}; -faultsAttrs[FaultID.FunctionExpression] = {migratable: true, cookBookRef: '46',}; -faultsAttrs[FaultID.LambdaWithTypeParameters] = {migratable: true, cookBookRef: '49',}; -faultsAttrs[FaultID.ClassExpression] = {migratable: true, cookBookRef: '50',}; -faultsAttrs[FaultID.ImplementsClass] = {cookBookRef: '51',}; -faultsAttrs[FaultID.MethodReassignment] = {cookBookRef: '52',}; -faultsAttrs[FaultID.TypeAssertion] = {migratable: true, cookBookRef: '53',}; -faultsAttrs[FaultID.JsxElement] = {cookBookRef: '54',}; -faultsAttrs[FaultID.UnaryArithmNotNumber] = {cookBookRef: '55',}; -faultsAttrs[FaultID.DeleteOperator] = {cookBookRef: '59',}; -faultsAttrs[FaultID.TypeQuery] = {cookBookRef: '60',}; -faultsAttrs[FaultID.InstanceofUnsupported] = {cookBookRef: '65',}; -faultsAttrs[FaultID.InOperator] = {cookBookRef: '66',}; -faultsAttrs[FaultID.DestructuringAssignment] = {migratable: true, cookBookRef: '69',}; -faultsAttrs[FaultID.CommaOperator] = {cookBookRef: '71',}; -faultsAttrs[FaultID.DestructuringDeclaration] = {migratable: true, cookBookRef: '74',}; -faultsAttrs[FaultID.CatchWithUnsupportedType] = {migratable: true, cookBookRef: '79',}; -faultsAttrs[FaultID.ForInStatement] = {cookBookRef: '80',}; -faultsAttrs[FaultID.MappedType] = {cookBookRef: '83',}; -faultsAttrs[FaultID.WithStatement] = {cookBookRef: '84',}; -faultsAttrs[FaultID.ThrowStatement] = {migratable: true, cookBookRef: '87',}; -faultsAttrs[FaultID.LimitedReturnTypeInference] = {migratable: true, cookBookRef: '90',}; -faultsAttrs[FaultID.DestructuringParameter] = {cookBookRef: '91',}; -faultsAttrs[FaultID.LocalFunction] = {migratable: true, cookBookRef: '92',}; -faultsAttrs[FaultID.FunctionContainsThis] = {cookBookRef: '93',}; -faultsAttrs[FaultID.GeneratorFunction] = {cookBookRef: '94',}; -faultsAttrs[FaultID.YieldExpression] = {cookBookRef: '94',}; -faultsAttrs[FaultID.IsOperator] = {cookBookRef: '96',}; -faultsAttrs[FaultID.SpreadOperator] = {cookBookRef: '99',}; -faultsAttrs[FaultID.IntefaceExtendDifProps] = {cookBookRef: '102',}; -faultsAttrs[FaultID.InterfaceMerging] = {cookBookRef: '103',}; -faultsAttrs[FaultID.InterfaceExtendsClass] = {cookBookRef: '104',}; -faultsAttrs[FaultID.ConstructorFuncs] = {cookBookRef: '106',}; -faultsAttrs[FaultID.EnumMemberNonConstInit] = {cookBookRef: '111',}; -faultsAttrs[FaultID.EnumMerging] = {cookBookRef: '113',}; -faultsAttrs[FaultID.NamespaceAsObject] = {cookBookRef: '114',}; -faultsAttrs[FaultID.NonDeclarationInNamespace] = {cookBookRef: '116',}; -faultsAttrs[FaultID.ImportFromPath] = {cookBookRef: '119',}; -faultsAttrs[FaultID.TypeOnlyImport] = {migratable: true, cookBookRef: '118',}; -faultsAttrs[FaultID.DefaultImport] = {migratable: true, cookBookRef: '120',}; -faultsAttrs[FaultID.ImportAssignment] = {cookBookRef: '121',}; -faultsAttrs[FaultID.ExportAssignment] = {cookBookRef: '126',}; -faultsAttrs[FaultID.TypeOnlyExport] = {migratable: true, cookBookRef: '127',}; -faultsAttrs[FaultID.ShorthandAmbientModuleDecl] = {cookBookRef: '128',}; -faultsAttrs[FaultID.WildcardsInModuleName] = {cookBookRef: '129',}; -faultsAttrs[FaultID.UMDModuleDefinition] = {cookBookRef: '130',}; -faultsAttrs[FaultID.NewTarget] = {cookBookRef: '132',}; -faultsAttrs[FaultID.DefiniteAssignment] = {warning: true, cookBookRef: '134',}; -faultsAttrs[FaultID.Prototype] = {cookBookRef: '136',}; -faultsAttrs[FaultID.GlobalThis] = {cookBookRef: '137',}; -faultsAttrs[FaultID.UtilityType] = {cookBookRef: '138',}; -faultsAttrs[FaultID.PropertyDeclOnFunction] = {cookBookRef: '139',}; -faultsAttrs[FaultID.FunctionApplyBindCall] = {cookBookRef: '140',}; -faultsAttrs[FaultID.ConstAssertion] = {cookBookRef: '142',}; -faultsAttrs[FaultID.ImportAssertion] = {cookBookRef: '143',}; -faultsAttrs[FaultID.LimitedStdLibApi] = {cookBookRef: '144',}; -faultsAttrs[FaultID.StrictDiagnostic] = {cookBookRef: '145',}; -faultsAttrs[FaultID.ErrorSuppression] = {cookBookRef: '146',}; -faultsAttrs[FaultID.UnsupportedDecorators] = {warning: true, cookBookRef: '148',}; -faultsAttrs[FaultID.ClassAsObject] = {cookBookRef: '149',}; -faultsAttrs[FaultID.ImportAfterStatement] = {cookBookRef: '150',}; -faultsAttrs[FaultID.EsObjectType] = {warning: true, cookBookRef: '151',}; +faultsAttrs[FaultID.LiteralAsPropertyName] = new FaultAttributes(1, true); +faultsAttrs[FaultID.ComputedPropertyName] = new FaultAttributes(1); +faultsAttrs[FaultID.SymbolType] = new FaultAttributes(2); +faultsAttrs[FaultID.PrivateIdentifier] = new FaultAttributes(3, true); +faultsAttrs[FaultID.DeclWithDuplicateName] = new FaultAttributes(4, true); +faultsAttrs[FaultID.VarDeclaration] = new FaultAttributes(5, true); +faultsAttrs[FaultID.AnyType] = new FaultAttributes(8); +faultsAttrs[FaultID.UnknownType] = new FaultAttributes(8); +faultsAttrs[FaultID.CallSignature] = new FaultAttributes(14); +faultsAttrs[FaultID.ConstructorType] = new FaultAttributes(15); +faultsAttrs[FaultID.MultipleStaticBlocks] = new FaultAttributes(16); +faultsAttrs[FaultID.IndexMember] = new FaultAttributes(17); +faultsAttrs[FaultID.IntersectionType] = new FaultAttributes(19); +faultsAttrs[FaultID.ThisType] = new FaultAttributes(21); +faultsAttrs[FaultID.ConditionalType] = new FaultAttributes(22); +faultsAttrs[FaultID.ParameterProperties] = new FaultAttributes(25, true); +faultsAttrs[FaultID.ConstructorIface] = new FaultAttributes(27); +faultsAttrs[FaultID.IndexedAccessType] = new FaultAttributes(28); +faultsAttrs[FaultID.PropertyAccessByIndex] = new FaultAttributes(29, true); +faultsAttrs[FaultID.StructuralIdentity] = new FaultAttributes(30); +faultsAttrs[FaultID.GenericCallNoTypeArgs] = new FaultAttributes(34); +faultsAttrs[FaultID.ObjectLiteralNoContextType] = new FaultAttributes(38); +faultsAttrs[FaultID.ObjectTypeLiteral] = new FaultAttributes(40); +faultsAttrs[FaultID.ArrayLiteralNoContextType] = new FaultAttributes(43); +faultsAttrs[FaultID.FunctionExpression] = new FaultAttributes(46, true); +faultsAttrs[FaultID.LambdaWithTypeParameters] = new FaultAttributes(49, true); +faultsAttrs[FaultID.ClassExpression] = new FaultAttributes(50, true); +faultsAttrs[FaultID.ImplementsClass] = new FaultAttributes(51); +faultsAttrs[FaultID.MethodReassignment] = new FaultAttributes(52); +faultsAttrs[FaultID.TypeAssertion] = new FaultAttributes(53, true); +faultsAttrs[FaultID.JsxElement] = new FaultAttributes(54); +faultsAttrs[FaultID.UnaryArithmNotNumber] = new FaultAttributes(55); +faultsAttrs[FaultID.DeleteOperator] = new FaultAttributes(59); +faultsAttrs[FaultID.TypeQuery] = new FaultAttributes(60); +faultsAttrs[FaultID.InstanceofUnsupported] = new FaultAttributes(65); +faultsAttrs[FaultID.InOperator] = new FaultAttributes(66); +faultsAttrs[FaultID.DestructuringAssignment] = new FaultAttributes(69, true); +faultsAttrs[FaultID.CommaOperator] = new FaultAttributes(71); +faultsAttrs[FaultID.DestructuringDeclaration] = new FaultAttributes(74, true); +faultsAttrs[FaultID.CatchWithUnsupportedType] = new FaultAttributes(79, true); +faultsAttrs[FaultID.ForInStatement] = new FaultAttributes(80); +faultsAttrs[FaultID.MappedType] = new FaultAttributes(83); +faultsAttrs[FaultID.WithStatement] = new FaultAttributes(84); +faultsAttrs[FaultID.ThrowStatement] = new FaultAttributes(87, true); +faultsAttrs[FaultID.LimitedReturnTypeInference] = new FaultAttributes(90, true); +faultsAttrs[FaultID.DestructuringParameter] = new FaultAttributes(91); +faultsAttrs[FaultID.LocalFunction] = new FaultAttributes(92, true); +faultsAttrs[FaultID.FunctionContainsThis] = new FaultAttributes(93); +faultsAttrs[FaultID.GeneratorFunction] = new FaultAttributes(94); +faultsAttrs[FaultID.YieldExpression] = new FaultAttributes(94); +faultsAttrs[FaultID.IsOperator] = new FaultAttributes(96); +faultsAttrs[FaultID.SpreadOperator] = new FaultAttributes(99); +faultsAttrs[FaultID.IntefaceExtendDifProps] = new FaultAttributes(102); +faultsAttrs[FaultID.InterfaceMerging] = new FaultAttributes(103); +faultsAttrs[FaultID.InterfaceExtendsClass] = new FaultAttributes(104); +faultsAttrs[FaultID.ConstructorFuncs] = new FaultAttributes(106); +faultsAttrs[FaultID.EnumMemberNonConstInit] = new FaultAttributes(111); +faultsAttrs[FaultID.EnumMerging] = new FaultAttributes(113); +faultsAttrs[FaultID.NamespaceAsObject] = new FaultAttributes(114); +faultsAttrs[FaultID.NonDeclarationInNamespace] = new FaultAttributes(116); +faultsAttrs[FaultID.ImportFromPath] = new FaultAttributes(119); +faultsAttrs[FaultID.DefaultImport] = new FaultAttributes(120, true); +faultsAttrs[FaultID.ImportAssignment] = new FaultAttributes(121); +faultsAttrs[FaultID.ExportAssignment] = new FaultAttributes(126); +faultsAttrs[FaultID.ShorthandAmbientModuleDecl] = new FaultAttributes(128); +faultsAttrs[FaultID.WildcardsInModuleName] = new FaultAttributes(129); +faultsAttrs[FaultID.UMDModuleDefinition] = new FaultAttributes(130); +faultsAttrs[FaultID.NewTarget] = new FaultAttributes(132); +faultsAttrs[FaultID.DefiniteAssignment] = new FaultAttributes(134, false, ProblemSeverity.WARNING); +faultsAttrs[FaultID.Prototype] = new FaultAttributes(136); +faultsAttrs[FaultID.GlobalThis] = new FaultAttributes(137, false, ProblemSeverity.WARNING); +faultsAttrs[FaultID.UtilityType] = new FaultAttributes(138); +faultsAttrs[FaultID.PropertyDeclOnFunction] = new FaultAttributes(139); +faultsAttrs[FaultID.FunctionApplyCall] = new FaultAttributes(140); +faultsAttrs[FaultID.FunctionBind] = new FaultAttributes(140, false, ProblemSeverity.WARNING); +faultsAttrs[FaultID.ConstAssertion] = new FaultAttributes(142); +faultsAttrs[FaultID.ImportAssertion] = new FaultAttributes(143); +faultsAttrs[FaultID.LimitedStdLibApi] = new FaultAttributes(144); +faultsAttrs[FaultID.StrictDiagnostic] = new FaultAttributes(145); +faultsAttrs[FaultID.ErrorSuppression] = new FaultAttributes(146); +faultsAttrs[FaultID.UnsupportedDecorators] = new FaultAttributes(148, false, ProblemSeverity.WARNING); +faultsAttrs[FaultID.ClassAsObject] = new FaultAttributes(149, false, ProblemSeverity.WARNING); +faultsAttrs[FaultID.ImportAfterStatement] = new FaultAttributes(150); +faultsAttrs[FaultID.EsObjectType] = new FaultAttributes(151, false, ProblemSeverity.WARNING); diff --git a/ets2panda/linter-4.2/src/TestRunner.ts b/ets2panda/linter-4.2/src/TestRunner.ts index 48bba0088914aaaca063c7e9170706076e08c27a..efc5e7d99de55a194206db04b143b589c9f0d930 100644 --- a/ets2panda/linter-4.2/src/TestRunner.ts +++ b/ets2panda/linter-4.2/src/TestRunner.ts @@ -16,8 +16,9 @@ import { TypeScriptLinter } from './TypeScriptLinter'; import { lint } from './LinterRunner'; import { parseCommandLine } from './CommandLineParser'; -import { Autofix } from './Autofixer'; -import Logger from '../utils/logger'; +import type { CommandLineOptions } from './CommandLineOptions'; +import type { Autofix } from './Autofixer'; +import logger from '../utils/logger'; import * as fs from 'node:fs'; import * as path from 'node:path'; import * as ts from 'typescript'; @@ -25,7 +26,7 @@ import * as ts from 'typescript'; const TEST_DIR = 'test'; const TAB = ' '; -const logger = Logger.getLogger(); +const loggerInstance = logger.getLogger(); interface TestNodeInfo { line: number; @@ -49,69 +50,57 @@ RESULT_EXT[Mode.RELAX] = '.relax.json'; RESULT_EXT[Mode.AUTOFIX] = '.autofix.json'; const AUTOFIX_CONFIG_EXT = '.autofix.cfg.json'; const AUTOFIX_SKIP_EXT = '.autofix.skip'; -const ARGS_CONFIG_EXT = '.args.json' +const ARGS_CONFIG_EXT = '.args.json'; const DIFF_EXT = '.diff'; function runTests(testDirs: string[]): number { let hasComparisonFailures = false; - - // Set the IDE mode manually to enable storing information - // about found bad nodes and also disable the log output. TypeScriptLinter.ideMode = true; TypeScriptLinter.testMode = true; - - let passed = 0, failed = 0; - - // Get tests from test directory - if (!testDirs?.length) testDirs = [ TEST_DIR ]; + let passed = 0; + let failed = 0; + if (!testDirs?.length) { + testDirs = [TEST_DIR]; + } for (const testDir of testDirs) { - let testFiles: string[] = fs.readdirSync(testDir) - .filter((x) => (x.trimEnd().endsWith(ts.Extension.Ts) && !x.trimEnd().endsWith(ts.Extension.Dts)) || x.trimEnd().endsWith(ts.Extension.Tsx)); - - logger.info(`\nProcessing "${testDir}" directory:\n`); - + const testFiles: string[] = fs.readdirSync(testDir).filter((x) => { + return ( + x.trimEnd().endsWith(ts.Extension.Ts) && !x.trimEnd().endsWith(ts.Extension.Dts) || + x.trimEnd().endsWith(ts.Extension.Tsx) + ); + }); + loggerInstance.info(`\nProcessing "${testDir}" directory:\n`); // Run each test in Strict, Autofix, and Relax mode: for (const testFile of testFiles) { if (runTest(testDir, testFile, Mode.STRICT)) { failed++; hasComparisonFailures = true; + } else { + passed++; } - else passed++; - if (runTest(testDir, testFile, Mode.AUTOFIX)) { failed++; hasComparisonFailures = true; + } else { + passed++; } - else passed++; - if (runTest(testDir, testFile, Mode.RELAX)) { failed++; hasComparisonFailures = true; + } else { + passed++; } - else passed++; } } - - logger.info(`\nSUMMARY: ${passed + failed} total, ${passed} passed or skipped, ${failed} failed.`); - logger.info((failed > 0) ? '\nTEST FAILED' : '\nTEST SUCCESSFUL'); - + loggerInstance.info(`\nSUMMARY: ${passed + failed} total, ${passed} passed or skipped, ${failed} failed.`); + loggerInstance.info(failed > 0 ? '\nTEST FAILED' : '\nTEST SUCCESSFUL'); process.exit(hasComparisonFailures ? -1 : 0); } -function runTest(testDir: string, testFile: string, mode: Mode): boolean { - let testFailed = false; - if (mode === Mode.AUTOFIX && fs.existsSync(path.join(testDir, testFile + AUTOFIX_SKIP_EXT))) { - logger.info(`Skipping test ${testFile} (${Mode[mode]} mode)`); - return false; - } - logger.info(`Running test ${testFile} (${Mode[mode]} mode)`); - - TypeScriptLinter.initGlobals(); - +function parseArgs(testDir: string, testFile: string, mode: Mode): CommandLineOptions { // Configure test parameters and run linter. const args: string[] = [path.join(testDir, testFile)]; - let argsFileName = path.join(testDir, testFile + ARGS_CONFIG_EXT); - let currentTestMode = TypeScriptLinter.testMode; + const argsFileName = path.join(testDir, testFile + ARGS_CONFIG_EXT); if (fs.existsSync(argsFileName)) { const data = fs.readFileSync(argsFileName).toString(); @@ -121,72 +110,84 @@ function runTest(testDir: string, testFile: string, mode: Mode): boolean { } } - if (mode === Mode.RELAX) args.push('--relax'); - else if (mode === Mode.AUTOFIX) { + if (mode === Mode.RELAX) { + args.push('--relax'); + } else if (mode === Mode.AUTOFIX) { args.push('--autofix'); - let autofixCfg = path.join(testDir, testFile + AUTOFIX_CONFIG_EXT); - if (fs.existsSync(autofixCfg)) args.push(autofixCfg); - } - const cmdOptions = parseCommandLine(args); - const result = lint({ cmdOptions: cmdOptions }); - const fileProblems = result.problemsInfos.get( path.normalize(cmdOptions.inputFiles[0]) ); - if (fileProblems === undefined) { - return true; + const autofixCfg = path.join(testDir, testFile + AUTOFIX_CONFIG_EXT); + if (fs.existsSync(autofixCfg)) { + args.push(autofixCfg); + } } - TypeScriptLinter.testMode = currentTestMode; - - const resultExt = RESULT_EXT[mode]; - const testResultFileName = testFile + resultExt; - - // Get list of bad nodes from the current run. - const resultNodes: TestNodeInfo[] = - fileProblems.map( - (x) => ({ - line: x.line, column: x.column, problem: x.problem, - autofixable: mode === Mode.AUTOFIX ? x.autofixable : undefined, - autofix: mode === Mode.AUTOFIX ? x.autofix : undefined, - suggest: x.suggest, - rule: x.rule - }) - ); + return parseCommandLine(args); +} +function compareExpectedAndActual(testDir: string, testFile: string, mode: Mode, resultNodes: TestNodeInfo[]): string { // Read file with expected test result. let expectedResult: { nodes: TestNodeInfo[] }; let diff: string = ''; + const resultExt = RESULT_EXT[mode]; + const testResultFileName = testFile + resultExt; try { const expectedResultFile = fs.readFileSync(path.join(testDir, testResultFileName)).toString(); expectedResult = JSON.parse(expectedResultFile); - - if (!expectedResult || !expectedResult.nodes || expectedResult.nodes.length !== resultNodes.length) { - testFailed = true; - let expectedResultCount = expectedResult && expectedResult.nodes ? expectedResult.nodes.length : 0; + if (!expectedResult?.nodes || expectedResult.nodes.length !== resultNodes.length) { + const expectedResultCount = expectedResult?.nodes ? expectedResult.nodes.length : 0; diff = `Expected count: ${expectedResultCount} vs actual count: ${resultNodes.length}`; - logger.info(`${TAB}${diff}`); + loggerInstance.info(`${TAB}${diff}`); } else { diff = expectedAndActualMatch(expectedResult.nodes, resultNodes); - testFailed = !!diff; } - - if (testFailed) { - logger.info(`${TAB}Test failed. Expected and actual results differ.`); + if (diff) { + loggerInstance.info(`${TAB}Test failed. Expected and actual results differ.`); } - } catch (error: any) { - testFailed = true; - logger.info(`${TAB}Test failed. ${error.message ?? error}`); + } catch (error) { + loggerInstance.info(`${TAB}Test failed. ` + error); } + return diff; +} +function runTest(testDir: string, testFile: string, mode: Mode): boolean { + const testFailed = false; + if (mode === Mode.AUTOFIX && fs.existsSync(path.join(testDir, testFile + AUTOFIX_SKIP_EXT))) { + loggerInstance.info(`Skipping test ${testFile} (${Mode[mode]} mode)`); + return false; + } + loggerInstance.info(`Running test ${testFile} (${Mode[mode]} mode)`); + TypeScriptLinter.initGlobals(); + const currentTestMode = TypeScriptLinter.testMode; + const cmdOptions = parseArgs(testDir, testFile, mode); + const result = lint({ cmdOptions: cmdOptions }); + const fileProblems = result.problemsInfos.get(path.normalize(cmdOptions.inputFiles[0])); + if (fileProblems === undefined) { + return true; + } + TypeScriptLinter.testMode = currentTestMode; + // Get list of bad nodes from the current run. + const resultNodes: TestNodeInfo[] = fileProblems.map((x) => { + return { + line: x.line, + column: x.column, + problem: x.problem, + autofixable: mode === Mode.AUTOFIX ? x.autofixable : undefined, + autofix: mode === Mode.AUTOFIX ? x.autofix : undefined, + suggest: x.suggest, + rule: x.rule + }; + }); + // Read file with expected test result. + const testResult = compareExpectedAndActual(testDir, testFile, mode, resultNodes); // Write file with actual test results. - writeActualResultFile(testDir, testFile, resultExt, resultNodes, diff); - + writeActualResultFile(testDir, testFile, RESULT_EXT[mode], resultNodes, testResult); return testFailed; } function expectedAndActualMatch(expectedNodes: TestNodeInfo[], actualNodes: TestNodeInfo[]): string { // Compare expected and actual results. for (let i = 0; i < actualNodes.length; i++) { - let actual = actualNodes[i]; - let expect = expectedNodes[i]; + const actual = actualNodes[i]; + const expect = expectedNodes[i]; if (actual.line !== expect.line || actual.column !== expect.column || actual.problem !== expect.problem) { return reportDiff(expect, actual); } @@ -205,22 +206,38 @@ function expectedAndActualMatch(expectedNodes: TestNodeInfo[], actualNodes: Test } function autofixArraysMatch(expected: Autofix[] | undefined, actual: Autofix[] | undefined): boolean { - if (!expected && !actual) return true; - if (!(expected && actual) || expected.length !== actual.length) return false; + if (!expected && !actual) { + return true; + } + if (!(expected && actual) || expected.length !== actual.length) { + return false; + } for (let i = 0; i < actual.length; ++i) { if ( - actual[i].start !== expected[i].start || actual[i].end !== expected[i].end || + actual[i].start !== expected[i].start || + actual[i].end !== expected[i].end || actual[i].replacementText.replace(/\r\n/g, '\n') !== expected[i].replacementText.replace(/\r\n/g, '\n') - ) return false; + ) { + return false; + } } return true; } -function writeActualResultFile(testDir: string, testFile: string, resultExt: string, resultNodes: TestNodeInfo[], diff: string) { +function writeActualResultFile( + testDir: string, + testFile: string, + resultExt: string, + resultNodes: TestNodeInfo[], + diff: string +): void { const actualResultsDir = path.join(testDir, 'results'); - if (!fs.existsSync(actualResultsDir)) fs.mkdirSync(actualResultsDir); + if (!fs.existsSync(actualResultsDir)) { + fs.mkdirSync(actualResultsDir); + } - const actualResultJSON = JSON.stringify({ nodes: resultNodes }, null, 4); + const tabWidth = 4; + const actualResultJSON = JSON.stringify({ nodes: resultNodes }, null, tabWidth); fs.writeFileSync(path.join(actualResultsDir, testFile + resultExt), actualResultJSON); if (diff) { @@ -229,17 +246,17 @@ function writeActualResultFile(testDir: string, testFile: string, resultExt: str } function reportDiff(expected: TestNodeInfo, actual: TestNodeInfo): string { - let expectedNode = JSON.stringify({ nodes: [expected] }, null, 4); - let actualNode = JSON.stringify({ nodes: [actual] }, null, 4); + const tabWidth = 4; + const expectedNode = JSON.stringify({ nodes: [expected] }, null, tabWidth); + const actualNode = JSON.stringify({ nodes: [actual] }, null, tabWidth); - let diff = -`Expected: + const diff = `Expected: ${expectedNode} Actual: ${actualNode}`; - logger.info(diff); + loggerInstance.info(diff); return diff; } -runTests(process.argv.slice(2)); \ No newline at end of file +runTests(process.argv.slice(2)); diff --git a/ets2panda/linter-4.2/src/TypeScriptLinter.ts b/ets2panda/linter-4.2/src/TypeScriptLinter.ts index 7e11b132f41355e168af9ffa09d4a8438d1d1fae..e699b331f271efd2dc07b22cc82a61693a68116c 100644 --- a/ets2panda/linter-4.2/src/TypeScriptLinter.ts +++ b/ets2panda/linter-4.2/src/TypeScriptLinter.ts @@ -15,33 +15,37 @@ import * as ts from 'typescript'; import * as path from 'node:path'; -import { TsUtils, getNodeOrLineEnd, CheckType, isAssignmentOperator } from './Utils'; +import { TsUtils, getNodeOrLineEnd, isAssignmentOperator } from './Utils'; import { FaultID, faultsAttrs } from './Problems'; import { cookBookMsg, cookBookTag } from './CookBookMsg'; import { LinterConfig } from './TypeScriptLinterConfig'; import type { Autofix, AutofixInfoSet } from './Autofixer'; -import * as Autofixer from './Autofixer'; +import * as autofixer from './Autofixer'; import type { ProblemInfo } from './ProblemInfo'; import { ProblemSeverity } from './ProblemSeverity'; -import Logger from '../utils/logger'; +import logger from '../utils/logger'; import type { DiagnosticChecker } from './DiagnosticChecker'; import { ARGUMENT_OF_TYPE_0_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_ERROR_CODE, TYPE_0_IS_NOT_ASSIGNABLE_TO_TYPE_1_ERROR_CODE, - LibraryTypeCallDiagnosticChecker + LibraryTypeCallDiagnosticChecker, } from './LibraryTypeCallDiagnosticChecker'; +import { LIMITED_STD_API } from './LimitedStdAPI'; +import { identiferUseInValueContext } from './identiferUseInValueContext'; -const logger = Logger.getLogger(); +const loggerInstance = logger.getLogger(); export function consoleLog(...args: any[]): void { - if (TypeScriptLinter.ideMode) return; + if (TypeScriptLinter.ideMode) { + return; + } - let outLine = ""; + let outLine = ''; for (let k = 0; k < args.length; k++) { outLine += `${args[k]} `; } - logger.info(outLine); + loggerInstance.info(outLine); } export class TypeScriptLinter { @@ -50,9 +54,9 @@ export class TypeScriptLinter { lineCounters: number[] = []; totalErrorLines: number = 0; - errorLineNumbersString: string = ""; + errorLineNumbersString: string = ''; totalWarningLines: number = 0; - warningLineNumbersString: string = ""; + warningLineNumbersString: string = ''; problemsInfos: ProblemInfo[] = []; @@ -61,6 +65,7 @@ export class TypeScriptLinter { currentErrorLine: number; currentWarningLine: number; staticBlocks: Set; + walkedComments: Set; libraryTypeCallDiagnosticChecker: LibraryTypeCallDiagnosticChecker; private sourceFile?: ts.SourceFile; @@ -69,26 +74,52 @@ export class TypeScriptLinter { static testMode: boolean = false; static initGlobals(): void { - TypeScriptLinter.filteredDiagnosticMessages = new Set(); + TypeScriptLinter.filteredDiagnosticMessages = + new Set(); + } + + private initEtsHandlers(): void { + /* + * some syntax elements are ArkTs-specific and are only implemented inside patched + * compiler, so we initialize those handlers if corresponding properties do exist + */ + const etsComponentExpression: ts.SyntaxKind | undefined = ( + ts.SyntaxKind as any + ).EtsComponentExpression; + if (etsComponentExpression) { + this.handlersMap.set( + etsComponentExpression, + this.handleEtsComponentExpression + ); + } + } + + private initCounters(): void { + for (let i = 0; i < FaultID.LAST_ID; i++) { + this.nodeCounters[i] = 0; + this.lineCounters[i] = 0; + } } constructor( - private tsTypeChecker: ts.TypeChecker, - private autofixesInfo: AutofixInfoSet, + private readonly tsTypeChecker: ts.TypeChecker, + private readonly autofixesInfo: AutofixInfoSet, public strictMode: boolean, public warningsAsErrors: boolean, - private tscStrictDiagnostics?: Map + private readonly tscStrictDiagnostics?: Map ) { this.tsUtils = new TsUtils(this.tsTypeChecker, TypeScriptLinter.testMode); this.currentErrorLine = 0; this.currentWarningLine = 0; this.staticBlocks = new Set(); - this.libraryTypeCallDiagnosticChecker = new LibraryTypeCallDiagnosticChecker(TypeScriptLinter.filteredDiagnosticMessages); + this.walkedComments = new Set(); + this.libraryTypeCallDiagnosticChecker = + new LibraryTypeCallDiagnosticChecker( + TypeScriptLinter.filteredDiagnosticMessages + ); - for (let i = 0; i < FaultID.LAST_ID; i++) { - this.nodeCounters[i] = 0; - this.lineCounters[i] = 0; - } + this.initEtsHandlers(); + this.initCounters(); } readonly handlersMap = new Map([ @@ -136,7 +167,6 @@ export class TypeScriptLinter { [ts.SyntaxKind.ElementAccessExpression, this.handleElementAccessExpression], [ts.SyntaxKind.EnumMember, this.handleEnumMember], [ts.SyntaxKind.TypeReference, this.handleTypeReference], - [ts.SyntaxKind.ExportDeclaration, this.handleExportDeclaration], [ts.SyntaxKind.ExportAssignment, this.handleExportAssignment], [ts.SyntaxKind.CallExpression, this.handleCallExpression], [ts.SyntaxKind.MetaProperty, this.handleMetaProperty], @@ -147,115 +177,155 @@ export class TypeScriptLinter { [ts.SyntaxKind.GetAccessor, this.handleGetAccessor], [ts.SyntaxKind.SetAccessor, this.handleSetAccessor], [ts.SyntaxKind.ConstructSignature, this.handleConstructSignature], - [ts.SyntaxKind.ExpressionWithTypeArguments, this.handleExpressionWithTypeArguments], + [ + ts.SyntaxKind.ExpressionWithTypeArguments, + this.handleExpressionWithTypeArguments, + ], + [ts.SyntaxKind.ComputedPropertyName, this.handleComputedPropertyName], ]); - public incrementCounters( + private getLineAndCharacterOfNode( + node: ts.Node | ts.CommentRange + ): ts.LineAndCharacter { + const startPos = TsUtils.getStartPos(node); + const { line, character } = + this.sourceFile!.getLineAndCharacterOfPosition(startPos); + // TSC counts lines and columns from zero + return { line: line + 1, character: character + 1 }; + } + + private incrementCountersIdeMode( + node: ts.Node | ts.CommentRange, + faultId: number, + line: number, + character: number, + autofixable: boolean, + autofix?: Autofix[] + ): void { + const startPos = TsUtils.getStartPos(node); + const endPos = TsUtils.getEndPos(node); + + const faultDescr = LinterConfig.nodeDesc[faultId]; + const faultType = LinterConfig.tsSyntaxKindNames[node.kind]; + + const cookBookMsgNum = faultsAttrs[faultId] ? faultsAttrs[faultId].cookBookRef : 0; + const cookBookTg = cookBookTag[cookBookMsgNum]; + const severity = faultsAttrs[faultId]?.severity ?? ProblemSeverity.ERROR; + const badNodeInfo: ProblemInfo = { + line: line, + column: character, + endColumn: getNodeOrLineEnd(this.sourceFile!, startPos, endPos, line), + start: startPos, + end: endPos, + type: faultType, + severity: severity, + problem: FaultID[faultId], + suggest: cookBookMsgNum > 0 ? cookBookMsg[cookBookMsgNum] : '', + rule: cookBookMsgNum > 0 && cookBookTg !== '' ? cookBookTg : faultDescr ? faultDescr : faultType, + ruleTag: cookBookMsgNum, + autofixable: autofixable, + autofix: autofix, + }; + + this.problemsInfos.push(badNodeInfo); + } + + incrementCounters( node: ts.Node | ts.CommentRange, faultId: number, autofixable: boolean = false, autofix?: Autofix[] - ) { - if (!this.strictMode && faultsAttrs[faultId].migratable) + ): void { + if (!this.strictMode && faultsAttrs[faultId].migratable) { // In relax mode skip migratable return; - - const startPos = this.tsUtils.getStartPos(node); - const endPos = this.tsUtils.getEndPos(node); - + } this.nodeCounters[faultId]++; - // TSC counts lines and columns from zero - let { line, character } = - this.sourceFile!.getLineAndCharacterOfPosition(startPos); - ++line; - ++character; - - let faultDescr = LinterConfig.nodeDesc[faultId]; - let faultType = LinterConfig.tsSyntaxKindNames[node.kind]; - + const { line, character } = this.getLineAndCharacterOfNode(node); if (TypeScriptLinter.ideMode) { - const cookBookMsgNum = faultsAttrs[faultId] - ? Number(faultsAttrs[faultId].cookBookRef) - : 0; - const cookBookTg = cookBookTag[cookBookMsgNum]; - let severity = ProblemSeverity.ERROR; - if (faultsAttrs[faultId] && faultsAttrs[faultId].warning) - severity = ProblemSeverity.WARNING; - - const badNodeInfo: ProblemInfo = { - line: line, - column: character, - endColumn: getNodeOrLineEnd(this.sourceFile!, startPos, endPos, line), - start: startPos, - end: endPos, - type: faultType, - severity: severity, - problem: FaultID[faultId], - suggest: cookBookMsgNum > 0 ? cookBookMsg[cookBookMsgNum] : "", - rule: - cookBookMsgNum > 0 && cookBookTg !== "" - ? cookBookTg - : faultDescr - ? faultDescr - : faultType, - ruleTag: cookBookMsgNum, - autofixable: autofixable, - autofix: autofix, - }; - - this.problemsInfos.push(badNodeInfo); + this.incrementCountersIdeMode( + node, + faultId, + line, + character, + autofixable, + autofix + ); } else { - logger.info( + const faultDescr = LinterConfig.nodeDesc[faultId]; + const faultType = LinterConfig.tsSyntaxKindNames[node.kind]; + loggerInstance.info( `Warning: ${this.sourceFile!.fileName} (${line}, ${character}): ${ faultDescr ? faultDescr : faultType }` ); } - this.lineCounters[faultId]++; - - if (faultsAttrs[faultId].warning) { - if (line !== this.currentWarningLine) { + switch (faultsAttrs[faultId].severity) { + case ProblemSeverity.ERROR: { + this.currentErrorLine = line; + ++this.totalErrorLines; + this.errorLineNumbersString += line + ', '; + break; + } + case ProblemSeverity.WARNING: { + if (line === this.currentWarningLine) { + break; + } this.currentWarningLine = line; ++this.totalWarningLines; - this.warningLineNumbersString += line + ", "; + this.warningLineNumbersString += line + ', '; + break; } - } else if (line !== this.currentErrorLine) { - this.currentErrorLine = line; - ++this.totalErrorLines; - this.errorLineNumbersString += line + ", "; } } - private visitTSNode(node: ts.Node): void { - const self = this; - visitTSNodeImpl(node); - function visitTSNodeImpl(node: ts.Node): void { - if (node === null || node.kind === null) return; - - self.totalVisitedNodes++; - - if (self.tsUtils.isStructDeclaration(node)) { - self.handleStructDeclaration(node); - return; - } - - self.handleComments(node); + private forEachNodeInSubtree( + node: ts.Node, + cb: (n: ts.Node) => void, + stopCond?: (n: ts.Node) => boolean + ): void { + cb.call(this, node); + if (stopCond?.call(this, node)) { + return; + } - if (LinterConfig.terminalTokens.has(node.kind)) return; + ts.forEachChild(node, (child) => { + this.forEachNodeInSubtree(child, cb, stopCond); + }); + } - let incrementedType = LinterConfig.incrementOnlyTokens.get(node.kind); + private visitSourceFile(sf: ts.SourceFile): void { + const callback = (node: ts.Node): void => { + this.totalVisitedNodes++; + const incrementedType = LinterConfig.incrementOnlyTokens.get(node.kind); if (incrementedType !== undefined) { - self.incrementCounters(node, incrementedType); + this.incrementCounters(node, incrementedType); } else { - let handler = self.handlersMap.get(node.kind); + const handler = this.handlersMap.get(node.kind); if (handler !== undefined) { - handler.call(self, node); + handler.call(this, node); } } - - ts.forEachChild(node, visitTSNodeImpl); - } + }; + const stopCondition = (node: ts.Node): boolean => { + if (!node) { + return true; + } + // Skip synthetic constructor in Struct declaration. + if ( + node.parent && + TsUtils.isStructDeclaration(node.parent) && + ts.isConstructorDeclaration(node) + ) { + return true; + } + if (LinterConfig.terminalTokens.has(node.kind)) { + return true; + } + return false; + }; + this.forEachNodeInSubtree(sf, callback, stopCondition); } private countInterfaceExtendsDifferentPropertyTypes( @@ -263,7 +333,7 @@ export class TypeScriptLinter { prop2type: Map, propName: string, type: ts.TypeNode | undefined - ) { + ): void { if (type) { const methodType = type.getText(); const propType = prop2type.get(propName); @@ -275,111 +345,139 @@ export class TypeScriptLinter { } } - private countDeclarationsWithDuplicateName(tsNode: ts.Node, tsDeclNode: ts.Node, tsDeclKind?: ts.SyntaxKind + private countDeclarationsWithDuplicateName( + tsNode: ts.Node, + tsDeclNode: ts.Node, + tsDeclKind?: ts.SyntaxKind ): void { - let symbol = this.tsTypeChecker.getSymbolAtLocation(tsNode); - // If specific declaration kind is provided, check against it. - // Otherwise, use syntax kind of corresponding declaration node. - if (!!symbol && this.tsUtils.symbolHasDuplicateName(symbol, tsDeclKind ?? tsDeclNode.kind)) { + const symbol = this.tsTypeChecker.getSymbolAtLocation(tsNode); + + /* + * If specific declaration kind is provided, check against it. + * Otherwise, use syntax kind of corresponding declaration node. + */ + if ( + !!symbol && + TsUtils.symbolHasDuplicateName(symbol, tsDeclKind ?? tsDeclNode.kind) + ) { this.incrementCounters(tsDeclNode, FaultID.DeclWithDuplicateName); } } + private static isPrivateIdentifierDuplicateOfIdentifier( + ident1: ts.Identifier | ts.PrivateIdentifier, + ident2: ts.Identifier | ts.PrivateIdentifier + ): boolean { + if (ts.isIdentifier(ident1) && ts.isPrivateIdentifier(ident2)) { + return ident1.text === ident2.text.substring(1); + } + if (ts.isIdentifier(ident2) && ts.isPrivateIdentifier(ident1)) { + return ident2.text === ident1.text.substring(1); + } + return false; + } + + private static isIdentifierOrPrivateIdentifier( + node?: ts.PropertyName + ): node is ts.Identifier | ts.PrivateIdentifier { + if (!node) { + return false; + } + return ts.isIdentifier(node) || ts.isPrivateIdentifier(node); + } + private countClassMembersWithDuplicateName( tsClassDecl: ts.ClassDeclaration ): void { - for (const tsCurrentMember of tsClassDecl.members) { + const nClassMembers = tsClassDecl.members.length; + const isNodeReported = new Array(nClassMembers); + for (let curIdx = 0; curIdx < nClassMembers; ++curIdx) { + const tsCurrentMember = tsClassDecl.members[curIdx]; if ( - !tsCurrentMember.name || - !( - ts.isIdentifier(tsCurrentMember.name) || - ts.isPrivateIdentifier(tsCurrentMember.name) - ) - ) + !TypeScriptLinter.isIdentifierOrPrivateIdentifier(tsCurrentMember.name) + ) { continue; - - for (const tsClassMember of tsClassDecl.members) { - if (tsCurrentMember === tsClassMember) continue; - - if ( - !tsClassMember.name || - !( - ts.isIdentifier(tsClassMember.name) || - ts.isPrivateIdentifier(tsClassMember.name) - ) - ) - continue; - + } + if (isNodeReported[curIdx]) { + continue; + } + for (let idx = curIdx + 1; idx < nClassMembers; ++idx) { + const tsClassMember = tsClassDecl.members[idx]; if ( - ts.isIdentifier(tsCurrentMember.name) && - ts.isPrivateIdentifier(tsClassMember.name) && - tsCurrentMember.name.text === tsClassMember.name.text.substring(1) + !TypeScriptLinter.isIdentifierOrPrivateIdentifier(tsClassMember.name) ) { - this.incrementCounters( - tsCurrentMember, - FaultID.DeclWithDuplicateName - ); - break; + continue; } - if ( - ts.isPrivateIdentifier(tsCurrentMember.name) && - ts.isIdentifier(tsClassMember.name) && - tsCurrentMember.name.text.substring(1) === tsClassMember.name.text + TypeScriptLinter.isPrivateIdentifierDuplicateOfIdentifier( + tsCurrentMember.name, + tsClassMember.name + ) ) { this.incrementCounters( tsCurrentMember, FaultID.DeclWithDuplicateName ); + this.incrementCounters(tsClassMember, FaultID.DeclWithDuplicateName); + isNodeReported[idx] = true; break; } } } } - private scopeContainsThis(tsNode: ts.Node): boolean { - let found = false; + private static scopeContainsThis(tsNode: ts.Expression | ts.Block): boolean { + function scopeContainsThisVisitor(tsNode: ts.Node): boolean { + if (tsNode.kind === ts.SyntaxKind.ThisKeyword) { + return true; + } - function visitNode(tsNode: ts.Node) { - // Stop visiting child nodes if finished searching. - if (found) return; + /* + * Visit children nodes. Skip any local declaration that defines + * its own scope as it needs to be checked separately. + */ + const isClassLike = + ts.isClassDeclaration(tsNode) || ts.isClassExpression(tsNode); + const isFunctionLike = + ts.isFunctionDeclaration(tsNode) || ts.isFunctionExpression(tsNode); + const isModuleDecl = ts.isModuleDeclaration(tsNode); + if (isClassLike || isFunctionLike || isModuleDecl) { + return false; + } - if (tsNode.kind === ts.SyntaxKind.ThisKeyword) { - found = true; - return; + for (const child of tsNode.getChildren()) { + if (scopeContainsThisVisitor(child)) { + return true; + } } - // Visit children nodes. Skip any local declaration that defines - // its own scope as it needs to be checked separately. - if ( - !ts.isClassDeclaration(tsNode) && - !ts.isClassExpression(tsNode) && - !ts.isModuleDeclaration(tsNode) && - !ts.isFunctionDeclaration(tsNode) && - !ts.isFunctionExpression(tsNode) - ) - tsNode.forEachChild(visitNode); + return false; } - visitNode(tsNode); - - return found; + return scopeContainsThisVisitor(tsNode); } - private isPrototypePropertyAccess(tsPropertyAccess: ts.PropertyAccessExpression, propAccessSym: ts.Symbol | undefined, - baseExprSym: ts.Symbol | undefined, baseExprType: ts.Type): boolean { + private isPrototypePropertyAccess( + tsPropertyAccess: ts.PropertyAccessExpression, + propAccessSym: ts.Symbol | undefined, + baseExprSym: ts.Symbol | undefined, + baseExprType: ts.Type + ): boolean { if ( !( ts.isIdentifier(tsPropertyAccess.name) && - tsPropertyAccess.name.text === "prototype" + tsPropertyAccess.name.text === 'prototype' ) - ) + ) { return false; + } // #13600: Relax prototype check when expression comes from interop. let curPropAccess: ts.Node = tsPropertyAccess; while (curPropAccess && ts.isPropertyAccessExpression(curPropAccess)) { - const baseExprSym = this.tsUtils.trueSymbolAtLocation(curPropAccess.expression); + const baseExprSym = this.tsUtils.trueSymbolAtLocation( + curPropAccess.expression + ); if (this.tsUtils.isLibrarySymbol(baseExprSym)) { return false; } @@ -388,25 +486,30 @@ export class TypeScriptLinter { if (ts.isIdentifier(curPropAccess) && curPropAccess.text !== 'prototype') { const type = this.tsTypeChecker.getTypeAtLocation(curPropAccess); - if (this.tsUtils.isAnyType(type)) { + if (TsUtils.isAnyType(type)) { return false; } } // Check if property symbol is 'Prototype' - if (this.tsUtils.isPrototypeSymbol(propAccessSym)) return true; + if (TsUtils.isPrototypeSymbol(propAccessSym)) { + return true; + } // Check if symbol of LHS-expression is Class or Function. if ( - this.tsUtils.isTypeSymbol(baseExprSym) || - this.tsUtils.isFunctionSymbol(baseExprSym) - ) + TsUtils.isTypeSymbol(baseExprSym) || + TsUtils.isFunctionSymbol(baseExprSym) + ) { return true; + } - // Check if type of LHS expression Function type or Any type. - // The latter check is to cover cases with multiple prototype - // chain (as the 'Prototype' property should be 'Any' type): - // X.prototype.prototype.prototype = ... + /* + * Check if type of LHS expression Function type or Any type. + * The latter check is to cover cases with multiple prototype + * chain (as the 'Prototype' property should be 'Any' type): + * X.prototype.prototype.prototype = ... + */ const baseExprTypeNode = this.tsTypeChecker.typeToTypeNode( baseExprType, undefined, @@ -415,7 +518,7 @@ export class TypeScriptLinter { return ( (baseExprTypeNode && ts.isFunctionTypeNode(baseExprTypeNode)) || - this.tsUtils.isAnyType(baseExprType) + TsUtils.isAnyType(baseExprType) ); } @@ -424,21 +527,24 @@ export class TypeScriptLinter { heritageClauses: ts.NodeArray ): void { for (const hClause of heritageClauses) { - if (hClause.token !== ts.SyntaxKind.ExtendsKeyword) continue; + if (hClause.token !== ts.SyntaxKind.ExtendsKeyword) { + continue; + } const prop2type = new Map(); for (const tsTypeExpr of hClause.types) { const tsExprType = this.tsTypeChecker.getTypeAtLocation( tsTypeExpr.expression ); - if (tsExprType.isClass()) + if (tsExprType.isClass()) { this.incrementCounters(node, FaultID.InterfaceExtendsClass); - else if (tsExprType.isClassOrInterface()) + } else if (tsExprType.isClassOrInterface()) { this.lintForInterfaceExtendsDifferentPorpertyTypes( node, tsExprType, prop2type ); + } } } } @@ -450,7 +556,9 @@ export class TypeScriptLinter { ): void { const props = tsExprType.getProperties(); for (const p of props) { - if (!p.declarations) continue; + if (!p.declarations) { + continue; + } const decl: ts.Declaration = p.declarations[0]; if (decl.kind === ts.SyntaxKind.MethodSignature) { @@ -485,41 +593,50 @@ export class TypeScriptLinter { } } - private handleObjectLiteralExpression(node: ts.Node) { - let objectLiteralExpr = node as ts.ObjectLiteralExpression; + private handleObjectLiteralExpression(node: ts.Node): void { + const objectLiteralExpr = node as ts.ObjectLiteralExpression; // If object literal is a part of destructuring assignment, then don't process it further. - if (this.tsUtils.isDestructuringAssignmentLHS(objectLiteralExpr)) return; + if (TsUtils.isDestructuringAssignmentLHS(objectLiteralExpr)) { + return; + } - let objectLiteralType = + const objectLiteralType = this.tsTypeChecker.getContextualType(objectLiteralExpr); if ( !this.tsUtils.isStructObjectInitializer(objectLiteralExpr) && !this.tsUtils.isDynamicLiteralInitializer(objectLiteralExpr) && - !this.tsUtils.isObjectLiteralAssignable(objectLiteralType, objectLiteralExpr) - ) + !this.tsUtils.isObjectLiteralAssignable( + objectLiteralType, + objectLiteralExpr + ) + ) { this.incrementCounters(node, FaultID.ObjectLiteralNoContextType); + } } - private handleArrayLiteralExpression(node: ts.Node) { - // If array literal is a part of destructuring assignment, then - // don't process it further. + private handleArrayLiteralExpression(node: ts.Node): void { + /* + * If array literal is a part of destructuring assignment, then + * don't process it further. + */ if ( - this.tsUtils.isDestructuringAssignmentLHS( - node as ts.ArrayLiteralExpression - ) - ) + TsUtils.isDestructuringAssignmentLHS(node as ts.ArrayLiteralExpression) + ) { return; + } - let arrayLitNode = node as ts.ArrayLiteralExpression; + const arrayLitNode = node as ts.ArrayLiteralExpression; let noContextTypeForArrayLiteral = false; - // check that array literal consists of inferrable types - // e.g. there is no element which is untyped object literals - let arrayLitElements = arrayLitNode.elements; - for (let element of arrayLitElements) { + /* + * check that array literal consists of inferrable types + * e.g. there is no element which is untyped object literals + */ + const arrayLitElements = arrayLitNode.elements; + for (const element of arrayLitElements) { if (ts.isObjectLiteralExpression(element)) { - let objectLiteralType = this.tsTypeChecker.getContextualType(element); + const objectLiteralType = this.tsTypeChecker.getContextualType(element); if ( !this.tsUtils.isDynamicLiteralInitializer(arrayLitNode) && !this.tsUtils.isObjectLiteralAssignable(objectLiteralType, element) @@ -530,71 +647,88 @@ export class TypeScriptLinter { } } - if (noContextTypeForArrayLiteral) + if (noContextTypeForArrayLiteral) { this.incrementCounters(node, FaultID.ArrayLiteralNoContextType); + } } - private handleParameter(node: ts.Node) { - let tsParam = node as ts.ParameterDeclaration; + private handleParameter(node: ts.Node): void { + const tsParam = node as ts.ParameterDeclaration; if ( ts.isArrayBindingPattern(tsParam.name) || ts.isObjectBindingPattern(tsParam.name) - ) + ) { this.incrementCounters(node, FaultID.DestructuringParameter); + } - let tsParamMods = tsParam.modifiers; + const tsParamMods = tsParam.modifiers; if ( tsParamMods && - (this.tsUtils.hasModifier(tsParamMods, ts.SyntaxKind.PublicKeyword) || - this.tsUtils.hasModifier(tsParamMods, ts.SyntaxKind.ProtectedKeyword) || - this.tsUtils.hasModifier(tsParamMods, ts.SyntaxKind.ReadonlyKeyword) || - this.tsUtils.hasModifier(tsParamMods, ts.SyntaxKind.PrivateKeyword)) - ) + (TsUtils.hasModifier(tsParamMods, ts.SyntaxKind.PublicKeyword) || + TsUtils.hasModifier(tsParamMods, ts.SyntaxKind.ProtectedKeyword) || + TsUtils.hasModifier(tsParamMods, ts.SyntaxKind.ReadonlyKeyword) || + TsUtils.hasModifier(tsParamMods, ts.SyntaxKind.PrivateKeyword)) + ) { this.incrementCounters(node, FaultID.ParameterProperties); + } this.handleDecorators(tsParam.decorators); this.handleDeclarationInferredType(tsParam); } - private handleEnumDeclaration(node: ts.Node) { - let enumNode = node as ts.EnumDeclaration; + private handleEnumDeclaration(node: ts.Node): void { + const enumNode = node as ts.EnumDeclaration; this.countDeclarationsWithDuplicateName(enumNode.name, enumNode); - let enumSymbol = this.tsUtils.trueSymbolAtLocation(enumNode.name); - if (!enumSymbol) return; + const enumSymbol = this.tsUtils.trueSymbolAtLocation(enumNode.name); + if (!enumSymbol) { + return; + } - let enumDecls = enumSymbol.getDeclarations(); - if (!enumDecls) return; + const enumDecls = enumSymbol.getDeclarations(); + if (!enumDecls) { + return; + } - // Since type checker merges all declarations with the same name - // into one symbol, we need to check that there's more than one - // enum declaration related to that specific symbol. - // See 'countDeclarationsWithDuplicateName' method for details. + /* + * Since type checker merges all declarations with the same name + * into one symbol, we need to check that there's more than one + * enum declaration related to that specific symbol. + * See 'countDeclarationsWithDuplicateName' method for details. + */ let enumDeclCount = 0; for (const decl of enumDecls) { - if (decl.kind === ts.SyntaxKind.EnumDeclaration) enumDeclCount++; + if (decl.kind === ts.SyntaxKind.EnumDeclaration) { + enumDeclCount++; + } } - if (enumDeclCount > 1) + if (enumDeclCount > 1) { this.incrementCounters(node, FaultID.EnumMerging); + } } - private handleInterfaceDeclaration(node: ts.Node) { - let interfaceNode = node as ts.InterfaceDeclaration; - let iSymbol = this.tsUtils.trueSymbolAtLocation(interfaceNode.name); - let iDecls = iSymbol ? iSymbol.getDeclarations() : null; + private handleInterfaceDeclaration(node: ts.Node): void { + const interfaceNode = node as ts.InterfaceDeclaration; + const iSymbol = this.tsUtils.trueSymbolAtLocation(interfaceNode.name); + const iDecls = iSymbol ? iSymbol.getDeclarations() : null; if (iDecls) { - // Since type checker merges all declarations with the same name - // into one symbol, we need to check that there's more than one - // interface declaration related to that specific symbol. - // See 'countDeclarationsWithDuplicateName' method for details. + /* + * Since type checker merges all declarations with the same name + * into one symbol, we need to check that there's more than one + * interface declaration related to that specific symbol. + * See 'countDeclarationsWithDuplicateName' method for details. + */ let iDeclCount = 0; for (const decl of iDecls) { - if (decl.kind === ts.SyntaxKind.InterfaceDeclaration) iDeclCount++; + if (decl.kind === ts.SyntaxKind.InterfaceDeclaration) { + iDeclCount++; + } } - if (iDeclCount > 1) + if (iDeclCount > 1) { this.incrementCounters(node, FaultID.InterfaceMerging); + } } if (interfaceNode.heritageClauses) { @@ -604,44 +738,46 @@ export class TypeScriptLinter { this.countDeclarationsWithDuplicateName(interfaceNode.name, interfaceNode); } - private handleThrowStatement(node: ts.Node) { - let throwStmt = node as ts.ThrowStatement; - let throwExprType = this.tsTypeChecker.getTypeAtLocation( + private handleThrowStatement(node: ts.Node): void { + const throwStmt = node as ts.ThrowStatement; + const throwExprType = this.tsTypeChecker.getTypeAtLocation( throwStmt.expression ); if ( !throwExprType.isClassOrInterface() || - !this.tsUtils.isDerivedFrom(throwExprType, CheckType.Error) + !this.tsUtils.isOrDerivedFrom(throwExprType, this.tsUtils.isStdErrorType) ) { this.incrementCounters(node, FaultID.ThrowStatement, false, undefined); } } - private handleForStatement(node: ts.Node) { - let tsForStmt = node as ts.ForStatement; - let tsForInit = tsForStmt.initializer; + private handleForStatement(node: ts.Node): void { + const tsForStmt = node as ts.ForStatement; + const tsForInit = tsForStmt.initializer; if ( tsForInit && (ts.isArrayLiteralExpression(tsForInit) || ts.isObjectLiteralExpression(tsForInit)) - ) + ) { this.incrementCounters(tsForInit, FaultID.DestructuringAssignment); + } } - private handleForInStatement(node: ts.Node) { - let tsForInStmt = node as ts.ForInStatement; - let tsForInInit = tsForInStmt.initializer; + private handleForInStatement(node: ts.Node): void { + const tsForInStmt = node as ts.ForInStatement; + const tsForInInit = tsForInStmt.initializer; if ( ts.isArrayLiteralExpression(tsForInInit) || ts.isObjectLiteralExpression(tsForInInit) - ) + ) { this.incrementCounters(tsForInInit, FaultID.DestructuringAssignment); + } this.incrementCounters(node, FaultID.ForInStatement); } - private handleForOfStatement(node: ts.Node) { - let tsForOfStmt = node as ts.ForOfStatement; - let tsForOfInit = tsForOfStmt.initializer; + private handleForOfStatement(node: ts.Node): void { + const tsForOfStmt = node as ts.ForOfStatement; + const tsForOfInit = tsForOfStmt.initializer; if ( ts.isArrayLiteralExpression(tsForOfInit) || ts.isObjectLiteralExpression(tsForOfInit) @@ -650,8 +786,8 @@ export class TypeScriptLinter { } } - private handleImportDeclaration(node: ts.Node) { - let importDeclNode = node as ts.ImportDeclaration; + private handleImportDeclaration(node: ts.Node): void { + const importDeclNode = node as ts.ImportDeclaration; for (const stmt of importDeclNode.parent.statements) { if (stmt === importDeclNode) { break; @@ -661,32 +797,47 @@ export class TypeScriptLinter { break; } } - let expr1 = importDeclNode.moduleSpecifier; + const expr1 = importDeclNode.moduleSpecifier; if (expr1.kind === ts.SyntaxKind.StringLiteral) { - if (!importDeclNode.importClause) + if (!importDeclNode.importClause) { this.incrementCounters(node, FaultID.ImportFromPath); + } } } - private handlePropertyAccessExpression(node: ts.Node) { - let propertyAccessNode = node as ts.PropertyAccessExpression; + private handlePropertyAccessExpression(node: ts.Node): void { + if (ts.isCallExpression(node.parent) && node === node.parent.expression) { + return; + } + + const propertyAccessNode = node as ts.PropertyAccessExpression; const exprSym = this.tsUtils.trueSymbolAtLocation(propertyAccessNode); - const baseExprSym = this.tsUtils.trueSymbolAtLocation(propertyAccessNode.expression); - const baseExprType = this.tsTypeChecker.getTypeAtLocation(propertyAccessNode.expression); + const baseExprSym = this.tsUtils.trueSymbolAtLocation( + propertyAccessNode.expression + ); + const baseExprType = this.tsTypeChecker.getTypeAtLocation( + propertyAccessNode.expression + ); - if (!!baseExprSym && this.tsUtils.symbolHasEsObjectType(baseExprSym)) { - this.incrementCounters(propertyAccessNode, FaultID.EsObjectType); - } - if (this.isPrototypePropertyAccess(propertyAccessNode, exprSym, baseExprSym, baseExprType)) { + if ( + this.isPrototypePropertyAccess( + propertyAccessNode, + exprSym, + baseExprSym, + baseExprType + ) + ) { this.incrementCounters(propertyAccessNode.name, FaultID.Prototype); } + if (!!baseExprSym && TsUtils.symbolHasEsObjectType(baseExprSym)) { + this.incrementCounters(propertyAccessNode, FaultID.EsObjectType); + } } - private handlePropertyAssignmentOrDeclaration(node: ts.Node) { - let propName = (node as ts.PropertyAssignment | ts.PropertyDeclaration) + private handlePropertyAssignmentOrDeclaration(node: ts.Node): void { + const propName = (node as ts.PropertyAssignment | ts.PropertyDeclaration) .name; - if ( propName && (propName.kind === ts.SyntaxKind.NumericLiteral || @@ -694,23 +845,27 @@ export class TypeScriptLinter { ) { // We can use literals as property names only when creating Record or any interop instances. let isRecordObjectInitializer = false; - let isDynamicLiteralInitializer = false; + let isLibraryType = false; + let isDynamic = false; if (ts.isPropertyAssignment(node)) { - let objectLiteralType = this.tsTypeChecker.getContextualType( - node.parent - ); - isRecordObjectInitializer = - !!objectLiteralType && - this.tsUtils.isStdRecordType(objectLiteralType); - isDynamicLiteralInitializer = this.tsUtils.isDynamicLiteralInitializer( + const objectLiteralType = this.tsTypeChecker.getContextualType( node.parent ); + if (objectLiteralType) { + isRecordObjectInitializer = this.tsUtils.checkTypeSet( + objectLiteralType, + this.tsUtils.isStdRecordType + ); + isLibraryType = this.tsUtils.isLibraryType(objectLiteralType); + } + isDynamic = + isLibraryType || + this.tsUtils.isDynamicLiteralInitializer(node.parent); } - - if (!isRecordObjectInitializer && !isDynamicLiteralInitializer) { + if (!isRecordObjectInitializer && !isDynamic) { let autofix: Autofix[] | undefined = - Autofixer.fixLiteralAsPropertyName(node); - let autofixable = autofix !== undefined; + autofixer.fixLiteralAsPropertyName(node); + const autofixable = autofix !== undefined; if ( !this.autofixesInfo.shouldAutofix(node, FaultID.LiteralAsPropertyName) ) { @@ -724,19 +879,24 @@ export class TypeScriptLinter { ); } } - if (ts.isPropertyDeclaration(node)) { const decorators = node.decorators; this.handleDecorators(decorators); - this.filterOutDecoratorsDiagnostics(decorators, TsUtils.NON_INITIALIZABLE_PROPERTY_DECORATORS, - {begin: propName.getStart(), end: propName.getStart()}, - TsUtils.PROPERTY_HAS_NO_INITIALIZER_ERROR_CODE); - + this.filterOutDecoratorsDiagnostics( + decorators, + TsUtils.NON_INITIALIZABLE_PROPERTY_DECORATORS, + { begin: propName.getStart(), end: propName.getStart() }, + TsUtils.PROPERTY_HAS_NO_INITIALIZER_ERROR_CODE + ); const classDecorators = node.parent.decorators; - const propType = (node as ts.PropertyDeclaration).type?.getText(); - this.filterOutDecoratorsDiagnostics(classDecorators, TsUtils.NON_INITIALIZABLE_PROPERTY_CLASS_DECORATORS, - {begin: propName.getStart(), end: propName.getStart()}, TsUtils.PROPERTY_HAS_NO_INITIALIZER_ERROR_CODE, propType); - + const propType = node.type?.getText(); + this.filterOutDecoratorsDiagnostics( + classDecorators, + TsUtils.NON_INITIALIZABLE_PROPERTY_CLASS_DECORATORS, + { begin: propName.getStart(), end: propName.getStart() }, + TsUtils.PROPERTY_HAS_NO_INITIALIZER_ERROR_CODE, + propType + ); this.handleDeclarationInferredType(node); this.handleDefiniteAssignmentAssertion(node); } @@ -745,62 +905,65 @@ export class TypeScriptLinter { private filterOutDecoratorsDiagnostics( decorators: readonly ts.Decorator[] | undefined, expectedDecorators: readonly string[], - range: {begin: number, end: number}, + range: { begin: number; end: number }, code: number, propType?: string - ) { + ): void { // Filter out non-initializable property decorators from strict diagnostics. if (this.tscStrictDiagnostics && this.sourceFile) { if ( decorators?.some((x) => { - let decoratorName = ""; - if (ts.isIdentifier(x.expression)) decoratorName = x.expression.text; - else if ( + let decoratorName = ''; + if (ts.isIdentifier(x.expression)) { + decoratorName = x.expression.text; + } else if ( ts.isCallExpression(x.expression) && ts.isIdentifier(x.expression.expression) - ) + ) { decoratorName = x.expression.expression.text; - + } // special case for property of type CustomDialogController of the @CustomDialog-decorated class - if (expectedDecorators.includes(TsUtils.NON_INITIALIZABLE_PROPERTY_CLASS_DECORATORS[0])) { - return expectedDecorators.includes(decoratorName) && propType === 'CustomDialogController' + if ( + expectedDecorators.includes( + TsUtils.NON_INITIALIZABLE_PROPERTY_CLASS_DECORATORS[0] + ) + ) { + return ( + expectedDecorators.includes(decoratorName) && + propType === 'CustomDialogController' + ); } - return expectedDecorators.includes( - decoratorName - ); + return expectedDecorators.includes(decoratorName); }) ) { - let file = path.normalize(this.sourceFile.fileName); - let tscDiagnostics = this.tscStrictDiagnostics.get(file); + const file = path.normalize(this.sourceFile.fileName); + const tscDiagnostics = this.tscStrictDiagnostics.get(file); if (tscDiagnostics) { - let filteredDiagnostics = tscDiagnostics.filter( - (val, idx, array) => { - if (val.code !== code) { - return true; - } - - if (val.start === undefined) { - return true; - } - - if (val.start < range.begin) { - return true; - } - - if (val.start > range.end) { - return true; - } - - return false; + const filteredDiagnostics = tscDiagnostics.filter((val) => { + if (val.code !== code) { + return true; } - ); + if (val.start === undefined) { + return true; + } + if (val.start < range.begin) { + return true; + } + if (val.start > range.end) { + return true; + } + return false; + }); this.tscStrictDiagnostics.set(file, filteredDiagnostics); } } } } - private checkInRange(rangesToFilter: { begin: number, end: number }[], pos: number): boolean { + private static checkInRange( + rangesToFilter: { begin: number; end: number }[], + pos: number + ): boolean { for (let i = 0; i < rangesToFilter.length; i++) { if (pos >= rangesToFilter[i].begin && pos < rangesToFilter[i].end) { return false; @@ -809,18 +972,20 @@ export class TypeScriptLinter { return true; } - private filterStrictDiagnostics(filters: { [code: number]: (pos: number) => boolean }, - diagnosticChecker: DiagnosticChecker): boolean { + private filterStrictDiagnostics( + filters: { [code: number]: (pos: number) => boolean }, + diagnosticChecker: DiagnosticChecker + ): boolean { if (!this.tscStrictDiagnostics || !this.sourceFile) { return false; } - let file = path.normalize(this.sourceFile.fileName); - let tscDiagnostics = this.tscStrictDiagnostics.get(file) + const file = path.normalize(this.sourceFile.fileName); + const tscDiagnostics = this.tscStrictDiagnostics.get(file); if (!tscDiagnostics) { return false; } - const checkDiagnostic = (val: ts.Diagnostic) => { + const checkDiagnostic = (val: ts.Diagnostic): boolean => { const checkInRange = filters[val.code]; if (!checkInRange) { return true; @@ -838,28 +1003,34 @@ export class TypeScriptLinter { return true; } - private handleFunctionExpression(node: ts.Node) { + private static isClassLikeOrIface(node: ts.Node): boolean { + return ts.isClassLike(node) || ts.isInterfaceDeclaration(node); + } + + private handleFunctionExpression(node: ts.Node): void { const funcExpr = node as ts.FunctionExpression; - const isGenerator = funcExpr.asteriskToken !== undefined; - const containsThis = this.scopeContainsThis(funcExpr.body); - const hasValidContext = - this.tsUtils.hasPredecessor(funcExpr, ts.isClassLike) || - this.tsUtils.hasPredecessor(funcExpr, ts.isInterfaceDeclaration); const isGeneric = funcExpr.typeParameters !== undefined && funcExpr.typeParameters.length > 0; - const isCalledRecursively = this.tsUtils.isFunctionCalledRecursively(funcExpr); + const isGenerator = funcExpr.asteriskToken !== undefined; + const hasThisKeyword = TypeScriptLinter.scopeContainsThis(funcExpr.body); + const isCalledRecursively = + this.tsUtils.isFunctionCalledRecursively(funcExpr); const [hasUnfixableReturnType, newRetTypeNode] = this.handleMissingReturnType(funcExpr); - const autofixable = !isGeneric && !isGenerator && !containsThis && !hasUnfixableReturnType && - !isCalledRecursively; + const autofixable = + !isGeneric && + !isGenerator && + !hasThisKeyword && + !isCalledRecursively && + !hasUnfixableReturnType; let autofix: Autofix[] | undefined; if ( autofixable && - this.autofixesInfo.shouldAutofix(node, FaultID.FunctionExpression) + this.autofixesInfo.shouldAutofix(funcExpr, FaultID.FunctionExpression) ) { autofix = [ - Autofixer.fixFunctionExpression( + autofixer.fixFunctionExpression( funcExpr, funcExpr.parameters, newRetTypeNode, @@ -868,7 +1039,7 @@ export class TypeScriptLinter { ]; } this.incrementCounters( - node, + funcExpr, FaultID.FunctionExpression, autofixable, autofix @@ -879,22 +1050,28 @@ export class TypeScriptLinter { if (isGenerator) { this.incrementCounters(funcExpr, FaultID.GeneratorFunction); } - if (containsThis && !hasValidContext) { - this.incrementCounters(funcExpr, FaultID.FunctionContainsThis); + if ( + !TypeScriptLinter.hasPredecessor( + funcExpr, + TypeScriptLinter.isClassLikeOrIface + ) + ) { + this.reportThisKeywordsInScope(funcExpr.body); } if (hasUnfixableReturnType) { this.incrementCounters(funcExpr, FaultID.LimitedReturnTypeInference); } } - private handleArrowFunction(node: ts.Node) { + private handleArrowFunction(node: ts.Node): void { const arrowFunc = node as ts.ArrowFunction; - const containsThis = this.scopeContainsThis(arrowFunc.body); - const hasValidContext = - this.tsUtils.hasPredecessor(arrowFunc, ts.isClassLike) || - this.tsUtils.hasPredecessor(arrowFunc, ts.isInterfaceDeclaration); - if (containsThis && !hasValidContext) { - this.incrementCounters(arrowFunc, FaultID.FunctionContainsThis); + if ( + !TypeScriptLinter.hasPredecessor( + arrowFunc, + TypeScriptLinter.isClassLikeOrIface + ) + ) { + this.reportThisKeywordsInScope(arrowFunc.body); } const contextType = this.tsTypeChecker.getContextualType(arrowFunc); if (!(contextType && this.tsUtils.isLibraryType(contextType))) { @@ -907,64 +1084,85 @@ export class TypeScriptLinter { } } - private handleClassExpression(node: ts.Node) { - let tsClassExpr = node as ts.ClassExpression; + private handleClassExpression(node: ts.Node): void { + const tsClassExpr = node as ts.ClassExpression; this.incrementCounters(node, FaultID.ClassExpression); this.handleDecorators(tsClassExpr.decorators); } - private handleFunctionDeclaration(node: ts.Node) { - let tsFunctionDeclaration = node as ts.FunctionDeclaration; - if (!tsFunctionDeclaration.type) + private handleFunctionDeclaration(node: ts.Node): void { + const tsFunctionDeclaration = node as ts.FunctionDeclaration; + if (!tsFunctionDeclaration.type) { this.handleMissingReturnType(tsFunctionDeclaration); - if (tsFunctionDeclaration.name) - this.countDeclarationsWithDuplicateName(tsFunctionDeclaration.name, tsFunctionDeclaration); - - if ( - tsFunctionDeclaration.body && - this.scopeContainsThis(tsFunctionDeclaration.body) - ) - this.incrementCounters(node, FaultID.FunctionContainsThis); - - if ( - !ts.isSourceFile(tsFunctionDeclaration.parent) && - !ts.isModuleBlock(tsFunctionDeclaration.parent) - ) + } + if (tsFunctionDeclaration.name) { + this.countDeclarationsWithDuplicateName( + tsFunctionDeclaration.name, + tsFunctionDeclaration + ); + } + if (tsFunctionDeclaration.body) { + this.reportThisKeywordsInScope(tsFunctionDeclaration.body); + } + const parent = tsFunctionDeclaration.parent; + if (!ts.isSourceFile(parent) && !ts.isModuleBlock(parent)) { this.incrementCounters(tsFunctionDeclaration, FaultID.LocalFunction); - - if (tsFunctionDeclaration.asteriskToken) + } + if (tsFunctionDeclaration.asteriskToken) { this.incrementCounters(node, FaultID.GeneratorFunction); + } } - private handleMissingReturnType(funcLikeDecl: ts.FunctionLikeDeclaration | ts.MethodSignature): [boolean, ts.TypeNode | undefined] { + private handleMissingReturnType( + funcLikeDecl: ts.FunctionLikeDeclaration | ts.MethodSignature + ): [boolean, ts.TypeNode | undefined] { // Note: Return type can't be inferred for function without body. if (ts.isMethodSignature(funcLikeDecl) || !funcLikeDecl.body) { // Ambient flag is not exposed, so we apply dirty hack to make it visible - const isAmbientDeclaration = !!(funcLikeDecl.flags & (ts.NodeFlags as any).Ambient); - const isSignature = ts.isMethodSignature(funcLikeDecl); - if ((isSignature || isAmbientDeclaration) && !funcLikeDecl.type) { - this.incrementCounters(funcLikeDecl, FaultID.LimitedReturnTypeInference); + const isAmbientDeclaration = !!( + funcLikeDecl.flags & (ts.NodeFlags as any).Ambient + ); + if ( + (ts.isMethodSignature(funcLikeDecl) || isAmbientDeclaration) && + !funcLikeDecl.type + ) { + this.incrementCounters( + funcLikeDecl, + FaultID.LimitedReturnTypeInference + ); } return [false, undefined]; } + + return this.tryAutofixMissingReturnType(funcLikeDecl); + } + + private tryAutofixMissingReturnType( + funcLikeDecl: ts.FunctionLikeDeclaration + ): [boolean, ts.TypeNode | undefined] { + if (!funcLikeDecl.body) { + return [false, undefined]; + } + let autofixable = false; let autofix: Autofix[] | undefined; let newRetTypeNode: ts.TypeNode | undefined; - let isFuncExpr = ts.isFunctionExpression(funcLikeDecl); + const isFuncExpr = ts.isFunctionExpression(funcLikeDecl); - // Currently, ArkTS can't infer return type of function, when expression - // in the return statement is a call to a function or method whose return - // value type is omitted. In that case, we attempt to prepare an autofix. + /* + * Currently, ArkTS can't infer return type of function, when expression + * in the return statement is a call to a function or method whose return + * value type is omitted. In that case, we attempt to prepare an autofix. + */ let hasLimitedRetTypeInference = this.hasLimitedTypeInferenceFromReturnExpr( funcLikeDecl.body ); - - let tsSignature = + const tsSignature = this.tsTypeChecker.getSignatureFromDeclaration(funcLikeDecl); if (tsSignature) { - let tsRetType = this.tsTypeChecker.getReturnTypeOfSignature(tsSignature); - - if (!tsRetType || this.tsUtils.isUnsupportedType(tsRetType)) { + const tsRetType = + this.tsTypeChecker.getReturnTypeOfSignature(tsSignature); + if (!tsRetType || TsUtils.isUnsupportedType(tsRetType)) { hasLimitedRetTypeInference = true; } else if (hasLimitedRetTypeInference) { newRetTypeNode = this.tsTypeChecker.typeToTypeNode( @@ -980,21 +1178,24 @@ export class TypeScriptLinter { FaultID.LimitedReturnTypeInference ) ) { - autofix = [Autofixer.fixReturnType(funcLikeDecl, newRetTypeNode)]; + autofix = [autofixer.fixReturnType(funcLikeDecl, newRetTypeNode)]; } } } } - // Don't report here if in function expression context. - // See handleFunctionExpression for details. - if (hasLimitedRetTypeInference && !isFuncExpr) + /* + * Don't report here if in function expression context. + * See handleFunctionExpression for details. + */ + if (hasLimitedRetTypeInference && !isFuncExpr) { this.incrementCounters( funcLikeDecl, FaultID.LimitedReturnTypeInference, autofixable, autofix ); + } return [hasLimitedRetTypeInference && !newRetTypeNode, newRetTypeNode]; } @@ -1003,36 +1204,36 @@ export class TypeScriptLinter { funBody: ts.ConciseBody ): boolean { let hasLimitedTypeInference = false; - const self = this; - function visitNode(tsNode: ts.Node): void { - if (hasLimitedTypeInference) return; + const callback = (node: ts.Node): void => { + if (hasLimitedTypeInference) { + return; + } if ( - ts.isReturnStatement(tsNode) && - tsNode.expression && - self.tsUtils.isCallToFunctionWithOmittedReturnType( - self.tsUtils.unwrapParenthesized(tsNode.expression) + ts.isReturnStatement(node) && + node.expression && + this.tsUtils.isCallToFunctionWithOmittedReturnType( + TsUtils.unwrapParenthesized(node.expression) ) ) { hasLimitedTypeInference = true; - return; } - - // Visit children nodes. Don't traverse other nested function-like declarations. - if ( - !ts.isFunctionDeclaration(tsNode) && - !ts.isFunctionExpression(tsNode) && - !ts.isMethodDeclaration(tsNode) && - !ts.isAccessor(tsNode) && - !ts.isArrowFunction(tsNode) - ) - tsNode.forEachChild(visitNode); - } + }; + // Don't traverse other nested function-like declarations. + const stopCondition = (node: ts.Node): boolean => { + return ( + ts.isFunctionDeclaration(node) || + ts.isFunctionExpression(node) || + ts.isMethodDeclaration(node) || + ts.isAccessor(node) || + ts.isArrowFunction(node) + ); + }; if (ts.isBlock(funBody)) { - visitNode(funBody); + this.forEachNodeInSubtree(funBody, callback, stopCondition); } else { - const tsExpr = this.tsUtils.unwrapParenthesized(funBody); + const tsExpr = TsUtils.unwrapParenthesized(funBody); hasLimitedTypeInference = this.tsUtils.isCallToFunctionWithOmittedReturnType(tsExpr); } @@ -1040,9 +1241,9 @@ export class TypeScriptLinter { return hasLimitedTypeInference; } - private handlePrefixUnaryExpression(node: ts.Node) { - let tsUnaryArithm = node as ts.PrefixUnaryExpression; - let tsUnaryOp = tsUnaryArithm.operator; + private handlePrefixUnaryExpression(node: ts.Node): void { + const tsUnaryArithm = node as ts.PrefixUnaryExpression; + const tsUnaryOp = tsUnaryArithm.operator; if ( tsUnaryOp === ts.SyntaxKind.PlusToken || tsUnaryOp === ts.SyntaxKind.MinusToken || @@ -1061,160 +1262,158 @@ export class TypeScriptLinter { !this.tsUtils.isIntegerConstantValue( tsUnaryArithm.operand as ts.NumericLiteral )) - ) + ) { this.incrementCounters(node, FaultID.UnaryArithmNotNumber); + } } } - private handleBinaryExpression(node: ts.Node) { - let tsBinaryExpr = node as ts.BinaryExpression; - let tsLhsExpr = tsBinaryExpr.left; - let tsRhsExpr = tsBinaryExpr.right; - + private handleBinaryExpression(node: ts.Node): void { + const tsBinaryExpr = node as ts.BinaryExpression; + const tsLhsExpr = tsBinaryExpr.left; + const tsRhsExpr = tsBinaryExpr.right; if (isAssignmentOperator(tsBinaryExpr.operatorToken)) { - if ( - ts.isObjectLiteralExpression(tsLhsExpr) || - ts.isArrayLiteralExpression(tsLhsExpr) - ) - this.incrementCounters(node, FaultID.DestructuringAssignment); - - if (ts.isPropertyAccessExpression(tsLhsExpr)) { - const tsLhsSymbol = this.tsUtils.trueSymbolAtLocation(tsLhsExpr); - const tsLhsBaseSymbol = this.tsUtils.trueSymbolAtLocation( - tsLhsExpr.expression - ); - if (tsLhsSymbol && (tsLhsSymbol.flags & ts.SymbolFlags.Method)) { - this.incrementCounters(tsLhsExpr, FaultID.MethodReassignment); - } + this.processBinaryAssignment(node, tsLhsExpr); + } + const leftOperandType = this.tsTypeChecker.getTypeAtLocation(tsLhsExpr); + const rightOperandType = this.tsTypeChecker.getTypeAtLocation(tsRhsExpr); + const typeNode = this.tsUtils.getVariableDeclarationTypeNode(tsLhsExpr); + switch (tsBinaryExpr.operatorToken.kind) { + // FaultID.BitOpWithWrongType - removed as rule #61 + case ts.SyntaxKind.CommaToken: + this.processBinaryComma(tsBinaryExpr); + break; + case ts.SyntaxKind.InstanceOfKeyword: + this.processBinaryInstanceOf(node, tsLhsExpr, leftOperandType); + break; + case ts.SyntaxKind.InKeyword: + this.incrementCounters(tsBinaryExpr.operatorToken, FaultID.InOperator); + break; + case ts.SyntaxKind.EqualsToken: if ( - this.tsUtils.isMethodAssignment(tsLhsSymbol) && - tsLhsBaseSymbol && - (tsLhsBaseSymbol.flags & ts.SymbolFlags.Function) !== 0 - ) - this.incrementCounters(tsLhsExpr, FaultID.PropertyDeclOnFunction); - } + this.tsUtils.needToDeduceStructuralIdentity( + leftOperandType, + rightOperandType, + tsRhsExpr + ) + ) { + this.incrementCounters(tsBinaryExpr, FaultID.StructuralIdentity); + } + this.handleEsObjectAssignment(tsBinaryExpr, typeNode, tsRhsExpr); + break; + default: } + } - let leftOperandType = this.tsTypeChecker.getTypeAtLocation(tsLhsExpr); - let rightOperandType = this.tsTypeChecker.getTypeAtLocation(tsRhsExpr); - - if (tsBinaryExpr.operatorToken.kind === ts.SyntaxKind.PlusToken) { - if ( - this.tsUtils.isEnumMemberType(leftOperandType) && - this.tsUtils.isEnumMemberType(rightOperandType) - ) { - if ( - (leftOperandType.getFlags() & ts.TypeFlags.NumberLike && - rightOperandType.getFlags() & ts.TypeFlags.NumberLike) || - (leftOperandType.getFlags() & ts.TypeFlags.StringLike && - rightOperandType.getFlags() & ts.TypeFlags.StringLike) - ) - return; - } else if ( - this.tsUtils.isNumberType(leftOperandType) && - this.tsUtils.isNumberType(rightOperandType) - ) - return; - else if ( - this.tsUtils.isStringLikeType(leftOperandType) || - this.tsUtils.isStringLikeType(rightOperandType) - ) - return; - } else if ( - tsBinaryExpr.operatorToken.kind === ts.SyntaxKind.AmpersandToken || - tsBinaryExpr.operatorToken.kind === ts.SyntaxKind.BarToken || - tsBinaryExpr.operatorToken.kind === ts.SyntaxKind.CaretToken || - tsBinaryExpr.operatorToken.kind === ts.SyntaxKind.LessThanLessThanToken || - tsBinaryExpr.operatorToken.kind === - ts.SyntaxKind.GreaterThanGreaterThanToken || - tsBinaryExpr.operatorToken.kind === - ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken + private processBinaryAssignment( + node: ts.Node, + tsLhsExpr: ts.Expression + ): void { + if ( + ts.isObjectLiteralExpression(tsLhsExpr) || + ts.isArrayLiteralExpression(tsLhsExpr) ) { + this.incrementCounters(node, FaultID.DestructuringAssignment); + } + if (ts.isPropertyAccessExpression(tsLhsExpr)) { + const tsLhsSymbol = this.tsUtils.trueSymbolAtLocation(tsLhsExpr); + const tsLhsBaseSymbol = this.tsUtils.trueSymbolAtLocation( + tsLhsExpr.expression + ); + if (tsLhsSymbol && tsLhsSymbol.flags & ts.SymbolFlags.Method) { + this.incrementCounters(tsLhsExpr, FaultID.MethodReassignment); + } if ( - !( - this.tsUtils.isNumberType(leftOperandType) && - this.tsUtils.isNumberType(rightOperandType) - ) || - (tsLhsExpr.kind === ts.SyntaxKind.NumericLiteral && - !this.tsUtils.isIntegerConstantValue( - tsLhsExpr as ts.NumericLiteral - )) || - (tsRhsExpr.kind === ts.SyntaxKind.NumericLiteral && - !this.tsUtils.isIntegerConstantValue(tsRhsExpr as ts.NumericLiteral)) - ) - return; // FaultID.BitOpWithWrongType -removed as rule #61 - } else if (tsBinaryExpr.operatorToken.kind === ts.SyntaxKind.CommaToken) { - // CommaOpertor is allowed in 'for' statement initalizer and incrementor - let tsExprNode: ts.Node = tsBinaryExpr; - let tsParentNode = tsExprNode.parent; - while ( - tsParentNode && - tsParentNode.kind === ts.SyntaxKind.BinaryExpression + TsUtils.isMethodAssignment(tsLhsSymbol) && + tsLhsBaseSymbol && + (tsLhsBaseSymbol.flags & ts.SymbolFlags.Function) !== 0 ) { - tsExprNode = tsParentNode; - tsParentNode = tsExprNode.parent; + this.incrementCounters(tsLhsExpr, FaultID.PropertyDeclOnFunction); } + } + } - if (tsParentNode && tsParentNode.kind === ts.SyntaxKind.ForStatement) { - const tsForNode = tsParentNode as ts.ForStatement; - if ( - tsExprNode === tsForNode.initializer || - tsExprNode === tsForNode.incrementor - ) - return; - } - this.incrementCounters(node, FaultID.CommaOperator); - } else if ( - tsBinaryExpr.operatorToken.kind === ts.SyntaxKind.InstanceOfKeyword + private processBinaryComma(tsBinaryExpr: ts.BinaryExpression): void { + // CommaOpertor is allowed in 'for' statement initalizer and incrementor + let tsExprNode: ts.Node = tsBinaryExpr; + let tsParentNode = tsExprNode.parent; + while ( + tsParentNode && + tsParentNode.kind === ts.SyntaxKind.BinaryExpression ) { - const leftExpr = this.tsUtils.unwrapParenthesized(tsBinaryExpr.left); - const leftSymbol = this.tsUtils.trueSymbolAtLocation(leftExpr); - // In STS, the left-hand side expression may be of any reference type, otherwise - // a compile-time error occurs. In addition, the left operand in STS cannot be a type. - if (tsLhsExpr.kind === ts.SyntaxKind.ThisKeyword) { - return; - } - + tsExprNode = tsParentNode; + tsParentNode = tsExprNode.parent; + } + if (tsParentNode && tsParentNode.kind === ts.SyntaxKind.ForStatement) { + const tsForNode = tsParentNode as ts.ForStatement; if ( - this.tsUtils.isPrimitiveType(leftOperandType) || - ts.isTypeNode(leftExpr) || - this.tsUtils.isTypeSymbol(leftSymbol) + tsExprNode === tsForNode.initializer || + tsExprNode === tsForNode.incrementor ) { - this.incrementCounters(node, FaultID.InstanceofUnsupported); - } - } else if (tsBinaryExpr.operatorToken.kind === ts.SyntaxKind.EqualsToken) { - if (this.tsUtils.needToDeduceStructuralIdentity(leftOperandType, rightOperandType, tsRhsExpr)) { - this.incrementCounters(tsBinaryExpr, FaultID.StructuralIdentity); + return; } + } + this.incrementCounters(tsBinaryExpr as ts.Node, FaultID.CommaOperator); + } - const typeNode = this.tsUtils.getVariableDeclarationTypeNode(tsLhsExpr); - this.handleEsObjectAssignment(tsBinaryExpr, typeNode, tsRhsExpr); + private processBinaryInstanceOf( + node: ts.Node, + tsLhsExpr: ts.Expression, + leftOperandType: ts.Type + ): void { + const leftExpr = TsUtils.unwrapParenthesized(tsLhsExpr); + const leftSymbol = this.tsUtils.trueSymbolAtLocation(leftExpr); + + /* + * In STS, the left-hand side expression may be of any reference type, otherwise + * a compile-time error occurs. In addition, the left operand in STS cannot be a type. + */ + if (tsLhsExpr.kind === ts.SyntaxKind.ThisKeyword) { + return; + } + + if ( + TsUtils.isPrimitiveType(leftOperandType) || + ts.isTypeNode(leftExpr) || + TsUtils.isTypeSymbol(leftSymbol) + ) { + this.incrementCounters(node, FaultID.InstanceofUnsupported); } } - private handleVariableDeclarationList(node: ts.Node) { - let varDeclFlags = ts.getCombinedNodeFlags(node); - if (!(varDeclFlags & (ts.NodeFlags.Let | ts.NodeFlags.Const))) + private handleVariableDeclarationList(node: ts.Node): void { + const varDeclFlags = ts.getCombinedNodeFlags(node); + if (!(varDeclFlags & (ts.NodeFlags.Let | ts.NodeFlags.Const))) { this.incrementCounters(node, FaultID.VarDeclaration); + } } private handleVariableDeclaration(node: ts.Node): void { - let tsVarDecl = node as ts.VariableDeclaration; + const tsVarDecl = node as ts.VariableDeclaration; if ( ts.isArrayBindingPattern(tsVarDecl.name) || ts.isObjectBindingPattern(tsVarDecl.name) - ) + ) { this.incrementCounters(node, FaultID.DestructuringDeclaration); + } { // Check variable declaration for duplicate name. - const visitBindingPatternNames = (tsBindingName: ts.BindingName) => { - if (ts.isIdentifier(tsBindingName)) + const visitBindingPatternNames = ( + tsBindingName: ts.BindingName + ): void => { + if (ts.isIdentifier(tsBindingName)) { // The syntax kind of the declaration is defined here by the parent of 'BindingName' node. - this.countDeclarationsWithDuplicateName(tsBindingName, tsBindingName, tsBindingName.parent.kind); - else { + this.countDeclarationsWithDuplicateName( + tsBindingName, + tsBindingName, + tsBindingName.parent.kind + ); + } else { for (const tsBindingElem of tsBindingName.elements) { - if (ts.isOmittedExpression(tsBindingElem)) continue; + if (ts.isOmittedExpression(tsBindingElem)) { + continue; + } visitBindingPatternNames(tsBindingElem.name); } @@ -1225,10 +1424,16 @@ export class TypeScriptLinter { } if (tsVarDecl.type && tsVarDecl.initializer) { - let tsVarInit = tsVarDecl.initializer; - let tsVarType = this.tsTypeChecker.getTypeAtLocation(tsVarDecl.type); - let tsInitType = this.tsTypeChecker.getTypeAtLocation(tsVarInit); - if (this.tsUtils.needToDeduceStructuralIdentity(tsVarType, tsInitType, tsVarInit)) { + const tsVarInit = tsVarDecl.initializer; + const tsVarType = this.tsTypeChecker.getTypeAtLocation(tsVarDecl.type); + const tsInitType = this.tsTypeChecker.getTypeAtLocation(tsVarInit); + if ( + this.tsUtils.needToDeduceStructuralIdentity( + tsVarType, + tsInitType, + tsVarInit + ) + ) { this.incrementCounters(tsVarDecl, FaultID.StructuralIdentity); } } @@ -1239,10 +1444,13 @@ export class TypeScriptLinter { } private handleEsObjectDelaration(node: ts.VariableDeclaration): void { - const isDeclaredESObject = !!node.type && this.tsUtils.isEsObjectType(node.type); - const initalizerTypeNode = node.initializer && this.tsUtils.getVariableDeclarationTypeNode(node.initializer); - const isInitializedWithESObject = !!initalizerTypeNode && this.tsUtils.isEsObjectType(initalizerTypeNode); - const isLocal = this.tsUtils.isInsideBlock(node); + const isDeclaredESObject = !!node.type && TsUtils.isEsObjectType(node.type); + const initalizerTypeNode = + node.initializer && + this.tsUtils.getVariableDeclarationTypeNode(node.initializer); + const isInitializedWithESObject = + !!initalizerTypeNode && TsUtils.isEsObjectType(initalizerTypeNode); + const isLocal = TsUtils.isInsideBlock(node); if ((isDeclaredESObject || isInitializedWithESObject) && !isLocal) { this.incrementCounters(node, FaultID.EsObjectType); return; @@ -1253,36 +1461,50 @@ export class TypeScriptLinter { } } - private handleEsObjectAssignment(node: ts.Node, nodeDeclType: ts.TypeNode | undefined, initializer: ts.Node): void { + private handleEsObjectAssignment( + node: ts.Node, + nodeDeclType: ts.TypeNode | undefined, + initializer: ts.Node + ): void { const isTypeAnnotated = !!nodeDeclType; - const isDeclaredESObject = !!nodeDeclType && this.tsUtils.isEsObjectType(nodeDeclType); - const initalizerTypeNode = this.tsUtils.getVariableDeclarationTypeNode(initializer); - const isInitializedWithESObject = !!initalizerTypeNode && this.tsUtils.isEsObjectType(initalizerTypeNode); + const isDeclaredESObject = + !!nodeDeclType && TsUtils.isEsObjectType(nodeDeclType); + const initalizerTypeNode = + this.tsUtils.getVariableDeclarationTypeNode(initializer); + const isInitializedWithESObject = + !!initalizerTypeNode && TsUtils.isEsObjectType(initalizerTypeNode); if (isTypeAnnotated && !isDeclaredESObject && isInitializedWithESObject) { this.incrementCounters(node, FaultID.EsObjectType); return; } - if (isDeclaredESObject && !this.tsUtils.isValueAssignableToESObject(initializer)) { + if ( + isDeclaredESObject && + !this.tsUtils.isValueAssignableToESObject(initializer) + ) { this.incrementCounters(node, FaultID.EsObjectType); } } - private handleCatchClause(node: ts.Node) { - let tsCatch = node as ts.CatchClause; - // In TS catch clause doesn't permit specification of the exception varible type except 'any' or 'unknown'. - // It is not compatible with STS 'catch' where the exception variable has to be of type - // Error or derived from it. - // So each 'catch' which has explicit type for the exception object goes to problems in strict mode. - if (tsCatch.variableDeclaration && tsCatch.variableDeclaration.type) { + private handleCatchClause(node: ts.Node): void { + const tsCatch = node as ts.CatchClause; + + /* + * In TS catch clause doesn't permit specification of the exception varible type except 'any' or 'unknown'. + * It is not compatible with STS 'catch' where the exception variable has to be of type + * Error or derived from it. + * So each 'catch' which has explicit type for the exception object goes to problems in strict mode. + */ + if (tsCatch.variableDeclaration?.type) { let autofix: Autofix[] | undefined; if ( this.autofixesInfo.shouldAutofix( tsCatch, FaultID.CatchWithUnsupportedType ) - ) - autofix = [Autofixer.dropTypeOnVarDecl(tsCatch.variableDeclaration)]; + ) { + autofix = [autofixer.dropTypeOnVarDecl(tsCatch.variableDeclaration)]; + } this.incrementCounters( node, FaultID.CatchWithUnsupportedType, @@ -1292,20 +1514,26 @@ export class TypeScriptLinter { } } - private handleClassDeclaration(node: ts.Node) { - let tsClassDecl = node as ts.ClassDeclaration; + private handleClassDeclaration(node: ts.Node): void { + const tsClassDecl = node as ts.ClassDeclaration; this.staticBlocks.clear(); - if (tsClassDecl.name) + if (tsClassDecl.name) { this.countDeclarationsWithDuplicateName(tsClassDecl.name, tsClassDecl); + } this.countClassMembersWithDuplicateName(tsClassDecl); - const visitHClause = (hClause: ts.HeritageClause) => { + const visitHClause = (hClause: ts.HeritageClause): void => { for (const tsTypeExpr of hClause.types) { - const tsExprType = this.tsTypeChecker.getTypeAtLocation(tsTypeExpr.expression); - if (tsExprType.isClass() && hClause.token === ts.SyntaxKind.ImplementsKeyword) { + const tsExprType = this.tsTypeChecker.getTypeAtLocation( + tsTypeExpr.expression + ); + if ( + tsExprType.isClass() && + hClause.token === ts.SyntaxKind.ImplementsKeyword + ) { this.incrementCounters(tsTypeExpr, FaultID.ImplementsClass); } } @@ -1323,87 +1551,101 @@ export class TypeScriptLinter { this.handleDecorators(tsClassDecl.decorators); } - private handleModuleDeclaration(node: ts.Node) { - let tsModuleDecl = node as ts.ModuleDeclaration; + private handleModuleDeclaration(node: ts.Node): void { + const tsModuleDecl = node as ts.ModuleDeclaration; this.countDeclarationsWithDuplicateName(tsModuleDecl.name, tsModuleDecl); - let tsModuleBody = tsModuleDecl.body; - let tsModifiers = tsModuleDecl.modifiers; // TSC 4.2 doesn't have 'ts.getModifiers()' method + const tsModuleBody = tsModuleDecl.body; if (tsModuleBody) { if (ts.isModuleBlock(tsModuleBody)) { - for (const tsModuleStmt of tsModuleBody.statements) { - switch (tsModuleStmt.kind) { - case ts.SyntaxKind.VariableStatement: - case ts.SyntaxKind.FunctionDeclaration: - case ts.SyntaxKind.ClassDeclaration: - case ts.SyntaxKind.InterfaceDeclaration: - case ts.SyntaxKind.TypeAliasDeclaration: - case ts.SyntaxKind.EnumDeclaration: - case ts.SyntaxKind.ExportDeclaration: - break; - // Nested namespace declarations are prohibited - // but there is no cookbook recipe for it! - case ts.SyntaxKind.ModuleDeclaration: - break; - default: - this.incrementCounters( - tsModuleStmt, - FaultID.NonDeclarationInNamespace - ); - break; - } - } + this.handleModuleBlock(tsModuleBody); } } if ( !(tsModuleDecl.flags & ts.NodeFlags.Namespace) && - this.tsUtils.hasModifier(tsModifiers, ts.SyntaxKind.DeclareKeyword) + TsUtils.hasModifier(tsModuleDecl.modifiers, ts.SyntaxKind.DeclareKeyword) ) { this.incrementCounters(tsModuleDecl, FaultID.ShorthandAmbientModuleDecl); } if ( ts.isStringLiteral(tsModuleDecl.name) && - tsModuleDecl.name.text.includes("*") - ) + tsModuleDecl.name.text.includes('*') + ) { this.incrementCounters(tsModuleDecl, FaultID.WildcardsInModuleName); + } } - private handleTypeAliasDeclaration(node: ts.Node) { - let tsTypeAlias = node as ts.TypeAliasDeclaration; + private handleModuleBlock(moduleBlock: ts.ModuleBlock): void { + for (const tsModuleStmt of moduleBlock.statements) { + switch (tsModuleStmt.kind) { + case ts.SyntaxKind.VariableStatement: + case ts.SyntaxKind.FunctionDeclaration: + case ts.SyntaxKind.ClassDeclaration: + case ts.SyntaxKind.InterfaceDeclaration: + case ts.SyntaxKind.TypeAliasDeclaration: + case ts.SyntaxKind.EnumDeclaration: + case ts.SyntaxKind.ExportDeclaration: + break; + + /* + * Nested namespace declarations are prohibited + * but there is no cookbook recipe for it! + */ + case ts.SyntaxKind.ModuleDeclaration: + break; + default: + this.incrementCounters( + tsModuleStmt, + FaultID.NonDeclarationInNamespace + ); + break; + } + } + } + + private handleTypeAliasDeclaration(node: ts.Node): void { + const tsTypeAlias = node as ts.TypeAliasDeclaration; this.countDeclarationsWithDuplicateName(tsTypeAlias.name, tsTypeAlias); } - private handleImportClause(node: ts.Node) { - let tsImportClause = node as ts.ImportClause; + private handleImportClause(node: ts.Node): void { + const tsImportClause = node as ts.ImportClause; if (tsImportClause.name) { - this.countDeclarationsWithDuplicateName(tsImportClause.name, tsImportClause); + this.countDeclarationsWithDuplicateName( + tsImportClause.name, + tsImportClause + ); } if ( tsImportClause.namedBindings && ts.isNamedImports(tsImportClause.namedBindings) ) { - let nonDefaultSpecs: ts.ImportSpecifier[] = []; + const nonDefaultSpecs: ts.ImportSpecifier[] = []; let defaultSpec: ts.ImportSpecifier | undefined; for (const importSpec of tsImportClause.namedBindings.elements) { - if (this.tsUtils.isDefaultImport(importSpec)) defaultSpec = importSpec; - else nonDefaultSpecs.push(importSpec); + if (TsUtils.isDefaultImport(importSpec)) { + defaultSpec = importSpec; + } else { + nonDefaultSpecs.push(importSpec); + } } if (defaultSpec) { let autofix: Autofix[] | undefined; if ( this.autofixesInfo.shouldAutofix(defaultSpec, FaultID.DefaultImport) - ) + ) { autofix = [ - Autofixer.fixDefaultImport( + autofixer.fixDefaultImport( tsImportClause, defaultSpec, nonDefaultSpecs ), ]; + } this.incrementCounters( defaultSpec, FaultID.DefaultImport, @@ -1412,41 +1654,37 @@ export class TypeScriptLinter { ); } } - - if (tsImportClause.isTypeOnly) { - let autofix: Autofix[] | undefined; - if (this.autofixesInfo.shouldAutofix(node, FaultID.TypeOnlyImport)) - autofix = [Autofixer.dropTypeOnlyFlag(tsImportClause)]; - this.incrementCounters(node, FaultID.TypeOnlyImport, true, autofix); - } } - private handleImportSpecifier(node: ts.Node) { - let importSpec = node as ts.ImportSpecifier; + private handleImportSpecifier(node: ts.Node): void { + const importSpec = node as ts.ImportSpecifier; this.countDeclarationsWithDuplicateName(importSpec.name, importSpec); } - private handleNamespaceImport(node: ts.Node) { - let tsNamespaceImport = node as ts.NamespaceImport; - this.countDeclarationsWithDuplicateName(tsNamespaceImport.name, tsNamespaceImport); + private handleNamespaceImport(node: ts.Node): void { + const tsNamespaceImport = node as ts.NamespaceImport; + this.countDeclarationsWithDuplicateName( + tsNamespaceImport.name, + tsNamespaceImport + ); } - private handleTypeAssertionExpression(node: ts.Node) { - let tsTypeAssertion = node as ts.TypeAssertion; - if (tsTypeAssertion.type.getText() === "const") + private handleTypeAssertionExpression(node: ts.Node): void { + const tsTypeAssertion = node as ts.TypeAssertion; + if (tsTypeAssertion.type.getText() === 'const') { this.incrementCounters(tsTypeAssertion, FaultID.ConstAssertion); - else + } else { this.incrementCounters(node, FaultID.TypeAssertion, true, [ - Autofixer.fixTypeAssertion(tsTypeAssertion), + autofixer.fixTypeAssertion(tsTypeAssertion), ]); + } } private handleMethodDeclaration(node: ts.Node): void { const tsMethodDecl = node as ts.MethodDeclaration; - const hasThis = this.scopeContainsThis(tsMethodDecl); let isStatic = false; if (tsMethodDecl.modifiers) { - for (let mod of tsMethodDecl.modifiers) { + for (const mod of tsMethodDecl.modifiers) { if (mod.kind === ts.SyntaxKind.StaticKeyword) { isStatic = true; break; @@ -1454,53 +1692,70 @@ export class TypeScriptLinter { } } - if (isStatic && hasThis) { - this.incrementCounters(node, FaultID.FunctionContainsThis); + if (tsMethodDecl.body && isStatic) { + this.reportThisKeywordsInScope(tsMethodDecl.body); } - if (!tsMethodDecl.type) this.handleMissingReturnType(tsMethodDecl); + if (!tsMethodDecl.type) { + this.handleMissingReturnType(tsMethodDecl); + } - if (tsMethodDecl.asteriskToken) + if (tsMethodDecl.asteriskToken) { this.incrementCounters(node, FaultID.GeneratorFunction); + } this.handleDecorators(tsMethodDecl.decorators); - this.filterOutDecoratorsDiagnostics(tsMethodDecl.decorators, TsUtils.NON_RETURN_FUNCTION_DECORATORS, - {begin: tsMethodDecl.parameters.end, end: tsMethodDecl.body?.getStart() ?? tsMethodDecl.parameters.end}, - TsUtils.FUNCTION_HAS_NO_RETURN_ERROR_CODE); + this.filterOutDecoratorsDiagnostics( + tsMethodDecl.decorators, + TsUtils.NON_RETURN_FUNCTION_DECORATORS, + { + begin: tsMethodDecl.parameters.end, + end: tsMethodDecl.body?.getStart() ?? tsMethodDecl.parameters.end, + }, + TsUtils.FUNCTION_HAS_NO_RETURN_ERROR_CODE + ); } private handleMethodSignature(node: ts.MethodSignature): void { - const tsMethodSign = node as ts.MethodSignature; + const tsMethodSign = node; if (!tsMethodSign.type) { this.handleMissingReturnType(tsMethodSign); } } - private handleIdentifier(node: ts.Node) { - let tsIdentifier = node as ts.Identifier; - let tsIdentSym = this.tsUtils.trueSymbolAtLocation(tsIdentifier); + private handleIdentifier(node: ts.Node): void { + const tsIdentifier = node as ts.Identifier; + const tsIdentSym = this.tsUtils.trueSymbolAtLocation(tsIdentifier); + if (!tsIdentSym) { return; } + if ( (tsIdentSym.flags & ts.SymbolFlags.Module) !== 0 && (tsIdentSym.flags & ts.SymbolFlags.Transient) !== 0 && tsIdentifier.text === 'globalThis' ) { - this.incrementCounters(tsIdentifier, FaultID.GlobalThis); + this.incrementCounters(node, FaultID.GlobalThis); } else { this.checkLimitedStdLib(tsIdentifier, tsIdentSym); this.handleRestrictedValues(tsIdentifier, tsIdentSym); } } - private isAllowedClassValueContext(tsIdentifier: ts.Identifier, tsIdentSym: ts.Symbol): boolean { + private isAllowedClassValueContext(tsIdentifier: ts.Identifier): boolean { let ctx: ts.Node = tsIdentifier; - while (ts.isPropertyAccessExpression(ctx.parent) || ts.isQualifiedName(ctx.parent)) { + while ( + ts.isPropertyAccessExpression(ctx.parent) || + ts.isQualifiedName(ctx.parent) + ) { ctx = ctx.parent; } - if (ts.isPropertyAssignment(ctx.parent) && ts.isObjectLiteralExpression(ctx.parent.parent)) { + if ( + ts.isPropertyAssignment(ctx.parent) && + ts.isObjectLiteralExpression(ctx.parent.parent) + ) { ctx = ctx.parent.parent; } if (ts.isArrowFunction(ctx.parent) && ctx.parent.body === ctx) { @@ -1508,32 +1763,54 @@ export class TypeScriptLinter { } if (ts.isCallExpression(ctx.parent) || ts.isNewExpression(ctx.parent)) { - let callee = ctx.parent.expression; - if (callee !== ctx && this.tsUtils.hasLibraryType(callee)) { + const callee = ctx.parent.expression; + const isAny = TsUtils.isAnyType( + this.tsTypeChecker.getTypeAtLocation(callee) + ); + const isDynamic = isAny || this.tsUtils.hasLibraryType(callee); + if (callee !== ctx && isDynamic) { return true; } } return false; } - private handleRestrictedValues(tsIdentifier: ts.Identifier, tsIdentSym: ts.Symbol) { - const illegalValues = ts.SymbolFlags.ConstEnum | ts.SymbolFlags.RegularEnum | ts.SymbolFlags.ValueModule | ts.SymbolFlags.Class; - - // If module name is duplicated by another declaration, this increases the possibility - // of finding a lot of false positives. Thus, do not check further in that case. + private handleRestrictedValues( + tsIdentifier: ts.Identifier, + tsIdentSym: ts.Symbol + ): void { + const illegalValues = + ts.SymbolFlags.ConstEnum | + ts.SymbolFlags.RegularEnum | + ts.SymbolFlags.ValueModule | + ts.SymbolFlags.Class; + + /* + * If module name is duplicated by another declaration, this increases the possibility + * of finding a lot of false positives. Thus, do not check further in that case. + */ if ((tsIdentSym.flags & ts.SymbolFlags.ValueModule) !== 0) { - if (!!tsIdentSym && this.tsUtils.symbolHasDuplicateName(tsIdentSym, ts.SyntaxKind.ModuleDeclaration)) { + if ( + !!tsIdentSym && + TsUtils.symbolHasDuplicateName( + tsIdentSym, + ts.SyntaxKind.ModuleDeclaration + ) + ) { return; } } - if ((tsIdentSym.flags & illegalValues) === 0 || this.tsUtils.isStruct(tsIdentSym) || - !this.identiferUseInValueContext(tsIdentifier, tsIdentSym)) { + if ( + (tsIdentSym.flags & illegalValues) === 0 || + TsUtils.isStruct(tsIdentSym) || + !identiferUseInValueContext(tsIdentifier, tsIdentSym) + ) { return; } if ((tsIdentSym.flags & ts.SymbolFlags.Class) !== 0) { - if (this.isAllowedClassValueContext(tsIdentifier, tsIdentSym)) { + if (this.isAllowedClassValueContext(tsIdentifier)) { return; } } @@ -1546,74 +1823,72 @@ export class TypeScriptLinter { } } - private identiferUseInValueContext( - ident: ts.Identifier, tsSym: ts.Symbol - ) { - // If identifier is the right-most name of Property Access chain or Qualified name, - // or it's a separate identifier expression, then identifier is being referenced as an value. - let qualifiedStart: ts.Node = ident; - while (ts.isPropertyAccessExpression(qualifiedStart.parent) || ts.isQualifiedName(qualifiedStart.parent)) { - qualifiedStart = qualifiedStart.parent; - } - let parent = qualifiedStart.parent; - return !( - // treat TypeQuery as valid because it's already forbidden (FaultID.TypeQuery) - (ts.isTypeNode(parent) && !ts.isTypeOfExpression(parent)) || - // ElementAccess is allowed for enum types - (ts.isElementAccessExpression(parent) && - (parent as ts.ElementAccessExpression).expression === ident && (tsSym.flags & ts.SymbolFlags.Enum)) || - ts.isExpressionWithTypeArguments(parent) || - ts.isExportAssignment(parent) || - ts.isExportSpecifier(parent) || - ts.isMetaProperty(parent) || - ts.isImportClause(parent) || - ts.isClassLike(parent) || - ts.isInterfaceDeclaration(parent) || - ts.isModuleDeclaration(parent) || - ts.isEnumDeclaration(parent) || - ts.isNamespaceImport(parent) || - ts.isImportSpecifier(parent) || - ts.isImportEqualsDeclaration(parent) || - (ts.isQualifiedName(qualifiedStart) && ident !== qualifiedStart.right) || - (ts.isPropertyAccessExpression(qualifiedStart) && - ident !== qualifiedStart.name) || - (ts.isNewExpression(qualifiedStart.parent) && - qualifiedStart === qualifiedStart.parent.expression) || - (ts.isBinaryExpression(qualifiedStart.parent) && - qualifiedStart.parent.operatorToken.kind === - ts.SyntaxKind.InstanceOfKeyword) + private static isEnumPropAccess( + ident: ts.Identifier, + tsSym: ts.Symbol, + context: ts.Node + ): boolean { + return ( + ts.isElementAccessExpression(context) && + !!(tsSym.flags & ts.SymbolFlags.Enum) && + (context.expression === ident || + (ts.isPropertyAccessExpression(context.expression) && + context.expression.name === ident)) ); } - private handleElementAccessExpression(node: ts.Node) { + private isElementAcessAllowed(type: ts.Type): boolean { + if (type.isUnion()) { + for (const t of type.types) { + if (!this.isElementAcessAllowed(t)) { + return false; + } + } + return true; + } + + const typeNode = this.tsTypeChecker.typeToTypeNode( + type, + undefined, + ts.NodeBuilderFlags.None + ); + + return ( + this.tsUtils.isLibraryType(type) || + TsUtils.isAnyType(type) || + this.tsUtils.isOrDerivedFrom(type, this.tsUtils.isArray) || + this.tsUtils.isOrDerivedFrom(type, TsUtils.isTuple) || + this.tsUtils.isOrDerivedFrom(type, this.tsUtils.isStdRecordType) || + TsUtils.isEnumType(type) || + // we allow EsObject here beacuse it is reported later using FaultId.EsObjectType + TsUtils.isEsObjectType(typeNode) + ); + } + + private handleElementAccessExpression(node: ts.Node): void { const tsElementAccessExpr = node as ts.ElementAccessExpression; - const tsElemAccessBaseExprType = this.tsTypeChecker.getTypeAtLocation( + const tsElementAccessExprSymbol = this.tsUtils.trueSymbolAtLocation( tsElementAccessExpr.expression ); - const tsElemAccessBaseExprTypeNode = this.tsTypeChecker.typeToTypeNode( - tsElemAccessBaseExprType, - undefined, - ts.NodeBuilderFlags.None + const tsElemAccessBaseExprType = TsUtils.getNonNullableType( + this.tsUtils.getTypeOrTypeConstraintAtLocation( + tsElementAccessExpr.expression + ) ); - const checkClassOrInterface = tsElemAccessBaseExprType.isClassOrInterface() && - !this.tsUtils.isGenericArrayType(tsElemAccessBaseExprType) && - !this.tsUtils.isDerivedFrom(tsElemAccessBaseExprType, CheckType.Array); - const checkThisOrSuper = this.tsUtils.isThisOrSuperExpr(tsElementAccessExpr.expression) && - !this.tsUtils.isDerivedFrom(tsElemAccessBaseExprType, CheckType.Array); if ( - !this.tsUtils.isLibraryType(tsElemAccessBaseExprType) && - !this.tsUtils.isTypedArray(tsElemAccessBaseExprTypeNode) && - ( checkClassOrInterface || - this.tsUtils.isObjectLiteralType(tsElemAccessBaseExprType) || checkThisOrSuper) + // unnamed types do not have symbol, so need to check that explicitly + !this.tsUtils.isLibrarySymbol(tsElementAccessExprSymbol) && + !ts.isArrayLiteralExpression(tsElementAccessExpr.expression) && + !this.isElementAcessAllowed(tsElemAccessBaseExprType) ) { - let autofix = Autofixer.fixPropertyAccessByIndex(node); + let autofix = autofixer.fixPropertyAccessByIndex(node); const autofixable = autofix !== undefined; if ( !this.autofixesInfo.shouldAutofix(node, FaultID.PropertyAccessByIndex) - ) + ) { autofix = undefined; - + } this.incrementCounters( node, FaultID.PropertyAccessByIndex, @@ -1627,72 +1902,68 @@ export class TypeScriptLinter { } } - private handleEnumMember(node: ts.Node) { - let tsEnumMember = node as ts.EnumMember; - let tsEnumMemberType = this.tsTypeChecker.getTypeAtLocation(tsEnumMember); - let constVal = this.tsTypeChecker.getConstantValue(tsEnumMember); + private handleEnumMember(node: ts.Node): void { + const tsEnumMember = node as ts.EnumMember; + const tsEnumMemberType = this.tsTypeChecker.getTypeAtLocation(tsEnumMember); + const constVal = this.tsTypeChecker.getConstantValue(tsEnumMember); if ( tsEnumMember.initializer && !this.tsUtils.isValidEnumMemberInit(tsEnumMember.initializer) - ) + ) { this.incrementCounters(node, FaultID.EnumMemberNonConstInit); + } // check for type - all members should be of same type - let enumDecl = tsEnumMember.parent; - let firstEnumMember = enumDecl.members[0]; - let firstEnumMemberType = + const enumDecl = tsEnumMember.parent; + const firstEnumMember = enumDecl.members[0]; + const firstEnumMemberType = this.tsTypeChecker.getTypeAtLocation(firstEnumMember); - let firstElewmVal = this.tsTypeChecker.getConstantValue(firstEnumMember); - // each string enum member has its own type - // so check that value type is string + const firstElewmVal = this.tsTypeChecker.getConstantValue(firstEnumMember); + + /* + * each string enum member has its own type + * so check that value type is string + */ if ( constVal !== undefined && - typeof constVal === "string" && + typeof constVal === 'string' && firstElewmVal !== undefined && - typeof firstElewmVal === "string" - ) + typeof firstElewmVal === 'string' + ) { return; + } if ( constVal !== undefined && - typeof constVal === "number" && + typeof constVal === 'number' && firstElewmVal !== undefined && - typeof firstElewmVal === "number" - ) + typeof firstElewmVal === 'number' + ) { return; + } if (firstEnumMemberType !== tsEnumMemberType) { this.incrementCounters(node, FaultID.EnumMemberNonConstInit); } } - private handleExportDeclaration(node: ts.Node) { - let tsExportDecl = node as ts.ExportDeclaration; - if (tsExportDecl.isTypeOnly) { - let autofix: Autofix[] | undefined; - if (this.autofixesInfo.shouldAutofix(node, FaultID.TypeOnlyExport)) - autofix = [Autofixer.dropTypeOnlyFlag(tsExportDecl)]; - this.incrementCounters(node, FaultID.TypeOnlyExport, true, autofix); - } - } - - private handleExportAssignment(node: ts.Node) { + private handleExportAssignment(node: ts.Node): void { const exportAssignment = node as ts.ExportAssignment; if (exportAssignment.isExportEquals) { this.incrementCounters(node, FaultID.ExportAssignment); } } - private handleCallExpression(node: ts.Node) { - let tsCallExpr = node as ts.CallExpression; + private handleCallExpression(node: ts.Node): void { + const tsCallExpr = node as ts.CallExpression; const calleeSym = this.tsUtils.trueSymbolAtLocation(tsCallExpr.expression); - const calleeType = this.tsTypeChecker.getTypeAtLocation(tsCallExpr.expression); const callSignature = this.tsTypeChecker.getResolvedSignature(tsCallExpr); this.handleImportCall(tsCallExpr); this.handleRequireCall(tsCallExpr); - if (!!calleeSym) { - if (this.tsUtils.symbolHasEsObjectType(calleeSym)) { + // NOTE: Keep handleFunctionApplyBindPropCall above handleGenericCallWithNoTypeArgs here!!! + if (calleeSym !== undefined) { + if (TsUtils.symbolHasEsObjectType(calleeSym)) { this.incrementCounters(tsCallExpr, FaultID.EsObjectType); } // need to process Symbol call separatey in order to not report two times when using Symbol API @@ -1700,30 +1971,42 @@ export class TypeScriptLinter { this.incrementCounters(tsCallExpr, FaultID.SymbolType); } } - if (!!callSignature) { + if (callSignature !== undefined) { if (!this.tsUtils.isLibrarySymbol(calleeSym)) { this.handleGenericCallWithNoTypeArgs(tsCallExpr, callSignature); } this.handleStructIdentAndUndefinedInArgs(tsCallExpr, callSignature); } - this.handleLibraryTypeCall(tsCallExpr, calleeType); + this.handleLibraryTypeCall(tsCallExpr); + + if ( + ts.isPropertyAccessExpression(tsCallExpr.expression) && + this.tsUtils.hasEsObjectType(tsCallExpr.expression.expression) + ) { + this.incrementCounters(node, FaultID.EsObjectType); + } } - private handleImportCall(tsCallExpr: ts.CallExpression) { + private handleEtsComponentExpression(node: ts.Node): void { + // for all the checks we make EtsComponentExpression is compatible with the CallExpression + const etsComponentExpression = node as ts.CallExpression; + this.handleLibraryTypeCall(etsComponentExpression); + } + + private handleImportCall(tsCallExpr: ts.CallExpression): void { if (tsCallExpr.expression.kind === ts.SyntaxKind.ImportKeyword) { - // relax rule#133 "arkts-no-runtime-import" + // relax rule#133 'arkts-no-runtime-import' const tsArgs = tsCallExpr.arguments; - if (tsArgs.length > 1 && ts.isObjectLiteralExpression(tsArgs[1])) { - let objLitExpr = tsArgs[1] as ts.ObjectLiteralExpression; + const objLitExpr = tsArgs[1]; + if (!!objLitExpr && ts.isObjectLiteralExpression(objLitExpr)) { for (const tsProp of objLitExpr.properties) { if ( - ts.isPropertyAssignment(tsProp) || - ts.isShorthandPropertyAssignment(tsProp) + (ts.isPropertyAssignment(tsProp) || + ts.isShorthandPropertyAssignment(tsProp)) && + tsProp.name.getText() === 'assert' ) { - if (tsProp.name.getText() === "assert") { - this.incrementCounters(tsProp, FaultID.ImportAssertion); - break; - } + this.incrementCounters(tsProp, FaultID.ImportAssertion); + break; } } } @@ -1733,23 +2016,29 @@ export class TypeScriptLinter { private handleRequireCall(tsCallExpr: ts.CallExpression): void { if ( ts.isIdentifier(tsCallExpr.expression) && - tsCallExpr.expression.text === "require" && + tsCallExpr.expression.text === 'require' && ts.isVariableDeclaration(tsCallExpr.parent) ) { - let tsType = this.tsTypeChecker.getTypeAtLocation(tsCallExpr.expression); + const tsType = this.tsTypeChecker.getTypeAtLocation( + tsCallExpr.expression + ); if ( - this.tsUtils.isInterfaceType(tsType) && - tsType.symbol.name === "NodeRequire" - ) + TsUtils.isInterfaceType(tsType) && + tsType.symbol.name === 'NodeRequire' + ) { this.incrementCounters(tsCallExpr.parent, FaultID.ImportAssignment); + } } } - private handleGenericCallWithNoTypeArgs(callLikeExpr: ts.CallExpression | ts.NewExpression, callSignature: ts.Signature): void { - let tsSyntaxKind = ts.isNewExpression(callLikeExpr) + private handleGenericCallWithNoTypeArgs( + callLikeExpr: ts.CallExpression | ts.NewExpression, + callSignature: ts.Signature + ): void { + const tsSyntaxKind = ts.isNewExpression(callLikeExpr) ? ts.SyntaxKind.Constructor : ts.SyntaxKind.FunctionDeclaration; - let signDecl = this.tsTypeChecker.signatureToSignatureDeclaration( + const signDecl = this.tsTypeChecker.signatureToSignatureDeclaration( callSignature, tsSyntaxKind, undefined, @@ -1758,15 +2047,18 @@ export class TypeScriptLinter { ); if (signDecl?.typeArguments) { - let resolvedTypeArgs = signDecl.typeArguments; - let startTypeArg = callLikeExpr.typeArguments?.length ?? 0; + const resolvedTypeArgs = signDecl.typeArguments; + const startTypeArg = callLikeExpr.typeArguments?.length ?? 0; for (let i = startTypeArg; i < resolvedTypeArgs.length; ++i) { const typeNode = resolvedTypeArgs[i]; - // if compiler infers 'unknown' type there are 2 possible cases: - // 1. Compiler unable to infer type from arguments and use 'unknown' - // 2. Compiler infer 'unknown' from arguments - // We report error in both cases. It is ok because we cannot use 'unknown' - // in ArkTS and already have separate check for it. + + /* + * if compiler infers 'unknown' type there are 2 possible cases: + * 1. Compiler unable to infer type from arguments and use 'unknown' + * 2. Compiler infer 'unknown' from arguments + * We report error in both cases. It is ok because we cannot use 'unknown' + * in ArkTS and already have separate check for it. + */ if (typeNode.kind === ts.SyntaxKind.UnknownKeyword) { this.incrementCounters(callLikeExpr, FaultID.GenericCallNoTypeArgs); break; @@ -1775,7 +2067,10 @@ export class TypeScriptLinter { } } - private handleStructIdentAndUndefinedInArgs(tsCallOrNewExpr: ts.CallExpression | ts.NewExpression, callSignature: ts.Signature): void { + private handleStructIdentAndUndefinedInArgs( + tsCallOrNewExpr: ts.CallExpression | ts.NewExpression, + callSignature: ts.Signature + ): void { if (!tsCallOrNewExpr.arguments) { return; } @@ -1784,15 +2079,22 @@ export class TypeScriptLinter { argIndex < tsCallOrNewExpr.arguments.length; ++argIndex ) { - let tsArg = tsCallOrNewExpr.arguments[argIndex]; - let tsArgType = this.tsTypeChecker.getTypeAtLocation(tsArg); - if (!tsArgType) continue; + const tsArg = tsCallOrNewExpr.arguments[argIndex]; + const tsArgType = this.tsTypeChecker.getTypeAtLocation(tsArg); + if (!tsArgType) { + continue; + } - let paramIndex = argIndex < callSignature.parameters.length ? argIndex : callSignature.parameters.length - 1; - let tsParamSym = callSignature.parameters[paramIndex]; - if (!tsParamSym) continue; + const paramIndex = + argIndex < callSignature.parameters.length ? + argIndex : + callSignature.parameters.length - 1; + const tsParamSym = callSignature.parameters[paramIndex]; + if (!tsParamSym) { + continue; + } - let tsParamDecl = tsParamSym.valueDeclaration; + const tsParamDecl = tsParamSym.valueDeclaration; if (tsParamDecl && ts.isParameter(tsParamDecl)) { let tsParamType = this.tsTypeChecker.getTypeOfSymbolAtLocation( tsParamSym, @@ -1800,14 +2102,23 @@ export class TypeScriptLinter { ); if ( tsParamDecl.dotDotDotToken && - this.tsUtils.isGenericArrayType(tsParamType) && + TsUtils.isGenericArrayType(tsParamType) && tsParamType.typeArguments - ) + ) { tsParamType = tsParamType.typeArguments[0]; + } - if (!tsParamType) continue; + if (!tsParamType) { + continue; + } - if (this.tsUtils.needToDeduceStructuralIdentity(tsParamType, tsArgType, tsArg)) { + if ( + this.tsUtils.needToDeduceStructuralIdentity( + tsParamType, + tsArgType, + tsArg + ) + ) { this.incrementCounters(tsArg, FaultID.StructuralIdentity); } } @@ -1816,161 +2127,192 @@ export class TypeScriptLinter { private checkLimitedStdLib(node: ts.Node, symbol: ts.Symbol): void { const parName = this.tsUtils.getParentSymbolName(symbol); - const res = parName ? TsUtils.LIMITED_STD_API.get(parName) : undefined; - if (res && res.arr.includes(symbol.name)) { - this.incrementCounters(node, res.fault); + const entries = LIMITED_STD_API.get(parName); + if (!entries) { return; } - const name = this.tsTypeChecker.getFullyQualifiedName(symbol); - if (TsUtils.LIMITED_STD_GLOBAL_API.includes(name)) { - this.incrementCounters(node, FaultID.LimitedStdLibApi); - return; + for (const entry of entries) { + if (entry.api.includes(symbol.name)) { + this.incrementCounters(node, entry.faultId); + return; + } } } - private findNonFilteringRangesFunctionCalls(callExpr: ts.CallExpression): { begin: number, end: number }[] { - let args = callExpr.arguments; - let result: { begin: number, end: number }[] = []; + private static findNonFilteringRangesFunctionCalls( + callExpr: ts.CallExpression + ): { begin: number; end: number }[] { + const args = callExpr.arguments; + const result: { begin: number; end: number }[] = []; for (const arg of args) { if (ts.isArrowFunction(arg)) { - let arrowFuncExpr = arg as ts.ArrowFunction; - result.push({begin: arrowFuncExpr.body.pos, end: arrowFuncExpr.body.end}); + const arrowFuncExpr = arg; + result.push({ + begin: arrowFuncExpr.body.pos, + end: arrowFuncExpr.body.end, + }); } else if (ts.isCallExpression(arg)) { - result.push({begin: arg.arguments.pos, end: arg.arguments.end}); + result.push({ begin: arg.arguments.pos, end: arg.arguments.end }); } // there may be other cases } return result; } - private handleLibraryTypeCall(callExpr: ts.CallExpression, calleeType: ts.Type): void { - let inLibCall = this.tsUtils.isLibraryType(calleeType); + private handleLibraryTypeCall(callExpr: ts.CallExpression): void { + const calleeType = this.tsTypeChecker.getTypeAtLocation( + callExpr.expression + ); + const inLibCall = this.tsUtils.isLibraryType(calleeType); const diagnosticMessages: Array = []; - this.libraryTypeCallDiagnosticChecker.configure(inLibCall, diagnosticMessages); + this.libraryTypeCallDiagnosticChecker.configure( + inLibCall, + diagnosticMessages + ); - let nonFilteringRanges = this.findNonFilteringRangesFunctionCalls(callExpr); - let rangesToFilter: { begin: number, end: number }[] = []; + const nonFilteringRanges = + TypeScriptLinter.findNonFilteringRangesFunctionCalls(callExpr); + const rangesToFilter: { begin: number; end: number }[] = []; if (nonFilteringRanges.length !== 0) { - let rangesSize = nonFilteringRanges.length; - rangesToFilter.push({ begin: callExpr.pos, end: nonFilteringRanges[0].begin }); - rangesToFilter.push({ begin: nonFilteringRanges[rangesSize - 1].end, end: callExpr.end }); + const rangesSize = nonFilteringRanges.length; + rangesToFilter.push({ + begin: callExpr.arguments.pos, + end: nonFilteringRanges[0].begin, + }); + rangesToFilter.push({ + begin: nonFilteringRanges[rangesSize - 1].end, + end: callExpr.arguments.end, + }); for (let i = 0; i < rangesSize - 1; i++) { - rangesToFilter.push({ begin: nonFilteringRanges[i].end, end: nonFilteringRanges[i + 1].begin }); + rangesToFilter.push({ + begin: nonFilteringRanges[i].end, + end: nonFilteringRanges[i + 1].begin, + }); } } else { - rangesToFilter.push({ begin: callExpr.pos, end: callExpr.end }); - } - - this.filterStrictDiagnostics({ - [ARGUMENT_OF_TYPE_0_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_ERROR_CODE]: (pos: number) => { - return this.checkInRange(rangesToFilter, pos); - }, + rangesToFilter.push({ + begin: callExpr.arguments.pos, + end: callExpr.arguments.end, + }); + } + + this.filterStrictDiagnostics( + { + [ARGUMENT_OF_TYPE_0_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_ERROR_CODE]: + (pos: number) => { + return TypeScriptLinter.checkInRange(rangesToFilter, pos); + }, [TYPE_0_IS_NOT_ASSIGNABLE_TO_TYPE_1_ERROR_CODE]: (pos: number) => { - return this.checkInRange(rangesToFilter, pos); - } + return TypeScriptLinter.checkInRange(rangesToFilter, pos); + }, }, this.libraryTypeCallDiagnosticChecker ); for (const msgChain of diagnosticMessages) { - TypeScriptLinter.filteredDiagnosticMessages.add(msgChain) + TypeScriptLinter.filteredDiagnosticMessages.add(msgChain); } } - private handleNewExpression(node: ts.Node) { - let tsNewExpr = node as ts.NewExpression; - let callSignature = this.tsTypeChecker.getResolvedSignature(tsNewExpr); + private handleNewExpression(node: ts.Node): void { + const tsNewExpr = node as ts.NewExpression; + const callSignature = this.tsTypeChecker.getResolvedSignature(tsNewExpr); if (callSignature !== undefined) { this.handleStructIdentAndUndefinedInArgs(tsNewExpr, callSignature); this.handleGenericCallWithNoTypeArgs(tsNewExpr, callSignature); } } - private handleAsExpression(node: ts.Node) { - let tsAsExpr = node as ts.AsExpression; - if (tsAsExpr.type.getText() === "const") + private handleAsExpression(node: ts.Node): void { + const tsAsExpr = node as ts.AsExpression; + if (tsAsExpr.type.getText() === 'const') { this.incrementCounters(node, FaultID.ConstAssertion); + } - let targetType = this.tsTypeChecker.getTypeAtLocation(tsAsExpr.type).getNonNullableType(); - let exprType = this.tsTypeChecker.getTypeAtLocation(tsAsExpr.expression).getNonNullableType(); - if (this.tsUtils.needToDeduceStructuralIdentity(targetType, exprType, tsAsExpr.expression, true)) { + const targetType = this.tsTypeChecker + .getTypeAtLocation(tsAsExpr.type) + .getNonNullableType(); + const exprType = this.tsTypeChecker + .getTypeAtLocation(tsAsExpr.expression) + .getNonNullableType(); + if ( + this.tsUtils.needToDeduceStructuralIdentity( + targetType, + exprType, + tsAsExpr.expression, + true + ) + ) { this.incrementCounters(tsAsExpr, FaultID.StructuralIdentity); } // check for rule#65: 'number as Number' and 'boolean as Boolean' are disabled if ( - (this.tsUtils.isNumberType(exprType) && - targetType.getSymbol()?.getName() === "Number") || - (this.tsUtils.isBooleanType(exprType) && - targetType.getSymbol()?.getName() === "Boolean") - ) + (TsUtils.isNumberType(exprType) && + targetType.getSymbol()?.getName() === 'Number') || + (TsUtils.isBooleanType(exprType) && + targetType.getSymbol()?.getName() === 'Boolean') + ) { this.incrementCounters(node, FaultID.TypeAssertion); + } } - private handleTypeReference(node: ts.Node) { + private handleTypeReference(node: ts.Node): void { const typeRef = node as ts.TypeReferenceNode; - const isESObject = this.tsUtils.isEsObjectType(typeRef); - const isPossiblyValidContext = this.tsUtils.isEsObjectPossiblyAllowed(typeRef); + const isESObject = TsUtils.isEsObjectType(typeRef); + const isPossiblyValidContext = TsUtils.isEsObjectPossiblyAllowed(typeRef); if (isESObject && !isPossiblyValidContext) { this.incrementCounters(node, FaultID.EsObjectType); return; } - const typeName = this.tsUtils.entityNameToString(typeRef.typeName); - const isStdUtilityType = TsUtils.LIMITED_STANDARD_UTILITY_TYPES.includes(typeName); + const isStdUtilityType = + TsUtils.LIMITED_STANDARD_UTILITY_TYPES.includes(typeName); if (isStdUtilityType) { this.incrementCounters(node, FaultID.UtilityType); return; } // Using Partial type is allowed only when its argument type is either Class or Interface. - const isStdPartial = this.tsUtils.entityNameToString(typeRef.typeName) === 'Partial'; - const hasSingleTypeArgument = !!typeRef.typeArguments && typeRef.typeArguments.length === 1; - const firstTypeArg = !!typeRef.typeArguments && hasSingleTypeArgument && typeRef.typeArguments[0]; - const argType = firstTypeArg && this.tsTypeChecker.getTypeFromTypeNode(firstTypeArg); + const isStdPartial = + this.tsUtils.entityNameToString(typeRef.typeName) === 'Partial'; + const hasSingleTypeArgument = + !!typeRef.typeArguments && typeRef.typeArguments.length === 1; + const firstTypeArg = + !!typeRef.typeArguments && + hasSingleTypeArgument && + typeRef.typeArguments[0]; + const argType = + firstTypeArg && this.tsTypeChecker.getTypeFromTypeNode(firstTypeArg); if (isStdPartial && argType && !argType.isClassOrInterface()) { this.incrementCounters(node, FaultID.UtilityType); - return; } } - private handleMetaProperty(node: ts.Node) { - let tsMetaProperty = node as ts.MetaProperty; + private handleMetaProperty(node: ts.Node): void { + const tsMetaProperty = node as ts.MetaProperty; if (tsMetaProperty.name.text === 'target') { this.incrementCounters(node, FaultID.NewTarget); } } - private handleStructDeclaration(node: ts.Node) { - node.forEachChild((child) => { - // Skip synthetic constructor in Struct declaration. - if (!ts.isConstructorDeclaration(child)) this.visitTSNode(child); - }); - } - - private handleSpreadOp(node: ts.Node) { - // spread assignment is disabled - // spread element is allowed only for arrays as rest parameter + private handleSpreadOp(node: ts.Node): void { + /* + * spread assignment is disabled + * spread element is allowed only for arrays as rest parameter + */ if (ts.isSpreadElement(node)) { - let spreadElemNode = node as ts.SpreadElement; - let spreadExprType = this.tsTypeChecker.getTypeAtLocation( + const spreadElemNode = node; + const spreadExprType = this.tsTypeChecker.getTypeAtLocation( spreadElemNode.expression ); if (spreadExprType) { - const spreadExprTypeNode = this.tsTypeChecker.typeToTypeNode( - spreadExprType, - undefined, - ts.NodeBuilderFlags.None - ); if ( - spreadExprTypeNode !== undefined && - (ts.isCallLikeExpression(node.parent) || - ts.isArrayLiteralExpression(node.parent)) + ts.isCallLikeExpression(node.parent) || + ts.isArrayLiteralExpression(node.parent) ) { if ( - ts.isArrayTypeNode(spreadExprTypeNode) || - this.tsUtils.isTypedArray(spreadExprTypeNode) || - this.tsUtils.isDerivedFrom(spreadExprType, CheckType.Array) + this.tsUtils.isOrDerivedFrom(spreadExprType, this.tsUtils.isArray) ) { return; } @@ -1980,7 +2322,7 @@ export class TypeScriptLinter { this.incrementCounters(node, FaultID.SpreadOperator); } - private handleConstructSignature(node: ts.Node) { + private handleConstructSignature(node: ts.Node): void { switch (node.parent.kind) { case ts.SyntaxKind.TypeLiteral: this.incrementCounters(node, FaultID.ConstructorType); @@ -1989,93 +2331,143 @@ export class TypeScriptLinter { this.incrementCounters(node, FaultID.ConstructorIface); break; default: - return; } } - private handleComments(node: ts.Node) { - // Note: Same comment may be owned by several nodes if their - // start/end position matches. Thus, look for the most parental - // owner of the specific comment (by the node's position). + private handleComments(node: ts.Node): void { + /* + * Note: Same comment may be owned by several nodes if their + * start/end position matches. Thus, look for the most parental + * owner of the specific comment (by the node's position). + */ const srcText = node.getSourceFile().getFullText(); const parent = node.parent; if (!parent || parent.getFullStart() !== node.getFullStart()) { - let leadingComments = ts.getLeadingCommentRanges( + const leadingComments = ts.getLeadingCommentRanges( srcText, node.getFullStart() ); if (leadingComments) { for (const comment of leadingComments) { - this.checkErrorSuppressingAnnotation(comment, srcText); + /* + * In the real-time linter comment from the first line is double proccessed. + * It may be caused by tsc, but it should be investigated. This is a workaround + */ + if ( + !this.walkedComments.has(comment.pos) && + comment.pos !== comment.end + ) { + this.walkedComments.add(comment.pos); + this.checkErrorSuppressingAnnotation(comment, srcText); + } } } } if (!parent || parent.getEnd() !== node.getEnd()) { - let trailingComments = ts.getTrailingCommentRanges( + const trailingComments = ts.getTrailingCommentRanges( srcText, node.getEnd() ); if (trailingComments) { for (const comment of trailingComments) { - this.checkErrorSuppressingAnnotation(comment, srcText); + /* + * In the real-time linter comment from the first line is double proccessed. + * It may be caused by tsc, but it should be investigated. This is a workaround + */ + if ( + !this.walkedComments.has(comment.pos) && + comment.pos !== comment.end + ) { + this.walkedComments.add(comment.pos); + this.checkErrorSuppressingAnnotation(comment, srcText); + } } } } } - private handleExpressionWithTypeArguments(node: ts.Node) { - let tsTypeExpr = node as ts.ExpressionWithTypeArguments; - let symbol = this.tsUtils.trueSymbolAtLocation(tsTypeExpr.expression); - if (!!symbol && this.tsUtils.isEsObjectSymbol(symbol)) { + private handleExpressionWithTypeArguments(node: ts.Node): void { + const tsTypeExpr = node as ts.ExpressionWithTypeArguments; + const symbol = this.tsUtils.trueSymbolAtLocation(tsTypeExpr.expression); + if (!!symbol && TsUtils.isEsObjectSymbol(symbol)) { this.incrementCounters(tsTypeExpr, FaultID.EsObjectType); } } + private handleComputedPropertyName(node: ts.Node): void { + const computedProperty = node as ts.ComputedPropertyName; + const symbol = this.tsUtils.trueSymbolAtLocation( + computedProperty.expression + ); + if (!!symbol && this.tsUtils.isSymbolIterator(symbol)) { + return; + } + const isEnumMember = + !!symbol && !!(symbol.flags & ts.SymbolFlags.EnumMember); + const type = this.tsTypeChecker.getTypeAtLocation( + computedProperty.expression + ); + const isStringEnumLiteral = + TsUtils.isEnumType(type) && !!(type.flags & ts.TypeFlags.StringLiteral); + if (isEnumMember && isStringEnumLiteral) { + return; + } + this.incrementCounters(node, FaultID.ComputedPropertyName); + } + private checkErrorSuppressingAnnotation( comment: ts.CommentRange, srcText: string - ) { + ): void { const commentContent = comment.kind === ts.SyntaxKind.MultiLineCommentTrivia ? srcText.slice(comment.pos + 2, comment.end - 2) : srcText.slice(comment.pos + 2, comment.end); - - let trimmedContent = commentContent.trim(); + // if comment is multiline end closing '*/' is not at the same line as '@ts-xxx' - do nothing (see #13851) + if (commentContent.endsWith('\n')) { + return; + } + const trimmedContent = commentContent.trim(); if ( - trimmedContent.startsWith("@ts-ignore") || - trimmedContent.startsWith("@ts-nocheck") || - trimmedContent.startsWith("@ts-expect-error") - ) + trimmedContent.startsWith('@ts-ignore') || + trimmedContent.startsWith('@ts-nocheck') || + trimmedContent.startsWith('@ts-expect-error') + ) { this.incrementCounters(comment, FaultID.ErrorSuppression); + } } private handleDecorators( decorators: readonly ts.Decorator[] | undefined ): void { - if (!decorators) return; + if (!decorators) { + return; + } for (const decorator of decorators) { - let decoratorName = ""; - if (ts.isIdentifier(decorator.expression)) + let decoratorName = ''; + if (ts.isIdentifier(decorator.expression)) { decoratorName = decorator.expression.text; - else if ( + } else if ( ts.isCallExpression(decorator.expression) && ts.isIdentifier(decorator.expression.expression) - ) + ) { decoratorName = decorator.expression.expression.text; + } - if (!TsUtils.ARKUI_DECORATORS.includes(decoratorName)) + if (!TsUtils.ARKUI_DECORATORS.includes(decoratorName)) { this.incrementCounters(decorator, FaultID.UnsupportedDecorators); + } } } - private handleGetAccessor(node: ts.Node) { + private handleGetAccessor(node: ts.Node): void { this.handleDecorators((node as ts.GetAccessorDeclaration).decorators); } - private handleSetAccessor(node: ts.Node) { + private handleSetAccessor(node: ts.Node): void { this.handleDecorators((node as ts.SetAccessorDeclaration).decorators); } @@ -2084,55 +2476,72 @@ export class TypeScriptLinter { | ts.VariableDeclaration | ts.PropertyDeclaration | ts.ParameterDeclaration - ) { + ): void { // The type is explicitly specified, no need to check inferred type. - if (decl.type) return; + if (decl.type) { + return; + } - // issue 13161: - // In TypeScript, the catch clause variable must be 'any' or 'unknown' type. Since - // ArkTS doesn't support these types, the type for such variable is simply omitted, - // and we don't report it as an error. - if (ts.isCatchClause(decl.parent)) return; + /* + * issue 13161: + * In TypeScript, the catch clause variable must be 'any' or 'unknown' type. Since + * ArkTS doesn't support these types, the type for such variable is simply omitted, + * and we don't report it as an error. + */ + if (ts.isCatchClause(decl.parent)) { + return; + } // Destructuring declarations are not supported, do not process them. if ( ts.isArrayBindingPattern(decl.name) || ts.isObjectBindingPattern(decl.name) - ) + ) { return; + } - // issue 13987: - // When variable have no type annotation and no initial value, and 'noImplicitAny' - // option is enabled, compiler attempts to infer type from variable references: - // see https://github.com/microsoft/TypeScript/pull/11263. - // In this case, we still want to report the error, since ArkTS doesn't allow - // to omit both type annotation and initializer. - if (((ts.isVariableDeclaration(decl) && ts.isVariableStatement(decl.parent.parent)) || ts.isPropertyDeclaration(decl)) && - !decl.initializer) { + /* + * issue 13987: + * When variable have no type annotation and no initial value, and 'noImplicitAny' + * option is enabled, compiler attempts to infer type from variable references: + * see https://github.com/microsoft/TypeScript/pull/11263. + * In this case, we still want to report the error, since ArkTS doesn't allow + * to omit both type annotation and initializer. + */ + if ( + ((ts.isVariableDeclaration(decl) && + ts.isVariableStatement(decl.parent.parent)) || + ts.isPropertyDeclaration(decl)) && + !decl.initializer + ) { this.incrementCounters(decl, FaultID.AnyType); return; } const type = this.tsTypeChecker.getTypeAtLocation(decl); - if (type) this.validateDeclInferredType(type, decl); + if (type) { + this.validateDeclInferredType(type, decl); + } } private handleDefiniteAssignmentAssertion( decl: ts.VariableDeclaration | ts.PropertyDeclaration - ) { + ): void { if (decl.exclamationToken !== undefined) { this.incrementCounters(decl, FaultID.DefiniteAssignment); } } - private validatedTypesSet = new Set(); + private readonly validatedTypesSet = new Set(); private checkAnyOrUnknownChildNode(node: ts.Node): boolean { - if (node.kind === ts.SyntaxKind.AnyKeyword || - node.kind === ts.SyntaxKind.UnknownKeyword) { + if ( + node.kind === ts.SyntaxKind.AnyKeyword || + node.kind === ts.SyntaxKind.UnknownKeyword + ) { return true; } - for (let child of node.getChildren()) { + for (const child of node.getChildren()) { if (this.checkAnyOrUnknownChildNode(child)) { return true; } @@ -2142,9 +2551,14 @@ export class TypeScriptLinter { private handleInferredObjectreference( type: ts.Type, - decl: ts.VariableDeclaration | ts.PropertyDeclaration | ts.ParameterDeclaration - ) { - const typeArgs = this.tsTypeChecker.getTypeArguments(type as ts.TypeReference); + decl: + | ts.VariableDeclaration + | ts.PropertyDeclaration + | ts.ParameterDeclaration + ): void { + const typeArgs = this.tsTypeChecker.getTypeArguments( + type as ts.TypeReference + ); if (typeArgs) { const haveAnyOrUnknownNodes = this.checkAnyOrUnknownChildNode(decl); if (!haveAnyOrUnknownNodes) { @@ -2165,9 +2579,10 @@ export class TypeScriptLinter { if (type.aliasSymbol !== undefined) { return; } - const isObject = type.flags & ts.TypeFlags.Object; - const isReference = (type as ts.ObjectType).objectFlags & ts.ObjectFlags.Reference; - if (isObject && isReference) { + if ( + TsUtils.isObjectType(type) && + !!(type.objectFlags & ts.ObjectFlags.Reference) + ) { this.handleInferredObjectreference(type, decl); return; } @@ -2176,19 +2591,120 @@ export class TypeScriptLinter { } if (type.isUnion()) { this.validatedTypesSet.add(type); - for (let unionElem of type.types) { - this.validateDeclInferredType(unionElem, decl) + for (const unionElem of type.types) { + this.validateDeclInferredType(unionElem, decl); } } - if (this.tsUtils.isAnyType(type)) + if (TsUtils.isAnyType(type)) { this.incrementCounters(decl, FaultID.AnyType); - else if (this.tsUtils.isUnknownType(type)) + } else if (TsUtils.isUnknownType(type)) { this.incrementCounters(decl, FaultID.UnknownType); + } + } + + private handleCommentDirectives(sourceFile: ts.SourceFile): void { + /* + * We use a dirty hack to retrieve list of parsed comment directives by accessing + * internal properties of SourceFile node. + */ + + // Handle comment directive '@ts-nocheck' + const pragmas = (sourceFile as any).pragmas; + if (pragmas && pragmas instanceof Map) { + const noCheckPragma = pragmas.get('ts-nocheck'); + if (noCheckPragma) { + /* + * The value is either a single entry or an array of entries. + * Wrap up single entry with array to simplify processing. + */ + const noCheckEntries: any[] = Array.isArray(noCheckPragma) ? + noCheckPragma : + [noCheckPragma]; + for (const entry of noCheckEntries) { + this.processNoCheckEntry(entry); + } + } + } + + // Handle comment directives '@ts-ignore' and '@ts-expect-error' + const commentDirectives = (sourceFile as any).commentDirectives; + if (commentDirectives && Array.isArray(commentDirectives)) { + for (const directive of commentDirectives) { + if ( + directive.range?.pos === undefined || + directive.range?.end === undefined + ) { + continue; + } + + const range = directive.range as ts.TextRange; + const commentOpen = '/*'; + const kind: ts.SyntaxKind = + sourceFile.text.slice(range.pos, range.pos + commentOpen.length) === commentOpen ? + ts.SyntaxKind.MultiLineCommentTrivia : + ts.SyntaxKind.SingleLineCommentTrivia; + const commentRange: ts.CommentRange = { + pos: range.pos, + end: range.end, + kind, + }; + + this.incrementCounters(commentRange, FaultID.ErrorSuppression); + } + } + } + + private processNoCheckEntry(entry: any): void { + if ( + entry.range?.kind === undefined || + entry.range?.pos === undefined || + entry.range?.end === undefined + ) { + return; + } + + this.incrementCounters( + entry.range as ts.CommentRange, + FaultID.ErrorSuppression + ); + } + + private reportThisKeywordsInScope(scope: ts.Block | ts.Expression): void { + const callback = (node: ts.Node): void => { + if (node.kind === ts.SyntaxKind.ThisKeyword) { + this.incrementCounters(node, FaultID.FunctionContainsThis); + } + }; + const stopCondition = (node: ts.Node): boolean => { + const isClassLike = + ts.isClassDeclaration(node) || ts.isClassExpression(node); + const isFunctionLike = + ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node); + const isModuleDecl = ts.isModuleDeclaration(node); + return isClassLike || isFunctionLike || isModuleDecl; + }; + this.forEachNodeInSubtree(scope, callback, stopCondition); + } + + private static hasPredecessor( + node: ts.Node, + predicate: (node: ts.Node) => boolean + ): boolean { + let parent = node.parent; + while (parent !== undefined) { + if (predicate(parent)) { + return true; + } + parent = parent.parent; + } + return false; } - public lint(sourceFile: ts.SourceFile) { + lint(sourceFile: ts.SourceFile): void { + this.walkedComments.clear(); this.sourceFile = sourceFile; - this.visitTSNode(this.sourceFile); + this.visitSourceFile(this.sourceFile); + this.handleCommentDirectives(this.sourceFile); } } diff --git a/ets2panda/linter-4.2/src/TypeScriptLinterConfig.ts b/ets2panda/linter-4.2/src/TypeScriptLinterConfig.ts index 1639e5664f9758c32dc413580caf9e94ec5956d9..44d9a44d19a060f6dd087c3be6c86a970ff5b3fd 100644 --- a/ets2panda/linter-4.2/src/TypeScriptLinterConfig.ts +++ b/ets2panda/linter-4.2/src/TypeScriptLinterConfig.ts @@ -19,27 +19,29 @@ import { FaultID } from './Problems'; export class LinterConfig { static nodeDesc: string[] = []; - // The SyntaxKind enum defines additional elements at the end of the enum - // that serve as markers (FirstX/LastX). Those elements are initialized - // with indices of the previously defined elements. As result, the enum - // may return incorrect name for a certain kind index (e.g. 'FirstStatement' - // instead of 'VariableStatement'). - // The following code creates a map with correct syntax kind names. - // It can be used when need to print name of syntax kind of certain - // AST node in diagnostic messages. + /* + * The SyntaxKind enum defines additional elements at the end of the enum + * that serve as markers (FirstX/LastX). Those elements are initialized + * with indices of the previously defined elements. As result, the enum + * may return incorrect name for a certain kind index (e.g. 'FirstStatement' + * instead of 'VariableStatement'). + * The following code creates a map with correct syntax kind names. + * It can be used when need to print name of syntax kind of certain + * AST node in diagnostic messages. + */ static tsSyntaxKindNames: string[] = []; // Use static init method, as TypeScript 4.2 doesn't support static blocks. - static initStatic() { + static initStatic(): void { // Set the feature descriptions (for the output). LinterConfig.nodeDesc[FaultID.AnyType] = '"any" type'; LinterConfig.nodeDesc[FaultID.SymbolType] = '"symbol" type'; - LinterConfig.nodeDesc[FaultID.ObjectLiteralNoContextType] = 'Object literals with no context Class or Interface type'; + LinterConfig.nodeDesc[FaultID.ObjectLiteralNoContextType] = + 'Object literals with no context Class or Interface type'; LinterConfig.nodeDesc[FaultID.ArrayLiteralNoContextType] = 'Array literals with no context Array type'; LinterConfig.nodeDesc[FaultID.ComputedPropertyName] = 'Computed properties'; LinterConfig.nodeDesc[FaultID.LiteralAsPropertyName] = 'String or integer literal as property name'; LinterConfig.nodeDesc[FaultID.TypeQuery] = '"typeof" operations'; - LinterConfig.nodeDesc[FaultID.RegexLiteral] = 'regex literals'; LinterConfig.nodeDesc[FaultID.IsOperator] = '"is" operations'; LinterConfig.nodeDesc[FaultID.DestructuringParameter] = 'destructuring parameters'; LinterConfig.nodeDesc[FaultID.YieldExpression] = '"yield" operations'; @@ -92,8 +94,6 @@ export class LinterConfig { LinterConfig.nodeDesc[FaultID.ThisType] = '"this" type'; LinterConfig.nodeDesc[FaultID.IntefaceExtendDifProps] = 'Extends same properties with different types'; LinterConfig.nodeDesc[FaultID.StructuralIdentity] = 'Use of type structural identity'; - LinterConfig.nodeDesc[FaultID.TypeOnlyImport] = 'Type-only imports'; - LinterConfig.nodeDesc[FaultID.TypeOnlyExport] = 'Type-only exports'; LinterConfig.nodeDesc[FaultID.DefaultImport] = 'Default import declarations'; LinterConfig.nodeDesc[FaultID.ExportAssignment] = 'Export assignments (export = ..)'; LinterConfig.nodeDesc[FaultID.ImportAssignment] = 'Import assignments (import = ..)'; @@ -109,7 +109,8 @@ export class LinterConfig { LinterConfig.nodeDesc[FaultID.GlobalThis] = 'Use of globalThis'; LinterConfig.nodeDesc[FaultID.UtilityType] = 'Standard Utility types'; LinterConfig.nodeDesc[FaultID.PropertyDeclOnFunction] = 'Property declaration on function'; - LinterConfig.nodeDesc[FaultID.FunctionApplyBindCall] = 'Invoking methods of function objects'; + LinterConfig.nodeDesc[FaultID.FunctionApplyCall] = 'Invoking methods of function objects'; + LinterConfig.nodeDesc[FaultID.FunctionBind] = 'Invoking methods of function objects'; LinterConfig.nodeDesc[FaultID.ConstAssertion] = '"as const" assertion'; LinterConfig.nodeDesc[FaultID.ImportAssertion] = 'Import assertion'; LinterConfig.nodeDesc[FaultID.SpreadOperator] = 'Spread operation'; @@ -119,7 +120,6 @@ export class LinterConfig { LinterConfig.nodeDesc[FaultID.UnsupportedDecorators] = 'Unsupported decorators'; LinterConfig.nodeDesc[FaultID.ImportAfterStatement] = 'Import declaration after other declaration or statement'; LinterConfig.nodeDesc[FaultID.EsObjectType] = 'Restricted "ESObject" type'; - LinterConfig.initTsSyntaxKindNames(); } @@ -138,48 +138,96 @@ export class LinterConfig { // must detect terminals during parsing static terminalTokens: Set = new Set([ - ts.SyntaxKind.OpenBraceToken, ts.SyntaxKind.CloseBraceToken, ts.SyntaxKind.OpenParenToken, - ts.SyntaxKind.CloseParenToken, ts.SyntaxKind.OpenBracketToken, ts.SyntaxKind.CloseBracketToken, - ts.SyntaxKind.DotToken, ts.SyntaxKind.DotDotDotToken, ts.SyntaxKind.SemicolonToken, ts.SyntaxKind.CommaToken, - ts.SyntaxKind.QuestionDotToken, ts.SyntaxKind.LessThanToken, ts.SyntaxKind.LessThanSlashToken, - ts.SyntaxKind.GreaterThanToken, ts.SyntaxKind.LessThanEqualsToken, ts.SyntaxKind.GreaterThanEqualsToken, - ts.SyntaxKind.EqualsEqualsToken, ts.SyntaxKind.ExclamationEqualsToken, ts.SyntaxKind.EqualsEqualsEqualsToken, - ts.SyntaxKind.ExclamationEqualsEqualsToken, ts.SyntaxKind.EqualsGreaterThanToken, ts.SyntaxKind.PlusToken, - ts.SyntaxKind.MinusToken, ts.SyntaxKind.AsteriskToken, ts.SyntaxKind.AsteriskAsteriskToken, - ts.SyntaxKind.SlashToken, ts.SyntaxKind.PercentToken, ts.SyntaxKind.PlusPlusToken, ts.SyntaxKind.MinusMinusToken, - ts.SyntaxKind.LessThanLessThanToken, ts.SyntaxKind.GreaterThanGreaterThanToken, - ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken, ts.SyntaxKind.AmpersandToken, ts.SyntaxKind.BarToken, - ts.SyntaxKind.CaretToken, ts.SyntaxKind.ExclamationToken, ts.SyntaxKind.TildeToken, - ts.SyntaxKind.AmpersandAmpersandToken, ts.SyntaxKind.BarBarToken, ts.SyntaxKind.QuestionQuestionToken, - ts.SyntaxKind.QuestionToken, ts.SyntaxKind.ColonToken, ts.SyntaxKind.AtToken, ts.SyntaxKind.BacktickToken, - ts.SyntaxKind.EqualsToken, ts.SyntaxKind.PlusEqualsToken, ts.SyntaxKind.MinusEqualsToken, - ts.SyntaxKind.AsteriskEqualsToken, ts.SyntaxKind.AsteriskAsteriskEqualsToken, ts.SyntaxKind.SlashEqualsToken, - ts.SyntaxKind.PercentEqualsToken, ts.SyntaxKind.LessThanLessThanEqualsToken, - ts.SyntaxKind.GreaterThanGreaterThanEqualsToken, ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken, - ts.SyntaxKind.AmpersandEqualsToken, ts.SyntaxKind.BarEqualsToken, ts.SyntaxKind.CaretEqualsToken, - ts.SyntaxKind.EndOfFileToken, ts.SyntaxKind.SingleLineCommentTrivia, - ts.SyntaxKind.MultiLineCommentTrivia, ts.SyntaxKind.NewLineTrivia, ts.SyntaxKind.WhitespaceTrivia, - ts.SyntaxKind.ShebangTrivia, /* We detect and preserve #! on the first line */ ts.SyntaxKind.ConflictMarkerTrivia, + ts.SyntaxKind.OpenBraceToken, + ts.SyntaxKind.CloseBraceToken, + ts.SyntaxKind.OpenParenToken, + ts.SyntaxKind.CloseParenToken, + ts.SyntaxKind.OpenBracketToken, + ts.SyntaxKind.CloseBracketToken, + ts.SyntaxKind.DotToken, + ts.SyntaxKind.DotDotDotToken, + ts.SyntaxKind.SemicolonToken, + ts.SyntaxKind.CommaToken, + ts.SyntaxKind.QuestionDotToken, + ts.SyntaxKind.LessThanToken, + ts.SyntaxKind.LessThanSlashToken, + ts.SyntaxKind.GreaterThanToken, + ts.SyntaxKind.LessThanEqualsToken, + ts.SyntaxKind.GreaterThanEqualsToken, + ts.SyntaxKind.EqualsEqualsToken, + ts.SyntaxKind.ExclamationEqualsToken, + ts.SyntaxKind.EqualsEqualsEqualsToken, + ts.SyntaxKind.ExclamationEqualsEqualsToken, + ts.SyntaxKind.EqualsGreaterThanToken, + ts.SyntaxKind.PlusToken, + ts.SyntaxKind.MinusToken, + ts.SyntaxKind.AsteriskToken, + ts.SyntaxKind.AsteriskAsteriskToken, + ts.SyntaxKind.SlashToken, + ts.SyntaxKind.PercentToken, + ts.SyntaxKind.PlusPlusToken, + ts.SyntaxKind.MinusMinusToken, + ts.SyntaxKind.LessThanLessThanToken, + ts.SyntaxKind.GreaterThanGreaterThanToken, + ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken, + ts.SyntaxKind.AmpersandToken, + ts.SyntaxKind.BarToken, + ts.SyntaxKind.CaretToken, + ts.SyntaxKind.ExclamationToken, + ts.SyntaxKind.TildeToken, + ts.SyntaxKind.AmpersandAmpersandToken, + ts.SyntaxKind.BarBarToken, + ts.SyntaxKind.QuestionQuestionToken, + ts.SyntaxKind.QuestionToken, + ts.SyntaxKind.ColonToken, + ts.SyntaxKind.AtToken, + ts.SyntaxKind.BacktickToken, + ts.SyntaxKind.EqualsToken, + ts.SyntaxKind.PlusEqualsToken, + ts.SyntaxKind.MinusEqualsToken, + ts.SyntaxKind.AsteriskEqualsToken, + ts.SyntaxKind.AsteriskAsteriskEqualsToken, + ts.SyntaxKind.SlashEqualsToken, + ts.SyntaxKind.PercentEqualsToken, + ts.SyntaxKind.LessThanLessThanEqualsToken, + ts.SyntaxKind.GreaterThanGreaterThanEqualsToken, + ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken, + ts.SyntaxKind.AmpersandEqualsToken, + ts.SyntaxKind.BarEqualsToken, + ts.SyntaxKind.CaretEqualsToken, + ts.SyntaxKind.EndOfFileToken, + ts.SyntaxKind.SingleLineCommentTrivia, + ts.SyntaxKind.MultiLineCommentTrivia, + ts.SyntaxKind.NewLineTrivia, + ts.SyntaxKind.WhitespaceTrivia, + // We detect and preserve #! on the first line + ts.SyntaxKind.ShebangTrivia, + ts.SyntaxKind.ConflictMarkerTrivia ]); // tokens which can be reported without additional parsing static incrementOnlyTokens: Map = new Map([ - [ts.SyntaxKind.AnyKeyword, FaultID.AnyType], [ts.SyntaxKind.SymbolKeyword, FaultID.SymbolType], + [ts.SyntaxKind.AnyKeyword, FaultID.AnyType], + [ts.SyntaxKind.SymbolKeyword, FaultID.SymbolType], [ts.SyntaxKind.ThisType, FaultID.ThisType], - [ts.SyntaxKind.ComputedPropertyName, FaultID.ComputedPropertyName], [ts.SyntaxKind.TypeQuery, FaultID.TypeQuery], [ts.SyntaxKind.DeleteExpression, FaultID.DeleteOperator], - [ts.SyntaxKind.RegularExpressionLiteral, FaultID.RegexLiteral], - [ts.SyntaxKind.TypePredicate, FaultID.IsOperator], [ts.SyntaxKind.YieldExpression, FaultID.YieldExpression], - [ts.SyntaxKind.IndexSignature, FaultID.IndexMember], [ts.SyntaxKind.WithStatement, FaultID.WithStatement], - [ts.SyntaxKind.IndexedAccessType, FaultID.IndexedAccessType],[ts.SyntaxKind.UnknownKeyword, FaultID.UnknownType], - [ts.SyntaxKind.InKeyword, FaultID.InOperator], [ts.SyntaxKind.CallSignature, FaultID.CallSignature], + [ts.SyntaxKind.TypePredicate, FaultID.IsOperator], + [ts.SyntaxKind.YieldExpression, FaultID.YieldExpression], + [ts.SyntaxKind.IndexSignature, FaultID.IndexMember], + [ts.SyntaxKind.WithStatement, FaultID.WithStatement], + [ts.SyntaxKind.IndexedAccessType, FaultID.IndexedAccessType], + [ts.SyntaxKind.UnknownKeyword, FaultID.UnknownType], + [ts.SyntaxKind.CallSignature, FaultID.CallSignature], [ts.SyntaxKind.IntersectionType, FaultID.IntersectionType], - [ts.SyntaxKind.TypeLiteral, FaultID.ObjectTypeLiteral], [ts.SyntaxKind.ConstructorType, FaultID.ConstructorFuncs], + [ts.SyntaxKind.TypeLiteral, FaultID.ObjectTypeLiteral], + [ts.SyntaxKind.ConstructorType, FaultID.ConstructorFuncs], [ts.SyntaxKind.PrivateIdentifier, FaultID.PrivateIdentifier], - [ts.SyntaxKind.ConditionalType, FaultID.ConditionalType], [ts.SyntaxKind.MappedType, FaultID.MappedType], - [ts.SyntaxKind.JsxElement, FaultID.JsxElement], [ts.SyntaxKind.JsxSelfClosingElement, FaultID.JsxElement], + [ts.SyntaxKind.ConditionalType, FaultID.ConditionalType], + [ts.SyntaxKind.MappedType, FaultID.MappedType], + [ts.SyntaxKind.JsxElement, FaultID.JsxElement], + [ts.SyntaxKind.JsxSelfClosingElement, FaultID.JsxElement], [ts.SyntaxKind.ImportEqualsDeclaration, FaultID.ImportAssignment], - [ts.SyntaxKind.NamespaceExportDeclaration, FaultID.UMDModuleDefinition], + [ts.SyntaxKind.NamespaceExportDeclaration, FaultID.UMDModuleDefinition] ]); } diff --git a/ets2panda/linter-4.2/src/Utils.ts b/ets2panda/linter-4.2/src/Utils.ts index fa383a3e68e71573ba75e0e221a1ab69aa9eaff3..46fe0d28b37aa0bd17127c4eeb7a885cdc94c38a 100644 --- a/ets2panda/linter-4.2/src/Utils.ts +++ b/ets2panda/linter-4.2/src/Utils.ts @@ -15,12 +15,15 @@ import * as path from 'node:path'; import * as ts from 'typescript'; -import { ProblemInfo } from './ProblemInfo'; -import { AutofixInfo } from './AutofixInfo'; -import { LinterConfig } from './TypeScriptLinterConfig' -import { FaultID } from './Problems'; - -export function logTscDiagnostic(diagnostics: readonly ts.Diagnostic[], log: (message: any, ...args: any[]) => void) { +import type { ProblemInfo } from './ProblemInfo'; +import type { AutofixInfo } from './AutofixInfo'; +import { LinterConfig } from './TypeScriptLinterConfig'; +import { forEachNodeInSubtree } from './ForEachNodeInSubtree'; + +export function logTscDiagnostic( + diagnostics: readonly ts.Diagnostic[], + log: (message: any, ...args: any[]) => void +): void { diagnostics.forEach((diagnostic) => { let message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'); @@ -38,7 +41,7 @@ export function encodeProblemInfo(problem: ProblemInfo): string { } export function decodeAutofixInfo(info: string): AutofixInfo { - let infos = info.split('%'); + const infos = info.split('%'); return { problemID: infos[0], start: Number.parseInt(infos[1]), end: Number.parseInt(infos[2]) }; } @@ -53,13 +56,11 @@ export function getNodeOrLineEnd( sourceFile: ts.SourceFile, nodeStartPos: number, nodeEndPos: number, - nodeStartLine: number, + nodeStartLine: number ): number { const pos = sourceFile.getLineAndCharacterOfPosition(nodeEndPos); // TSC counts lines and columns from zero - return (pos.line + 1) === nodeStartLine - ? pos.character - : sourceFile.getLineEndOfPosition(nodeStartPos); + return pos.line + 1 === nodeStartLine ? pos.character : sourceFile.getLineEndOfPosition(nodeStartPos); } export function mergeArrayMaps(lhs: Map, rhs: Map): Map { @@ -95,135 +96,11 @@ export function isAssignmentOperator(tsBinOp: ts.BinaryOperatorToken): boolean { return tsBinOp.kind >= ts.SyntaxKind.FirstAssignment && tsBinOp.kind <= ts.SyntaxKind.LastAssignment; } -export enum CheckType { - Array, - String = "String", - Set = "Set", - Map = "Map", - Error = "Error", -}; +export type CheckType = (this: TsUtils, t: ts.Type) => boolean; export class TsUtils { static readonly ES_OBJECT = 'ESObject'; - private static readonly LIMITED_STD_ARRAYBUFFER_API = [ - // properties - // methods - 'isView' - ]; - - private static readonly LIMITED_STD_OBJECT_API = [ - // properties - '__proto__', - // methods - '__defineGetter__', - '__defineSetter__', - '__lookupGetter__', - '__lookupSetter__', - 'assign', - 'create', - 'defineProperties', - 'defineProperty', - 'freeze', - 'fromEntries', - 'getOwnPropertyDescriptor', - 'getOwnPropertyDescriptors', - 'getOwnPropertySymbols', - 'getPrototypeOf', - 'hasOwnProperty', - 'is', - 'isExtensible', - 'isFrozen', - 'isPrototypeOf', - 'isSealed', - 'preventExtensions', - 'propertyIsEnumerable', - 'seal', - 'setPrototypeOf', - ]; - - private static readonly LIMITED_STD_PROXYHANDLER_API = [ - // properties - // methods - 'apply', - 'construct', - 'defineProperty', - 'deleteProperty', - 'get', - 'getOwnPropertyDescriptor', - 'getPrototypeOf', - 'has', - 'isExtensible', - 'ownKeys', - 'preventExtensions', - 'set', - 'setPrototypeOf' - ]; - - private static readonly LIMITED_STD_REFLECT_API = [ - // properties - // methods - 'apply', - 'construct', - 'defineProperty', - 'deleteProperty', - 'getOwnPropertyDescriptor', - 'getPrototypeOf', - 'isExtensible', - 'preventExtensions', - 'setPrototypeOf', - ]; - - private static readonly LIMITED_STD_SYMBOL_API = [ - 'Symbol', - // properties - 'asyncIterator', - 'description', - 'hasInstance', - 'isConcatSpreadable', - 'match', - 'matchAll', - 'replace', - 'search', - 'species', - 'split', - 'toPrimitive', - 'toStringTag', - 'unscopables', - // methods - 'for', - 'keyFor', - 'toString', - 'valueOf', - ]; - - private static readonly LIMITED_STD_FUNCTION_API = [ - // properties - // methods - 'apply', - 'bind', - 'call', - ]; - - static readonly LIMITED_STD_GLOBAL_API = [ - // properties - // methods - 'eval', - ]; - - static readonly LIMITED_STD_API = new Map, fault: FaultID}>([ - ['Object', {arr: TsUtils.LIMITED_STD_OBJECT_API, fault: FaultID.LimitedStdLibApi}], - ['ObjectConstructor', {arr: TsUtils.LIMITED_STD_OBJECT_API, fault: FaultID.LimitedStdLibApi}], - ['Reflect', {arr: TsUtils.LIMITED_STD_REFLECT_API, fault: FaultID.LimitedStdLibApi}], - ['ProxyHandler', {arr: TsUtils.LIMITED_STD_PROXYHANDLER_API, fault: FaultID.LimitedStdLibApi}], - ['ArrayBuffer', {arr: TsUtils.LIMITED_STD_ARRAYBUFFER_API, fault: FaultID.LimitedStdLibApi}], - ['ArrayBufferConstructor', {arr: TsUtils.LIMITED_STD_ARRAYBUFFER_API, fault: FaultID.LimitedStdLibApi}], - ['Symbol', {arr: TsUtils.LIMITED_STD_SYMBOL_API, fault: FaultID.SymbolType}], - ['SymbolConstructor', {arr: TsUtils.LIMITED_STD_SYMBOL_API, fault: FaultID.SymbolType}], - ['Function', {arr: TsUtils.LIMITED_STD_FUNCTION_API, fault: FaultID.FunctionApplyBindCall}], - ['CallableFunction', {arr: TsUtils.LIMITED_STD_FUNCTION_API, fault: FaultID.FunctionApplyBindCall}], - ]); - static readonly NON_INITIALIZABLE_PROPERTY_DECORATORS = ['Link', 'Consume', 'ObjectLink', 'Prop', 'BuilderParam']; static readonly NON_INITIALIZABLE_PROPERTY_CLASS_DECORATORS = ['CustomDialog']; @@ -235,9 +112,23 @@ export class TsUtils { static readonly FUNCTION_HAS_NO_RETURN_ERROR_CODE = 2366; static readonly LIMITED_STANDARD_UTILITY_TYPES = [ - 'Awaited', 'Pick', 'Omit', 'Exclude', 'Extract', 'NonNullable', 'Parameters', - 'ConstructorParameters', 'ReturnType', 'InstanceType', 'ThisParameterType', 'OmitThisParameter', - 'ThisType', 'Uppercase', 'Lowercase', 'Capitalize', 'Uncapitalize', + 'Awaited', + 'Pick', + 'Omit', + 'Exclude', + 'Extract', + 'NonNullable', + 'Parameters', + 'ConstructorParameters', + 'ReturnType', + 'InstanceType', + 'ThisParameterType', + 'OmitThisParameter', + 'ThisType', + 'Uppercase', + 'Lowercase', + 'Capitalize', + 'Uncapitalize' ]; static readonly ARKUI_DECORATORS = [ @@ -263,24 +154,64 @@ export class TsUtils { 'StorageLink', 'StorageProp', 'Styles', - 'Watch', + 'Watch' ]; static readonly STANDARD_LIBRARIES = [ - "lib.dom.d.ts", "lib.dom.iterable.d.ts", "lib.webworker.d.ts", "lib.webworker.importscripts.d.ts", - "lib.webworker.iterable.d.ts", "lib.scripthost.d.ts", "lib.decorators.d.ts", "lib.decorators.legacy.d.ts", - "lib.es5.d.ts", "lib.es2015.core.d.ts", "lib.es2015.collection.d.ts", "lib.es2015.generator.d.ts", - "lib.es2015.iterable.d.ts", "lib.es2015.promise.d.ts", "lib.es2015.proxy.d.ts", "lib.es2015.reflect.d.ts", - "lib.es2015.symbol.d.ts", "lib.es2015.symbol.wellknown.d.ts", "lib.es2016.array.include.d.ts", - "lib.es2017.object.d.ts", "lib.es2017.sharedmemory.d.ts", "lib.es2017.string.d.ts", "lib.es2017.intl.d.ts", - "lib.es2017.typedarrays.d.ts", "lib.es2018.asyncgenerator.d.ts", "lib.es2018.asynciterable.d.ts", - "lib.es2018.intl.d.ts", "lib.es2018.promise.d.ts", "lib.es2018.regexp.d.ts", "lib.es2019.array.d.ts", - "lib.es2019.object.d.ts", "lib.es2019.string.d.ts", "lib.es2019.symbol.d.ts", "lib.es2019.intl.d.ts", - "lib.es2020.bigint.d.ts", "lib.es2020.date.d.ts", "lib.es2020.promise.d.ts", "lib.es2020.sharedmemory.d.ts", - "lib.es2020.string.d.ts", "lib.es2020.symbol.wellknown.d.ts", "lib.es2020.intl.d.ts", "lib.es2020.number.d.ts", - "lib.es2021.promise.d.ts", "lib.es2021.string.d.ts", "lib.es2021.weakref.d.ts", "lib.es2021.intl.d.ts", - "lib.es2022.array.d.ts", "lib.es2022.error.d.ts", "lib.es2022.intl.d.ts", "lib.es2022.object.d.ts", - "lib.es2022.sharedmemory.d.ts", "lib.es2022.string.d.ts", "lib.es2022.regexp.d.ts", "lib.es2023.array.d.ts", + 'lib.dom.d.ts', + 'lib.dom.iterable.d.ts', + 'lib.webworker.d.ts', + 'lib.webworker.importscripts.d.ts', + 'lib.webworker.iterable.d.ts', + 'lib.scripthost.d.ts', + 'lib.decorators.d.ts', + 'lib.decorators.legacy.d.ts', + 'lib.es5.d.ts', + 'lib.es2015.core.d.ts', + 'lib.es2015.collection.d.ts', + 'lib.es2015.generator.d.ts', + 'lib.es2015.iterable.d.ts', + 'lib.es2015.promise.d.ts', + 'lib.es2015.proxy.d.ts', + 'lib.es2015.reflect.d.ts', + 'lib.es2015.symbol.d.ts', + 'lib.es2015.symbol.wellknown.d.ts', + 'lib.es2016.array.include.d.ts', + 'lib.es2017.object.d.ts', + 'lib.es2017.sharedmemory.d.ts', + 'lib.es2017.string.d.ts', + 'lib.es2017.intl.d.ts', + 'lib.es2017.typedarrays.d.ts', + 'lib.es2018.asyncgenerator.d.ts', + 'lib.es2018.asynciterable.d.ts', + 'lib.es2018.intl.d.ts', + 'lib.es2018.promise.d.ts', + 'lib.es2018.regexp.d.ts', + 'lib.es2019.array.d.ts', + 'lib.es2019.object.d.ts', + 'lib.es2019.string.d.ts', + 'lib.es2019.symbol.d.ts', + 'lib.es2019.intl.d.ts', + 'lib.es2020.bigint.d.ts', + 'lib.es2020.date.d.ts', + 'lib.es2020.promise.d.ts', + 'lib.es2020.sharedmemory.d.ts', + 'lib.es2020.string.d.ts', + 'lib.es2020.symbol.wellknown.d.ts', + 'lib.es2020.intl.d.ts', + 'lib.es2020.number.d.ts', + 'lib.es2021.promise.d.ts', + 'lib.es2021.string.d.ts', + 'lib.es2021.weakref.d.ts', + 'lib.es2021.intl.d.ts', + 'lib.es2022.array.d.ts', + 'lib.es2022.error.d.ts', + 'lib.es2022.intl.d.ts', + 'lib.es2022.object.d.ts', + 'lib.es2022.sharedmemory.d.ts', + 'lib.es2022.string.d.ts', + 'lib.es2022.regexp.d.ts', + 'lib.es2023.array.d.ts' ]; static readonly TYPED_ARRAYS = [ @@ -294,98 +225,89 @@ export class TsUtils { 'Float32Array', 'Float64Array', 'BigInt64Array', - 'BigUint64Array', - ] + 'BigUint64Array' + ]; - static readonly ARKTS_IGNORE_DIRS = ['node_modules', 'oh_modules', 'build', '.preview']; + static readonly ARKTS_IGNORE_DIRS_NO_OH_MODULES = ['node_modules', 'build', '.preview']; + static readonly ARKTS_IGNORE_DIRS_OH_MODULES = 'oh_modules'; + static readonly ARKTS_IGNORE_DIRS = [ + ...TsUtils.ARKTS_IGNORE_DIRS_NO_OH_MODULES, + TsUtils.ARKTS_IGNORE_DIRS_OH_MODULES + ]; static readonly ARKTS_IGNORE_FILES = ['hvigorfile.ts']; - constructor(private tsTypeChecker: ts.TypeChecker, private testMode: boolean) { - } + constructor( + private readonly tsTypeChecker: ts.TypeChecker, + private readonly testMode: boolean + ) {} - public isTypedArray(tsType: ts.TypeNode | undefined): boolean { - if (tsType === undefined || !ts.isTypeReferenceNode(tsType)) { - return false; - } - return TsUtils.TYPED_ARRAYS.includes(this.entityNameToString(tsType.typeName)); - } - - public isType(tsType: ts.TypeNode | undefined, checkType: string): boolean { + isType(tsType: ts.TypeNode | undefined, checkType: string): boolean { if (tsType === undefined || !ts.isTypeReferenceNode(tsType)) { return false; } return this.entityNameToString(tsType.typeName) === checkType; } - public entityNameToString(name: ts.EntityName): string { + entityNameToString(name: ts.EntityName): string { if (ts.isIdentifier(name)) { return name.escapedText.toString(); - } else { - return this.entityNameToString(name.left) + this.entityNameToString(name.right); } + return this.entityNameToString(name.left) + this.entityNameToString(name.right); } - public isNumberType(tsType: ts.Type): boolean { + static isNumberType(tsType: ts.Type): boolean { if (tsType.isUnion()) { - for (let tsCompType of tsType.types) { - if ((tsCompType.flags & ts.TypeFlags.NumberLike) === 0) return false; + for (const tsCompType of tsType.types) { + if ((tsCompType.flags & ts.TypeFlags.NumberLike) === 0) { + return false; + } } return true; } return (tsType.getFlags() & ts.TypeFlags.NumberLike) !== 0; } - public isBooleanType(tsType: ts.Type): boolean { + static isBooleanType(tsType: ts.Type): boolean { return (tsType.getFlags() & ts.TypeFlags.BooleanLike) !== 0; } - public isStringLikeType(tsType: ts.Type): boolean { + static isStringLikeType(tsType: ts.Type): boolean { if (tsType.isUnion()) { - for (let tsCompType of tsType.types) { - if ((tsCompType.flags & ts.TypeFlags.StringLike) === 0) return false; + for (const tsCompType of tsType.types) { + if ((tsCompType.flags & ts.TypeFlags.StringLike) === 0) { + return false; + } } return true; } return (tsType.getFlags() & ts.TypeFlags.StringLike) !== 0; } - public isStringType(type: ts.Type): boolean { + static isStringType(type: ts.Type): boolean { return (type.getFlags() & ts.TypeFlags.String) !== 0; } - public isPrimitiveEnumType(type: ts.Type, primitiveType: ts.TypeFlags): boolean { - const isNonPrimitive = (type.flags & ts.TypeFlags.NonPrimitive) !== 0; - if (!this.isEnumType(type) || !type.isUnion() || isNonPrimitive) { - return false; - } - for (const t of type.types) { - if ((t.flags & primitiveType) === 0) { - return false; - } - } - return true; - } - - public isPrimitiveEnumMemberType(type: ts.Type, primitiveType: ts.TypeFlags): boolean { + static isPrimitiveEnumMemberType(type: ts.Type, primitiveType: ts.TypeFlags): boolean { const isNonPrimitive = (type.flags & ts.TypeFlags.NonPrimitive) !== 0; - if (!this.isEnumMemberType(type) || isNonPrimitive) { + if (!TsUtils.isEnumMemberType(type) || isNonPrimitive) { return false; } return (type.flags & primitiveType) !== 0; } - public unwrapParenthesizedType(tsType: ts.TypeNode): ts.TypeNode { + static unwrapParenthesizedType(tsType: ts.TypeNode): ts.TypeNode { while (ts.isParenthesizedTypeNode(tsType)) { tsType = tsType.type; } return tsType; } - public findParentIf(asExpr: ts.AsExpression): ts.IfStatement | null { + static findParentIf(asExpr: ts.AsExpression): ts.IfStatement | null { let node = asExpr.parent; while (node) { - if (node.kind === ts.SyntaxKind.IfStatement) + if (node.kind === ts.SyntaxKind.IfStatement) { return node as ts.IfStatement; + } node = node.parent; } @@ -393,25 +315,29 @@ export class TsUtils { return null; } - public isDestructuringAssignmentLHS( - tsExpr: ts.ArrayLiteralExpression | ts.ObjectLiteralExpression - ): boolean { - // Check whether given expression is the LHS part of the destructuring - // assignment (or is a nested element of destructuring pattern). + static isDestructuringAssignmentLHS(tsExpr: ts.ArrayLiteralExpression | ts.ObjectLiteralExpression): boolean { + /* + * Check whether given expression is the LHS part of the destructuring + * assignment (or is a nested element of destructuring pattern). + */ let tsParent = tsExpr.parent; let tsCurrentExpr: ts.Node = tsExpr; while (tsParent) { if ( - ts.isBinaryExpression(tsParent) && isAssignmentOperator(tsParent.operatorToken) && + ts.isBinaryExpression(tsParent) && + isAssignmentOperator(tsParent.operatorToken) && tsParent.left === tsCurrentExpr - ) + ) { return true; + } if ( (ts.isForStatement(tsParent) || ts.isForInStatement(tsParent) || ts.isForOfStatement(tsParent)) && - tsParent.initializer && tsParent.initializer === tsCurrentExpr - ) + tsParent.initializer && + tsParent.initializer === tsCurrentExpr + ) { return true; + } tsCurrentExpr = tsParent; tsParent = tsParent.parent; @@ -420,57 +346,70 @@ export class TsUtils { return false; } - public isEnumType(tsType: ts.Type): boolean { - // Note: For some reason, test (tsType.flags & ts.TypeFlags.Enum) !== 0 doesn't work here. - // Must use SymbolFlags to figure out if this is an enum type. - return tsType.symbol && (tsType.symbol.flags & ts.SymbolFlags.Enum) !== 0; + static isEnumType(tsType: ts.Type): boolean { + // when type equals `typeof `, only symbol contains information about it's type. + const isEnumSymbol = tsType.symbol && this.isEnum(tsType.symbol); + // otherwise, we should analyze flags of the type itself + const isEnumType = !!(tsType.flags & ts.TypeFlags.Enum) || !!(tsType.flags & ts.TypeFlags.EnumLiteral); + return isEnumSymbol || isEnumType; } - public isEnumMemberType(tsType: ts.Type): boolean { - // Note: For some reason, test (tsType.flags & ts.TypeFlags.Enum) != 0 doesn't work here. - // Must use SymbolFlags to figure out if this is an enum type. + static isEnum(tsSymbol: ts.Symbol): boolean { + return !!(tsSymbol.flags & ts.SymbolFlags.Enum); + } + + static isEnumMemberType(tsType: ts.Type): boolean { + /* + * Note: For some reason, test (tsType.flags & ts.TypeFlags.Enum) !== 0 doesn't work here. + * Must use SymbolFlags to figure out if this is an enum type. + */ return tsType.symbol && (tsType.symbol.flags & ts.SymbolFlags.EnumMember) !== 0; } - public isObjectLiteralType(tsType: ts.Type): boolean { + static isObjectLiteralType(tsType: ts.Type): boolean { return tsType.symbol && (tsType.symbol.flags & ts.SymbolFlags.ObjectLiteral) !== 0; } - public isNumberLikeType(tsType: ts.Type): boolean { + static isNumberLikeType(tsType: ts.Type): boolean { return (tsType.getFlags() & ts.TypeFlags.NumberLike) !== 0; } - public hasModifier(tsModifiers: readonly ts.Modifier[] | undefined, tsModifierKind: number): boolean { + static hasModifier(tsModifiers: readonly ts.Modifier[] | undefined, tsModifierKind: number): boolean { // Sanity check. - if (!tsModifiers) return false; + if (!tsModifiers) { + return false; + } for (const tsModifier of tsModifiers) { - if (tsModifier.kind === tsModifierKind) return true; + if (tsModifier.kind === tsModifierKind) { + return true; + } } return false; } - public unwrapParenthesized(tsExpr: ts.Expression): ts.Expression { + static unwrapParenthesized(tsExpr: ts.Expression): ts.Expression { let unwrappedExpr = tsExpr; - while (ts.isParenthesizedExpression(unwrappedExpr)) + while (ts.isParenthesizedExpression(unwrappedExpr)) { unwrappedExpr = unwrappedExpr.expression; + } return unwrappedExpr; } - public followIfAliased(sym: ts.Symbol): ts.Symbol { + followIfAliased(sym: ts.Symbol): ts.Symbol { if ((sym.getFlags() & ts.SymbolFlags.Alias) !== 0) { return this.tsTypeChecker.getAliasedSymbol(sym); } return sym; } - private trueSymbolAtLocationCache = new Map(); + private readonly trueSymbolAtLocationCache = new Map(); - public trueSymbolAtLocation(node: ts.Node): ts.Symbol | undefined { - let cache = this.trueSymbolAtLocationCache; - let val = cache.get(node); + trueSymbolAtLocation(node: ts.Node): ts.Symbol | undefined { + const cache = this.trueSymbolAtLocationCache; + const val = cache.get(node); if (val !== undefined) { return val !== null ? val : undefined; } @@ -484,141 +423,186 @@ export class TsUtils { return sym; } - private isTypeDeclSyntaxKind(kind: ts.SyntaxKind) { - return this.isStructDeclarationKind(kind) || + private static isTypeDeclSyntaxKind(kind: ts.SyntaxKind): boolean { + return ( + TsUtils.isStructDeclarationKind(kind) || kind === ts.SyntaxKind.EnumDeclaration || kind === ts.SyntaxKind.ClassDeclaration || kind === ts.SyntaxKind.InterfaceDeclaration || - kind === ts.SyntaxKind.TypeAliasDeclaration; + kind === ts.SyntaxKind.TypeAliasDeclaration + ); } - public symbolHasDuplicateName(symbol: ts.Symbol, tsDeclKind: ts.SyntaxKind): boolean { - // Type Checker merges all declarations with the same name in one scope into one symbol. - // Thus, check whether the symbol of certain declaration has any declaration with - // different syntax kind. + static symbolHasDuplicateName(symbol: ts.Symbol, tsDeclKind: ts.SyntaxKind): boolean { + /* + * Type Checker merges all declarations with the same name in one scope into one symbol. + * Thus, check whether the symbol of certain declaration has any declaration with + * different syntax kind. + */ const symbolDecls = symbol?.getDeclarations(); if (symbolDecls) { for (const symDecl of symbolDecls) { const declKind = symDecl.kind; // we relax arkts-unique-names for namespace collision with class/interface/enum/type/struct const isNamespaceTypeCollision = - (this.isTypeDeclSyntaxKind(declKind) && tsDeclKind === ts.SyntaxKind.ModuleDeclaration) || - (this.isTypeDeclSyntaxKind(tsDeclKind) && declKind === ts.SyntaxKind.ModuleDeclaration) - - // Don't count declarations with 'Identifier' syntax kind as those - // usually depict declaring an object's property through assignment. - if (declKind !== ts.SyntaxKind.Identifier && declKind !== tsDeclKind && !isNamespaceTypeCollision) return true; + (TsUtils.isTypeDeclSyntaxKind(declKind) && tsDeclKind === ts.SyntaxKind.ModuleDeclaration) || + (TsUtils.isTypeDeclSyntaxKind(tsDeclKind) && declKind === ts.SyntaxKind.ModuleDeclaration); + + /* + * Don't count declarations with 'Identifier' syntax kind as those + * usually depict declaring an object's property through assignment. + */ + if (declKind !== ts.SyntaxKind.Identifier && declKind !== tsDeclKind && !isNamespaceTypeCollision) { + return true; + } } } return false; } - public isReferenceType(tsType: ts.Type): boolean { + static isReferenceType(tsType: ts.Type): boolean { const f = tsType.getFlags(); return ( - (f & ts.TypeFlags.InstantiableNonPrimitive) !== 0 || (f & ts.TypeFlags.Object) !== 0 || - (f & ts.TypeFlags.Boolean) !== 0 || (f & ts.TypeFlags.Enum) !== 0 || (f & ts.TypeFlags.NonPrimitive) !== 0 || - (f & ts.TypeFlags.Number) !== 0 || (f & ts.TypeFlags.String) !== 0 + (f & ts.TypeFlags.InstantiableNonPrimitive) !== 0 || + (f & ts.TypeFlags.Object) !== 0 || + (f & ts.TypeFlags.Boolean) !== 0 || + (f & ts.TypeFlags.Enum) !== 0 || + (f & ts.TypeFlags.NonPrimitive) !== 0 || + (f & ts.TypeFlags.Number) !== 0 || + (f & ts.TypeFlags.String) !== 0 ); } - public isPrimitiveType(type: ts.Type): boolean { + static isPrimitiveType(type: ts.Type): boolean { const f = type.getFlags(); return ( - (f & ts.TypeFlags.Boolean) !== 0 || (f & ts.TypeFlags.BooleanLiteral) !== 0 || - (f & ts.TypeFlags.Number) !== 0 || (f & ts.TypeFlags.NumberLiteral) !== 0 - // In ArkTS 'string' is not a primitive type. So for the common subset 'string' - // should be considered as a reference type. That is why next line is commented out. - //(f & ts.TypeFlags.String) != 0 || (f & ts.TypeFlags.StringLiteral) != 0 + (f & ts.TypeFlags.Boolean) !== 0 || + (f & ts.TypeFlags.BooleanLiteral) !== 0 || + (f & ts.TypeFlags.Number) !== 0 || + (f & ts.TypeFlags.NumberLiteral) !== 0 + + /* + * In ArkTS 'string' is not a primitive type. So for the common subset 'string' + * should be considered as a reference type. That is why next line is commented out. + * (f & ts.TypeFlags.String) !== 0 || (f & ts.TypeFlags.StringLiteral) !== 0 + */ ); } - public isTypeSymbol(symbol: ts.Symbol | undefined): boolean { + static isTypeSymbol(symbol: ts.Symbol | undefined): boolean { return ( - !!symbol && !!symbol.flags && + !!symbol && + !!symbol.flags && ((symbol.flags & ts.SymbolFlags.Class) !== 0 || (symbol.flags & ts.SymbolFlags.Interface) !== 0) ); } // Check whether type is generic 'Array' type defined in TypeScript standard library. - public isGenericArrayType(tsType: ts.Type): tsType is ts.TypeReference { + static isGenericArrayType(tsType: ts.Type): tsType is ts.TypeReference { return ( - this.isTypeReference(tsType) && tsType.typeArguments?.length === 1 && tsType.target.typeParameters?.length === 1 && + TsUtils.isTypeReference(tsType) && + tsType.typeArguments?.length === 1 && + tsType.target.typeParameters?.length === 1 && tsType.getSymbol()?.getName() === 'Array' ); } + isTypedArray(tsType: ts.Type): boolean { + const symbol = tsType.symbol; + if (!symbol) { + return false; + } + const name = this.tsTypeChecker.getFullyQualifiedName(symbol); + return this.isGlobalSymbol(symbol) && TsUtils.TYPED_ARRAYS.includes(name); + } + + isArray(tsType: ts.Type): boolean { + return TsUtils.isGenericArrayType(tsType) || this.isTypedArray(tsType); + } + + static isTuple(tsType: ts.Type): boolean { + return TsUtils.isTypeReference(tsType) && !!(tsType.objectFlags & ts.ObjectFlags.Tuple); + } + // does something similar to relatedByInheritanceOrIdentical function - public isDerivedFrom(tsType: ts.Type, checkType: CheckType): tsType is ts.TypeReference { - if (this.isTypeReference(tsType) && tsType.target !== tsType) tsType = tsType.target; + isOrDerivedFrom(tsType: ts.Type, checkType: CheckType): boolean { + if (TsUtils.isTypeReference(tsType) && tsType.target !== tsType) { + tsType = tsType.target; + } - const tsTypeNode = this.tsTypeChecker.typeToTypeNode(tsType, undefined, ts.NodeBuilderFlags.None); - if (checkType === CheckType.Array && (this.isGenericArrayType(tsType) || this.isTypedArray(tsTypeNode))) - return true; - if (checkType !== CheckType.Array && this.isType(tsTypeNode, checkType.toString())) + if (checkType.call(this, tsType)) { return true; - if (!tsType.symbol || !tsType.symbol.declarations) return false; + } - for (let tsTypeDecl of tsType.symbol.declarations) { - if ( - (!ts.isClassDeclaration(tsTypeDecl) && !ts.isInterfaceDeclaration(tsTypeDecl)) || - !tsTypeDecl.heritageClauses - ) continue; - for (let heritageClause of tsTypeDecl.heritageClauses) { - if (this.processParentTypesCheck(heritageClause.types, checkType)) return true; + if (!tsType.symbol?.declarations) { + return false; + } + + for (const tsTypeDecl of tsType.symbol.declarations) { + if (!ts.isClassDeclaration(tsTypeDecl) && !ts.isInterfaceDeclaration(tsTypeDecl)) { + continue; + } + if (!tsTypeDecl.heritageClauses) { + continue; + } + for (const heritageClause of tsTypeDecl.heritageClauses) { + if (this.processParentTypesCheck(heritageClause.types, checkType)) { + return true; + } } } return false; } - public isTypeReference(tsType: ts.Type): tsType is ts.TypeReference { + static isTypeReference(tsType: ts.Type): tsType is ts.TypeReference { return ( (tsType.getFlags() & ts.TypeFlags.Object) !== 0 && ((tsType as ts.ObjectType).objectFlags & ts.ObjectFlags.Reference) !== 0 ); } - public isNullType(tsTypeNode: ts.TypeNode): boolean { - return (ts.isLiteralTypeNode(tsTypeNode) && tsTypeNode.literal.kind === ts.SyntaxKind.NullKeyword); + static isNullType(tsTypeNode: ts.TypeNode): boolean { + return ts.isLiteralTypeNode(tsTypeNode) && tsTypeNode.literal.kind === ts.SyntaxKind.NullKeyword; } - public isThisOrSuperExpr(tsExpr: ts.Expression): boolean { - return (tsExpr.kind === ts.SyntaxKind.ThisKeyword || tsExpr.kind === ts.SyntaxKind.SuperKeyword); + static isThisOrSuperExpr(tsExpr: ts.Expression): boolean { + return tsExpr.kind === ts.SyntaxKind.ThisKeyword || tsExpr.kind === ts.SyntaxKind.SuperKeyword; } - public isPrototypeSymbol(symbol: ts.Symbol | undefined): boolean { - return (!!symbol && !!symbol.flags && (symbol.flags & ts.SymbolFlags.Prototype) !== 0); + static isPrototypeSymbol(symbol: ts.Symbol | undefined): boolean { + return !!symbol && !!symbol.flags && (symbol.flags & ts.SymbolFlags.Prototype) !== 0; } - public isFunctionSymbol(symbol: ts.Symbol | undefined): boolean { - return (!!symbol && !!symbol.flags && (symbol.flags & ts.SymbolFlags.Function) !== 0); + static isFunctionSymbol(symbol: ts.Symbol | undefined): boolean { + return !!symbol && !!symbol.flags && (symbol.flags & ts.SymbolFlags.Function) !== 0; } - public isInterfaceType(tsType: ts.Type | undefined): boolean { + static isInterfaceType(tsType: ts.Type | undefined): boolean { return ( - !!tsType && !!tsType.symbol && !!tsType.symbol.flags && - (tsType.symbol.flags & ts.SymbolFlags.Interface) !== 0 + !!tsType && !!tsType.symbol && !!tsType.symbol.flags && (tsType.symbol.flags & ts.SymbolFlags.Interface) !== 0 ); } - public isAnyType(tsType: ts.Type): tsType is ts.TypeReference { + static isAnyType(tsType: ts.Type): tsType is ts.TypeReference { return (tsType.getFlags() & ts.TypeFlags.Any) !== 0; } - public isUnknownType(tsType: ts.Type): boolean { + static isUnknownType(tsType: ts.Type): boolean { return (tsType.getFlags() & ts.TypeFlags.Unknown) !== 0; } - public isUnsupportedType(tsType: ts.Type): boolean { + static isUnsupportedType(tsType: ts.Type): boolean { return ( - !!tsType.flags && ((tsType.flags & ts.TypeFlags.Any) !== 0 || (tsType.flags & ts.TypeFlags.Unknown) !== 0 || + !!tsType.flags && + ((tsType.flags & ts.TypeFlags.Any) !== 0 || + (tsType.flags & ts.TypeFlags.Unknown) !== 0 || (tsType.flags & ts.TypeFlags.Intersection) !== 0) ); } - public isNullableUnionType(type: ts.Type): boolean { + static isNullableUnionType(type: ts.Type): boolean { if (type.isUnion()) { for (const t of type.types) { if (!!(t.flags & ts.TypeFlags.Undefined) || !!(t.flags & ts.TypeFlags.Null)) { @@ -630,43 +614,70 @@ export class TsUtils { return false; } - public isFunctionOrMethod(tsSymbol: ts.Symbol | undefined): boolean { + static isFunctionOrMethod(tsSymbol: ts.Symbol | undefined): boolean { return ( - !!tsSymbol && - ((tsSymbol.flags & ts.SymbolFlags.Function) !== 0 || (tsSymbol.flags & ts.SymbolFlags.Method) !== 0) + !!tsSymbol && ((tsSymbol.flags & ts.SymbolFlags.Function) !== 0 || (tsSymbol.flags & ts.SymbolFlags.Method) !== 0) ); } - public isMethodAssignment(tsSymbol: ts.Symbol | undefined): boolean { + static isMethodAssignment(tsSymbol: ts.Symbol | undefined): boolean { return ( - !!tsSymbol && - ((tsSymbol.flags & ts.SymbolFlags.Method) !== 0 && (tsSymbol.flags & ts.SymbolFlags.Assignment) !== 0) + !!tsSymbol && (tsSymbol.flags & ts.SymbolFlags.Method) !== 0 && (tsSymbol.flags & ts.SymbolFlags.Assignment) !== 0 ); } - public getDeclaration(tsSymbol: ts.Symbol | undefined): ts.Declaration | undefined { - if (tsSymbol && tsSymbol.declarations && tsSymbol.declarations.length > 0) + static getDeclaration(tsSymbol: ts.Symbol | undefined): ts.Declaration | undefined { + if (tsSymbol?.declarations && tsSymbol.declarations.length > 0) { return tsSymbol.declarations[0]; + } return undefined; } - private isVarDeclaration(tsDecl: ts.Node): boolean { + private static isVarDeclaration(tsDecl: ts.Node): boolean { return ts.isVariableDeclaration(tsDecl) && ts.isVariableDeclarationList(tsDecl.parent); } - public isValidEnumMemberInit(tsExpr: ts.Expression): boolean { - if (this.isNumberConstantValue(tsExpr.parent as ts.EnumMember)) + isValidEnumMemberInit(tsExpr: ts.Expression): boolean { + if (this.isNumberConstantValue(tsExpr.parent as ts.EnumMember)) { return true; - if (this.isStringConstantValue(tsExpr.parent as ts.EnumMember)) + } + if (this.isStringConstantValue(tsExpr.parent as ts.EnumMember)) { return true; + } return this.isCompileTimeExpression(tsExpr); } - public isCompileTimeExpression(tsExpr: ts.Expression): boolean { + private isCompileTimeExpressionHandlePropertyAccess(tsExpr: ts.Expression): boolean { + if (!ts.isPropertyAccessExpression(tsExpr)) { + return false; + } + + /* + * if enum member is in current enum declaration try to get value + * if it comes from another enum consider as constant + */ + const propertyAccess = tsExpr; + if (this.isNumberConstantValue(propertyAccess)) { + return true; + } + const leftHandSymbol = this.trueSymbolAtLocation(propertyAccess.expression); + if (!leftHandSymbol) { + return false; + } + const decls = leftHandSymbol.getDeclarations(); + if (!decls || decls.length !== 1) { + return false; + } + return ts.isEnumDeclaration(decls[0]); + } + + isCompileTimeExpression(tsExpr: ts.Expression): boolean { if ( ts.isParenthesizedExpression(tsExpr) || - (ts.isAsExpression(tsExpr) && tsExpr.type.kind === ts.SyntaxKind.NumberKeyword)) + (ts.isAsExpression(tsExpr) && tsExpr.type.kind === ts.SyntaxKind.NumberKeyword) + ) { return this.isCompileTimeExpression(tsExpr.expression); + } switch (tsExpr.kind) { case ts.SyntaxKind.PrefixUnaryExpression: @@ -682,126 +693,123 @@ export class TsUtils { return true; case ts.SyntaxKind.StringLiteral: return true; - case ts.SyntaxKind.PropertyAccessExpression: { - // if enum member is in current enum declaration try to get value - // if it comes from another enum consider as constant - const propertyAccess = tsExpr as ts.PropertyAccessExpression; - if (this.isNumberConstantValue(propertyAccess)) - return true; - const leftHandSymbol = this.tsTypeChecker.getSymbolAtLocation(propertyAccess.expression); - if (!leftHandSymbol) - return false; - const decls = leftHandSymbol.getDeclarations(); - if (!decls || decls.length !== 1) - return false; - return ts.isEnumDeclaration(decls[0]); - } + case ts.SyntaxKind.PropertyAccessExpression: + return this.isCompileTimeExpressionHandlePropertyAccess(tsExpr); default: return false; } } private isPrefixUnaryExprValidEnumMemberInit(tsExpr: ts.PrefixUnaryExpression): boolean { - return (this.isUnaryOpAllowedForEnumMemberInit(tsExpr.operator) && this.isCompileTimeExpression(tsExpr.operand)); + return TsUtils.isUnaryOpAllowedForEnumMemberInit(tsExpr.operator) && this.isCompileTimeExpression(tsExpr.operand); } private isBinaryExprValidEnumMemberInit(tsExpr: ts.BinaryExpression): boolean { return ( - this.isBinaryOpAllowedForEnumMemberInit(tsExpr.operatorToken) && this.isCompileTimeExpression(tsExpr.left) && + TsUtils.isBinaryOpAllowedForEnumMemberInit(tsExpr.operatorToken) && + this.isCompileTimeExpression(tsExpr.left) && this.isCompileTimeExpression(tsExpr.right) ); } private isConditionalExprValidEnumMemberInit(tsExpr: ts.ConditionalExpression): boolean { - return (this.isCompileTimeExpression(tsExpr.whenTrue) && this.isCompileTimeExpression(tsExpr.whenFalse)); + return this.isCompileTimeExpression(tsExpr.whenTrue) && this.isCompileTimeExpression(tsExpr.whenFalse); } private isIdentifierValidEnumMemberInit(tsExpr: ts.Identifier): boolean { - let tsSymbol = this.tsTypeChecker.getSymbolAtLocation(tsExpr); - let tsDecl = this.getDeclaration(tsSymbol); - return (!!tsDecl && - ((this.isVarDeclaration(tsDecl) && this.isConst(tsDecl.parent)) || - (tsDecl.kind === ts.SyntaxKind.EnumMember) - ) + const tsSymbol = this.tsTypeChecker.getSymbolAtLocation(tsExpr); + const tsDecl = TsUtils.getDeclaration(tsSymbol); + return ( + !!tsDecl && + ((TsUtils.isVarDeclaration(tsDecl) && TsUtils.isConst(tsDecl.parent)) || tsDecl.kind === ts.SyntaxKind.EnumMember) ); } - private isUnaryOpAllowedForEnumMemberInit(tsPrefixUnaryOp: ts.PrefixUnaryOperator): boolean { + private static isUnaryOpAllowedForEnumMemberInit(tsPrefixUnaryOp: ts.PrefixUnaryOperator): boolean { return ( - tsPrefixUnaryOp === ts.SyntaxKind.PlusToken || tsPrefixUnaryOp === ts.SyntaxKind.MinusToken || + tsPrefixUnaryOp === ts.SyntaxKind.PlusToken || + tsPrefixUnaryOp === ts.SyntaxKind.MinusToken || tsPrefixUnaryOp === ts.SyntaxKind.TildeToken ); } - private isBinaryOpAllowedForEnumMemberInit(tsBinaryOp: ts.BinaryOperatorToken): boolean { + private static isBinaryOpAllowedForEnumMemberInit(tsBinaryOp: ts.BinaryOperatorToken): boolean { return ( - tsBinaryOp.kind === ts.SyntaxKind.AsteriskToken || tsBinaryOp.kind === ts.SyntaxKind.SlashToken || - tsBinaryOp.kind === ts.SyntaxKind.PercentToken || tsBinaryOp.kind === ts.SyntaxKind.MinusToken || - tsBinaryOp.kind === ts.SyntaxKind.PlusToken || tsBinaryOp.kind === ts.SyntaxKind.LessThanLessThanToken || - tsBinaryOp.kind === ts.SyntaxKind.GreaterThanGreaterThanToken || tsBinaryOp.kind === ts.SyntaxKind.BarBarToken || + tsBinaryOp.kind === ts.SyntaxKind.AsteriskToken || + tsBinaryOp.kind === ts.SyntaxKind.SlashToken || + tsBinaryOp.kind === ts.SyntaxKind.PercentToken || + tsBinaryOp.kind === ts.SyntaxKind.MinusToken || + tsBinaryOp.kind === ts.SyntaxKind.PlusToken || + tsBinaryOp.kind === ts.SyntaxKind.LessThanLessThanToken || + tsBinaryOp.kind === ts.SyntaxKind.GreaterThanGreaterThanToken || + tsBinaryOp.kind === ts.SyntaxKind.BarBarToken || tsBinaryOp.kind === ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken || - tsBinaryOp.kind === ts.SyntaxKind.AmpersandToken || tsBinaryOp.kind === ts.SyntaxKind.CaretToken || - tsBinaryOp.kind === ts.SyntaxKind.BarToken || tsBinaryOp.kind === ts.SyntaxKind.AmpersandAmpersandToken + tsBinaryOp.kind === ts.SyntaxKind.AmpersandToken || + tsBinaryOp.kind === ts.SyntaxKind.CaretToken || + tsBinaryOp.kind === ts.SyntaxKind.BarToken || + tsBinaryOp.kind === ts.SyntaxKind.AmpersandAmpersandToken ); } - public isConst(tsNode: ts.Node): boolean { + static isConst(tsNode: ts.Node): boolean { return !!(ts.getCombinedNodeFlags(tsNode) & ts.NodeFlags.Const); } - public isNumberConstantValue( + isNumberConstantValue( tsExpr: ts.EnumMember | ts.PropertyAccessExpression | ts.ElementAccessExpression | ts.NumericLiteral ): boolean { + const tsConstValue = + tsExpr.kind === ts.SyntaxKind.NumericLiteral ? + Number(tsExpr.getText()) : + this.tsTypeChecker.getConstantValue(tsExpr); - const tsConstValue = (tsExpr.kind === ts.SyntaxKind.NumericLiteral) ? - Number(tsExpr.getText()) : - this.tsTypeChecker.getConstantValue(tsExpr); - - return tsConstValue !== undefined && typeof tsConstValue === 'number'; + return tsConstValue !== undefined && typeof tsConstValue === 'number'; } - public isIntegerConstantValue( + isIntegerConstantValue( tsExpr: ts.EnumMember | ts.PropertyAccessExpression | ts.ElementAccessExpression | ts.NumericLiteral ): boolean { - - const tsConstValue = (tsExpr.kind === ts.SyntaxKind.NumericLiteral) ? - Number(tsExpr.getText()) : - this.tsTypeChecker.getConstantValue(tsExpr); + const tsConstValue = + tsExpr.kind === ts.SyntaxKind.NumericLiteral ? + Number(tsExpr.getText()) : + this.tsTypeChecker.getConstantValue(tsExpr); return ( - tsConstValue !== undefined && typeof tsConstValue === 'number' && + tsConstValue !== undefined && + typeof tsConstValue === 'number' && tsConstValue.toFixed(0) === tsConstValue.toString() ); } - public isStringConstantValue( - tsExpr: ts.EnumMember | ts.PropertyAccessExpression | ts.ElementAccessExpression - ): boolean { + isStringConstantValue(tsExpr: ts.EnumMember | ts.PropertyAccessExpression | ts.ElementAccessExpression): boolean { const tsConstValue = this.tsTypeChecker.getConstantValue(tsExpr); - return ( - tsConstValue !== undefined && typeof tsConstValue === 'string' - ); + return tsConstValue !== undefined && typeof tsConstValue === 'string'; } // Returns true iff typeA is a subtype of typeB - public relatedByInheritanceOrIdentical(typeA: ts.Type, typeB: ts.Type): boolean { - if (this.isTypeReference(typeA) && typeA.target !== typeA) typeA = typeA.target; - if (this.isTypeReference(typeB) && typeB.target !== typeB) typeB = typeB.target; + relatedByInheritanceOrIdentical(typeA: ts.Type, typeB: ts.Type): boolean { + if (TsUtils.isTypeReference(typeA) && typeA.target !== typeA) { + typeA = typeA.target; + } + if (TsUtils.isTypeReference(typeB) && typeB.target !== typeB) { + typeB = typeB.target; + } if (typeA === typeB || this.isObject(typeB)) { return true; } - if (!typeA.symbol || !typeA.symbol.declarations) { + if (!typeA.symbol?.declarations) { return false; } - for (let typeADecl of typeA.symbol.declarations) { - if ( - (!ts.isClassDeclaration(typeADecl) && !ts.isInterfaceDeclaration(typeADecl)) || - !typeADecl.heritageClauses - ) continue; - for (let heritageClause of typeADecl.heritageClauses) { - let processInterfaces = typeA.isClass() ? (heritageClause.token !== ts.SyntaxKind.ExtendsKeyword) : true; - if (this.processParentTypes(heritageClause.types, typeB, processInterfaces)) return true; + for (const typeADecl of typeA.symbol.declarations) { + if ((!ts.isClassDeclaration(typeADecl) && !ts.isInterfaceDeclaration(typeADecl)) || !typeADecl.heritageClauses) { + continue; + } + for (const heritageClause of typeADecl.heritageClauses) { + const processInterfaces = typeA.isClass() ? heritageClause.token !== ts.SyntaxKind.ExtendsKeyword : true; + if (this.processParentTypes(heritageClause.types, typeB, processInterfaces)) { + return true; + } } } @@ -809,20 +817,25 @@ export class TsUtils { } // return true if two class types are not related by inheritance and structural identity check is needed - public needToDeduceStructuralIdentity(lhsType: ts.Type, rhsType: ts.Type, rhsExpr: ts.Expression, - allowPromotion: boolean = false): boolean { + needToDeduceStructuralIdentity( + lhsType: ts.Type, + rhsType: ts.Type, + rhsExpr: ts.Expression, + allowPromotion: boolean = false + ): boolean { // Compare non-nullable version of types. - lhsType = this.getNonNullableType(lhsType); - rhsType = this.getNonNullableType(rhsType); - + lhsType = TsUtils.getNonNullableType(lhsType); + rhsType = TsUtils.getNonNullableType(rhsType); if (this.isLibraryType(lhsType)) { return false; } - if (this.isDynamicObjectAssignedToStdType(lhsType, rhsExpr)) { return false; } - + // #14569: Check for Function type. + if (this.areCompatibleFunctionals(lhsType, rhsType)) { + return false; + } if (rhsType.isUnion()) { // Each Class/Interface of the RHS union type must be compatible with LHS type. for (const compType of rhsType.types) { @@ -832,7 +845,6 @@ export class TsUtils { } return false; } - if (lhsType.isUnion()) { // RHS type needs to be compatible with at least one type of the LHS union. for (const compType of lhsType.types) { @@ -842,17 +854,17 @@ export class TsUtils { } return true; } - - let res = lhsType.isClassOrInterface() && rhsType.isClassOrInterface() && !this.relatedByInheritanceOrIdentical(rhsType, lhsType); - + let res = + lhsType.isClassOrInterface() && + rhsType.isClassOrInterface() && + !this.relatedByInheritanceOrIdentical(rhsType, lhsType); if (allowPromotion) { res &&= !this.relatedByInheritanceOrIdentical(lhsType, rhsType); } - return res; } - public hasPredecessor(node: ts.Node, predicate: (node: ts.Node) => boolean): boolean { + static hasPredecessor(node: ts.Node, predicate: (node: ts.Node) => boolean): boolean { let parent = node.parent; while (parent !== undefined) { if (predicate(parent)) { @@ -863,64 +875,87 @@ export class TsUtils { return false; } - private processParentTypes(parentTypes: ts.NodeArray, typeB: ts.Type, processInterfaces: boolean): boolean { - for (let baseTypeExpr of parentTypes) { + private processParentTypes( + parentTypes: ts.NodeArray, + typeB: ts.Type, + processInterfaces: boolean + ): boolean { + for (const baseTypeExpr of parentTypes) { let baseType = this.tsTypeChecker.getTypeAtLocation(baseTypeExpr); - if (this.isTypeReference(baseType) && baseType.target !== baseType) baseType = baseType.target; - if (baseType && (baseType.isClass() !== processInterfaces) && this.relatedByInheritanceOrIdentical(baseType, typeB)) { + if (TsUtils.isTypeReference(baseType) && baseType.target !== baseType) { + baseType = baseType.target; + } + if ( + baseType && + baseType.isClass() !== processInterfaces && + this.relatedByInheritanceOrIdentical(baseType, typeB) + ) { return true; } } return false; } - private processParentTypesCheck(parentTypes: ts.NodeArray, checkType: CheckType): boolean { - for (let baseTypeExpr of parentTypes) { + private processParentTypesCheck( + parentTypes: ts.NodeArray, + checkType: CheckType + ): boolean { + for (const baseTypeExpr of parentTypes) { let baseType = this.tsTypeChecker.getTypeAtLocation(baseTypeExpr); - if (this.isTypeReference(baseType) && baseType.target !== baseType) baseType = baseType.target; - if (baseType && this.isDerivedFrom(baseType, checkType)) return true; + if (TsUtils.isTypeReference(baseType) && baseType.target !== baseType) { + baseType = baseType.target; + } + if (baseType && this.isOrDerivedFrom(baseType, checkType)) { + return true; + } } return false; } - public isObject(tsType: ts.Type): boolean { + isObject(tsType: ts.Type): boolean { if (!tsType) { return false; } - if (tsType.symbol && (tsType.isClassOrInterface() && tsType.symbol.name === 'Object')) { + if (tsType.symbol && tsType.isClassOrInterface() && tsType.symbol.name === 'Object') { return true; } - let node = this.tsTypeChecker.typeToTypeNode(tsType, undefined, undefined); + const node = this.tsTypeChecker.typeToTypeNode(tsType, undefined, undefined); return node !== undefined && node.kind === ts.SyntaxKind.ObjectKeyword; } - public isCallToFunctionWithOmittedReturnType(tsExpr: ts.Expression): boolean { + isCallToFunctionWithOmittedReturnType(tsExpr: ts.Expression): boolean { if (ts.isCallExpression(tsExpr)) { - let tsCallSignature = this.tsTypeChecker.getResolvedSignature(tsExpr); + const tsCallSignature = this.tsTypeChecker.getResolvedSignature(tsExpr); if (tsCallSignature) { const tsSignDecl = tsCallSignature.getDeclaration(); // `tsSignDecl` is undefined when `getResolvedSignature` returns `unknownSignature` - if (!tsSignDecl || !tsSignDecl.type) return true; + if (!tsSignDecl?.type) { + return true; + } } } return false; } - private hasReadonlyFields(type: ts.Type): boolean { - if (type.symbol.members === undefined) return false; // No members -> no readonly fields + private static hasReadonlyFields(type: ts.Type): boolean { + // No members -> no readonly fields + if (type.symbol.members === undefined) { + return false; + } let result: boolean = false; - type.symbol.members.forEach((value, key) => { + type.symbol.members.forEach((value) => { if ( - value.declarations !== undefined && value.declarations.length > 0 && + value.declarations !== undefined && + value.declarations.length > 0 && ts.isPropertyDeclaration(value.declarations[0]) ) { - let propmMods = value.declarations[0].modifiers; // TSC 4.2 doesn't have 'ts.getModifiers()' method - if (this.hasModifier(propmMods, ts.SyntaxKind.ReadonlyKeyword)) { + // TSC 4.2 doesn't have 'ts.getModifiers()' method + const propmMods = value.declarations[0].modifiers; + if (TsUtils.hasModifier(propmMods, ts.SyntaxKind.ReadonlyKeyword)) { result = true; - return; } } }); @@ -928,55 +963,66 @@ export class TsUtils { return result; } - private hasDefaultCtor(type: ts.Type): boolean { - if (type.symbol.members === undefined) return true; // No members -> no explicite constructors -> there is default ctor + private static hasDefaultCtor(type: ts.Type): boolean { + // No members -> no explicite constructors -> there is default ctor + if (type.symbol.members === undefined) { + return true; + } - let hasCtor: boolean = false; // has any constructor - let hasDefaultCtor: boolean = false; // has default constructor + let hasCtor: boolean = false; + let hasDefaultCtor: boolean = false; - type.symbol.members.forEach((value, key) => { + type.symbol.members.forEach((value) => { if ((value.flags & ts.SymbolFlags.Constructor) !== 0) { hasCtor = true; if (value.declarations !== undefined && value.declarations.length > 0) { - let declCtor = value.declarations[0] as ts.ConstructorDeclaration; + const declCtor = value.declarations[0] as ts.ConstructorDeclaration; if (declCtor.parameters.length === 0) { hasDefaultCtor = true; - return; } } } }); - - return !hasCtor || hasDefaultCtor; // Has no any explicite constructor -> has implicite default constructor. + // Has no any explicit constructor -> has implicit default constructor. + return !hasCtor || hasDefaultCtor; } - private isAbstractClass(type: ts.Type): boolean { + private static isAbstractClass(type: ts.Type): boolean { if (type.isClass() && type.symbol.declarations && type.symbol.declarations.length > 0) { - let declClass = type.symbol.declarations[0] as ts.ClassDeclaration; - let classMods = declClass.modifiers; // TSC 4.2 doesn't have 'ts.getModifiers()' method - if (this.hasModifier(classMods, ts.SyntaxKind.AbstractKeyword)) + const declClass = type.symbol.declarations[0] as ts.ClassDeclaration; + // TSC 4.2 doesn't have 'ts.getModifiers()' method + const classMods = declClass.modifiers; + if (TsUtils.hasModifier(classMods, ts.SyntaxKind.AbstractKeyword)) { return true; + } } return false; } - public validateObjectLiteralType(type: ts.Type | undefined): boolean { - if (!type) return false; + static validateObjectLiteralType(type: ts.Type | undefined): boolean { + if (!type) { + return false; + } - type = this.getTargetType(type); + type = TsUtils.getTargetType(type); return ( - type && type.isClassOrInterface() && this.hasDefaultCtor(type) && - !this.hasReadonlyFields(type) && !this.isAbstractClass(type) + type && + type.isClassOrInterface() && + TsUtils.hasDefaultCtor(type) && + !TsUtils.hasReadonlyFields(type) && + !TsUtils.isAbstractClass(type) ); } - public hasMethods(type: ts.Type): boolean { + hasMethods(type: ts.Type): boolean { const properties = this.tsTypeChecker.getPropertiesOfType(type); if (properties?.length) { for (const prop of properties) { - if (prop.getFlags() & ts.SymbolFlags.Method) return true; + if (prop.getFlags() & ts.SymbolFlags.Method) { + return true; + } } } return false; @@ -986,57 +1032,66 @@ export class TsUtils { const properties = this.tsTypeChecker.getPropertiesOfType(type); if (properties?.length) { for (const prop of properties) { - if (prop.name === name) return prop; + if (prop.name === name) { + return prop; + } } } return undefined; } - public checkTypeSet(uType: ts.Type, predicate: (t: ts.Type) => boolean): boolean { - if (!uType.isUnionOrIntersection()) { - return predicate(uType); + checkTypeSet(typeSet: ts.Type, predicate: CheckType): boolean { + if (!typeSet.isUnionOrIntersection()) { + return predicate.call(this, typeSet); } - for (let elemType of uType.types) { - if (!this.checkTypeSet(elemType, predicate)) { - return false; + for (const elemType of typeSet.types) { + if (this.checkTypeSet(elemType, predicate)) { + return true; } } - return true; + return false; } - private getNonNullableType(t: ts.Type): ts.Type { - if (this.isNullableUnionType(t)) { + static getNonNullableType(t: ts.Type): ts.Type { + if (TsUtils.isNullableUnionType(t)) { return t.getNonNullableType(); } return t; } - public isObjectLiteralAssignable(lhsType: ts.Type | undefined, rhsExpr: ts.ObjectLiteralExpression): boolean { + private isObjectLiteralAssignableToUnion(lhsType: ts.UnionType, rhsExpr: ts.ObjectLiteralExpression): boolean { + for (const compType of lhsType.types) { + if (this.isObjectLiteralAssignable(compType, rhsExpr)) { + return true; + } + } + return false; + } + + isObjectLiteralAssignable(lhsType: ts.Type | undefined, rhsExpr: ts.ObjectLiteralExpression): boolean { if (lhsType === undefined) { return false; } - // Always check with the non-nullable variant of lhs type. - lhsType = this.getNonNullableType(lhsType); - - if (lhsType.isUnion()) { - for (const compType of lhsType.types) { - if (this.isObjectLiteralAssignable(compType, rhsExpr)) { - return true; - } - } + lhsType = TsUtils.getNonNullableType(lhsType); + if (lhsType.isUnion() && this.isObjectLiteralAssignableToUnion(lhsType, rhsExpr)) { + return true; } - // Allow initializing with anything when the type - // originates from the library. - if (this.isAnyType(lhsType) || this.isLibraryType(lhsType)) { + /* + * Allow initializing with anything when the type + * originates from the library. + */ + if (TsUtils.isAnyType(lhsType) || this.isLibraryType(lhsType)) { return true; } - // issue 13412: - // Allow initializing with a dynamic object when the LHS type - // is primitive or defined in standard library. + /* + * issue 13412: + * Allow initializing with a dynamic object when the LHS type + * is primitive or defined in standard library. + */ if (this.isDynamicObjectAssignedToStdType(lhsType, rhsExpr)) { return true; } @@ -1050,29 +1105,33 @@ export class TsUtils { } } - // Allow initializing Record objects with object initializer. - // Record supports any type for a its value, but the key value - // must be either a string or number literal. + /* + * Allow initializing Record objects with object initializer. + * Record supports any type for a its value, but the key value + * must be either a string or number literal. + */ if (this.isStdRecordType(lhsType)) { - return this.validateRecordObjectKeys(rhsExpr); + return TsUtils.validateRecordObjectKeys(rhsExpr); } - - return this.validateObjectLiteralType(lhsType) && !this.hasMethods(lhsType) && this.validateFields(lhsType, rhsExpr); + return ( + TsUtils.validateObjectLiteralType(lhsType) && !this.hasMethods(lhsType) && this.validateFields(lhsType, rhsExpr) + ); } private isDynamicObjectAssignedToStdType(lhsType: ts.Type, rhsExpr: ts.Expression): boolean { - if (this.isStdLibraryType(lhsType) || this.isPrimitiveType(lhsType)) { - let rhsSym = ts.isCallExpression(rhsExpr) + if (TsUtils.isStdLibraryType(lhsType) || TsUtils.isPrimitiveType(lhsType)) { + const rhsSym = ts.isCallExpression(rhsExpr) ? this.getSymbolOfCallExpression(rhsExpr) : this.tsTypeChecker.getSymbolAtLocation(rhsExpr); - if (rhsSym && this.isLibrarySymbol(rhsSym)) + if (rhsSym && this.isLibrarySymbol(rhsSym)) { return true; + } } return false; } - isFunctionalType(type: ts.Type): boolean { + static isFunctionalType(type: ts.Type): boolean { const callSigns = type.getCallSignatures(); return callSigns && callSigns.length > 0; } @@ -1084,181 +1143,194 @@ export class TsUtils { return false; } } - }; + } return true; } private validateField(type: ts.Type, prop: ts.PropertyAssignment): boolean { - const propName = prop.name.getText(); + const propNameSymbol = this.tsTypeChecker.getSymbolAtLocation(prop.name); + const propName = propNameSymbol?.escapedName.toString() ?? prop.name.getText(); const propSym = this.findProperty(type, propName); - if (!propSym || !propSym.declarations?.length) { + if (!propSym?.declarations?.length) { return false; } const propType = this.tsTypeChecker.getTypeOfSymbolAtLocation(propSym, propSym.declarations[0]); - const initExpr = this.unwrapParenthesized(prop.initializer); + const initExpr = TsUtils.unwrapParenthesized(prop.initializer); if (ts.isObjectLiteralExpression(initExpr)) { if (!this.isObjectLiteralAssignable(propType, initExpr)) { return false; } - } else { + } else if ( + this.needToDeduceStructuralIdentity(propType, this.tsTypeChecker.getTypeAtLocation(initExpr), initExpr) + ) { // Only check for structural sub-typing. - if (this.needToDeduceStructuralIdentity(propType, this.tsTypeChecker.getTypeAtLocation(initExpr), initExpr)) { - return false; - } + return false; } return true; } - validateRecordObjectKeys(objectLiteral: ts.ObjectLiteralExpression): boolean { + static validateRecordObjectKeys(objectLiteral: ts.ObjectLiteralExpression): boolean { for (const prop of objectLiteral.properties) { - if (!prop.name || (!ts.isStringLiteral(prop.name) && !ts.isNumericLiteral(prop.name))) return false; + if (!prop.name || (!ts.isStringLiteral(prop.name) && !ts.isNumericLiteral(prop.name))) { + return false; + } } return true; } - getTargetType(type: ts.Type): ts.Type { - return (type.getFlags() & ts.TypeFlags.Object) && - (type as ts.ObjectType).objectFlags & ts.ObjectFlags.Reference ? (type as ts.TypeReference).target : type; + static getTargetType(type: ts.Type): ts.Type { + return type.getFlags() & ts.TypeFlags.Object && (type as ts.ObjectType).objectFlags & ts.ObjectFlags.Reference ? + (type as ts.TypeReference).target : + type; + } + + private static isSupportedTypeNodeKind(kind: ts.SyntaxKind): boolean { + return ( + kind !== ts.SyntaxKind.AnyKeyword && + kind !== ts.SyntaxKind.UnknownKeyword && + kind !== ts.SyntaxKind.SymbolKeyword && + kind !== ts.SyntaxKind.IndexedAccessType && + kind !== ts.SyntaxKind.ConditionalType && + kind !== ts.SyntaxKind.MappedType && + kind !== ts.SyntaxKind.InferType + ); + } + + private isSupportedTypeHandleUnionTypeNode(typeNode: ts.UnionTypeNode): boolean { + for (const unionTypeElem of typeNode.types) { + if (!this.isSupportedType(unionTypeElem)) { + return false; + } + } + return true; } - private isSupportedTypeNodeKind(kind: ts.SyntaxKind): boolean { - return kind !== ts.SyntaxKind.AnyKeyword && kind !== ts.SyntaxKind.UnknownKeyword && - kind !== ts.SyntaxKind.SymbolKeyword && kind !== ts.SyntaxKind.IndexedAccessType && - kind !== ts.SyntaxKind.ConditionalType && kind !== ts.SyntaxKind.MappedType && - kind !== ts.SyntaxKind.InferType; + private isSupportedTypeHandleTupleTypeNode(typeNode: ts.TupleTypeNode): boolean { + for (const elem of typeNode.elements) { + if (ts.isTypeNode(elem) && !this.isSupportedType(elem)) { + return false; + } + if (ts.isNamedTupleMember(elem) && !this.isSupportedType(elem.type)) { + return false; + } + } + return true; } - public isSupportedType(typeNode: ts.TypeNode): boolean { - if (ts.isParenthesizedTypeNode(typeNode)) return this.isSupportedType(typeNode.type); + isSupportedType(typeNode: ts.TypeNode): boolean { + if (ts.isParenthesizedTypeNode(typeNode)) { + return this.isSupportedType(typeNode.type); + } - if (ts.isArrayTypeNode(typeNode)) return this.isSupportedType(typeNode.elementType); + if (ts.isArrayTypeNode(typeNode)) { + return this.isSupportedType(typeNode.elementType); + } if (ts.isTypeReferenceNode(typeNode) && typeNode.typeArguments) { - for (const typeArg of typeNode.typeArguments) - if (!this.isSupportedType(typeArg)) return false; + for (const typeArg of typeNode.typeArguments) { + if (!this.isSupportedType(typeArg)) { + return false; + } + } return true; } if (ts.isUnionTypeNode(typeNode)) { - for (const unionTypeElem of typeNode.types) - if (!this.isSupportedType(unionTypeElem)) return false; - return true; + return this.isSupportedTypeHandleUnionTypeNode(typeNode); } if (ts.isTupleTypeNode(typeNode)) { - for (const elem of typeNode.elements) { - if (ts.isTypeNode(elem) && !this.isSupportedType(elem)) { - return false; - } - if (ts.isNamedTupleMember(elem) && !this.isSupportedType(elem.type)) { - return false; - } - } - return true; + return this.isSupportedTypeHandleTupleTypeNode(typeNode); } - return !ts.isTypeLiteralNode(typeNode) && !ts.isTypeQueryNode(typeNode) && - !ts.isIntersectionTypeNode(typeNode) && this.isSupportedTypeNodeKind(typeNode.kind); + return ( + !ts.isTypeLiteralNode(typeNode) && + !ts.isIntersectionTypeNode(typeNode) && + TsUtils.isSupportedTypeNodeKind(typeNode.kind) + ); } - public isStruct(symbol: ts.Symbol) { + static isStruct(symbol: ts.Symbol): boolean { if (!symbol.declarations) { return false; } for (const decl of symbol.declarations) { - if (this.isStructDeclaration(decl)) { + if (TsUtils.isStructDeclaration(decl)) { return true; } } return false; } - public isStructDeclarationKind(kind: ts.SyntaxKind) { + static isStructDeclarationKind(kind: ts.SyntaxKind): boolean { return LinterConfig.tsSyntaxKindNames[kind] === 'StructDeclaration'; } - public isStructDeclaration(node: ts.Node) { - return this.isStructDeclarationKind(node.kind); + static isStructDeclaration(node: ts.Node): boolean { + return TsUtils.isStructDeclarationKind(node.kind); } - public isStructObjectInitializer(objectLiteral: ts.ObjectLiteralExpression): boolean { + isStructObjectInitializer(objectLiteral: ts.ObjectLiteralExpression): boolean { if (ts.isCallLikeExpression(objectLiteral.parent)) { const signature = this.tsTypeChecker.getResolvedSignature(objectLiteral.parent); const signDecl = signature?.declaration; - return !!signDecl && ts.isConstructorDeclaration(signDecl) && this.isStructDeclaration(signDecl.parent); + return !!signDecl && ts.isConstructorDeclaration(signDecl) && TsUtils.isStructDeclaration(signDecl.parent); } return false; } - public getParentSymbolName(symbol: ts.Symbol): string | undefined { + getParentSymbolName(symbol: ts.Symbol): string | undefined { const name = this.tsTypeChecker.getFullyQualifiedName(symbol); const dotPosition = name.lastIndexOf('.'); - return (dotPosition === -1) ? undefined : name.substring(0, dotPosition); + return dotPosition === -1 ? undefined : name.substring(0, dotPosition); } - public isGlobalSymbol(symbol: ts.Symbol): boolean { - let parentName = this.getParentSymbolName(symbol); + isGlobalSymbol(symbol: ts.Symbol): boolean { + const parentName = this.getParentSymbolName(symbol); return !parentName || parentName === 'global'; } - public isStdObjectAPI(symbol: ts.Symbol): boolean { - let parentName = this.getParentSymbolName(symbol); - return !!parentName && (parentName === 'Object'); - } - - public isStdReflectAPI(symbol: ts.Symbol): boolean { - let parentName = this.getParentSymbolName(symbol); - return !!parentName && (parentName === 'Reflect'); - } - - public isStdProxyHandlerAPI(symbol: ts.Symbol): boolean { - let parentName = this.getParentSymbolName(symbol); - return !!parentName && (parentName === 'ProxyHandler'); - } - - public isStdArrayAPI(symbol: ts.Symbol): boolean { - let parentName = this.getParentSymbolName(symbol); - return !!parentName && (parentName === 'Array'); - } - - public isStdArrayBufferAPI(symbol: ts.Symbol): boolean { - let parentName = this.getParentSymbolName(symbol); - return !!parentName && (parentName === 'ArrayBuffer'); + isSymbolAPI(symbol: ts.Symbol): boolean { + const parentName = this.getParentSymbolName(symbol); + return !!parentName && (parentName === 'Symbol' || parentName === 'SymbolConstructor'); } - public isStdSymbol(symbol: ts.Symbol): boolean { + isStdSymbol(symbol: ts.Symbol): boolean { const name = this.tsTypeChecker.getFullyQualifiedName(symbol); - return name === 'Symbol'; + return name === 'Symbol' && this.isGlobalSymbol(symbol); } - public isStdSymbolAPI(symbol: ts.Symbol): boolean { - let parentName = this.getParentSymbolName(symbol); - return !!parentName && parentName === 'Symbol'; + isSymbolIterator(symbol: ts.Symbol): boolean { + return this.isSymbolAPI(symbol) && symbol.name === 'iterator'; } - public isDefaultImport(importSpec: ts.ImportSpecifier): boolean { + static isDefaultImport(importSpec: ts.ImportSpecifier): boolean { return importSpec?.propertyName?.text === 'default'; } - public getStartPos(nodeOrComment: ts.Node | ts.CommentRange): number { - return (nodeOrComment.kind === ts.SyntaxKind.SingleLineCommentTrivia || nodeOrComment.kind === ts.SyntaxKind.MultiLineCommentTrivia) + static getStartPos(nodeOrComment: ts.Node | ts.CommentRange): number { + return nodeOrComment.kind === ts.SyntaxKind.SingleLineCommentTrivia || + nodeOrComment.kind === ts.SyntaxKind.MultiLineCommentTrivia ? (nodeOrComment as ts.CommentRange).pos : (nodeOrComment as ts.Node).getStart(); } - public getEndPos(nodeOrComment: ts.Node | ts.CommentRange): number { - return (nodeOrComment.kind === ts.SyntaxKind.SingleLineCommentTrivia || nodeOrComment.kind === ts.SyntaxKind.MultiLineCommentTrivia) + static getEndPos(nodeOrComment: ts.Node | ts.CommentRange): number { + return nodeOrComment.kind === ts.SyntaxKind.SingleLineCommentTrivia || + nodeOrComment.kind === ts.SyntaxKind.MultiLineCommentTrivia ? (nodeOrComment as ts.CommentRange).end : (nodeOrComment as ts.Node).getEnd(); } - public isStdRecordType(type: ts.Type): boolean { - // In TypeScript, 'Record' is defined as type alias to a mapped type. - // Thus, it should have 'aliasSymbol' and 'target' properties. The 'target' - // in this case will resolve to origin 'Record' symbol. + isStdRecordType(type: ts.Type): boolean { + /* + * In TypeScript, 'Record' is defined as type alias to a mapped type. + * Thus, it should have 'aliasSymbol' and 'target' properties. The 'target' + * in this case will resolve to origin 'Record' symbol. + */ if (type.aliasSymbol) { const target = (type as ts.TypeReference).target; if (target) { @@ -1270,22 +1342,31 @@ export class TsUtils { return false; } - public isStdPartialType(type: ts.Type): boolean { + isStdErrorType(type: ts.Type): boolean { + const symbol = type.symbol; + if (!symbol) { + return false; + } + const name = this.tsTypeChecker.getFullyQualifiedName(symbol); + return name === 'Error' && this.isGlobalSymbol(symbol); + } + + isStdPartialType(type: ts.Type): boolean { const sym = type.aliasSymbol; return !!sym && sym.getName() === 'Partial' && this.isGlobalSymbol(sym); } - public isStdRequiredType(type: ts.Type): boolean { + isStdRequiredType(type: ts.Type): boolean { const sym = type.aliasSymbol; return !!sym && sym.getName() === 'Required' && this.isGlobalSymbol(sym); } - public isStdReadonlyType(type: ts.Type): boolean { + isStdReadonlyType(type: ts.Type): boolean { const sym = type.aliasSymbol; return !!sym && sym.getName() === 'Readonly' && this.isGlobalSymbol(sym); } - public isLibraryType(type: ts.Type): boolean { + isLibraryType(type: ts.Type): boolean { const nonNullableType = type.getNonNullableType(); if (nonNullableType.isUnion()) { for (const componentType of nonNullableType.types) { @@ -1298,46 +1379,56 @@ export class TsUtils { return this.isLibrarySymbol(nonNullableType.aliasSymbol ?? nonNullableType.getSymbol()); } - public hasLibraryType(node: ts.Node): boolean { + hasLibraryType(node: ts.Node): boolean { return this.isLibraryType(this.tsTypeChecker.getTypeAtLocation(node)); } - public isLibrarySymbol(sym: ts.Symbol | undefined) { - if (sym && sym.declarations && sym.declarations.length > 0) { + isLibrarySymbol(sym: ts.Symbol | undefined): boolean { + if (sym?.declarations && sym.declarations.length > 0) { const srcFile = sym.declarations[0].getSourceFile(); if (!srcFile) { return false; } - const fileName = srcFile.fileName - // Symbols from both *.ts and *.d.ts files should obey interop rules. - // We disable such behavior for *.ts files in the test mode due to lack of 'ets' - // extension support. + const fileName = srcFile.fileName; + + /* + * Symbols from both *.ts and *.d.ts files should obey interop rules. + * We disable such behavior for *.ts files in the test mode due to lack of 'ets' + * extension support. + */ const ext = path.extname(fileName).toLowerCase(); const isThirdPartyCode = - TsUtils.ARKTS_IGNORE_DIRS.some(ignore => pathContainsDirectory(path.normalize(fileName), ignore)) || - TsUtils.ARKTS_IGNORE_FILES.some(ignore => path.basename(fileName) === ignore); - const isEts = (ext === '.ets'); - const isTs = (ext === '.ts' && !srcFile.isDeclarationFile); + TsUtils.ARKTS_IGNORE_DIRS.some((ignore) => { + return pathContainsDirectory(path.normalize(fileName), ignore); + }) || + TsUtils.ARKTS_IGNORE_FILES.some((ignore) => { + return path.basename(fileName) === ignore; + }); + const isEts = ext === '.ets'; + const isTs = ext === '.ts' && !srcFile.isDeclarationFile; const isStatic = (isEts || (isTs && this.testMode)) && !isThirdPartyCode; - // We still need to confirm support for certain API from the - // TypeScript standard library in ArkTS. Thus, for now do not - // count standard library modules. - return !isStatic && - !TsUtils.STANDARD_LIBRARIES.includes(path.basename(srcFile.fileName).toLowerCase()); + const isStdLib = TsUtils.STANDARD_LIBRARIES.includes(path.basename(fileName).toLowerCase()); + + /* + * We still need to confirm support for certain API from the + * TypeScript standard library in ArkTS. Thus, for now do not + * count standard library modules. + */ + return !isStatic && !isStdLib; } return false; } - public isStdFunctionType(type: ts.Type) { + isStdFunctionType(type: ts.Type): boolean { const sym = type.getSymbol(); - return sym && sym.getName() === 'Function' && this.isGlobalSymbol(sym); + return !!sym && sym.getName() === 'Function' && this.isGlobalSymbol(sym); } - public getScriptKind(srcFile: ts.SourceFile): ts.ScriptKind { - const fileName = srcFile.fileName - const ext = path.extname(fileName) + static getScriptKind(srcFile: ts.SourceFile): ts.ScriptKind { + const fileName = srcFile.fileName; + const ext = path.extname(fileName); switch (ext.toLowerCase()) { case ts.Extension.Js: return ts.ScriptKind.JS; @@ -1354,40 +1445,43 @@ export class TsUtils { } } - public isStdLibraryType(type: ts.Type): boolean { - return this.isStdLibrarySymbol(type.aliasSymbol ?? type.getSymbol()); + static isStdLibraryType(type: ts.Type): boolean { + return TsUtils.isStdLibrarySymbol(type.aliasSymbol ?? type.getSymbol()); } - public isStdLibrarySymbol(sym: ts.Symbol | undefined) { - if (sym && sym.declarations && sym.declarations.length > 0) { + static isStdLibrarySymbol(sym: ts.Symbol | undefined): boolean { + if (sym?.declarations && sym.declarations.length > 0) { const srcFile = sym.declarations[0].getSourceFile(); - return srcFile && - TsUtils.STANDARD_LIBRARIES.includes(path.basename(srcFile.fileName).toLowerCase()); + return srcFile && TsUtils.STANDARD_LIBRARIES.includes(path.basename(srcFile.fileName).toLowerCase()); } return false; } - public isIntrinsicObjectType(type: ts.Type): boolean { + static isIntrinsicObjectType(type: ts.Type): boolean { return !!(type.flags & ts.TypeFlags.NonPrimitive); } - public isDynamicType(type: ts.Type | undefined): boolean | undefined { + isDynamicType(type: ts.Type | undefined): boolean | undefined { if (type === undefined) { return false; } - // Return 'true' if it is an object of library type initialization, otherwise - // return 'false' if it is not an object of standard library type one. - // In the case of standard library type we need to determine context. + /* + * Return 'true' if it is an object of library type initialization, otherwise + * return 'false' if it is not an object of standard library type one. + * In the case of standard library type we need to determine context. + */ - // Check the non-nullable version of type to eliminate 'undefined' type - // from the union type elements. + /* + * Check the non-nullable version of type to eliminate 'undefined' type + * from the union type elements. + */ type = type.getNonNullableType(); if (type.isUnion()) { - for (let compType of type.types) { - let isDynamic = this.isDynamicType(compType); + for (const compType of type.types) { + const isDynamic = this.isDynamicType(compType); if (isDynamic || isDynamic === undefined) { return isDynamic; } @@ -1399,36 +1493,64 @@ export class TsUtils { return true; } - if (!this.isStdLibraryType(type) && !this.isIntrinsicObjectType(type) && !this.isAnyType(type)) { + if (!TsUtils.isStdLibraryType(type) && !TsUtils.isIntrinsicObjectType(type) && !TsUtils.isAnyType(type)) { return false; } return undefined; } - public isObjectType(type: ts.Type): type is ts.ObjectType { + static isObjectType(type: ts.Type): type is ts.ObjectType { return !!(type.flags & ts.TypeFlags.Object); } - private isAnonymous(type: ts.Type): boolean { - if (this.isObjectType(type)) { + private static isAnonymous(type: ts.Type): boolean { + if (TsUtils.isObjectType(type)) { return !!(type.objectFlags & ts.ObjectFlags.Anonymous); } return false; } - public isDynamicLiteralInitializer(expr: ts.Expression): boolean { + private isDynamicLiteralInitializerHandleCallExpression(callExpr: ts.CallExpression): boolean { + const type = this.tsTypeChecker.getTypeAtLocation(callExpr.expression); + + if (TsUtils.isAnyType(type)) { + return true; + } + + let sym: ts.Symbol | undefined = type.symbol; + if (this.isLibrarySymbol(sym)) { + return true; + } + + /* + * #13483: + * x.foo({ ... }), where 'x' is exported from some library: + */ + if (ts.isPropertyAccessExpression(callExpr.expression)) { + sym = this.trueSymbolAtLocation(callExpr.expression.expression); + if (sym && this.isLibrarySymbol(sym)) { + return true; + } + } + + return false; + } + + isDynamicLiteralInitializer(expr: ts.Expression): boolean { if (!ts.isObjectLiteralExpression(expr) && !ts.isArrayLiteralExpression(expr)) { return false; } - // Handle nested literals: - // { f: { ... } } + /* + * Handle nested literals: + * { f: { ... } } + */ let curNode: ts.Node = expr; while (ts.isObjectLiteralExpression(curNode) || ts.isArrayLiteralExpression(curNode)) { const exprType = this.tsTypeChecker.getContextualType(curNode); - if (exprType !== undefined && !this.isAnonymous(exprType)) { - const res = this.isDynamicType(exprType) + if (exprType !== undefined && !TsUtils.isAnonymous(exprType)) { + const res = this.isDynamicType(exprType); if (res !== undefined) { return res; } @@ -1440,40 +1562,22 @@ export class TsUtils { } } - // Handle calls with literals: - // foo({ ... }) - if (ts.isCallExpression(curNode)) { - const callExpr = curNode as ts.CallExpression; - const type = this.tsTypeChecker.getTypeAtLocation(callExpr.expression) - - if (this.isAnyType(type)) { - return true; - } - - let sym: ts.Symbol | undefined = type.symbol; - if(this.isLibrarySymbol(sym)) { - return true; - } - - // #13483: - // x.foo({ ... }), where 'x' is exported from some library: - if (ts.isPropertyAccessExpression(callExpr.expression)) { - sym = this.tsTypeChecker.getSymbolAtLocation(callExpr.expression.expression); - if (sym && sym.getFlags() & ts.SymbolFlags.Alias) { - sym = this.tsTypeChecker.getAliasedSymbol(sym); - if (this.isLibrarySymbol(sym)) { - return true; - } - } - } + /* + * Handle calls with literals: + * foo({ ... }) + */ + if (ts.isCallExpression(curNode) && this.isDynamicLiteralInitializerHandleCallExpression(curNode)) { + return true; } - // Handle property assignments with literals: - // obj.f = { ... } + /* + * Handle property assignments with literals: + * obj.f = { ... } + */ if (ts.isBinaryExpression(curNode)) { - const binExpr = curNode as ts.BinaryExpression; + const binExpr = curNode; if (ts.isPropertyAccessExpression(binExpr.left)) { - const propAccessExpr = binExpr.left as ts.PropertyAccessExpression; + const propAccessExpr = binExpr.left; const type = this.tsTypeChecker.getTypeAtLocation(propAccessExpr.expression); return this.isLibrarySymbol(type.symbol); } @@ -1482,12 +1586,16 @@ export class TsUtils { return false; } - public isEsObjectType(typeNode: ts.TypeNode): boolean { - return ts.isTypeReferenceNode(typeNode) && ts.isIdentifier(typeNode.typeName) && - typeNode.typeName.text === TsUtils.ES_OBJECT; + static isEsObjectType(typeNode: ts.TypeNode | undefined): boolean { + return ( + !!typeNode && + ts.isTypeReferenceNode(typeNode) && + ts.isIdentifier(typeNode.typeName) && + typeNode.typeName.text === TsUtils.ES_OBJECT + ); } - public isInsideBlock(node: ts.Node): boolean { + static isInsideBlock(node: ts.Node): boolean { let par = node.parent; while (par) { if (ts.isBlock(par)) { @@ -1498,130 +1606,122 @@ export class TsUtils { return false; } - public isEsObjectPossiblyAllowed(typeRef: ts.TypeReferenceNode): boolean { + static isEsObjectPossiblyAllowed(typeRef: ts.TypeReferenceNode): boolean { return ts.isVariableDeclaration(typeRef.parent); } - public isValueAssignableToESObject(node: ts.Node): boolean { + isValueAssignableToESObject(node: ts.Node): boolean { if (ts.isArrayLiteralExpression(node) || ts.isObjectLiteralExpression(node)) { return false; } const valueType = this.tsTypeChecker.getTypeAtLocation(node); - return this.isUnsupportedType(valueType) || this.isAnonymousType(valueType); + return TsUtils.isUnsupportedType(valueType) || TsUtils.isAnonymousType(valueType); } - public getVariableDeclarationTypeNode(node: ts.Node): ts.TypeNode | undefined { - let sym = this.trueSymbolAtLocation(node); + getVariableDeclarationTypeNode(node: ts.Node): ts.TypeNode | undefined { + const sym = this.trueSymbolAtLocation(node); if (sym === undefined) { return undefined; } - return this.getSymbolDeclarationTypeNode(sym); + return TsUtils.getSymbolDeclarationTypeNode(sym); } - public getSymbolDeclarationTypeNode(sym: ts.Symbol): ts.TypeNode | undefined { - const decl = this.getDeclaration(sym); + static getSymbolDeclarationTypeNode(sym: ts.Symbol): ts.TypeNode | undefined { + const decl = TsUtils.getDeclaration(sym); if (!!decl && ts.isVariableDeclaration(decl)) { return decl.type; } return undefined; } - public hasEsObjectType(node: ts.Node): boolean { - const typeNode = this.getVariableDeclarationTypeNode(node) - return typeNode !== undefined && this.isEsObjectType(typeNode); + hasEsObjectType(node: ts.Node): boolean { + const typeNode = this.getVariableDeclarationTypeNode(node); + return typeNode !== undefined && TsUtils.isEsObjectType(typeNode); } - public symbolHasEsObjectType(sym: ts.Symbol): boolean { - const typeNode = this.getSymbolDeclarationTypeNode(sym); - return typeNode !== undefined && this.isEsObjectType(typeNode); + static symbolHasEsObjectType(sym: ts.Symbol): boolean { + const typeNode = TsUtils.getSymbolDeclarationTypeNode(sym); + return typeNode !== undefined && TsUtils.isEsObjectType(typeNode); } - public isEsObjectSymbol(sym: ts.Symbol): boolean { - let decl = this.getDeclaration(sym); - return !!decl && ts.isTypeAliasDeclaration(decl) && decl.name.escapedText === TsUtils.ES_OBJECT && - decl.type.kind === ts.SyntaxKind.AnyKeyword; + static isEsObjectSymbol(sym: ts.Symbol): boolean { + const decl = TsUtils.getDeclaration(sym); + return ( + !!decl && + ts.isTypeAliasDeclaration(decl) && + decl.name.escapedText === TsUtils.ES_OBJECT && + decl.type.kind === ts.SyntaxKind.AnyKeyword + ); } - public isAnonymousType(type: ts.Type): boolean { + static isAnonymousType(type: ts.Type): boolean { if (type.isUnionOrIntersection()) { - for (let compType of type.types) { - if (this.isAnonymousType(compType)) { + for (const compType of type.types) { + if (TsUtils.isAnonymousType(compType)) { return true; } } return false; } - return (type.flags & ts.TypeFlags.Object) !== 0 && - ((type as ts.ObjectType).objectFlags & ts.ObjectFlags.Anonymous) !== 0; + return ( + (type.flags & ts.TypeFlags.Object) !== 0 && ((type as ts.ObjectType).objectFlags & ts.ObjectFlags.Anonymous) !== 0 + ); } - public getSymbolOfCallExpression(callExpr: ts.CallExpression): ts.Symbol | undefined { + getSymbolOfCallExpression(callExpr: ts.CallExpression): ts.Symbol | undefined { const signature = this.tsTypeChecker.getResolvedSignature(callExpr); const signDecl = signature?.getDeclaration(); - if (signDecl && signDecl.name) { + if (signDecl?.name) { return this.tsTypeChecker.getSymbolAtLocation(signDecl.name); } return undefined; } - // has to be re-implemented with local loop detection - public typeIsRecursive(topType: ts.Type, type: ts.Type | undefined = undefined): boolean { - if (type === undefined) { - type = topType; - } else if (type === topType) { - return true; - } else if (type.aliasSymbol) { + isFunctionCalledRecursively(funcExpr: ts.FunctionExpression): boolean { + if (!funcExpr.name) { return false; } - if (type.isUnion()) { - for (let unionElem of type.types) { - if (this.typeIsRecursive(topType, unionElem)) { - return true; - } - } - } - if (type.flags & ts.TypeFlags.Object && (type as ts.ObjectType).objectFlags & ts.ObjectFlags.Reference) { - const typeArgs = this.tsTypeChecker.getTypeArguments(type as ts.TypeReference); - if (typeArgs) { - for (const typeArg of typeArgs) { - if (this.typeIsRecursive(topType, typeArg)) { - return true; - } - } - } - } - return false; - } - - public isFunctionCalledRecursively(funcExpr: ts.FunctionExpression): boolean { - if (!funcExpr.name) return false; - const sym = this.tsTypeChecker.getSymbolAtLocation(funcExpr.name); - if (!sym) return false; + if (!sym) { + return false; + } let found = false; - const self = this; - function visitNode(tsNode: ts.Node) { - // Stop visiting child nodes if finished searching. - if (found) { - return; - } - - if (ts.isCallExpression(tsNode) && ts.isIdentifier(tsNode.expression)) { - const callSym = self.tsTypeChecker.getSymbolAtLocation(tsNode.expression); + const callback = (node: ts.Node): void => { + if (ts.isCallExpression(node) && ts.isIdentifier(node.expression)) { + const callSym = this.tsTypeChecker.getSymbolAtLocation(node.expression); if (callSym && callSym === sym) { found = true; - return; } } + }; - // Visit children nodes. - tsNode.forEachChild(visitNode); - } + const stopCondition = (node: ts.Node): boolean => { + void node; + return found; + }; - visitNode(funcExpr); + forEachNodeInSubtree(funcExpr, callback, stopCondition); return found; } + + getTypeOrTypeConstraintAtLocation(expr: ts.Expression): ts.Type { + const type = this.tsTypeChecker.getTypeAtLocation(expr); + if (type.isTypeParameter()) { + const constraint = type.getConstraint(); + if (constraint) { + return constraint; + } + } + return type; + } + + private areCompatibleFunctionals(lhsType: ts.Type, rhsType: ts.Type): boolean { + return ( + (this.isStdFunctionType(lhsType) || TsUtils.isFunctionalType(lhsType)) && + (this.isStdFunctionType(rhsType) || TsUtils.isFunctionalType(rhsType)) + ); + } } diff --git a/ets2panda/linter/src/utils/functions/identiferUseInValueContext.ts b/ets2panda/linter-4.2/src/identiferUseInValueContext.ts similarity index 69% rename from ets2panda/linter/src/utils/functions/identiferUseInValueContext.ts rename to ets2panda/linter-4.2/src/identiferUseInValueContext.ts index 43c1389a96b66fc1843d3fc26fc7d0d8415258e1..0ebd1ed973f32db1c527876c1b949b60d9991f8a 100644 --- a/ets2panda/linter/src/utils/functions/identiferUseInValueContext.ts +++ b/ets2panda/linter-4.2/src/identiferUseInValueContext.ts @@ -16,22 +16,28 @@ import * as ts from 'typescript'; function isInstanceofContext(tsIdentStart: ts.Node): boolean { - return ts.isBinaryExpression(tsIdentStart.parent) && + return ( + ts.isBinaryExpression(tsIdentStart.parent) && tsIdentStart.parent.operatorToken.kind === ts.SyntaxKind.InstanceOfKeyword + ); } function isNewExpressionContext(tsIdentStart: ts.Node): boolean { - return ts.isNewExpression(tsIdentStart.parent) && tsIdentStart === tsIdentStart.parent.expression + return ts.isNewExpression(tsIdentStart.parent) && tsIdentStart === tsIdentStart.parent.expression; } +/* + * If identifier is the right-most name of Property Access chain or Qualified name, + * or it's a separate identifier expression, then identifier is being referenced as an value. + */ function isQualifiedNameContext(tsIdentStart: ts.Node, tsIdentifier: ts.Identifier): boolean { // rightmost in AST is rightmost in qualified name chain - return ts.isQualifiedName(tsIdentStart) && tsIdentifier !== tsIdentStart.right + return ts.isQualifiedName(tsIdentStart) && tsIdentifier !== tsIdentStart.right; } function isPropertyAccessContext(tsIdentStart: ts.Node, tsIdentifier: ts.Identifier): boolean { // rightmost in AST is rightmost in qualified name chain - return ts.isPropertyAccessExpression(tsIdentStart) && tsIdentifier !== tsIdentStart.name + return ts.isPropertyAccessExpression(tsIdentStart) && tsIdentifier !== tsIdentStart.name; } function getQualifiedStart(ident: ts.Node): ts.Node { @@ -43,27 +49,18 @@ function getQualifiedStart(ident: ts.Node): ts.Node { } function isEnumPropAccess(ident: ts.Identifier, tsSym: ts.Symbol, context: ts.Node): boolean { - return ts.isElementAccessExpression(context) && - (context as ts.ElementAccessExpression).expression == ident && - !!(tsSym.flags & ts.SymbolFlags.Enum); -} - -function isValidTypeNode(node: ts.TypeNode): boolean { - return !ts.isTypeOfExpression(node); + return ( + ts.isElementAccessExpression(context) && + !!(tsSym.flags & ts.SymbolFlags.Enum) && + (context.expression === ident || + ts.isPropertyAccessExpression(context.expression) && context.expression.name === ident) + ); } -export function identiferUseInValueContext( - ident: ts.Identifier, tsSym: ts.Symbol -) { - let qualifiedStart = getQualifiedStart(ident); - let parent = qualifiedStart.parent; - - return !( - // treat TypeQuery as valid because it's already forbidden (FaultID.TypeQuery) - ts.isTypeNode(parent) && isValidTypeNode(parent) || - // If identifier is the right-most name of Property Access chain or Qualified name, - // or it's a separate identifier expression, then identifier is being referenced as an value. - isEnumPropAccess(ident, tsSym, parent) || +function isValidParent(parent: ts.Node): boolean { + // treat TypeQuery as valid because it's already forbidden (FaultID.TypeQuery) + return ( + ts.isTypeNode(parent) && !ts.isTypeOfExpression(parent) || ts.isExpressionWithTypeArguments(parent) || ts.isExportAssignment(parent) || ts.isExportSpecifier(parent) || @@ -75,10 +72,19 @@ export function identiferUseInValueContext( ts.isEnumDeclaration(parent) || ts.isNamespaceImport(parent) || ts.isImportSpecifier(parent) || + ts.isImportEqualsDeclaration(parent) + ); +} + +export function identiferUseInValueContext(ident: ts.Identifier, tsSym: ts.Symbol): boolean { + const qualifiedStart = getQualifiedStart(ident); + const parent = qualifiedStart.parent; + const isValidUse = + isValidParent(parent) || + isEnumPropAccess(ident, tsSym, parent) || isQualifiedNameContext(qualifiedStart, ident) || isPropertyAccessContext(qualifiedStart, ident) || isNewExpressionContext(qualifiedStart) || - isInstanceofContext(qualifiedStart) || - ts.isImportEqualsDeclaration(parent) - ); + isInstanceofContext(qualifiedStart); + return !isValidUse; } diff --git a/ets2panda/linter-4.2/src/ts-diagnostics/TSCCompiledProgram.ts b/ets2panda/linter-4.2/src/ts-diagnostics/TSCCompiledProgram.ts index 29548ef9d8c38d5d0772d0a9dd1691ccc4b02a46..ef320535f95bbe088d8eb9c7511d596e34bac43e 100644 --- a/ets2panda/linter-4.2/src/ts-diagnostics/TSCCompiledProgram.ts +++ b/ets2panda/linter-4.2/src/ts-diagnostics/TSCCompiledProgram.ts @@ -14,17 +14,17 @@ */ import * as ts from 'typescript'; -import { ProblemInfo } from '../ProblemInfo'; +import type { ProblemInfo } from '../ProblemInfo'; import { ProblemSeverity } from '../ProblemSeverity'; -import { LintOptions } from '../LintOptions'; +import type { LintOptions } from '../LintOptions'; import { TypeScriptDiagnosticsExtractor } from './TypeScriptDiagnosticsExtractor'; import { compile } from '../CompilerWrapper'; import { getNodeOrLineEnd } from '../Utils'; import { FaultID, faultsAttrs } from '../Problems'; export class TSCCompiledProgram { - private diagnosticsExtractor: TypeScriptDiagnosticsExtractor; - private wasStrict: boolean; + private readonly diagnosticsExtractor: TypeScriptDiagnosticsExtractor; + private readonly wasStrict: boolean; constructor(program: ts.Program, options: LintOptions) { const { strict, nonStrict, wasStrict } = getTwoCompiledVersions(program, options); @@ -32,54 +32,61 @@ export class TSCCompiledProgram { this.wasStrict = wasStrict; } - public getOriginalProgram(): ts.Program { - return this.wasStrict - ? this.diagnosticsExtractor.strictProgram - : this.diagnosticsExtractor.nonStrictProgram; + getOriginalProgram(): ts.Program { + return this.wasStrict ? this.diagnosticsExtractor.strictProgram : this.diagnosticsExtractor.nonStrictProgram; } - public getStrictDiagnostics(fileName: string): ts.Diagnostic[] { + getStrictDiagnostics(fileName: string): ts.Diagnostic[] { return this.diagnosticsExtractor.getStrictDiagnostics(fileName); } } -export function getStrictOptions(strict: boolean = true) { +export function getStrictOptions(): { + strictNullChecks: boolean; + strictFunctionTypes: boolean; + strictPropertyInitialization: boolean; + noImplicitReturns: boolean; +} { return { - strictNullChecks: strict, - strictFunctionTypes: strict, - strictPropertyInitialization: strict, - noImplicitReturns: strict, - } + strictNullChecks: true, + strictFunctionTypes: true, + strictPropertyInitialization: true, + noImplicitReturns: true + }; +} + +function isStrict(compilerOptions: ts.CompilerOptions): boolean { + const strictOptions = getStrictOptions(); + let wasStrict = false; + // wasStrict evaluates true if any of the strict options was set + Object.keys(strictOptions).forEach((x) => { + wasStrict = wasStrict || !!compilerOptions[x]; + }); + return wasStrict; } function getTwoCompiledVersions( program: ts.Program, - options: LintOptions, + options: LintOptions ): { strict: ts.Program; nonStrict: ts.Program; wasStrict: boolean } { - const compilerOptions = { ...program.getCompilerOptions()}; - - const wasStrict = inverseStrictOptions(compilerOptions); - const inversedOptions = getStrictOptions(!wasStrict); + const compilerOptions = program.getCompilerOptions(); + const inversedOptions = getInversedOptions(compilerOptions); const withInversedOptions = compile(options, inversedOptions); - + const wasStrict = isStrict(compilerOptions); return { strict: wasStrict ? program : withInversedOptions, nonStrict: wasStrict ? withInversedOptions : program, - wasStrict: wasStrict, - } + wasStrict: wasStrict + }; } -/** - * Returns true if options were initially strict - */ -function inverseStrictOptions(compilerOptions: ts.CompilerOptions): boolean { - const strictOptions = getStrictOptions(); - let wasStrict = false; - Object.keys(strictOptions).forEach(x => { - wasStrict = wasStrict || !!compilerOptions[x]; +function getInversedOptions(compilerOptions: ts.CompilerOptions): ts.CompilerOptions { + const newOptions = { ...compilerOptions }; + const wasStrict = isStrict(compilerOptions); + Object.keys(getStrictOptions()).forEach((key) => { + newOptions[key] = !wasStrict; }); - // wasStrict evaluates true if any of the strict options was set - return wasStrict; + return newOptions; } export function transformDiagnostic(diagnostic: ts.Diagnostic): ProblemInfo { @@ -96,12 +103,12 @@ export function transformDiagnostic(diagnostic: ts.Diagnostic): ProblemInfo { start: startPos, end: endPos, type: 'StrictModeError', - severity: ProblemSeverity.ERROR, // expect strict options to always present + severity: ProblemSeverity.ERROR, problem: FaultID[faultId], suggest: messageText, rule: messageText, ruleTag: faultsAttrs[faultId] ? Number(faultsAttrs[faultId].cookBookRef) : 0, - autofixable: false, + autofixable: false }; } @@ -109,10 +116,10 @@ export function transformDiagnostic(diagnostic: ts.Diagnostic): ProblemInfo { * Returns line and column of the diagnostic's node, counts from 1 */ function getLineAndColumn(diagnostic: ts.Diagnostic): { line: number; column: number } { - let { line, character } = diagnostic.file!.getLineAndCharacterOfPosition(diagnostic.start!); + const { line, character } = diagnostic.file!.getLineAndCharacterOfPosition(diagnostic.start!); // TSC counts lines and columns from zero return { line: line + 1, - column: character + 1, - } + column: character + 1 + }; } diff --git a/ets2panda/linter-4.2/src/ts-diagnostics/TypeScriptDiagnosticsExtractor.ts b/ets2panda/linter-4.2/src/ts-diagnostics/TypeScriptDiagnosticsExtractor.ts index 0d5106dd9f116531b356ca406fc9f4fff8ef053a..7ce161575f759311a4bfc550c1ac81c0ff97c1ef 100644 --- a/ets2panda/linter-4.2/src/ts-diagnostics/TypeScriptDiagnosticsExtractor.ts +++ b/ets2panda/linter-4.2/src/ts-diagnostics/TypeScriptDiagnosticsExtractor.ts @@ -13,19 +13,22 @@ * limitations under the License. */ -import * as ts from 'typescript'; +import type * as ts from 'typescript'; export class TypeScriptDiagnosticsExtractor { - constructor(public strictProgram: ts.Program, public nonStrictProgram: ts.Program) { - } + constructor( + public strictProgram: ts.Program, + public nonStrictProgram: ts.Program + ) {} /** * Returns diagnostics which appear in strict compilation mode only */ - public getStrictDiagnostics(fileName: string): ts.Diagnostic[] { + getStrictDiagnostics(fileName: string): ts.Diagnostic[] { // applying filter is a workaround for tsc bug - const strict = getAllDiagnostics(this.strictProgram, fileName) - .filter(diag => !(diag.length === 0 && diag.start === 0)); + const strict = getAllDiagnostics(this.strictProgram, fileName).filter((diag) => { + return !(diag.length === 0 && diag.start === 0); + }); const nonStrict = getAllDiagnostics(this.nonStrictProgram, fileName); // collect hashes for later easier comparison @@ -37,18 +40,21 @@ export class TypeScriptDiagnosticsExtractor { return result; }, new Set()); // return diagnostics which weren't detected in non-strict mode - return strict.filter(value => { + return strict.filter((value) => { const hash = hashDiagnostic(value); - return (hash && !nonStrictHashes.has(hash)); + return hash && !nonStrictHashes.has(hash); }); } } function getAllDiagnostics(program: ts.Program, fileName: string): ts.Diagnostic[] { const sourceFile = program.getSourceFile(fileName); - return program.getSemanticDiagnostics(sourceFile) - .concat(program.getSyntacticDiagnostics(sourceFile)) - .filter(diag => diag.file === sourceFile); + return program. + getSemanticDiagnostics(sourceFile). + concat(program.getSyntacticDiagnostics(sourceFile)). + filter((diag) => { + return diag.file === sourceFile; + }); } function hashDiagnostic(diagnostic: ts.Diagnostic): string | undefined { diff --git a/ets2panda/linter-4.2/stats_calculator/src/calculate-stats.ts b/ets2panda/linter-4.2/stats_calculator/src/calculate-stats.ts index bd0f5a4bff858416ea0c5fb1e29814117b7d11b6..3f8eb2410e7e586dc36918afd67bad9233abad6b 100644 --- a/ets2panda/linter-4.2/stats_calculator/src/calculate-stats.ts +++ b/ets2panda/linter-4.2/stats_calculator/src/calculate-stats.ts @@ -44,7 +44,7 @@ interface ArkTsIssueType { description: string; type: string; count: number; -}; +} interface Statistics { totalErrors: number; @@ -52,22 +52,22 @@ interface Statistics { linesWithErrors: number; linesWithWarnings: number; issues: Map; -}; +} function isError(defectInfo: DefectInfo): boolean { return defectInfo.category === ARK_TS_ISSUES_ERROR_CATEGORY; } -function fillIssueInfo(statistics: Statistics, defectInfo: DefectInfo) { - const recipeNo = parseInt(defectInfo.ruleDocPath!!.substring( - 'docs/recipe'.length, - defectInfo.ruleDocPath!!.length - '.md'.length)); - let issueInfo = statistics.issues.get(recipeNo); +function fillIssueInfo(statistics: Statistics, defectInfo: DefectInfo): void { + const recipeNo = parseInt( + defectInfo.ruleDocPath.substring('docs/recipe'.length, defectInfo.ruleDocPath.length - '.md'.length) + ); + const issueInfo = statistics.issues.get(recipeNo); if (!issueInfo) { statistics.issues.set(recipeNo, { description: defectInfo.description, type: isError(defectInfo) ? 'error' : 'warn', - count: 1, + count: 1 }); } else { issueInfo.count += 1; @@ -75,7 +75,7 @@ function fillIssueInfo(statistics: Statistics, defectInfo: DefectInfo) { } function parse(reportJson: ReportJson): Statistics { - let statistics: Statistics = { + const statistics: Statistics = { totalErrors: 0, totalWarnings: 0, linesWithErrors: 0, @@ -93,7 +93,7 @@ function parse(reportJson: ReportJson): Statistics { continue; } - fillIssueInfo(statistics, defectInfo) + fillIssueInfo(statistics, defectInfo); if (isError(defectInfo)) { statistics.totalErrors += 1; @@ -114,7 +114,7 @@ function read(filePath: string): ReportJson { return JSON.parse(fs.readFileSync(filePath, { encoding: 'utf8', flag: 'r' })); } -function main() { +function main(): void { if (process.argv.length < 3) { console.error('Path to input json was not provided, exiting'); process.exit(1); @@ -122,6 +122,8 @@ function main() { console.log(parse(read(process.argv[2]))); } -// file is stored in project's directory under the following path: -// /.idea/code-linter/eslintAgent/output.json -main() +/* + * file is stored in project's directory under the following path: + * /.idea/code-linter/eslintAgent/output.json + */ +main(); diff --git a/ets2panda/linter-4.2/test/ambient_module.ts.relax.json b/ets2panda/linter-4.2/test/ambient_module.ts.relax.json old mode 100644 new mode 100755 index a02ca7fc8935464e83947500d0dfc1e6558752dd..81f9bd90b4a34906dbf4f322ce7315bf6de3de12 --- a/ets2panda/linter-4.2/test/ambient_module.ts.relax.json +++ b/ets2panda/linter-4.2/test/ambient_module.ts.relax.json @@ -26,7 +26,7 @@ "column": 3, "problem": "NonDeclarationInNamespace", "suggest": "", - "rule": "Non-declaration statements in namespaces are not supported (arkts-no-ns-statements)" + "rule": "Non-declaration statements in namespaces are not supported (single semicolons are considered as empty non-declaration statements) (arkts-no-ns-statements)" }, { "line": 20, @@ -47,7 +47,7 @@ "column": 3, "problem": "NonDeclarationInNamespace", "suggest": "", - "rule": "Non-declaration statements in namespaces are not supported (arkts-no-ns-statements)" + "rule": "Non-declaration statements in namespaces are not supported (single semicolons are considered as empty non-declaration statements) (arkts-no-ns-statements)" }, { "line": 25, diff --git a/ets2panda/linter-4.2/test/ambient_module.ts.strict.json b/ets2panda/linter-4.2/test/ambient_module.ts.strict.json old mode 100644 new mode 100755 index a02ca7fc8935464e83947500d0dfc1e6558752dd..81f9bd90b4a34906dbf4f322ce7315bf6de3de12 --- a/ets2panda/linter-4.2/test/ambient_module.ts.strict.json +++ b/ets2panda/linter-4.2/test/ambient_module.ts.strict.json @@ -26,7 +26,7 @@ "column": 3, "problem": "NonDeclarationInNamespace", "suggest": "", - "rule": "Non-declaration statements in namespaces are not supported (arkts-no-ns-statements)" + "rule": "Non-declaration statements in namespaces are not supported (single semicolons are considered as empty non-declaration statements) (arkts-no-ns-statements)" }, { "line": 20, @@ -47,7 +47,7 @@ "column": 3, "problem": "NonDeclarationInNamespace", "suggest": "", - "rule": "Non-declaration statements in namespaces are not supported (arkts-no-ns-statements)" + "rule": "Non-declaration statements in namespaces are not supported (single semicolons are considered as empty non-declaration statements) (arkts-no-ns-statements)" }, { "line": 25, diff --git a/ets2panda/linter-4.2/test/classB.ts.relax.json b/ets2panda/linter-4.2/test/classB.ts.relax.json index e44fd80c47fb55cd43e0a82e2db3b2b8cf8c8858..dc2b9ced44f0b0fed43aafdf3a49426c859daf07 100644 --- a/ets2panda/linter-4.2/test/classB.ts.relax.json +++ b/ets2panda/linter-4.2/test/classB.ts.relax.json @@ -15,8 +15,8 @@ ], "nodes": [ { - "line": 24, - "column": 5, + "line": 25, + "column": 16, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" diff --git a/ets2panda/linter-4.2/test/classB.ts.strict.json b/ets2panda/linter-4.2/test/classB.ts.strict.json index e44fd80c47fb55cd43e0a82e2db3b2b8cf8c8858..dc2b9ced44f0b0fed43aafdf3a49426c859daf07 100644 --- a/ets2panda/linter-4.2/test/classB.ts.strict.json +++ b/ets2panda/linter-4.2/test/classB.ts.strict.json @@ -15,8 +15,8 @@ ], "nodes": [ { - "line": 24, - "column": 5, + "line": 25, + "column": 16, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" diff --git a/ets2panda/linter-4.2/test/class_as_object.ts b/ets2panda/linter-4.2/test/class_as_object.ts index 302432975cbb76c7c446fa410aad9b6220153f5d..1ee2fb6e857889da224b5f4618da118e29d4072c 100644 --- a/ets2panda/linter-4.2/test/class_as_object.ts +++ b/ets2panda/linter-4.2/test/class_as_object.ts @@ -13,7 +13,7 @@ * limitations under the License. */ -import { Something, SomethingFactory, SomethingBar, Bar } from "./oh_modules/ohos_factory"; +import { Something, SomethingFactory, SomethingBar, Bar, Select } from "./oh_modules/ohos_factory"; class C { static a = 5; @@ -91,3 +91,33 @@ for (let item = 0; item < Object.keys(Color).length; item++) { foo2(() => C); export { C as H }; + +// #14228 +let data = new Select().from(C).eq('key').query(C); // Ok +invalid_func(C); // Ok +let a: any; +a.foo(C); // Ok + +let col = 'WHITE'; +console.log(Color[col]) + +// #14184 +namespace NS { + export enum E { + A = 'A', + B = 'B', + C = 'C' + } +} + +let s: string = 'B'; +let s2: string = NS.E[s]; + +for (let item = 0; item < Object.keys(NS.E).length; item++) { + console.log(item); +} + +/** + * {@link C} - should not report error + */ +class JSDocClass {} \ No newline at end of file diff --git a/ets2panda/linter-4.2/test/class_as_object.ts.relax.json b/ets2panda/linter-4.2/test/class_as_object.ts.relax.json index fbae23bf5543a366a545d54810c03be11b97a304..87b0d7889dcea4ea46095d741c38bf3a6555c40e 100644 --- a/ets2panda/linter-4.2/test/class_as_object.ts.relax.json +++ b/ets2panda/linter-4.2/test/class_as_object.ts.relax.json @@ -15,72 +15,121 @@ { "line": 27, "column": 9, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 28, "column": 5, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 29, "column": 11, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 30, "column": 7, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 38, "column": 20, - "problem": "TypeQuery" + "problem": "TypeQuery", + "suggest": "", + "rule": "\"typeof\" operator is allowed only in expression contexts (arkts-no-type-query)" }, { "line": 39, "column": 6, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 42, "column": 10, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 45, "column": 20, - "problem": "TypeQuery" + "problem": "TypeQuery", + "suggest": "", + "rule": "\"typeof\" operator is allowed only in expression contexts (arkts-no-type-query)" }, { "line": 46, "column": 8, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 49, "column": 12, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 58, "column": 20, - "problem": "TypeQuery" + "problem": "TypeQuery", + "suggest": "", + "rule": "\"typeof\" operator is allowed only in expression contexts (arkts-no-type-query)" }, { "line": 60, "column": 7, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 87, "column": 39, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 91, "column": 12, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" + }, + { + "line": 96, + "column": 5, + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + }, + { + "line": 98, + "column": 8, + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + }, + { + "line": 116, + "column": 42, + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" } ] } \ No newline at end of file diff --git a/ets2panda/linter-4.2/test/class_as_object.ts.strict.json b/ets2panda/linter-4.2/test/class_as_object.ts.strict.json index fbae23bf5543a366a545d54810c03be11b97a304..87b0d7889dcea4ea46095d741c38bf3a6555c40e 100644 --- a/ets2panda/linter-4.2/test/class_as_object.ts.strict.json +++ b/ets2panda/linter-4.2/test/class_as_object.ts.strict.json @@ -15,72 +15,121 @@ { "line": 27, "column": 9, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 28, "column": 5, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 29, "column": 11, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 30, "column": 7, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 38, "column": 20, - "problem": "TypeQuery" + "problem": "TypeQuery", + "suggest": "", + "rule": "\"typeof\" operator is allowed only in expression contexts (arkts-no-type-query)" }, { "line": 39, "column": 6, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 42, "column": 10, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 45, "column": 20, - "problem": "TypeQuery" + "problem": "TypeQuery", + "suggest": "", + "rule": "\"typeof\" operator is allowed only in expression contexts (arkts-no-type-query)" }, { "line": 46, "column": 8, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 49, "column": 12, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 58, "column": 20, - "problem": "TypeQuery" + "problem": "TypeQuery", + "suggest": "", + "rule": "\"typeof\" operator is allowed only in expression contexts (arkts-no-type-query)" }, { "line": 60, "column": 7, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 87, "column": 39, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 91, "column": 12, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" + }, + { + "line": 96, + "column": 5, + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + }, + { + "line": 98, + "column": 8, + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + }, + { + "line": 116, + "column": 42, + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" } ] } \ No newline at end of file diff --git a/ets2panda/linter-4.2/test/dynamic_lib.d.ts b/ets2panda/linter-4.2/test/dynamic_lib.d.ts index e3da589b5418ec259aa310a41fea0f454f17ede3..85dddff2c0af534270c57807f24c3232e3691d89 100644 --- a/ets2panda/linter-4.2/test/dynamic_lib.d.ts +++ b/ets2panda/linter-4.2/test/dynamic_lib.d.ts @@ -93,3 +93,9 @@ declare class B { } export declare function bad_func(): A & B; + +export type IndexedSignatureType = { + [key: string]: string; +} + +export declare function postCardAction(comp: Object, action: Object): void; \ No newline at end of file diff --git a/ets2panda/linter-4.2/test/dynamic_object_literals.ts b/ets2panda/linter-4.2/test/dynamic_object_literals.ts index ea7f9759a2e8cf76d1eb0dd81536959bfcf1beeb..dc4de64dffcdc883ee0d3fa120cc0ab65598a816 100644 --- a/ets2panda/linter-4.2/test/dynamic_object_literals.ts +++ b/ets2panda/linter-4.2/test/dynamic_object_literals.ts @@ -24,7 +24,9 @@ import { dynamic_array, padding, margin, - position + position, + IndexedSignatureType, + postCardAction } from "./dynamic_lib" function main(): void { @@ -84,4 +86,22 @@ dynamic_array.splice(2, 0, {a: 1, b: '2'}); // #13550 - allow literals as property names in dynamic context padding({'top': '0px', 'right': '5px', 'bottom': '10px', 'left': '15px'}); margin({'top': '10px', 'right': '20px', 'bottom': '30px', 'left': '40px'}); -position({'x': '20', 'y': '40'}); \ No newline at end of file +position({'x': '20', 'y': '40'}); + +// allow literal as property name for type aliases that come from interop +function typeAliasLitAsPropName(): IndexedSignatureType { + return { + 'a': '1', + 'b': '2', + 'c': '3' + } +} + +// #14399 +postCardAction({}, { + "action": 'router', + "abilityName": 'SomeAbility', + "params": { + "message": 'add detail' + } +}); \ No newline at end of file diff --git a/ets2panda/linter-4.2/test/es_object.ts b/ets2panda/linter-4.2/test/es_object.ts index 445b5c0c07e1db8eb18c4006d860e3476659f6ae..8b62f20f81b12743f1c4fb4aa2025ee6c99cd454 100644 --- a/ets2panda/linter-4.2/test/es_object.ts +++ b/ets2panda/linter-4.2/test/es_object.ts @@ -145,7 +145,7 @@ foo4([2, 3]) foo5([2, 3]) foo4(["str1", "str2"]) foo5(["str1", "str2"]) -let n = new ESObject[0] +let n: ESObject; n = null foo4(n) diff --git a/ets2panda/linter-4.2/test/es_object.ts.relax.json b/ets2panda/linter-4.2/test/es_object.ts.relax.json index f3d5acdb48589622baa499e265b74a732fa003f6..5bab1a3eee7a781b35c0996be116badfe9617820 100644 --- a/ets2panda/linter-4.2/test/es_object.ts.relax.json +++ b/ets2panda/linter-4.2/test/es_object.ts.relax.json @@ -26,231 +26,231 @@ "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 21, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 22, "column": 11, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 25, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 26, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 27, "column": 11, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 29, "column": 21, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 29, "column": 35, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 29, "column": 53, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 35, "column": 14, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 35, "column": 28, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 35, "column": 46, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 35, "column": 58, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 39, "column": 14, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 39, "column": 28, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 39, "column": 46, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 39, "column": 58, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 43, "column": 14, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 43, "column": 28, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 43, "column": 46, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 43, "column": 60, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 48, "column": 19, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 48, "column": 33, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 48, "column": 51, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 48, "column": 63, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 52, "column": 19, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 52, "column": 33, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 52, "column": 51, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 52, "column": 63, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 56, "column": 19, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 56, "column": 33, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 56, "column": 51, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 56, "column": 65, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 60, @@ -271,175 +271,175 @@ "column": 18, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 64, "column": 32, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 64, "column": 50, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 66, "column": 15, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 67, "column": 17, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 70, "column": 13, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 71, "column": 15, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 77, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 78, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 79, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 80, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 82, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 83, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 85, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 86, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 87, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 88, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 90, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 91, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 93, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 94, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 95, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 96, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 98, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 99, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 103, @@ -453,28 +453,28 @@ "column": 11, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 106, "column": 11, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 108, "column": 18, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 109, "column": 18, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 114, @@ -488,105 +488,112 @@ "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 119, "column": 25, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 119, "column": 36, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 136, "column": 25, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 136, "column": 38, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 148, "column": 5, - "problem": "AnyType", + "problem": "EsObjectType", "suggest": "", - "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" + }, + { + "line": 149, + "column": 1, + "problem": "EsObjectType", + "suggest": "", + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 154, "column": 32, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 154, "column": 45, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 154, "column": 58, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 162, "column": 32, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 162, "column": 47, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 162, "column": 60, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 170, "column": 28, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 172, "column": 22, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 174, "column": 30, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 176, @@ -600,21 +607,21 @@ "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 178, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 179, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 181, @@ -628,49 +635,49 @@ "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 183, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 184, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 185, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 188, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 189, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 190, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" } ] } \ No newline at end of file diff --git a/ets2panda/linter-4.2/test/es_object.ts.strict.json b/ets2panda/linter-4.2/test/es_object.ts.strict.json index 5c869e41f36b202e2f7537f9bfa7a312c7073b8a..86c0f49acf614ff7fd4b654983ee1a6684b0d3a3 100644 --- a/ets2panda/linter-4.2/test/es_object.ts.strict.json +++ b/ets2panda/linter-4.2/test/es_object.ts.strict.json @@ -26,231 +26,231 @@ "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 21, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 22, "column": 11, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 25, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 26, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 27, "column": 11, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 29, "column": 21, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 29, "column": 35, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 29, "column": 53, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 35, "column": 14, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 35, "column": 28, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 35, "column": 46, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 35, "column": 58, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 39, "column": 14, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 39, "column": 28, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 39, "column": 46, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 39, "column": 58, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 43, "column": 14, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 43, "column": 28, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 43, "column": 46, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 43, "column": 60, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 48, "column": 19, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 48, "column": 33, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 48, "column": 51, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 48, "column": 63, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 52, "column": 19, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 52, "column": 33, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 52, "column": 51, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 52, "column": 63, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 56, "column": 19, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 56, "column": 33, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 56, "column": 51, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 56, "column": 65, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 60, @@ -271,175 +271,175 @@ "column": 18, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 64, "column": 32, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 64, "column": 50, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 66, "column": 15, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 67, "column": 17, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 70, "column": 13, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 71, "column": 15, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 77, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 78, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 79, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 80, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 82, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 83, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 85, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 86, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 87, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 88, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 90, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 91, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 93, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 94, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 95, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 96, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 98, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 99, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 103, @@ -453,28 +453,28 @@ "column": 11, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 106, "column": 11, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 108, "column": 18, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 109, "column": 18, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 114, @@ -488,105 +488,112 @@ "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 119, "column": 25, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 119, "column": 36, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 136, "column": 25, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 136, "column": 38, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 148, "column": 5, - "problem": "AnyType", + "problem": "EsObjectType", "suggest": "", - "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" + }, + { + "line": 149, + "column": 1, + "problem": "EsObjectType", + "suggest": "", + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 154, "column": 32, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 154, "column": 45, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 154, "column": 58, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 162, "column": 32, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 162, "column": 47, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 162, "column": 60, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 170, "column": 28, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 172, "column": 22, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 174, "column": 30, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 176, @@ -600,21 +607,21 @@ "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 178, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 179, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 181, @@ -628,49 +635,49 @@ "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 183, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 184, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 185, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 188, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 189, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 190, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" } ] } \ No newline at end of file diff --git a/ets2panda/linter-4.2/test/function_expression.ts b/ets2panda/linter-4.2/test/function_expression.ts index e9010cca483f049f8a7866f83d6631bfd2554a8d..1016e1733d2093a094e8f6382a8424478f2f1f2c 100644 --- a/ets2panda/linter-4.2/test/function_expression.ts +++ b/ets2panda/linter-4.2/test/function_expression.ts @@ -62,7 +62,7 @@ const asyncFun = async function() { }; const factorial = function f(n: number): number { - return n == 1 ? 1 : n * f(n - 1); + return n === 1 ? 1 : n * f(n - 1); }; class C { diff --git a/ets2panda/linter-4.2/test/function_expression.ts.autofix.json b/ets2panda/linter-4.2/test/function_expression.ts.autofix.json index 90e6f3f3b1843e1b27a91717ae8ec47ced32aeaa..645f7e48d85a7f3dd45bf9f0b69bf382559b4d37 100644 --- a/ets2panda/linter-4.2/test/function_expression.ts.autofix.json +++ b/ets2panda/linter-4.2/test/function_expression.ts.autofix.json @@ -38,7 +38,7 @@ { "start": 658, "end": 709, - "replacementText": "(x: number, y): number => {\r\n return x * y;\r\n}" + "replacementText": "(x: number, y): number => {\n return x * y;\n}" } ], "suggest": "", @@ -61,7 +61,7 @@ { "start": 759, "end": 792, - "replacementText": "() => {\r\n return 100;\r\n}" + "replacementText": "() => {\n return 100;\n}" } ], "suggest": "", @@ -76,7 +76,7 @@ { "start": 813, "end": 863, - "replacementText": "() => {\r\n return 'get result immediately';\r\n}" + "replacementText": "() => {\n return 'get result immediately';\n}" } ], "suggest": "", @@ -91,7 +91,7 @@ { "start": 870, "end": 908, - "replacementText": "() => {\r\n console.log('foo!');\r\n}" + "replacementText": "() => {\n console.log('foo!');\n}" } ], "suggest": "", @@ -106,7 +106,7 @@ { "start": 920, "end": 958, - "replacementText": "() => {\r\n console.log('bar!');\r\n}" + "replacementText": "() => {\n console.log('bar!');\n}" } ], "suggest": "", @@ -121,7 +121,7 @@ { "start": 1023, "end": 1055, - "replacementText": "(e) => {\r\n return e * 2;\r\n}" + "replacementText": "(e) => {\n return e * 2;\n}" } ], "suggest": "", @@ -136,7 +136,7 @@ { "start": 1084, "end": 1122, - "replacementText": "(x) => {\r\n return x % 2 === 0;\r\n}" + "replacementText": "(x) => {\n return x % 2 === 0;\n}" } ], "suggest": "", @@ -236,9 +236,9 @@ "autofixable": true, "autofix": [ { - "start": 1492, - "end": 1649, - "replacementText": "(p: () => number): void => {\r\n let a = factorial(3);\r\n let b = p();\r\n let c = new C();\r\n c.m();\r\n}" + "start": 1493, + "end": 1650, + "replacementText": "(p: () => number): void => {\n let a = factorial(3);\n let b = p();\n let c = new C();\n c.m();\n}" } ], "suggest": "", @@ -251,9 +251,9 @@ "autofixable": true, "autofix": [ { - "start": 1663, - "end": 1714, - "replacementText": "(() => {\r\n console.log('called immediately');\r\n})" + "start": 1664, + "end": 1715, + "replacementText": "(() => {\n console.log('called immediately');\n})" } ], "suggest": "", @@ -267,6 +267,14 @@ "suggest": "", "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" }, + { + "line": 82, + "column": 19, + "problem": "PropertyAccessByIndex", + "autofixable": true, + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, { "line": 82, "column": 19, @@ -274,9 +282,9 @@ "autofixable": true, "autofix": [ { - "start": 1737, - "end": 1782, - "replacementText": "(() => {\r\n console.log('index access');\r\n})" + "start": 1738, + "end": 1783, + "replacementText": "(() => {\n console.log('index access');\n})" } ], "suggest": "", @@ -289,9 +297,9 @@ "autofixable": true, "autofix": [ { - "start": 1793, - "end": 1830, - "replacementText": "(() => {\r\n console.log('void');\r\n})" + "start": 1794, + "end": 1831, + "replacementText": "(() => {\n console.log('void');\n})" } ], "suggest": "", @@ -304,9 +312,9 @@ "autofixable": true, "autofix": [ { - "start": 1870, - "end": 1912, - "replacementText": "(() => {\r\n console.log('async');\r\n})" + "start": 1871, + "end": 1913, + "replacementText": "(() => {\n console.log('async');\n})" } ], "suggest": "", @@ -319,9 +327,9 @@ "autofixable": true, "autofix": [ { - "start": 1941, - "end": 1980, - "replacementText": "(() => {\r\n console.log('typeof');\r\n})" + "start": 1942, + "end": 1981, + "replacementText": "(() => {\n console.log('typeof');\n})" } ], "suggest": "", @@ -342,9 +350,9 @@ "autofixable": true, "autofix": [ { - "start": 2027, - "end": 2097, - "replacementText": "((p: boolean) => {\r\n console.log('Function.bind(this)');\r\n})" + "start": 2028, + "end": 2098, + "replacementText": "((p: boolean) => {\n console.log('Function.bind(this)');\n})" } ], "suggest": "", @@ -353,7 +361,7 @@ { "line": 104, "column": 7, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "autofixable": false, "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" @@ -365,8 +373,8 @@ "autofixable": true, "autofix": [ { - "start": 2132, - "end": 2171, + "start": 2133, + "end": 2172, "replacementText": "() => { console.log('callback'); }" } ], @@ -380,8 +388,8 @@ "autofixable": true, "autofix": [ { - "start": 2195, - "end": 2246, + "start": 2196, + "end": 2247, "replacementText": "(() => { console.log('expr || function(){}'); })" } ], @@ -395,8 +403,8 @@ "autofixable": true, "autofix": [ { - "start": 2282, - "end": 2322, + "start": 2283, + "end": 2323, "replacementText": "(() => { console.log('ternary 1'); })" } ], @@ -410,8 +418,8 @@ "autofixable": true, "autofix": [ { - "start": 2337, - "end": 2377, + "start": 2338, + "end": 2378, "replacementText": "(() => { console.log('ternary 2'); })" } ], diff --git a/ets2panda/linter-4.2/test/function_expression.ts.relax.json b/ets2panda/linter-4.2/test/function_expression.ts.relax.json index 8e60772364f880a2b1734887f006e79ab0075cdd..94c08acb658484a5b7b4c3c112b7822b7babdce7 100644 --- a/ets2panda/linter-4.2/test/function_expression.ts.relax.json +++ b/ets2panda/linter-4.2/test/function_expression.ts.relax.json @@ -59,7 +59,7 @@ { "line": 104, "column": 7, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" } diff --git a/ets2panda/linter-4.2/test/function_expression.ts.strict.json b/ets2panda/linter-4.2/test/function_expression.ts.strict.json index 1233c4e7db472396252e3157aac380db56d8e583..63b4a107b9d64b55a16e0fc9b24efcff093463f3 100644 --- a/ets2panda/linter-4.2/test/function_expression.ts.strict.json +++ b/ets2panda/linter-4.2/test/function_expression.ts.strict.json @@ -168,6 +168,13 @@ "suggest": "", "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" }, + { + "line": 82, + "column": 19, + "problem": "PropertyAccessByIndex", + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, { "line": 82, "column": 19, @@ -213,7 +220,7 @@ { "line": 104, "column": 7, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, diff --git a/ets2panda/linter-4.2/test/function_object_methods.ts.relax.json b/ets2panda/linter-4.2/test/function_object_methods.ts.relax.json index 790856d4f7ac64e42723ba8be5a20b7ae18f32a2..8b7d98debbbbd3287590b514756cf045d5bd7815 100644 --- a/ets2panda/linter-4.2/test/function_object_methods.ts.relax.json +++ b/ets2panda/linter-4.2/test/function_object_methods.ts.relax.json @@ -24,7 +24,7 @@ { "line": 29, "column": 35, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, @@ -38,21 +38,21 @@ { "line": 30, "column": 45, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 37, "column": 37, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 38, "column": 40, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, @@ -66,7 +66,7 @@ { "line": 68, "column": 44, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, @@ -80,42 +80,42 @@ { "line": 70, "column": 35, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 75, "column": 48, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 78, "column": 48, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 81, "column": 31, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 82, "column": 31, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 87, "column": 32, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, @@ -129,7 +129,7 @@ { "line": 94, "column": 37, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, @@ -143,42 +143,42 @@ { "line": 96, "column": 30, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 101, "column": 42, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 104, "column": 42, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 107, "column": 20, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 108, "column": 20, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 113, "column": 21, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, @@ -190,29 +190,29 @@ "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)" }, { - "line": 118, - "column": 7, + "line": 119, + "column": 12, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" }, { - "line": 121, - "column": 7, + "line": 122, + "column": 12, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" }, { - "line": 124, - "column": 7, + "line": 125, + "column": 12, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" }, { - "line": 127, - "column": 7, + "line": 128, + "column": 12, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" @@ -227,35 +227,35 @@ { "line": 136, "column": 23, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 137, "column": 23, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 138, "column": 23, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 139, "column": 23, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 141, "column": 5, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" } diff --git a/ets2panda/linter-4.2/test/function_object_methods.ts.strict.json b/ets2panda/linter-4.2/test/function_object_methods.ts.strict.json index 2a7c8ccd943aded4391752c20d2e26ecc66b4c29..27edb865a6acb750b13a44b8284fb70765e80099 100644 --- a/ets2panda/linter-4.2/test/function_object_methods.ts.strict.json +++ b/ets2panda/linter-4.2/test/function_object_methods.ts.strict.json @@ -24,7 +24,7 @@ { "line": 29, "column": 35, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, @@ -38,21 +38,21 @@ { "line": 30, "column": 45, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 37, "column": 37, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 38, "column": 40, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, @@ -66,7 +66,7 @@ { "line": 68, "column": 44, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, @@ -80,42 +80,42 @@ { "line": 70, "column": 35, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 75, "column": 48, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 78, "column": 48, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 81, "column": 31, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 82, "column": 31, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 87, "column": 32, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, @@ -129,7 +129,7 @@ { "line": 94, "column": 37, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, @@ -143,42 +143,42 @@ { "line": 96, "column": 30, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 101, "column": 42, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 104, "column": 42, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 107, "column": 20, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 108, "column": 20, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 113, "column": 21, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, @@ -197,8 +197,8 @@ "rule": "Use arrow functions instead of function expressions (arkts-no-func-expressions)" }, { - "line": 118, - "column": 7, + "line": 119, + "column": 12, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" @@ -218,15 +218,15 @@ "rule": "Use arrow functions instead of function expressions (arkts-no-func-expressions)" }, { - "line": 121, - "column": 7, + "line": 122, + "column": 12, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" }, { - "line": 124, - "column": 7, + "line": 125, + "column": 12, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" @@ -239,8 +239,8 @@ "rule": "Function return type inference is limited (arkts-no-implicit-return-types)" }, { - "line": 127, - "column": 7, + "line": 128, + "column": 12, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" @@ -255,35 +255,35 @@ { "line": 136, "column": 23, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 137, "column": 23, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 138, "column": 23, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 139, "column": 23, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 141, "column": 5, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" } diff --git a/ets2panda/linter-4.2/test/functions.ts b/ets2panda/linter-4.2/test/functions.ts index 8d67f99ff3292a00005ed662c0798ca5ab08801e..57ea35bf979b30150cc3a3bc92ea4b15486d7c6f 100644 --- a/ets2panda/linter-4.2/test/functions.ts +++ b/ets2panda/linter-4.2/test/functions.ts @@ -55,7 +55,7 @@ function arrowFunctionTest() { const sqrt = (x) => Math.sqrt(x); // shortcut syntax const even = [1, 2, 3, 4, 5, 6].filter((x) => x % 2 === 0); // shortcut syntax - const foo = (x: number, y): boolean => x == y; // types are partly omitted + const foo = (x: number, y): boolean => x === y; // types are partly omitted const generic = (t: T, e: E) => t; // Generic lambda } @@ -105,4 +105,23 @@ bar(() => { bar(() => { f(null); }, null, f(null)); -}, null, foo(f(null))); \ No newline at end of file +}, null, foo(f(null))); + +type PropDecorator = () => void; +let Builder: PropDecorator; + +// this test is useless until we use custom tsc +@Builder +function buildSwiper() { + f(null) + foo(null) { + f(null) + foo(null) { + f(null) + foo(() => { + f(null) + }) + } + .foo(null) + } +} diff --git a/ets2panda/linter-4.2/test/functions.ts.autofix.json b/ets2panda/linter-4.2/test/functions.ts.autofix.json index bdec3c538a0a38afa2968f452019ea0cc98ccffe..0b6af45e6e03484b2768c01a8afaeb72405fa74d 100644 --- a/ets2panda/linter-4.2/test/functions.ts.autofix.json +++ b/ets2panda/linter-4.2/test/functions.ts.autofix.json @@ -87,8 +87,8 @@ "rule": "Use generic functions instead of generic arrow functions (arkts-no-generic-lambdas)" }, { - "line": 63, - "column": 1, + "line": 64, + "column": 3, "problem": "FunctionContainsThis", "autofixable": false, "suggest": "", @@ -157,6 +157,38 @@ "autofixable": false, "suggest": "Argument of type 'null' is not assignable to parameter of type 'string'.", "rule": "Argument of type 'null' is not assignable to parameter of type 'string'." + }, + { + "line": 116, + "column": 5, + "problem": "StrictDiagnostic", + "autofixable": false, + "suggest": "Argument of type 'null' is not assignable to parameter of type 'string'.", + "rule": "Argument of type 'null' is not assignable to parameter of type 'string'." + }, + { + "line": 118, + "column": 7, + "problem": "StrictDiagnostic", + "autofixable": false, + "suggest": "Argument of type 'null' is not assignable to parameter of type 'string'.", + "rule": "Argument of type 'null' is not assignable to parameter of type 'string'." + }, + { + "line": 120, + "column": 11, + "problem": "StrictDiagnostic", + "autofixable": false, + "suggest": "Argument of type 'null' is not assignable to parameter of type 'string'.", + "rule": "Argument of type 'null' is not assignable to parameter of type 'string'." + }, + { + "line": 122, + "column": 11, + "problem": "StrictDiagnostic", + "autofixable": false, + "suggest": "Argument of type 'null' is not assignable to parameter of type 'string'.", + "rule": "Argument of type 'null' is not assignable to parameter of type 'string'." } ] } \ No newline at end of file diff --git a/ets2panda/linter-4.2/test/functions.ts.relax.json b/ets2panda/linter-4.2/test/functions.ts.relax.json index e11f7f9c4b4ba8fd83a515d2609ee21e3b7f6c08..6729c50d23c446b1835bbe8bb90355aa46755ce6 100644 --- a/ets2panda/linter-4.2/test/functions.ts.relax.json +++ b/ets2panda/linter-4.2/test/functions.ts.relax.json @@ -64,8 +64,8 @@ "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" }, { - "line": 63, - "column": 1, + "line": 64, + "column": 3, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" @@ -125,6 +125,34 @@ "problem": "StrictDiagnostic", "suggest": "Argument of type 'null' is not assignable to parameter of type 'string'.", "rule": "Argument of type 'null' is not assignable to parameter of type 'string'." + }, + { + "line": 116, + "column": 5, + "problem": "StrictDiagnostic", + "suggest": "Argument of type 'null' is not assignable to parameter of type 'string'.", + "rule": "Argument of type 'null' is not assignable to parameter of type 'string'." + }, + { + "line": 118, + "column": 7, + "problem": "StrictDiagnostic", + "suggest": "Argument of type 'null' is not assignable to parameter of type 'string'.", + "rule": "Argument of type 'null' is not assignable to parameter of type 'string'." + }, + { + "line": 120, + "column": 11, + "problem": "StrictDiagnostic", + "suggest": "Argument of type 'null' is not assignable to parameter of type 'string'.", + "rule": "Argument of type 'null' is not assignable to parameter of type 'string'." + }, + { + "line": 122, + "column": 11, + "problem": "StrictDiagnostic", + "suggest": "Argument of type 'null' is not assignable to parameter of type 'string'.", + "rule": "Argument of type 'null' is not assignable to parameter of type 'string'." } ] } \ No newline at end of file diff --git a/ets2panda/linter-4.2/test/functions.ts.strict.json b/ets2panda/linter-4.2/test/functions.ts.strict.json index d39199ff0024aa2f8a01d89ad214a4e3c83e8212..d57b74aa35f727c11969750a8ddbd117512ecf46 100644 --- a/ets2panda/linter-4.2/test/functions.ts.strict.json +++ b/ets2panda/linter-4.2/test/functions.ts.strict.json @@ -78,8 +78,8 @@ "rule": "Use generic functions instead of generic arrow functions (arkts-no-generic-lambdas)" }, { - "line": 63, - "column": 1, + "line": 64, + "column": 3, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" @@ -139,6 +139,34 @@ "problem": "StrictDiagnostic", "suggest": "Argument of type 'null' is not assignable to parameter of type 'string'.", "rule": "Argument of type 'null' is not assignable to parameter of type 'string'." + }, + { + "line": 116, + "column": 5, + "problem": "StrictDiagnostic", + "suggest": "Argument of type 'null' is not assignable to parameter of type 'string'.", + "rule": "Argument of type 'null' is not assignable to parameter of type 'string'." + }, + { + "line": 118, + "column": 7, + "problem": "StrictDiagnostic", + "suggest": "Argument of type 'null' is not assignable to parameter of type 'string'.", + "rule": "Argument of type 'null' is not assignable to parameter of type 'string'." + }, + { + "line": 120, + "column": 11, + "problem": "StrictDiagnostic", + "suggest": "Argument of type 'null' is not assignable to parameter of type 'string'.", + "rule": "Argument of type 'null' is not assignable to parameter of type 'string'." + }, + { + "line": 122, + "column": 11, + "problem": "StrictDiagnostic", + "suggest": "Argument of type 'null' is not assignable to parameter of type 'string'.", + "rule": "Argument of type 'null' is not assignable to parameter of type 'string'." } ] } \ No newline at end of file diff --git a/ets2panda/linter-4.2/test/limited_stdlib_api.ts b/ets2panda/linter-4.2/test/limited_stdlib_api.ts index b7f800683008fab4164c535332ab894927002c27..36d3c47833355bc3523f703da0857639f5ab3fb7 100644 --- a/ets2panda/linter-4.2/test/limited_stdlib_api.ts +++ b/ets2panda/linter-4.2/test/limited_stdlib_api.ts @@ -28,17 +28,23 @@ decodeURIComponent(''); escape(''); unescape(''); -// global and window are not portabe, so they are excluded from test suite +global.eval('console.log("foo")'); globalThis.eval('console.log("foo")'); class C {} let c = new C(); /// Object +// DO NOT CHECK IN TYPESCRIPT 4.2 Object.__proto__(), +// DO NOT CHECK IN TYPESCRIPT 4.2 Object.__defineGetter__(), +// DO NOT CHECK IN TYPESCRIPT 4.2 Object.__defineSetter__(); +// DO NOT CHECK IN TYPESCRIPT 4.2 Object.__lookupGetter__(); +// DO NOT CHECK IN TYPESCRIPT 4.2 Object.__lookupSetter__(); Object.assign(c, c); Object.create(c); Object.defineProperties(c, {}); Object.defineProperty(c, 'p', c); +// DO NOT CHECK IN TYPESCRIPT 4.2 Object.entries([]); Object.freeze(() => {}); Object.fromEntries([]); Object.getOwnPropertyDescriptor(c, 'p'); @@ -84,10 +90,4 @@ if (handler.set) handler.set(c, "prop", 1, c); if (handler.setPrototypeOf) handler.setPrototypeOf(c, null); /// Array -ArrayBuffer.isView({}); - -Number.NaN; -Number.isFinite(1); -Number.isNaN(2); -Number.parseFloat('3'); -Number.parseInt('4', 10); +ArrayBuffer.isView({}); \ No newline at end of file diff --git a/ets2panda/linter-4.2/test/limited_stdlib_api.ts.autofix.json b/ets2panda/linter-4.2/test/limited_stdlib_api.ts.autofix.json index 8ec4a491b6b662060b6be38154efd24dbd6c99bf..7cb974ee1ffaf522a20c6e1f2ff61263541fe170 100644 --- a/ets2panda/linter-4.2/test/limited_stdlib_api.ts.autofix.json +++ b/ets2panda/linter-4.2/test/limited_stdlib_api.ts.autofix.json @@ -22,6 +22,14 @@ "suggest": "", "rule": "Usage of standard library is restricted (arkts-limited-stdlib)" }, + { + "line": 31, + "column": 8, + "problem": "LimitedStdLibApi", + "autofixable": false, + "suggest": "", + "rule": "Usage of standard library is restricted (arkts-limited-stdlib)" + }, { "line": 32, "column": 1, @@ -517,14 +525,6 @@ "autofixable": false, "suggest": "", "rule": "Usage of standard library is restricted (arkts-limited-stdlib)" - }, - { - "line": 93, - "column": 13, - "problem": "LimitedStdLibApi", - "autofixable": false, - "suggest": "", - "rule": "Usage of standard library is restricted (arkts-limited-stdlib)" } ] } \ No newline at end of file diff --git a/ets2panda/linter-4.2/test/limited_stdlib_api.ts.relax.json b/ets2panda/linter-4.2/test/limited_stdlib_api.ts.relax.json index 42cdbd1fe131c853a60210958a6a25ef33b163ba..fec1aacaf10a6f5f60c56563a90832736229f35e 100644 --- a/ets2panda/linter-4.2/test/limited_stdlib_api.ts.relax.json +++ b/ets2panda/linter-4.2/test/limited_stdlib_api.ts.relax.json @@ -21,6 +21,13 @@ "suggest": "", "rule": "Usage of standard library is restricted (arkts-limited-stdlib)" }, + { + "line": 31, + "column": 8, + "problem": "LimitedStdLibApi", + "suggest": "", + "rule": "Usage of standard library is restricted (arkts-limited-stdlib)" + }, { "line": 32, "column": 1, @@ -454,13 +461,6 @@ "problem": "LimitedStdLibApi", "suggest": "", "rule": "Usage of standard library is restricted (arkts-limited-stdlib)" - }, - { - "line": 93, - "column": 13, - "problem": "LimitedStdLibApi", - "suggest": "", - "rule": "Usage of standard library is restricted (arkts-limited-stdlib)" } ] } \ No newline at end of file diff --git a/ets2panda/linter-4.2/test/limited_stdlib_api.ts.strict.json b/ets2panda/linter-4.2/test/limited_stdlib_api.ts.strict.json index 42cdbd1fe131c853a60210958a6a25ef33b163ba..fec1aacaf10a6f5f60c56563a90832736229f35e 100644 --- a/ets2panda/linter-4.2/test/limited_stdlib_api.ts.strict.json +++ b/ets2panda/linter-4.2/test/limited_stdlib_api.ts.strict.json @@ -21,6 +21,13 @@ "suggest": "", "rule": "Usage of standard library is restricted (arkts-limited-stdlib)" }, + { + "line": 31, + "column": 8, + "problem": "LimitedStdLibApi", + "suggest": "", + "rule": "Usage of standard library is restricted (arkts-limited-stdlib)" + }, { "line": 32, "column": 1, @@ -454,13 +461,6 @@ "problem": "LimitedStdLibApi", "suggest": "", "rule": "Usage of standard library is restricted (arkts-limited-stdlib)" - }, - { - "line": 93, - "column": 13, - "problem": "LimitedStdLibApi", - "suggest": "", - "rule": "Usage of standard library is restricted (arkts-limited-stdlib)" } ] } \ No newline at end of file diff --git a/ets2panda/linter-4.2/test/literals_as_prop_names.ts.autofix.json b/ets2panda/linter-4.2/test/literals_as_prop_names.ts.autofix.json index 0a9a07f38cbae7aae301133153373a54f827a526..d6ae92d315c201218886ae656cfda3df31eebc51 100644 --- a/ets2panda/linter-4.2/test/literals_as_prop_names.ts.autofix.json +++ b/ets2panda/linter-4.2/test/literals_as_prop_names.ts.autofix.json @@ -30,14 +30,6 @@ "suggest": "", "rule": "Objects with property names that are not identifiers are not supported (arkts-identifiers-as-prop-names)" }, - { - "line": 22, - "column": 46, - "problem": "ObjectLiteralNoContextType", - "autofixable": false, - "suggest": "", - "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)" - }, { "line": 24, "column": 3, diff --git a/ets2panda/linter-4.2/test/literals_as_prop_names.ts.relax.json b/ets2panda/linter-4.2/test/literals_as_prop_names.ts.relax.json index ed183dfd90179d2a3df25b2ecd66720f03d919ae..d0e9e2159796ceb8a87f9a4a309557f668d7dc24 100644 --- a/ets2panda/linter-4.2/test/literals_as_prop_names.ts.relax.json +++ b/ets2panda/linter-4.2/test/literals_as_prop_names.ts.relax.json @@ -14,13 +14,6 @@ "limitations under the License." ], "nodes": [ - { - "line": 22, - "column": 46, - "problem": "ObjectLiteralNoContextType", - "suggest": "", - "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)" - }, { "line": 49, "column": 9, diff --git a/ets2panda/linter-4.2/test/literals_as_prop_names.ts.strict.json b/ets2panda/linter-4.2/test/literals_as_prop_names.ts.strict.json index 0e33009041a8eaafa051e8fba20806f94afb98b7..edae4c16a30b3741d2d551600a925f6f648881db 100644 --- a/ets2panda/linter-4.2/test/literals_as_prop_names.ts.strict.json +++ b/ets2panda/linter-4.2/test/literals_as_prop_names.ts.strict.json @@ -28,13 +28,6 @@ "suggest": "", "rule": "Objects with property names that are not identifiers are not supported (arkts-identifiers-as-prop-names)" }, - { - "line": 22, - "column": 46, - "problem": "ObjectLiteralNoContextType", - "suggest": "", - "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)" - }, { "line": 24, "column": 3, diff --git a/ets2panda/linter-4.2/test/modules.ts.autofix.json b/ets2panda/linter-4.2/test/modules.ts.autofix.json index 567b9a523a2bc47149edca0b119738a94115535a..476aed0b64742d7aba1b026823c41bba6d4f7cb9 100644 --- a/ets2panda/linter-4.2/test/modules.ts.autofix.json +++ b/ets2panda/linter-4.2/test/modules.ts.autofix.json @@ -111,51 +111,12 @@ "problem": "ImportAfterStatement", "autofixable": false }, - { - "line": 92, - "column": 8, - "problem": "TypeOnlyImport", - "autofixable": true, - "autofix": [ - { - "start": 1923, - "end": 1947, - "replacementText": "{ APIResponseType }" - } - ] - }, { "line": 93, "column": 1, "problem": "ImportAfterStatement", "autofixable": false }, - { - "line": 93, - "column": 8, - "problem": "TypeOnlyImport", - "autofixable": true, - "autofix": [ - { - "start": 1969, - "end": 1980, - "replacementText": "* as P" - } - ] - }, - { - "line": 96, - "column": 1, - "problem": "TypeOnlyExport", - "autofixable": true, - "autofix": [ - { - "start": 2014, - "end": 2045, - "replacementText": "export { TypeA as TypeB };" - } - ] - }, { "line": 104, "column": 1, diff --git a/ets2panda/linter-4.2/test/modules.ts.strict.json b/ets2panda/linter-4.2/test/modules.ts.strict.json index 9481d07d8d642c6b8d190fbe9ae603a826ac99a8..88de583880c7d9279cda15172273567053297ae4 100644 --- a/ets2panda/linter-4.2/test/modules.ts.strict.json +++ b/ets2panda/linter-4.2/test/modules.ts.strict.json @@ -89,26 +89,11 @@ "column": 1, "problem": "ImportAfterStatement" }, - { - "line": 92, - "column": 8, - "problem": "TypeOnlyImport" - }, { "line": 93, "column": 1, "problem": "ImportAfterStatement" }, - { - "line": 93, - "column": 8, - "problem": "TypeOnlyImport" - }, - { - "line": 96, - "column": 1, - "problem": "TypeOnlyExport" - }, { "line": 104, "column": 1, diff --git a/ets2panda/linter-4.2/test/new_target.ts.relax.json b/ets2panda/linter-4.2/test/new_target.ts.relax.json index 0b83a11983237ced2f41aa64eaaeb370019ee51f..f3f5c213f9efade1ac2a7ac7b272649a654a8131 100644 --- a/ets2panda/linter-4.2/test/new_target.ts.relax.json +++ b/ets2panda/linter-4.2/test/new_target.ts.relax.json @@ -17,22 +17,30 @@ { "line": 19, "column": 12, - "problem": "LimitedStdLibApi" + "problem": "LimitedStdLibApi", + "suggest": "", + "rule": "Usage of standard library is restricted (arkts-limited-stdlib)" }, { "line": 19, "column": 44, - "problem": "Prototype" + "problem": "Prototype", + "suggest": "", + "rule": "Prototype assignment is not supported (arkts-no-prototype-assignment)" }, { "line": 19, "column": 33, - "problem": "NewTarget" + "problem": "NewTarget", + "suggest": "", + "rule": "\"new.target\" is not supported (arkts-no-new-target)" }, { "line": 24, "column": 7, - "problem": "NewTarget" + "problem": "NewTarget", + "suggest": "", + "rule": "\"new.target\" is not supported (arkts-no-new-target)" } ] } \ No newline at end of file diff --git a/ets2panda/linter-4.2/test/new_target.ts.strict.json b/ets2panda/linter-4.2/test/new_target.ts.strict.json index 0b83a11983237ced2f41aa64eaaeb370019ee51f..f3f5c213f9efade1ac2a7ac7b272649a654a8131 100644 --- a/ets2panda/linter-4.2/test/new_target.ts.strict.json +++ b/ets2panda/linter-4.2/test/new_target.ts.strict.json @@ -17,22 +17,30 @@ { "line": 19, "column": 12, - "problem": "LimitedStdLibApi" + "problem": "LimitedStdLibApi", + "suggest": "", + "rule": "Usage of standard library is restricted (arkts-limited-stdlib)" }, { "line": 19, "column": 44, - "problem": "Prototype" + "problem": "Prototype", + "suggest": "", + "rule": "Prototype assignment is not supported (arkts-no-prototype-assignment)" }, { "line": 19, "column": 33, - "problem": "NewTarget" + "problem": "NewTarget", + "suggest": "", + "rule": "\"new.target\" is not supported (arkts-no-new-target)" }, { "line": 24, "column": 7, - "problem": "NewTarget" + "problem": "NewTarget", + "suggest": "", + "rule": "\"new.target\" is not supported (arkts-no-new-target)" } ] } \ No newline at end of file diff --git a/ets2panda/linter-4.2/test/object_literals_2.ts b/ets2panda/linter-4.2/test/object_literals_2.ts index b8e6f2dd50243d453649327948f25dec4bc1d890..d2068109eb5de32ab25a82dcf72b50d7b266566f 100644 --- a/ets2panda/linter-4.2/test/object_literals_2.ts +++ b/ets2panda/linter-4.2/test/object_literals_2.ts @@ -78,6 +78,7 @@ class C3 { } + class C4 { readonly n: number = 0; readonly s: string = ''; @@ -117,6 +118,7 @@ class C7 { n: number = 0; s: string = ''; } + let o1: Object = {s: '', n: 42} // OK in TS, CTE in ArkTS: no fields 'n' and 's' in Object let o2: object = {n: 42, s: ''} // OK in TS, CTE in ArkTS: no fields 'n' and 's' in object diff --git a/ets2panda/linter-4.2/test/object_literals_prop_func_type.ts b/ets2panda/linter-4.2/test/object_literals_prop_func_type.ts index 19ce7f092a6e925ea3b6aff8ac41eafe33a53bb2..51df3217ff2425e013a839c44eb84c3a0701d0bb 100644 --- a/ets2panda/linter-4.2/test/object_literals_prop_func_type.ts +++ b/ets2panda/linter-4.2/test/object_literals_prop_func_type.ts @@ -43,4 +43,12 @@ a = { b: w, c: e, d: r, -} \ No newline at end of file +} + +// #14569 - initialize field with 'Function' object +class B { + f: Function = () => {}; +} +let b: B = { + f: Function +}; \ No newline at end of file diff --git a/ets2panda/linter-4.2/test/oh_modules/ohos_factory.ts b/ets2panda/linter-4.2/test/oh_modules/ohos_factory.ts index e26c4c911ba886e1d930a38404f9a87434d454b3..b4fc3d2d2fe025f7deb68faa0cf3374beece71ee 100644 --- a/ets2panda/linter-4.2/test/oh_modules/ohos_factory.ts +++ b/ets2panda/linter-4.2/test/oh_modules/ohos_factory.ts @@ -16,3 +16,18 @@ export declare class SomethingBar extends Something { } export declare class Bar { constructor(arg: { new(): T }); } + +export class Select { + public from(cls: any) { + return this; + } + + // we intentionally omit generic argument of 'Select', see #14228 + public eq(name: string): Select { + return this; + } + + public query(cls: any): Promise { + return cls.foo(); + } +} \ No newline at end of file diff --git a/ets2panda/linter-4.2/test/oh_modules/ohos_lib.ts b/ets2panda/linter-4.2/test/oh_modules/ohos_lib.ts index c69856185bae174591b09b69829d4ba9aa96b5bc..6a7022b4bcbdc1d7f5b5235c6184a21898019bf9 100644 --- a/ets2panda/linter-4.2/test/oh_modules/ohos_lib.ts +++ b/ets2panda/linter-4.2/test/oh_modules/ohos_lib.ts @@ -23,3 +23,10 @@ export function ohFunction2(p: {d: OhosI}): void {} export function fooOh(): any {} export function barOh(a: any) {} + + +export interface OhosInterface { + fld: A & { + [key: string]: number; + }; +} diff --git a/ets2panda/linter-4.2/test/property_access_by_index.ts b/ets2panda/linter-4.2/test/property_access_by_index.ts new file mode 100644 index 0000000000000000000000000000000000000000..4fb0eb92220e59470f7fd3597a2ef2e39ca9ebc0 --- /dev/null +++ b/ets2panda/linter-4.2/test/property_access_by_index.ts @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2023-2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import {OhosInterface} from './oh_modules/ohos_lib' +// #14071 +class A { + v: string = ''; +} +function SetProperty(oldObj: T, str: string, obj: Object): void { + oldObj[str] = obj; // Should report error +} +function GetProperty(oldObj: T, str: string): U { + return oldObj[str]; // Should report error +} +function test() { + let a: A = { v: 'abc' }; + SetProperty(a, 'u', 'def'); + return GetProperty(a, 'v') + GetProperty(a, 'u'); +} + +let ar1 = [1, 2, 3, 4]; +let ar2 = [1, '2', 3, 4]; +let ar3: number[] = []; + +ar1[2]; +ar2[2]; +ar3[2]; + +const r0 = [1, 2, 3][1]; +let r1 = [1, 2, 3, 4][0] +let r2 = [1, '2', 3, 4][0] + +function fobject1(o: object) { + o['j'] +} + +function fobject2(o: Object) { + o['k'] +} + +let array1 = [0,1] +let array2 = [1,2,3,4,5] +let array3: number[] = [1,2,3,4,5] +let array4: Array = [1,2,3,4,5] +let array5 = new Array(10) +let array6 = new Int8Array(10) +let array7 = new Uint8Array(10) +let array8 = new Uint8ClampedArray(10) +let array9 = new Int16Array(10) +let array10 = new Uint16Array(10) +let array11 = new Int32Array(10) +let array12 = new Uint32Array(10) +let array13 = new Float32Array(10) +let array14 = new Float64Array(10) +let array15 = new BigInt64Array(10) +let array16 = new BigUint64Array(10) + +array1[0]; +array2[0]; +array3[0]; +array4[0]; +array5[0]; +array6[0]; +array7[0]; +array8[0]; +array9[0]; +array10[0]; +array11[0]; +array12[0]; +array13[0]; +array14[0]; +array15[0]; +array16[0]; + +function fff1(r: Record) { + r['bob'] +} + +enum CCCCCCCCC { + KATE, + BOB, + ROB, +} + +CCCCCCCCC['KATE'] +CCCCCCCCC['BOB'] +CCCCCCCCC['ROB'] + +CCCCCCCCC[CCCCCCCCC.KATE] +CCCCCCCCC[CCCCCCCCC.BOB] +CCCCCCCCC[CCCCCCCCC.ROB] + +let arr32 = new Float32Array([1,2,3]) + +let iter_arr32 = arr32[Symbol.iterator]() +let tmp_arr32 = iter_arr32.next().value; +while (!!tmp_arr32) { + console.log(tmp_arr32[0]) + + tmp_arr32 = iter_arr32.next().value +} + +let arr = new Array() +arr = ['a','f','g'] +let iter_arr = arr[Symbol.iterator]() +let tmp_arr = iter_arr.next().value; +while (!!tmp_arr) { + console.log(tmp_arr[0]) + tmp_arr = iter_arr.next().value +} + +// #14415 +class ArrayContainer { + numbers: number[] = []; +} +class NullableArray { + container: ArrayContainer | null = null; + + print() { + console.log(this.container?.numbers[0]); + } +} + +let str1 = 'sssss' +let str2 = "aaaaa" +let str3 = `sssss` +let str4 = new String('sssss') +let str5 = str1 +let str6 = str2 + +str1[1] +str2[1] +str3[1] +str4[1] +str5[1] +str6[1] + +class AString extends String {} +let str7 = new AString('dwdd') +str7[1] + +type IndexableUnion = string[] | (number | string)[] | Uint8Array; +type NonIndexableUnion = string[] | number[] | Uint8Array | number; + +function indexUnion(iu: IndexableUnion, niu: NonIndexableUnion) { + iu[0]; + niu[0]; +} + +function testLibraryUnnamedType(a: OhosInterface) { + a['kek']; +} + +class MMap extends Map {} + +let mmap1 = new Map(); +let mmap2 = new Map(); +let mmap3 = new MMap(); + +mmap1[1]; +mmap2['222']; +mmap3["kkr"]; diff --git a/ets2panda/linter-4.2/test/property_access_by_index.ts.autofix.json b/ets2panda/linter-4.2/test/property_access_by_index.ts.autofix.json new file mode 100644 index 0000000000000000000000000000000000000000..8a928547655065fa02e35fd259a18de7556e7b0a --- /dev/null +++ b/ets2panda/linter-4.2/test/property_access_by_index.ts.autofix.json @@ -0,0 +1,162 @@ +{ + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + "nodes": [ + { + "line": 21, + "column": 3, + "problem": "PropertyAccessByIndex", + "autofixable": false, + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 24, + "column": 10, + "problem": "PropertyAccessByIndex", + "autofixable": false, + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 45, + "column": 3, + "problem": "PropertyAccessByIndex", + "autofixable": true, + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 49, + "column": 3, + "problem": "PropertyAccessByIndex", + "autofixable": true, + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 56, + "column": 5, + "problem": "AnyType", + "autofixable": false, + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + }, + { + "line": 107, + "column": 5, + "problem": "AnyType", + "autofixable": false, + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + }, + { + "line": 117, + "column": 5, + "problem": "AnyType", + "autofixable": false, + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + }, + { + "line": 142, + "column": 1, + "problem": "PropertyAccessByIndex", + "autofixable": true, + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 143, + "column": 1, + "problem": "PropertyAccessByIndex", + "autofixable": true, + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 144, + "column": 1, + "problem": "PropertyAccessByIndex", + "autofixable": true, + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 145, + "column": 1, + "problem": "PropertyAccessByIndex", + "autofixable": true, + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 146, + "column": 1, + "problem": "PropertyAccessByIndex", + "autofixable": true, + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 147, + "column": 1, + "problem": "PropertyAccessByIndex", + "autofixable": true, + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 151, + "column": 1, + "problem": "PropertyAccessByIndex", + "autofixable": true, + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 158, + "column": 3, + "problem": "PropertyAccessByIndex", + "autofixable": true, + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 171, + "column": 1, + "problem": "PropertyAccessByIndex", + "autofixable": true, + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 172, + "column": 1, + "problem": "PropertyAccessByIndex", + "autofixable": true, + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 173, + "column": 1, + "problem": "PropertyAccessByIndex", + "autofixable": true, + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + } + ] +} \ No newline at end of file diff --git a/ets2panda/linter-4.2/test/property_access_by_index.ts.relax.json b/ets2panda/linter-4.2/test/property_access_by_index.ts.relax.json new file mode 100644 index 0000000000000000000000000000000000000000..b492ed7380feb4b306433cd940c731223e71b0ad --- /dev/null +++ b/ets2panda/linter-4.2/test/property_access_by_index.ts.relax.json @@ -0,0 +1,39 @@ +{ + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + "nodes": [ + { + "line": 56, + "column": 5, + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + }, + { + "line": 107, + "column": 5, + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + }, + { + "line": 117, + "column": 5, + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + } + ] +} \ No newline at end of file diff --git a/ets2panda/linter-4.2/test/property_access_by_index.ts.strict.json b/ets2panda/linter-4.2/test/property_access_by_index.ts.strict.json new file mode 100644 index 0000000000000000000000000000000000000000..0afff457f48e0802a36bc06eef8e5fefde828a12 --- /dev/null +++ b/ets2panda/linter-4.2/test/property_access_by_index.ts.strict.json @@ -0,0 +1,144 @@ +{ + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + "nodes": [ + { + "line": 21, + "column": 3, + "problem": "PropertyAccessByIndex", + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 24, + "column": 10, + "problem": "PropertyAccessByIndex", + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 45, + "column": 3, + "problem": "PropertyAccessByIndex", + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 49, + "column": 3, + "problem": "PropertyAccessByIndex", + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 56, + "column": 5, + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + }, + { + "line": 107, + "column": 5, + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + }, + { + "line": 117, + "column": 5, + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + }, + { + "line": 142, + "column": 1, + "problem": "PropertyAccessByIndex", + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 143, + "column": 1, + "problem": "PropertyAccessByIndex", + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 144, + "column": 1, + "problem": "PropertyAccessByIndex", + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 145, + "column": 1, + "problem": "PropertyAccessByIndex", + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 146, + "column": 1, + "problem": "PropertyAccessByIndex", + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 147, + "column": 1, + "problem": "PropertyAccessByIndex", + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 151, + "column": 1, + "problem": "PropertyAccessByIndex", + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 158, + "column": 3, + "problem": "PropertyAccessByIndex", + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 171, + "column": 1, + "problem": "PropertyAccessByIndex", + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 172, + "column": 1, + "problem": "PropertyAccessByIndex", + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 173, + "column": 1, + "problem": "PropertyAccessByIndex", + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + } + ] +} \ No newline at end of file diff --git a/ets2panda/linter-4.2/test/prototype_assignment.ts.autofix.json b/ets2panda/linter-4.2/test/prototype_assignment.ts.autofix.json index 381cadf69d898a62ea6f5b7b50ff017f9d8c06d8..c1e3b5c631a1796b036aa3c90164158e70e21a2a 100644 --- a/ets2panda/linter-4.2/test/prototype_assignment.ts.autofix.json +++ b/ets2panda/linter-4.2/test/prototype_assignment.ts.autofix.json @@ -15,8 +15,8 @@ ], "nodes": [ { - "line": 19, - "column": 1, + "line": 26, + "column": 19, "problem": "FunctionContainsThis", "autofixable": false, "suggest": "", @@ -31,8 +31,8 @@ "rule": "Use arrow functions instead of function expressions (arkts-no-func-expressions)" }, { - "line": 20, - "column": 20, + "line": 21, + "column": 5, "problem": "FunctionContainsThis", "autofixable": false, "suggest": "", @@ -71,8 +71,8 @@ "rule": "Use arrow functions instead of function expressions (arkts-no-func-expressions)" }, { - "line": 30, - "column": 26, + "line": 31, + "column": 12, "problem": "FunctionContainsThis", "autofixable": false, "suggest": "", diff --git a/ets2panda/linter-4.2/test/prototype_assignment.ts.relax.json b/ets2panda/linter-4.2/test/prototype_assignment.ts.relax.json index dc1c5be2ce6b380ee0700c0881a6314027de7d9b..79345f433f3e59b20e1a36a003d330eb58c5f4eb 100644 --- a/ets2panda/linter-4.2/test/prototype_assignment.ts.relax.json +++ b/ets2panda/linter-4.2/test/prototype_assignment.ts.relax.json @@ -15,15 +15,15 @@ ], "nodes": [ { - "line": 19, - "column": 1, + "line": 26, + "column": 19, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" }, { - "line": 20, - "column": 20, + "line": 21, + "column": 5, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" @@ -50,8 +50,8 @@ "rule": "Prototype assignment is not supported (arkts-no-prototype-assignment)" }, { - "line": 30, - "column": 26, + "line": 31, + "column": 12, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" diff --git a/ets2panda/linter-4.2/test/prototype_assignment.ts.strict.json b/ets2panda/linter-4.2/test/prototype_assignment.ts.strict.json index ad3f542923b0e6db48ebf7877232046f1b070e7e..f1ebe772db7f34dbdfb39e3ff7cf91c80121ccde 100644 --- a/ets2panda/linter-4.2/test/prototype_assignment.ts.strict.json +++ b/ets2panda/linter-4.2/test/prototype_assignment.ts.strict.json @@ -15,8 +15,8 @@ ], "nodes": [ { - "line": 19, - "column": 1, + "line": 26, + "column": 19, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" @@ -29,8 +29,8 @@ "rule": "Use arrow functions instead of function expressions (arkts-no-func-expressions)" }, { - "line": 20, - "column": 20, + "line": 21, + "column": 5, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" @@ -64,8 +64,8 @@ "rule": "Use arrow functions instead of function expressions (arkts-no-func-expressions)" }, { - "line": 30, - "column": 26, + "line": 31, + "column": 12, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" diff --git a/ets2panda/linter-4.2/test/record_type.ts b/ets2panda/linter-4.2/test/record_type.ts index c08cdea85578c29e128554f06f7ed6add3e05e8b..8a07e7e019f77e4596295632f71c286d8903d4ce 100644 --- a/ets2panda/linter-4.2/test/record_type.ts +++ b/ets2panda/linter-4.2/test/record_type.ts @@ -98,4 +98,44 @@ rec = { // NOT OK, key must be either string or number literal address: '124213', birthday: '2020-03-10', 23:'xx' -} \ No newline at end of file +} + +// #14360 - Record in a union type +class RecUnion { + v: Record | null = null; +} +let recU: RecUnion = { + v: { + 'a': 'v' + } +}; + +interface RecUnion2 { + param: Record; + extra?: Record; + value: Record | string; +} +let recU2: RecUnion2 = { + param: { + 'aa': 111 + }, + extra: { + 'bbb': 222 + }, + value: { + 'bbb': 222 + } +}; + +class RecUnion3 { + extra: Record | string = ''; + param?: Record | string = ''; +} +let recU3: RecUnion3 = { + extra: { + 'bbb': 222 + }, + param: { + 'bbb': 222 + } +}; \ No newline at end of file diff --git a/ets2panda/linter-4.2/test/recursive_types.ts b/ets2panda/linter-4.2/test/recursive_types.ts index 52402491bcdf2a3ada94cbd7a307cd10ecd7e4e9..c2340362a82fcaa25b41873186e3d355b74a206b 100644 --- a/ets2panda/linter-4.2/test/recursive_types.ts +++ b/ets2panda/linter-4.2/test/recursive_types.ts @@ -63,7 +63,7 @@ namespace test3 { let cur = parent.get(name); if (cur instanceof Array) { (cur as Array).push(value); - } else if (cur != undefined) { + } else if (cur !== undefined) { let array = [cur, value]; parent.set(name, array); } diff --git a/ets2panda/linter-4.2/test/symbol_api.ts b/ets2panda/linter-4.2/test/symbol_api.ts index 2d33c448f2d46c9150ee43b838b41f7dc5d98a37..8d7d25d3e16a892137741335ce918d61bd964a59 100644 --- a/ets2panda/linter-4.2/test/symbol_api.ts +++ b/ets2panda/linter-4.2/test/symbol_api.ts @@ -50,3 +50,9 @@ class TestClass { return Symbol(); } } + +class BIter { + [Symbol.iterator] = () => { + return 1; + }; +} diff --git a/ets2panda/linter-4.2/test/this_type.ts.relax.json b/ets2panda/linter-4.2/test/this_type.ts.relax.json index 9d215c36e2b62102e81581abcbd1487052fcdb2f..21ba10e79c50f983bb1e8f8f9bcb28b35bb100e1 100644 --- a/ets2panda/linter-4.2/test/this_type.ts.relax.json +++ b/ets2panda/linter-4.2/test/this_type.ts.relax.json @@ -50,23 +50,23 @@ "problem": "ThisType" }, { - "line": 72, - "column": 3, + "line": 73, + "column": 5, "problem": "FunctionContainsThis" }, { - "line": 77, - "column": 9, + "line": 78, + "column": 3, "problem": "FunctionContainsThis" }, { - "line": 80, - "column": 10, + "line": 81, + "column": 3, "problem": "FunctionContainsThis" }, { - "line": 83, - "column": 1, + "line": 84, + "column": 3, "problem": "FunctionContainsThis" } ] diff --git a/ets2panda/linter-4.2/test/this_type.ts.strict.json b/ets2panda/linter-4.2/test/this_type.ts.strict.json index 673d4a501832e7ed4ede5482905a2965fc7075ec..274c793d580a7c12020842b57cfb15492c7e1d73 100644 --- a/ets2panda/linter-4.2/test/this_type.ts.strict.json +++ b/ets2panda/linter-4.2/test/this_type.ts.strict.json @@ -50,13 +50,13 @@ "problem": "ThisType" }, { - "line": 72, - "column": 3, + "line": 73, + "column": 5, "problem": "FunctionContainsThis" }, { - "line": 77, - "column": 9, + "line": 78, + "column": 3, "problem": "FunctionContainsThis" }, { @@ -65,13 +65,13 @@ "problem": "FunctionExpression" }, { - "line": 80, - "column": 10, + "line": 81, + "column": 3, "problem": "FunctionContainsThis" }, { - "line": 83, - "column": 1, + "line": 84, + "column": 3, "problem": "FunctionContainsThis" } ] diff --git a/ets2panda/linter-4.2/test/ts_ignore.ts b/ets2panda/linter-4.2/test/ts_ignore.ts index 3676a1550465e43c9205d6645b9262154c268fe3..5cee019035189a17a674aee7a28e83590af1420c 100644 --- a/ets2panda/linter-4.2/test/ts_ignore.ts +++ b/ets2panda/linter-4.2/test/ts_ignore.ts @@ -31,11 +31,11 @@ let b: number = null; console.log("Hello" * 42); /* - @ts-expect-error + @ts-expect-error (shouldn't be reported) */ console.log("Hello" * 42); -// no @ts-expect-error +// no @ts-expect-error (shouldn't be reported) console.log("Hello" * 42); const l1 = (): void => { @@ -74,3 +74,14 @@ class ChainedCallsClass { let cc = new ChainedCallsClass() // @ts-ignore .methodOne().methodTwo(); + +// #14305 +/* +@ts-ignore (shouldn't be reported) + */ +let c: number = '1'; + +/* + @ts-ignore (shouldn't be reported) + */ +let d: number = '1'; \ No newline at end of file diff --git a/ets2panda/linter-4.2/test/ts_ignore.ts.relax.json b/ets2panda/linter-4.2/test/ts_ignore.ts.relax.json index d1f5176718b5277f4744fd1d2237f1bec3ddae0e..f802c561b84b47c7e4c752428ed86cd5c76e97ae 100644 --- a/ets2panda/linter-4.2/test/ts_ignore.ts.relax.json +++ b/ets2panda/linter-4.2/test/ts_ignore.ts.relax.json @@ -14,65 +14,89 @@ "limitations under the License." ], "nodes": [ + { + "line": 53, + "column": 3, + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + }, + { + "line": 55, + "column": 22, + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + }, { "line": 16, "column": 1, - "problem": "ErrorSuppression" + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" }, { "line": 21, "column": 3, - "problem": "ErrorSuppression" + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" }, { "line": 25, "column": 19, - "problem": "ErrorSuppression" + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" }, { "line": 30, "column": 9, - "problem": "ErrorSuppression" - }, - { - "line": 33, - "column": 2, - "problem": "ErrorSuppression" + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" }, { "line": 43, "column": 9, - "problem": "ErrorSuppression" + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" }, { "line": 52, "column": 3, - "problem": "ErrorSuppression" - }, - { - "line": 53, - "column": 3, - "problem": "AnyType" - }, - { - "line": 55, - "column": 22, - "problem": "AnyType" + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" }, { "line": 56, "column": 9, - "problem": "ErrorSuppression" + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" }, { "line": 58, "column": 9, - "problem": "ErrorSuppression" + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" + }, + { + "line": 75, + "column": 1, + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" }, { "line": 18, "column": 5, - "problem": "StrictDiagnostic" + "problem": "StrictDiagnostic", + "suggest": "Type 'null' is not assignable to type 'number'.", + "rule": "Type 'null' is not assignable to type 'number'." } ] -} +} \ No newline at end of file diff --git a/ets2panda/linter-4.2/test/ts_ignore.ts.strict.json b/ets2panda/linter-4.2/test/ts_ignore.ts.strict.json index d1f5176718b5277f4744fd1d2237f1bec3ddae0e..f802c561b84b47c7e4c752428ed86cd5c76e97ae 100644 --- a/ets2panda/linter-4.2/test/ts_ignore.ts.strict.json +++ b/ets2panda/linter-4.2/test/ts_ignore.ts.strict.json @@ -14,65 +14,89 @@ "limitations under the License." ], "nodes": [ + { + "line": 53, + "column": 3, + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + }, + { + "line": 55, + "column": 22, + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + }, { "line": 16, "column": 1, - "problem": "ErrorSuppression" + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" }, { "line": 21, "column": 3, - "problem": "ErrorSuppression" + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" }, { "line": 25, "column": 19, - "problem": "ErrorSuppression" + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" }, { "line": 30, "column": 9, - "problem": "ErrorSuppression" - }, - { - "line": 33, - "column": 2, - "problem": "ErrorSuppression" + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" }, { "line": 43, "column": 9, - "problem": "ErrorSuppression" + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" }, { "line": 52, "column": 3, - "problem": "ErrorSuppression" - }, - { - "line": 53, - "column": 3, - "problem": "AnyType" - }, - { - "line": 55, - "column": 22, - "problem": "AnyType" + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" }, { "line": 56, "column": 9, - "problem": "ErrorSuppression" + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" }, { "line": 58, "column": 9, - "problem": "ErrorSuppression" + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" + }, + { + "line": 75, + "column": 1, + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" }, { "line": 18, "column": 5, - "problem": "StrictDiagnostic" + "problem": "StrictDiagnostic", + "suggest": "Type 'null' is not assignable to type 'number'.", + "rule": "Type 'null' is not assignable to type 'number'." } ] -} +} \ No newline at end of file diff --git a/ets2panda/linter-4.2/test/ts_ignore_2.ts b/ets2panda/linter-4.2/test/ts_ignore_2.ts new file mode 100644 index 0000000000000000000000000000000000000000..bd5602e33b0392ab0775c9f5008896e6a8e9d75d --- /dev/null +++ b/ets2panda/linter-4.2/test/ts_ignore_2.ts @@ -0,0 +1,17 @@ +// @ts-ignore - check for [1, 1] position +let x: number = null; // No error, type checking is suppressed + +/* + * Copyright (c) 2023-2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ \ No newline at end of file diff --git a/ets2panda/linter-4.2/test/ts_ignore_2.ts.autofix.json b/ets2panda/linter-4.2/test/ts_ignore_2.ts.autofix.json new file mode 100644 index 0000000000000000000000000000000000000000..87d88d712fc3a3270dfea505f0c516f13315746c --- /dev/null +++ b/ets2panda/linter-4.2/test/ts_ignore_2.ts.autofix.json @@ -0,0 +1,26 @@ +{ + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + "nodes": [ + { + "line": 1, + "column": 1, + "problem": "ErrorSuppression", + "autofixable": false, + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" + } + ] +} \ No newline at end of file diff --git a/ets2panda/linter-4.2/test/ts_ignore_2.ts.relax.json b/ets2panda/linter-4.2/test/ts_ignore_2.ts.relax.json new file mode 100644 index 0000000000000000000000000000000000000000..4f498993ad6702fb887f19305de80f93303dc86a --- /dev/null +++ b/ets2panda/linter-4.2/test/ts_ignore_2.ts.relax.json @@ -0,0 +1,25 @@ +{ + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + "nodes": [ + { + "line": 1, + "column": 1, + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" + } + ] +} \ No newline at end of file diff --git a/ets2panda/linter-4.2/test/ts_ignore_2.ts.strict.json b/ets2panda/linter-4.2/test/ts_ignore_2.ts.strict.json new file mode 100644 index 0000000000000000000000000000000000000000..4f498993ad6702fb887f19305de80f93303dc86a --- /dev/null +++ b/ets2panda/linter-4.2/test/ts_ignore_2.ts.strict.json @@ -0,0 +1,25 @@ +{ + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + "nodes": [ + { + "line": 1, + "column": 1, + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" + } + ] +} \ No newline at end of file diff --git a/ets2panda/linter-4.2/test/ts_nocheck_2.ts b/ets2panda/linter-4.2/test/ts_nocheck_2.ts new file mode 100644 index 0000000000000000000000000000000000000000..950ed1bf04c4f93d2aa3ed18dd3f7cf3608c0592 --- /dev/null +++ b/ets2panda/linter-4.2/test/ts_nocheck_2.ts @@ -0,0 +1,20 @@ +// @ts-nocheck - check for [1, 1] position +// @ts-nocheck - check for pragma with two entries +let x: number = null; // No error, type checking is suppressed + +/* + * Copyright (c) 2023-2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +let y: number = null; // No error, type checking is suppressed \ No newline at end of file diff --git a/ets2panda/linter-4.2/test/ts_nocheck_2.ts.autofix.json b/ets2panda/linter-4.2/test/ts_nocheck_2.ts.autofix.json new file mode 100644 index 0000000000000000000000000000000000000000..e56817ef3edce23345c358dbc361fd68ae80794e --- /dev/null +++ b/ets2panda/linter-4.2/test/ts_nocheck_2.ts.autofix.json @@ -0,0 +1,34 @@ +{ + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + "nodes": [ + { + "line": 1, + "column": 1, + "problem": "ErrorSuppression", + "autofixable": false, + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" + }, + { + "line": 2, + "column": 1, + "problem": "ErrorSuppression", + "autofixable": false, + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" + } + ] +} \ No newline at end of file diff --git a/ets2panda/linter-4.2/test/ts_nocheck_2.ts.relax.json b/ets2panda/linter-4.2/test/ts_nocheck_2.ts.relax.json new file mode 100644 index 0000000000000000000000000000000000000000..a8844f27fc64b1418c7a0a6929a5c77d38e3a4dc --- /dev/null +++ b/ets2panda/linter-4.2/test/ts_nocheck_2.ts.relax.json @@ -0,0 +1,32 @@ +{ + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + "nodes": [ + { + "line": 1, + "column": 1, + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" + }, + { + "line": 2, + "column": 1, + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" + } + ] +} \ No newline at end of file diff --git a/ets2panda/linter-4.2/test/ts_nocheck_2.ts.strict.json b/ets2panda/linter-4.2/test/ts_nocheck_2.ts.strict.json new file mode 100644 index 0000000000000000000000000000000000000000..a8844f27fc64b1418c7a0a6929a5c77d38e3a4dc --- /dev/null +++ b/ets2panda/linter-4.2/test/ts_nocheck_2.ts.strict.json @@ -0,0 +1,32 @@ +{ + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + "nodes": [ + { + "line": 1, + "column": 1, + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" + }, + { + "line": 2, + "column": 1, + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" + } + ] +} \ No newline at end of file diff --git a/ets2panda/linter-4.2/test/types.ts b/ets2panda/linter-4.2/test/types.ts index 28ee81f242efe5ca0e63c6e868a070db6d215251..d34b3c9167612e47974ac7fcfa2e0d8848b37a23 100644 --- a/ets2panda/linter-4.2/test/types.ts +++ b/ets2panda/linter-4.2/test/types.ts @@ -98,7 +98,7 @@ type DescribableFunction = { function callFunctionObject(fn: DescribableFunction) { console.log(fn.description + ' returned ' + fn(5)); } -const funcWithDescr: DescribableFunction = (x: number) => x % 2 == 0; +const funcWithDescr: DescribableFunction = (x: number) => x % 2 === 0; funcWithDescr.description = 'isEven'; callFunctionObject(funcWithDescr); diff --git a/ets2panda/linter-4.2/test/types.ts.autofix.json b/ets2panda/linter-4.2/test/types.ts.autofix.json index 7833b9da06c47271168dc5b463b35b669eed9910..f51682c47bc8fa4b3f7535debbbaa7ccd62c5ed7 100644 --- a/ets2panda/linter-4.2/test/types.ts.autofix.json +++ b/ets2panda/linter-4.2/test/types.ts.autofix.json @@ -102,14 +102,6 @@ "suggest": "", "rule": "Type guarding is supported with \"instanceof\" and \"as\" (arkts-no-is)" }, - { - "line": 47, - "column": 17, - "problem": "RegexLiteral", - "autofixable": false, - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, { "line": 54, "column": 26, @@ -150,14 +142,6 @@ "suggest": "", "rule": "Objects with property names that are not identifiers are not supported (arkts-identifiers-as-prop-names)" }, - { - "line": 66, - "column": 46, - "problem": "ObjectLiteralNoContextType", - "autofixable": false, - "suggest": "", - "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)" - }, { "line": 67, "column": 3, @@ -214,6 +198,14 @@ "suggest": "", "rule": "\"in\" operator is not supported (arkts-no-in)" }, + { + "line": 78, + "column": 5, + "problem": "PropertyAccessByIndex", + "autofixable": false, + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, { "line": 92, "column": 22, @@ -293,8 +285,8 @@ "autofixable": true, "autofix": [ { - "start": 2630, - "end": 2636, + "start": 2631, + "end": 2637, "replacementText": "1 as any" } ], @@ -316,8 +308,8 @@ "autofixable": true, "autofix": [ { - "start": 2657, - "end": 2714, + "start": 2658, + "end": 2715, "replacementText": "document.getElementById('main_canvas') as HTMLCanvasElement" } ], diff --git a/ets2panda/linter-4.2/test/types.ts.relax.json b/ets2panda/linter-4.2/test/types.ts.relax.json index cb4261c22858190b840627fe507e40d918ba6500..ed2fa5fb0df50e59839f6212b6ab40b228d4a7a9 100644 --- a/ets2panda/linter-4.2/test/types.ts.relax.json +++ b/ets2panda/linter-4.2/test/types.ts.relax.json @@ -84,13 +84,6 @@ "suggest": "", "rule": "Type guarding is supported with \"instanceof\" and \"as\" (arkts-no-is)" }, - { - "line": 47, - "column": 17, - "problem": "RegexLiteral", - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, { "line": 54, "column": 26, @@ -112,13 +105,6 @@ "suggest": "", "rule": "Objects with property names that are not identifiers are not supported (arkts-identifiers-as-prop-names)" }, - { - "line": 66, - "column": 46, - "problem": "ObjectLiteralNoContextType", - "suggest": "", - "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)" - }, { "line": 71, "column": 19, diff --git a/ets2panda/linter-4.2/test/types.ts.strict.json b/ets2panda/linter-4.2/test/types.ts.strict.json index 12af0241f0080993d1207515856d070dd647e821..0daaa7cf3bb185a5489a8bccc033c12226a89049 100644 --- a/ets2panda/linter-4.2/test/types.ts.strict.json +++ b/ets2panda/linter-4.2/test/types.ts.strict.json @@ -91,13 +91,6 @@ "suggest": "", "rule": "Type guarding is supported with \"instanceof\" and \"as\" (arkts-no-is)" }, - { - "line": 47, - "column": 17, - "problem": "RegexLiteral", - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, { "line": 54, "column": 26, @@ -133,13 +126,6 @@ "suggest": "", "rule": "Objects with property names that are not identifiers are not supported (arkts-identifiers-as-prop-names)" }, - { - "line": 66, - "column": 46, - "problem": "ObjectLiteralNoContextType", - "suggest": "", - "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)" - }, { "line": 67, "column": 3, @@ -189,6 +175,13 @@ "suggest": "", "rule": "\"in\" operator is not supported (arkts-no-in)" }, + { + "line": 78, + "column": 5, + "problem": "PropertyAccessByIndex", + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, { "line": 92, "column": 22, diff --git a/ets2panda/linter-4.2/test/utility_types.ts.relax.json b/ets2panda/linter-4.2/test/utility_types.ts.relax.json index fe6131546f4334735729e5e3c1ab931d44572443..3cf1d725ebb4be862b07200488f2af0aaf9408f6 100644 --- a/ets2panda/linter-4.2/test/utility_types.ts.relax.json +++ b/ets2panda/linter-4.2/test/utility_types.ts.relax.json @@ -17,382 +17,541 @@ { "line": 17, "column": 12, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 19, "column": 12, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 21, "column": 12, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 53, "column": 22, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 55, "column": 29, - "problem": "ObjectLiteralNoContextType" + "problem": "ObjectLiteralNoContextType", + "suggest": "", + "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)" }, { "line": 71, "column": 22, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 73, "column": 29, - "problem": "ObjectLiteralNoContextType" + "problem": "ObjectLiteralNoContextType", + "suggest": "", + "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)" }, { "line": 81, "column": 19, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 83, "column": 30, - "problem": "ObjectLiteralNoContextType" + "problem": "ObjectLiteralNoContextType", + "suggest": "", + "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)" }, { "line": 92, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 94, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 96, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 100, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 102, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 106, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 108, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 108, "column": 43, - "problem": "UnknownType" + "problem": "UnknownType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" }, { "line": 112, "column": 20, - "problem": "ObjectTypeLiteral" + "problem": "ObjectTypeLiteral", + "suggest": "", + "rule": "Object literals cannot be used as type declarations (arkts-no-obj-literals-as-types)" }, { "line": 116, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 118, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 120, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 122, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 122, "column": 24, - "problem": "TypeQuery" + "problem": "TypeQuery", + "suggest": "", + "rule": "\"typeof\" operator is allowed only in expression contexts (arkts-no-type-query)" }, { "line": 124, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 124, "column": 24, - "problem": "AnyType" + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" }, { "line": 126, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 130, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 132, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 134, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 136, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 136, "column": 35, - "problem": "AnyType" + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" }, { "line": 140, "column": 18, - "problem": "ObjectTypeLiteral" + "problem": "ObjectTypeLiteral", + "suggest": "", + "rule": "Object literals cannot be used as type declarations (arkts-no-obj-literals-as-types)" }, { "line": 141, "column": 12, - "problem": "ObjectLiteralNoContextType" + "problem": "ObjectLiteralNoContextType", + "suggest": "", + "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)" }, { "line": 144, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 146, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 148, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 150, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 152, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 152, "column": 24, - "problem": "TypeQuery" + "problem": "TypeQuery", + "suggest": "", + "rule": "\"typeof\" operator is allowed only in expression contexts (arkts-no-type-query)" }, { "line": 154, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 154, "column": 24, - "problem": "AnyType" + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" }, { "line": 156, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 165, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 165, "column": 26, - "problem": "TypeQuery" + "problem": "TypeQuery", + "suggest": "", + "rule": "\"typeof\" operator is allowed only in expression contexts (arkts-no-type-query)" }, { "line": 167, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 167, "column": 26, - "problem": "AnyType" + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" }, { "line": 169, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { - "line": 173, - "column": 3, - "problem": "FunctionContainsThis" + "line": 174, + "column": 12, + "problem": "FunctionContainsThis", + "suggest": "", + "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" }, { "line": 177, "column": 30, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 177, "column": 48, - "problem": "TypeQuery" + "problem": "TypeQuery", + "suggest": "", + "rule": "\"typeof\" operator is allowed only in expression contexts (arkts-no-type-query)" }, { "line": 178, "column": 18, - "problem": "FunctionApplyBindCall" + "problem": "FunctionApplyCall", + "suggest": "", + "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { - "line": 183, - "column": 3, - "problem": "FunctionContainsThis" + "line": 184, + "column": 12, + "problem": "FunctionContainsThis", + "suggest": "", + "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" }, { "line": 187, "column": 20, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 187, "column": 38, - "problem": "TypeQuery" + "problem": "TypeQuery", + "suggest": "", + "rule": "\"typeof\" operator is allowed only in expression contexts (arkts-no-type-query)" }, { "line": 187, "column": 60, - "problem": "FunctionApplyBindCall" + "problem": "FunctionBind", + "suggest": "", + "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" + }, + { + "line": 208, + "column": 9, + "problem": "FunctionContainsThis", + "suggest": "", + "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" }, { - "line": 192, - "column": 1, - "problem": "FunctionContainsThis" + "line": 209, + "column": 9, + "problem": "FunctionContainsThis", + "suggest": "", + "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" }, { "line": 193, "column": 33, - "problem": "ObjectTypeLiteral" + "problem": "ObjectTypeLiteral", + "suggest": "", + "rule": "Object literals cannot be used as type declarations (arkts-no-obj-literals-as-types)" }, { "line": 195, "column": 15, - "problem": "IntersectionType" + "problem": "IntersectionType", + "suggest": "", + "rule": "Use inheritance instead of intersection types (arkts-no-intersection-types)" }, { "line": 195, "column": 19, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 195, "column": 28, - "problem": "IntersectionType" + "problem": "IntersectionType", + "suggest": "", + "rule": "Use inheritance instead of intersection types (arkts-no-intersection-types)" }, { "line": 198, "column": 60, - "problem": "IntersectionType" + "problem": "IntersectionType", + "suggest": "", + "rule": "Use inheritance instead of intersection types (arkts-no-intersection-types)" }, { "line": 199, "column": 39, - "problem": "ObjectLiteralNoContextType" + "problem": "ObjectLiteralNoContextType", + "suggest": "", + "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)" }, { "line": 200, "column": 45, - "problem": "ObjectLiteralNoContextType" + "problem": "ObjectLiteralNoContextType", + "suggest": "", + "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)" }, { "line": 201, "column": 12, - "problem": "ObjectLiteralNoContextType" + "problem": "ObjectLiteralNoContextType", + "suggest": "", + "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)" }, { "line": 201, "column": 14, - "problem": "SpreadOperator" + "problem": "SpreadOperator", + "suggest": "", + "rule": "It is possible to spread only arrays or classes derived from arrays into the rest parameter or array literals (arkts-no-spread)" }, { "line": 201, "column": 23, - "problem": "SpreadOperator" + "problem": "SpreadOperator", + "suggest": "", + "rule": "It is possible to spread only arrays or classes derived from arrays into the rest parameter or array literals (arkts-no-spread)" }, { "line": 201, "column": 39, - "problem": "IntersectionType" + "problem": "IntersectionType", + "suggest": "", + "rule": "Use inheritance instead of intersection types (arkts-no-intersection-types)" }, { "line": 204, "column": 26, - "problem": "ObjectLiteralNoContextType" + "problem": "ObjectLiteralNoContextType", + "suggest": "", + "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)" }, { "line": 205, "column": 11, - "problem": "ObjectLiteralNoContextType" + "problem": "ObjectLiteralNoContextType", + "suggest": "", + "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)" }, { "line": 206, "column": 14, - "problem": "ObjectLiteralNoContextType" + "problem": "ObjectLiteralNoContextType", + "suggest": "", + "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)" }, { "line": 221, "column": 25, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 223, "column": 50, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 229, "column": 24, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 231, "column": 50, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 237, "column": 19, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 242, "column": 32, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" } ] } \ No newline at end of file diff --git a/ets2panda/linter-4.2/test/utility_types.ts.strict.json b/ets2panda/linter-4.2/test/utility_types.ts.strict.json index c5686de4b4c5b53e4bfe52a623bec80e0c788296..a861ec3611dcf71302672746415236b0ee4912eb 100644 --- a/ets2panda/linter-4.2/test/utility_types.ts.strict.json +++ b/ets2panda/linter-4.2/test/utility_types.ts.strict.json @@ -17,417 +17,590 @@ { "line": 17, "column": 12, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 19, "column": 12, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 21, "column": 12, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 53, "column": 22, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 55, "column": 29, - "problem": "ObjectLiteralNoContextType" + "problem": "ObjectLiteralNoContextType", + "suggest": "", + "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)" }, { "line": 71, "column": 22, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 73, "column": 29, - "problem": "ObjectLiteralNoContextType" + "problem": "ObjectLiteralNoContextType", + "suggest": "", + "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)" }, { "line": 81, "column": 19, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 83, "column": 30, - "problem": "ObjectLiteralNoContextType" + "problem": "ObjectLiteralNoContextType", + "suggest": "", + "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)" }, { "line": 92, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 94, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 96, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 100, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 102, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 106, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 108, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 108, "column": 43, - "problem": "UnknownType" + "problem": "UnknownType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" }, { "line": 112, "column": 3, - "problem": "LocalFunction" + "problem": "LocalFunction", + "suggest": "", + "rule": "Nested functions are not supported (arkts-no-nested-funcs)" }, { "line": 112, "column": 20, - "problem": "ObjectTypeLiteral" + "problem": "ObjectTypeLiteral", + "suggest": "", + "rule": "Object literals cannot be used as type declarations (arkts-no-obj-literals-as-types)" }, { "line": 116, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 118, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 120, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 122, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 122, "column": 24, - "problem": "TypeQuery" + "problem": "TypeQuery", + "suggest": "", + "rule": "\"typeof\" operator is allowed only in expression contexts (arkts-no-type-query)" }, { "line": 124, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 124, "column": 24, - "problem": "AnyType" + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" }, { "line": 126, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 130, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 132, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 134, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 136, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 136, "column": 35, - "problem": "AnyType" + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" }, { "line": 140, "column": 3, - "problem": "LocalFunction" + "problem": "LocalFunction", + "suggest": "", + "rule": "Nested functions are not supported (arkts-no-nested-funcs)" }, { "line": 140, "column": 18, - "problem": "ObjectTypeLiteral" + "problem": "ObjectTypeLiteral", + "suggest": "", + "rule": "Object literals cannot be used as type declarations (arkts-no-obj-literals-as-types)" }, { "line": 141, "column": 12, - "problem": "ObjectLiteralNoContextType" + "problem": "ObjectLiteralNoContextType", + "suggest": "", + "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)" }, { "line": 144, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 146, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 148, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 150, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 152, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 152, "column": 24, - "problem": "TypeQuery" + "problem": "TypeQuery", + "suggest": "", + "rule": "\"typeof\" operator is allowed only in expression contexts (arkts-no-type-query)" }, { "line": 154, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 154, "column": 24, - "problem": "AnyType" + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" }, { "line": 156, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 165, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 165, "column": 26, - "problem": "TypeQuery" + "problem": "TypeQuery", + "suggest": "", + "rule": "\"typeof\" operator is allowed only in expression contexts (arkts-no-type-query)" }, { "line": 167, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 167, "column": 26, - "problem": "AnyType" + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" }, { "line": 169, "column": 13, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { - "line": 173, - "column": 3, - "problem": "FunctionContainsThis" + "line": 174, + "column": 12, + "problem": "FunctionContainsThis", + "suggest": "", + "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" }, { "line": 173, "column": 3, - "problem": "LocalFunction" + "problem": "LocalFunction", + "suggest": "", + "rule": "Nested functions are not supported (arkts-no-nested-funcs)" }, { "line": 177, "column": 3, - "problem": "LimitedReturnTypeInference" + "problem": "LimitedReturnTypeInference", + "suggest": "", + "rule": "Function return type inference is limited (arkts-no-implicit-return-types)" }, { "line": 177, "column": 3, - "problem": "LocalFunction" + "problem": "LocalFunction", + "suggest": "", + "rule": "Nested functions are not supported (arkts-no-nested-funcs)" }, { "line": 177, "column": 30, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 177, "column": 48, - "problem": "TypeQuery" + "problem": "TypeQuery", + "suggest": "", + "rule": "\"typeof\" operator is allowed only in expression contexts (arkts-no-type-query)" }, { "line": 178, "column": 18, - "problem": "FunctionApplyBindCall" + "problem": "FunctionApplyCall", + "suggest": "", + "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { - "line": 183, - "column": 3, - "problem": "FunctionContainsThis" + "line": 184, + "column": 12, + "problem": "FunctionContainsThis", + "suggest": "", + "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" }, { "line": 183, "column": 3, - "problem": "LocalFunction" + "problem": "LocalFunction", + "suggest": "", + "rule": "Nested functions are not supported (arkts-no-nested-funcs)" }, { "line": 187, "column": 20, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 187, "column": 38, - "problem": "TypeQuery" + "problem": "TypeQuery", + "suggest": "", + "rule": "\"typeof\" operator is allowed only in expression contexts (arkts-no-type-query)" }, { "line": 187, "column": 60, - "problem": "FunctionApplyBindCall" + "problem": "FunctionBind", + "suggest": "", + "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" + }, + { + "line": 208, + "column": 9, + "problem": "FunctionContainsThis", + "suggest": "", + "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" }, { - "line": 192, - "column": 1, - "problem": "FunctionContainsThis" + "line": 209, + "column": 9, + "problem": "FunctionContainsThis", + "suggest": "", + "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" }, { "line": 193, "column": 33, - "problem": "ObjectTypeLiteral" + "problem": "ObjectTypeLiteral", + "suggest": "", + "rule": "Object literals cannot be used as type declarations (arkts-no-obj-literals-as-types)" }, { "line": 195, "column": 15, - "problem": "IntersectionType" + "problem": "IntersectionType", + "suggest": "", + "rule": "Use inheritance instead of intersection types (arkts-no-intersection-types)" }, { "line": 195, "column": 19, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 195, "column": 28, - "problem": "IntersectionType" + "problem": "IntersectionType", + "suggest": "", + "rule": "Use inheritance instead of intersection types (arkts-no-intersection-types)" }, { "line": 198, "column": 3, - "problem": "LocalFunction" + "problem": "LocalFunction", + "suggest": "", + "rule": "Nested functions are not supported (arkts-no-nested-funcs)" }, { "line": 198, "column": 60, - "problem": "IntersectionType" + "problem": "IntersectionType", + "suggest": "", + "rule": "Use inheritance instead of intersection types (arkts-no-intersection-types)" }, { "line": 199, "column": 39, - "problem": "ObjectLiteralNoContextType" + "problem": "ObjectLiteralNoContextType", + "suggest": "", + "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)" }, { "line": 200, "column": 45, - "problem": "ObjectLiteralNoContextType" + "problem": "ObjectLiteralNoContextType", + "suggest": "", + "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)" }, { "line": 201, "column": 12, - "problem": "ObjectLiteralNoContextType" + "problem": "ObjectLiteralNoContextType", + "suggest": "", + "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)" }, { "line": 201, "column": 14, - "problem": "SpreadOperator" + "problem": "SpreadOperator", + "suggest": "", + "rule": "It is possible to spread only arrays or classes derived from arrays into the rest parameter or array literals (arkts-no-spread)" }, { "line": 201, "column": 23, - "problem": "SpreadOperator" + "problem": "SpreadOperator", + "suggest": "", + "rule": "It is possible to spread only arrays or classes derived from arrays into the rest parameter or array literals (arkts-no-spread)" }, { "line": 201, "column": 39, - "problem": "IntersectionType" + "problem": "IntersectionType", + "suggest": "", + "rule": "Use inheritance instead of intersection types (arkts-no-intersection-types)" }, { "line": 204, "column": 26, - "problem": "ObjectLiteralNoContextType" + "problem": "ObjectLiteralNoContextType", + "suggest": "", + "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)" }, { "line": 205, "column": 11, - "problem": "ObjectLiteralNoContextType" + "problem": "ObjectLiteralNoContextType", + "suggest": "", + "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)" }, { "line": 206, "column": 14, - "problem": "ObjectLiteralNoContextType" + "problem": "ObjectLiteralNoContextType", + "suggest": "", + "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)" }, { "line": 221, "column": 25, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 223, "column": 50, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 229, "column": 24, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 231, "column": 50, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 237, "column": 19, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" }, { "line": 242, "column": 32, - "problem": "UtilityType" + "problem": "UtilityType", + "suggest": "", + "rule": "Some of utility types are not supported (arkts-no-utility-types)" } ] } \ No newline at end of file diff --git a/ets2panda/linter-4.2/test_extended_features/double_dollar_operation.ets b/ets2panda/linter-4.2/test_extended_features/$$_operation.ets similarity index 95% rename from ets2panda/linter-4.2/test_extended_features/double_dollar_operation.ets rename to ets2panda/linter-4.2/test_extended_features/$$_operation.ets index 8af66c25fd4b6130a18e17825bf151bac54eb2cf..a8d7ba16d91de330a359bcb8133b7bde784f5aaf 100644 --- a/ets2panda/linter-4.2/test_extended_features/double_dollar_operation.ets +++ b/ets2panda/linter-4.2/test_extended_features/$$_operation.ets @@ -2,10 +2,10 @@ * Copyright (c) 2022-2023 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* +* You may obtain a copy of the License at +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable low or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/ets2panda/linter-4.2/test_extended_features/double_dollar_operation1.ets b/ets2panda/linter-4.2/test_extended_features/$$_operation1.ets similarity index 100% rename from ets2panda/linter-4.2/test_extended_features/double_dollar_operation1.ets rename to ets2panda/linter-4.2/test_extended_features/$$_operation1.ets diff --git a/ets2panda/linter-4.2/test_extended_features/dollar_operation.ets b/ets2panda/linter-4.2/test_extended_features/$_operation.ets similarity index 100% rename from ets2panda/linter-4.2/test_extended_features/dollar_operation.ets rename to ets2panda/linter-4.2/test_extended_features/$_operation.ets diff --git a/ets2panda/linter-4.2/test_extended_features/object_literals_sdk_diff.ets b/ets2panda/linter-4.2/test_extended_features/object_literals_sdk_diff.ets index b19fdb4c607e0eadde10332f5690726487c8d08a..2c03a29324cb08a9b819e11087dad703e5ecefb4 100644 --- a/ets2panda/linter-4.2/test_extended_features/object_literals_sdk_diff.ets +++ b/ets2panda/linter-4.2/test_extended_features/object_literals_sdk_diff.ets @@ -1,17 +1,17 @@ /* - * Copyright (c) 2022-2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable low or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +* Copyright (c) 2022-2023 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable low or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ //issue 13689 p4 @Entry diff --git a/ets2panda/linter-4.2/test_extended_features/object_literals_sdk_struct.ets b/ets2panda/linter-4.2/test_extended_features/object_literals_sdk_struct.ets index d69b943e0588877979c041cbe0e2b3d8bdc25d5e..008178b0a723f16be25455e97f292bec3e65d20a 100644 --- a/ets2panda/linter-4.2/test_extended_features/object_literals_sdk_struct.ets +++ b/ets2panda/linter-4.2/test_extended_features/object_literals_sdk_struct.ets @@ -1,17 +1,17 @@ /* - * Copyright (c) 2022-2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable low or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +* Copyright (c) 2022-2023 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable low or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ //issue 13689 p3 @Entry diff --git a/ets2panda/linter-4.2/test_extended_features/dollar_operation_TN.ets b/ets2panda/linter-4.2/test_regression/14412.ts similarity index 74% rename from ets2panda/linter-4.2/test_extended_features/dollar_operation_TN.ets rename to ets2panda/linter-4.2/test_regression/14412.ts index fb14c4fba2cf72dca8a591747af6c9745f324a54..1cd4fdfb298121102f2ed3524d968712ac3eff8b 100644 --- a/ets2panda/linter-4.2/test_extended_features/dollar_operation_TN.ets +++ b/ets2panda/linter-4.2/test_regression/14412.ts @@ -12,20 +12,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -//test for defect 14031 -export class Foo1 { - foo1?: string[] = []; -} - -@Component -export struct SomeStruct { - @Link someStruct: Foo1[]; -} +class C {} -export class Foo { - SomeStruct({ - someStruct: $someStruct - }); +class A { + v: [C, number][] = []; + foo() { + Array.from(this.v).sort(); + } } +let c: [C, number][] = []; +Array.from(c).sort(); diff --git a/ets2panda/linter-4.2/test_regression/14412.ts.autofix.json b/ets2panda/linter-4.2/test_regression/14412.ts.autofix.json new file mode 100644 index 0000000000000000000000000000000000000000..e4efb45db7a6974ed8ca27490e5acb755dfb13ca --- /dev/null +++ b/ets2panda/linter-4.2/test_regression/14412.ts.autofix.json @@ -0,0 +1,17 @@ +{ + "copyright": [ + "Copyright (c) 2022-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + "nodes": [] +} diff --git a/ets2panda/linter-4.2/test_regression/14412.ts.relax.json b/ets2panda/linter-4.2/test_regression/14412.ts.relax.json new file mode 100755 index 0000000000000000000000000000000000000000..e4efb45db7a6974ed8ca27490e5acb755dfb13ca --- /dev/null +++ b/ets2panda/linter-4.2/test_regression/14412.ts.relax.json @@ -0,0 +1,17 @@ +{ + "copyright": [ + "Copyright (c) 2022-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + "nodes": [] +} diff --git a/ets2panda/linter-4.2/test_regression/14412.ts.strict.json b/ets2panda/linter-4.2/test_regression/14412.ts.strict.json new file mode 100755 index 0000000000000000000000000000000000000000..e4efb45db7a6974ed8ca27490e5acb755dfb13ca --- /dev/null +++ b/ets2panda/linter-4.2/test_regression/14412.ts.strict.json @@ -0,0 +1,17 @@ +{ + "copyright": [ + "Copyright (c) 2022-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + "nodes": [] +} diff --git a/ets2panda/linter-4.2/test_rules/rule1.ts b/ets2panda/linter-4.2/test_rules/rule1.ts index b0332f6435e764e006ec357ac106077999a5b8f6..4fdb6bf8c9763e56174841c614debe807bdf0cbc 100644 --- a/ets2panda/linter-4.2/test_rules/rule1.ts +++ b/ets2panda/linter-4.2/test_rules/rule1.ts @@ -1,14 +1,41 @@ -var x = {"name": 1, 2: 3} +var x = { 'name': 1, 2: 3 }; -console.log(x["name"]) -console.log(x[2]) +console.log(x['name']); +console.log(x[2]); class X { - public name: number = 0 + public name: number = 0; } -let y = {name: 1} -console.log(x.name) +let y = { name: 1 }; +console.log(x.name); -let z = [1, 2, 3] -console.log(y[2]) \ No newline at end of file +let z = [1, 2, 3]; +console.log(y[2]); + +enum S1 { + SS = 'qwqwq' +} + +enum S2 { + SS = 123 +} + +interface A1 { + [S1.SS]: string; +} + +interface A2 { + [S2.SS]: string; +} + +const a1: A1 = { + [S1.SS]: 'fld1' +}; + +const a2: A2 = { + [S2.SS]: 'fld2' +}; + +S1['SS']; +S2['SS']; diff --git a/ets2panda/linter-4.2/test_rules/rule1.ts.autofix.json b/ets2panda/linter-4.2/test_rules/rule1.ts.autofix.json index 32a60bb65fcb0cb40bb1b2c2b5f7a80779c2f0cf..5ca84dd5491ea12909f106ef51f57287d156baa5 100644 --- a/ets2panda/linter-4.2/test_rules/rule1.ts.autofix.json +++ b/ets2panda/linter-4.2/test_rules/rule1.ts.autofix.json @@ -32,7 +32,7 @@ }, { "line": 2, - "column": 10, + "column": 11, "problem": "LiteralAsPropertyName", "autofixable": true, "suggest": "", @@ -40,7 +40,7 @@ }, { "line": 2, - "column": 21, + "column": 22, "problem": "LiteralAsPropertyName", "autofixable": true, "suggest": "", @@ -77,6 +77,22 @@ "autofixable": true, "suggest": "", "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 29, + "column": 3, + "problem": "ComputedPropertyName", + "autofixable": false, + "suggest": "", + "rule": "Objects with property names that are not identifiers are not supported (arkts-identifiers-as-prop-names)" + }, + { + "line": 37, + "column": 3, + "problem": "ComputedPropertyName", + "autofixable": false, + "suggest": "", + "rule": "Objects with property names that are not identifiers are not supported (arkts-identifiers-as-prop-names)" } ] } \ No newline at end of file diff --git a/ets2panda/linter-4.2/test_rules/rule1.ts.relax.json b/ets2panda/linter-4.2/test_rules/rule1.ts.relax.json index 861e06d092491f53b401c5fa2a633ce8e932a417..0e101cb06ac20a3aa0593256cdf157084728c469 100644 --- a/ets2panda/linter-4.2/test_rules/rule1.ts.relax.json +++ b/ets2panda/linter-4.2/test_rules/rule1.ts.relax.json @@ -13,6 +13,20 @@ "problem": "ObjectLiteralNoContextType", "suggest": "", "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)" + }, + { + "line": 29, + "column": 3, + "problem": "ComputedPropertyName", + "suggest": "", + "rule": "Objects with property names that are not identifiers are not supported (arkts-identifiers-as-prop-names)" + }, + { + "line": 37, + "column": 3, + "problem": "ComputedPropertyName", + "suggest": "", + "rule": "Objects with property names that are not identifiers are not supported (arkts-identifiers-as-prop-names)" } ] } \ No newline at end of file diff --git a/ets2panda/linter-4.2/test_rules/rule1.ts.strict.json b/ets2panda/linter-4.2/test_rules/rule1.ts.strict.json index 5728ad6cdb5502758ccbdee12f51fe5023b9ef89..5bd7f047e69e6755a73856803de279a57bde56c3 100644 --- a/ets2panda/linter-4.2/test_rules/rule1.ts.strict.json +++ b/ets2panda/linter-4.2/test_rules/rule1.ts.strict.json @@ -16,14 +16,14 @@ }, { "line": 2, - "column": 10, + "column": 11, "problem": "LiteralAsPropertyName", "suggest": "", "rule": "Objects with property names that are not identifiers are not supported (arkts-identifiers-as-prop-names)" }, { "line": 2, - "column": 21, + "column": 22, "problem": "LiteralAsPropertyName", "suggest": "", "rule": "Objects with property names that are not identifiers are not supported (arkts-identifiers-as-prop-names)" @@ -55,6 +55,20 @@ "problem": "PropertyAccessByIndex", "suggest": "", "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 29, + "column": 3, + "problem": "ComputedPropertyName", + "suggest": "", + "rule": "Objects with property names that are not identifiers are not supported (arkts-identifiers-as-prop-names)" + }, + { + "line": 37, + "column": 3, + "problem": "ComputedPropertyName", + "suggest": "", + "rule": "Objects with property names that are not identifiers are not supported (arkts-identifiers-as-prop-names)" } ] } \ No newline at end of file diff --git a/ets2panda/linter-4.2/test_rules/rule116.ts.autofix.json b/ets2panda/linter-4.2/test_rules/rule116.ts.autofix.json index 8449964f6b0aebed7a3a412a87e959afbcb33487..d623882bd479636af0d59d8773f1f8a22b032488 100644 --- a/ets2panda/linter-4.2/test_rules/rule116.ts.autofix.json +++ b/ets2panda/linter-4.2/test_rules/rule116.ts.autofix.json @@ -1,4 +1,18 @@ { + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], "nodes": [ { "line": 3, @@ -6,7 +20,7 @@ "problem": "NonDeclarationInNamespace", "autofixable": false, "suggest": "", - "rule": "Non-declaration statements in namespaces are not supported (arkts-no-ns-statements)" + "rule": "Non-declaration statements in namespaces are not supported (single semicolons are considered as empty non-declaration statements) (arkts-no-ns-statements)" } ] } \ No newline at end of file diff --git a/ets2panda/linter-4.2/test_rules/rule116.ts.relax.json b/ets2panda/linter-4.2/test_rules/rule116.ts.relax.json index a5030d64e8a3e5c962cfc25bcfc7d49e88e73a86..a13f450c7608f607236040ef625cc1bf432c068f 100644 --- a/ets2panda/linter-4.2/test_rules/rule116.ts.relax.json +++ b/ets2panda/linter-4.2/test_rules/rule116.ts.relax.json @@ -1,11 +1,25 @@ { + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], "nodes": [ { "line": 3, "column": 5, "problem": "NonDeclarationInNamespace", "suggest": "", - "rule": "Non-declaration statements in namespaces are not supported (arkts-no-ns-statements)" + "rule": "Non-declaration statements in namespaces are not supported (single semicolons are considered as empty non-declaration statements) (arkts-no-ns-statements)" } ] } \ No newline at end of file diff --git a/ets2panda/linter-4.2/test_rules/rule116.ts.strict.json b/ets2panda/linter-4.2/test_rules/rule116.ts.strict.json index a5030d64e8a3e5c962cfc25bcfc7d49e88e73a86..a13f450c7608f607236040ef625cc1bf432c068f 100644 --- a/ets2panda/linter-4.2/test_rules/rule116.ts.strict.json +++ b/ets2panda/linter-4.2/test_rules/rule116.ts.strict.json @@ -1,11 +1,25 @@ { + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], "nodes": [ { "line": 3, "column": 5, "problem": "NonDeclarationInNamespace", "suggest": "", - "rule": "Non-declaration statements in namespaces are not supported (arkts-no-ns-statements)" + "rule": "Non-declaration statements in namespaces are not supported (single semicolons are considered as empty non-declaration statements) (arkts-no-ns-statements)" } ] } \ No newline at end of file diff --git a/ets2panda/linter-4.2/test_rules/rule118.ts.autofix.json b/ets2panda/linter-4.2/test_rules/rule118.ts.autofix.json index 3ff68f6c73f0870b0af431b06348497a9bf6a784..13f13363f579325755e8954b4011963971667481 100644 --- a/ets2panda/linter-4.2/test_rules/rule118.ts.autofix.json +++ b/ets2panda/linter-4.2/test_rules/rule118.ts.autofix.json @@ -1,19 +1,3 @@ { - "nodes": [ - { - "line": 5, - "column": 12, - "problem": "TypeOnlyImport", - "autofixable": true, - "autofix": [ - { - "start": 120, - "end": 144, - "replacementText": "{ APIResponseType }" - } - ], - "suggest": "", - "rule": "Special import type declarations are not supported (arkts-no-special-imports)" - } - ] + "nodes": [] } \ No newline at end of file diff --git a/ets2panda/linter-4.2/test_rules/rule118.ts.strict.json b/ets2panda/linter-4.2/test_rules/rule118.ts.strict.json index d30836a74acd706fd8fe37f455f2cb38b9d30f96..13f13363f579325755e8954b4011963971667481 100644 --- a/ets2panda/linter-4.2/test_rules/rule118.ts.strict.json +++ b/ets2panda/linter-4.2/test_rules/rule118.ts.strict.json @@ -1,11 +1,3 @@ { - "nodes": [ - { - "line": 5, - "column": 12, - "problem": "TypeOnlyImport", - "suggest": "", - "rule": "Special import type declarations are not supported (arkts-no-special-imports)" - } - ] + "nodes": [] } \ No newline at end of file diff --git a/ets2panda/linter-4.2/test_rules/rule127.ts.autofix.json b/ets2panda/linter-4.2/test_rules/rule127.ts.autofix.json index c32c0f461dd55626cd83972b7aa2a8d7bff66f60..13f13363f579325755e8954b4011963971667481 100644 --- a/ets2panda/linter-4.2/test_rules/rule127.ts.autofix.json +++ b/ets2panda/linter-4.2/test_rules/rule127.ts.autofix.json @@ -1,19 +1,3 @@ { - "nodes": [ - { - "line": 12, - "column": 5, - "problem": "TypeOnlyExport", - "autofixable": true, - "autofix": [ - { - "start": 218, - "end": 240, - "replacementText": "export { Class2 };" - } - ], - "suggest": "", - "rule": "Special \"export type\" declarations are not supported (arkts-no-special-exports)" - } - ] + "nodes": [] } \ No newline at end of file diff --git a/ets2panda/linter-4.2/test_rules/rule127.ts.strict.json b/ets2panda/linter-4.2/test_rules/rule127.ts.strict.json index 35f1a58896b2262887e713eb7ea091a5342d8b98..13f13363f579325755e8954b4011963971667481 100644 --- a/ets2panda/linter-4.2/test_rules/rule127.ts.strict.json +++ b/ets2panda/linter-4.2/test_rules/rule127.ts.strict.json @@ -1,11 +1,3 @@ { - "nodes": [ - { - "line": 12, - "column": 5, - "problem": "TypeOnlyExport", - "suggest": "", - "rule": "Special \"export type\" declarations are not supported (arkts-no-special-exports)" - } - ] + "nodes": [] } \ No newline at end of file diff --git a/ets2panda/linter-4.2/test_rules/rule129.ts.autofix.json b/ets2panda/linter-4.2/test_rules/rule129.ts.autofix.json index 6708658aec6699b02802c0afa208ab7c076c39a4..897f1d325796ef6f4807901695a64a2380426250 100644 --- a/ets2panda/linter-4.2/test_rules/rule129.ts.autofix.json +++ b/ets2panda/linter-4.2/test_rules/rule129.ts.autofix.json @@ -1,4 +1,18 @@ { + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], "nodes": [ { "line": 4, @@ -6,7 +20,7 @@ "problem": "NonDeclarationInNamespace", "autofixable": false, "suggest": "", - "rule": "Non-declaration statements in namespaces are not supported (arkts-no-ns-statements)" + "rule": "Non-declaration statements in namespaces are not supported (single semicolons are considered as empty non-declaration statements) (arkts-no-ns-statements)" }, { "line": 2, @@ -29,6 +43,7 @@ "column": 5, "problem": "ImportAfterStatement", "autofixable": false, + "suggest": "", "rule": "\"import\" statements after other statements are not allowed (arkts-no-misplaced-imports)" } ] diff --git a/ets2panda/linter-4.2/test_rules/rule129.ts.relax.json b/ets2panda/linter-4.2/test_rules/rule129.ts.relax.json index cc882319377db9e73f996ceb24a77cc508180fdd..0cb1a6d72636f892b2dbb36e0361ac0a8bc1088d 100644 --- a/ets2panda/linter-4.2/test_rules/rule129.ts.relax.json +++ b/ets2panda/linter-4.2/test_rules/rule129.ts.relax.json @@ -1,11 +1,25 @@ { + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], "nodes": [ { "line": 4, "column": 9, "problem": "NonDeclarationInNamespace", "suggest": "", - "rule": "Non-declaration statements in namespaces are not supported (arkts-no-ns-statements)" + "rule": "Non-declaration statements in namespaces are not supported (single semicolons are considered as empty non-declaration statements) (arkts-no-ns-statements)" }, { "line": 2, @@ -25,6 +39,7 @@ "line": 8, "column": 5, "problem": "ImportAfterStatement", + "suggest": "", "rule": "\"import\" statements after other statements are not allowed (arkts-no-misplaced-imports)" } ] diff --git a/ets2panda/linter-4.2/test_rules/rule129.ts.strict.json b/ets2panda/linter-4.2/test_rules/rule129.ts.strict.json index cc882319377db9e73f996ceb24a77cc508180fdd..0cb1a6d72636f892b2dbb36e0361ac0a8bc1088d 100644 --- a/ets2panda/linter-4.2/test_rules/rule129.ts.strict.json +++ b/ets2panda/linter-4.2/test_rules/rule129.ts.strict.json @@ -1,11 +1,25 @@ { + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], "nodes": [ { "line": 4, "column": 9, "problem": "NonDeclarationInNamespace", "suggest": "", - "rule": "Non-declaration statements in namespaces are not supported (arkts-no-ns-statements)" + "rule": "Non-declaration statements in namespaces are not supported (single semicolons are considered as empty non-declaration statements) (arkts-no-ns-statements)" }, { "line": 2, @@ -25,6 +39,7 @@ "line": 8, "column": 5, "problem": "ImportAfterStatement", + "suggest": "", "rule": "\"import\" statements after other statements are not allowed (arkts-no-misplaced-imports)" } ] diff --git a/ets2panda/linter-4.2/test_rules/rule132.ts.autofix.json b/ets2panda/linter-4.2/test_rules/rule132.ts.autofix.json index d1868ca7f895576a790a872e83c05b3f7b619086..237d72d680231addc4afea7ac7624580e8eb8e59 100644 --- a/ets2panda/linter-4.2/test_rules/rule132.ts.autofix.json +++ b/ets2panda/linter-4.2/test_rules/rule132.ts.autofix.json @@ -1,4 +1,18 @@ { + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], "nodes": [ { "line": 7, diff --git a/ets2panda/linter-4.2/test_rules/rule132.ts.relax.json b/ets2panda/linter-4.2/test_rules/rule132.ts.relax.json index 21468ce90773221bb8d0e37917a8f9a5eb974223..705f8279b13aac9b4b06aee3343140c3c969f710 100644 --- a/ets2panda/linter-4.2/test_rules/rule132.ts.relax.json +++ b/ets2panda/linter-4.2/test_rules/rule132.ts.relax.json @@ -1,4 +1,18 @@ { + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], "nodes": [ { "line": 7, diff --git a/ets2panda/linter-4.2/test_rules/rule132.ts.strict.json b/ets2panda/linter-4.2/test_rules/rule132.ts.strict.json index 21468ce90773221bb8d0e37917a8f9a5eb974223..705f8279b13aac9b4b06aee3343140c3c969f710 100644 --- a/ets2panda/linter-4.2/test_rules/rule132.ts.strict.json +++ b/ets2panda/linter-4.2/test_rules/rule132.ts.strict.json @@ -1,4 +1,18 @@ { + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], "nodes": [ { "line": 7, diff --git a/ets2panda/linter-4.2/test_rules/rule136.ts b/ets2panda/linter-4.2/test_rules/rule136.ts index 41d0b50953924c05f2ff989b0b23d98f5c2f92b8..92706ab87a00fb37782ca552bad7d445efac9734 100644 --- a/ets2panda/linter-4.2/test_rules/rule136.ts +++ b/ets2panda/linter-4.2/test_rules/rule136.ts @@ -1,13 +1,13 @@ -var C = function(p: number) { - this.p = p // Compile-time error only with noImplicitThis -} +var C = function (p: number) { + this.p = p; // Compile-time error only with noImplicitThis +}; C.prototype = { - m() { - console.log(this.p) - } -} + m() { + console.log(this.p); + } +}; -C.prototype.q = function(r: number) { - return this.p == r -} \ No newline at end of file +C.prototype.q = function (r: number) { + return this.p === r; +}; diff --git a/ets2panda/linter-4.2/test_rules/rule136.ts.autofix.json b/ets2panda/linter-4.2/test_rules/rule136.ts.autofix.json index fb3ab2780e7b41d6ce964d9fdaae740e3df24cb2..3a63117c56d241e6360b6147e2aeac0d27f31d78 100644 --- a/ets2panda/linter-4.2/test_rules/rule136.ts.autofix.json +++ b/ets2panda/linter-4.2/test_rules/rule136.ts.autofix.json @@ -17,8 +17,8 @@ "rule": "Use arrow functions instead of function expressions (arkts-no-func-expressions)" }, { - "line": 1, - "column": 9, + "line": 2, + "column": 3, "problem": "FunctionContainsThis", "autofixable": false, "suggest": "", @@ -49,8 +49,8 @@ "rule": "Use arrow functions instead of function expressions (arkts-no-func-expressions)" }, { - "line": 11, - "column": 17, + "line": 12, + "column": 10, "problem": "FunctionContainsThis", "autofixable": false, "suggest": "", diff --git a/ets2panda/linter-4.2/test_rules/rule136.ts.relax.json b/ets2panda/linter-4.2/test_rules/rule136.ts.relax.json index d9d652719abe74c16916a18a344c7bc48bb31a1c..d118bff3e2c814b40b650b0831e4b7aee9b37f2f 100644 --- a/ets2panda/linter-4.2/test_rules/rule136.ts.relax.json +++ b/ets2panda/linter-4.2/test_rules/rule136.ts.relax.json @@ -1,8 +1,8 @@ { "nodes": [ { - "line": 1, - "column": 9, + "line": 2, + "column": 3, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" @@ -22,8 +22,8 @@ "rule": "Prototype assignment is not supported (arkts-no-prototype-assignment)" }, { - "line": 11, - "column": 17, + "line": 12, + "column": 10, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" diff --git a/ets2panda/linter-4.2/test_rules/rule136.ts.strict.json b/ets2panda/linter-4.2/test_rules/rule136.ts.strict.json index 1df9367d3575996793782d76b077665c8275a070..df0aabd38fc76570832ab8a2d0af899725a4eb3f 100644 --- a/ets2panda/linter-4.2/test_rules/rule136.ts.strict.json +++ b/ets2panda/linter-4.2/test_rules/rule136.ts.strict.json @@ -15,8 +15,8 @@ "rule": "Use arrow functions instead of function expressions (arkts-no-func-expressions)" }, { - "line": 1, - "column": 9, + "line": 2, + "column": 3, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" @@ -43,8 +43,8 @@ "rule": "Use arrow functions instead of function expressions (arkts-no-func-expressions)" }, { - "line": 11, - "column": 17, + "line": 12, + "column": 10, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" diff --git a/ets2panda/linter-4.2/test_rules/rule145.ts b/ets2panda/linter-4.2/test_rules/rule145.ts index cb34a60bcb147ecffdf618d27b7d53edb23f3a88..bcf14f0f735a1828f387a627b1a02ab29f590e31 100644 --- a/ets2panda/linter-4.2/test_rules/rule145.ts +++ b/ets2panda/linter-4.2/test_rules/rule145.ts @@ -1,51 +1,52 @@ class C { - n: number // Compile-time error only with strictPropertyInitialization - s: string // Compile-time error only with strictPropertyInitialization + n: number; // Compile-time error only with strictPropertyInitialization + s: string; // Compile-time error only with strictPropertyInitialization } // Compile-time error only with noImplicitReturns function foo(s: string): string { - if (s != "") { - console.log(s) - return s - } else { - console.log(s) - } + if (s !== '') { + console.log(s); + return s; + } else { + console.log(s); + } } -let n: number = null // Compile-time error only with strictNullChecks +let n: number = null; // Compile-time error only with strictNullChecks -function bar(): number { -} +function bar(): number {} function get1(): boolean { - return true; + return true; } function get2(): boolean { - return false; + return false; } function solve(): boolean { - if(get1() && get2()) { - } else if(!get2()) { - return true; - } else { - } + if (get1() && get2()) { + } else if (!get2()) { + return true; + } else { + } } - - -let lr = (): number => {} -let le = (): number => { if(get()) return 1; } +let lr = (): number => {}; +let le = (): number => { + if (get()) return 1; +}; class testClass { - static readonly lem = (): number => { if(get()) return 1; } - - solve(): boolean { - if(get1() && get2()) { - } else if(!get2()) { - return true; - } else { - } + static readonly lem = (): number => { + if (get()) return 1; + }; + + solve(): boolean { + if (get1() && get2()) { + } else if (!get2()) { + return true; + } else { } + } } diff --git a/ets2panda/linter-4.2/test_rules/rule145.ts.autofix.json b/ets2panda/linter-4.2/test_rules/rule145.ts.autofix.json index a9ba4df8ce6247ed97c444eb13a136ca4e1e5f03..308c5d9c2ac5e7f18ee4791d1c956af10cea2a6d 100644 --- a/ets2panda/linter-4.2/test_rules/rule145.ts.autofix.json +++ b/ets2panda/linter-4.2/test_rules/rule145.ts.autofix.json @@ -1,8 +1,22 @@ { + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], "nodes": [ { "line": 2, - "column": 5, + "column": 3, "problem": "StrictDiagnostic", "autofixable": false, "suggest": "Property 'n' has no initializer and is not definitely assigned in the constructor.", @@ -10,7 +24,7 @@ }, { "line": 3, - "column": 5, + "column": 3, "problem": "StrictDiagnostic", "autofixable": false, "suggest": "Property 's' has no initializer and is not definitely assigned in the constructor.", @@ -33,7 +47,7 @@ "rule": "Type 'null' is not assignable to type 'number'." }, { - "line": 28, + "line": 27, "column": 19, "problem": "StrictDiagnostic", "autofixable": false, @@ -41,7 +55,7 @@ "rule": "Function lacks ending return statement and return type does not include 'undefined'." }, { - "line": 39, + "line": 36, "column": 14, "problem": "StrictDiagnostic", "autofixable": false, @@ -49,20 +63,20 @@ "rule": "Function lacks ending return statement and return type does not include 'undefined'." }, { - "line": 42, - "column": 32, + "line": 41, + "column": 29, "problem": "StrictDiagnostic", "autofixable": false, "suggest": "Function lacks ending return statement and return type does not include 'undefined'.", "rule": "Function lacks ending return statement and return type does not include 'undefined'." }, { - "line": 44, - "column": 14, + "line": 45, + "column": 12, "problem": "StrictDiagnostic", "autofixable": false, "suggest": "Function lacks ending return statement and return type does not include 'undefined'.", "rule": "Function lacks ending return statement and return type does not include 'undefined'." } ] -} +} \ No newline at end of file diff --git a/ets2panda/linter-4.2/test_rules/rule145.ts.relax.json b/ets2panda/linter-4.2/test_rules/rule145.ts.relax.json index c27f5888b89d68a3128497fe8235fca0958db536..fd48119d99cfdcb806208f93d3ca8260e4008670 100644 --- a/ets2panda/linter-4.2/test_rules/rule145.ts.relax.json +++ b/ets2panda/linter-4.2/test_rules/rule145.ts.relax.json @@ -1,15 +1,29 @@ { + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], "nodes": [ { "line": 2, - "column": 5, + "column": 3, "problem": "StrictDiagnostic", "suggest": "Property 'n' has no initializer and is not definitely assigned in the constructor.", "rule": "Property 'n' has no initializer and is not definitely assigned in the constructor." }, { "line": 3, - "column": 5, + "column": 3, "problem": "StrictDiagnostic", "suggest": "Property 's' has no initializer and is not definitely assigned in the constructor.", "rule": "Property 's' has no initializer and is not definitely assigned in the constructor." @@ -29,32 +43,32 @@ "rule": "Type 'null' is not assignable to type 'number'." }, { - "line": 28, + "line": 27, "column": 19, "problem": "StrictDiagnostic", "suggest": "Function lacks ending return statement and return type does not include 'undefined'.", "rule": "Function lacks ending return statement and return type does not include 'undefined'." }, { - "line": 39, + "line": 36, "column": 14, "problem": "StrictDiagnostic", "suggest": "Function lacks ending return statement and return type does not include 'undefined'.", "rule": "Function lacks ending return statement and return type does not include 'undefined'." }, { - "line": 42, - "column": 32, + "line": 41, + "column": 29, "problem": "StrictDiagnostic", "suggest": "Function lacks ending return statement and return type does not include 'undefined'.", "rule": "Function lacks ending return statement and return type does not include 'undefined'." }, { - "line": 44, - "column": 14, + "line": 45, + "column": 12, "problem": "StrictDiagnostic", "suggest": "Function lacks ending return statement and return type does not include 'undefined'.", "rule": "Function lacks ending return statement and return type does not include 'undefined'." } ] -} +} \ No newline at end of file diff --git a/ets2panda/linter-4.2/test_rules/rule145.ts.strict.json b/ets2panda/linter-4.2/test_rules/rule145.ts.strict.json index c27f5888b89d68a3128497fe8235fca0958db536..fd48119d99cfdcb806208f93d3ca8260e4008670 100644 --- a/ets2panda/linter-4.2/test_rules/rule145.ts.strict.json +++ b/ets2panda/linter-4.2/test_rules/rule145.ts.strict.json @@ -1,15 +1,29 @@ { + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], "nodes": [ { "line": 2, - "column": 5, + "column": 3, "problem": "StrictDiagnostic", "suggest": "Property 'n' has no initializer and is not definitely assigned in the constructor.", "rule": "Property 'n' has no initializer and is not definitely assigned in the constructor." }, { "line": 3, - "column": 5, + "column": 3, "problem": "StrictDiagnostic", "suggest": "Property 's' has no initializer and is not definitely assigned in the constructor.", "rule": "Property 's' has no initializer and is not definitely assigned in the constructor." @@ -29,32 +43,32 @@ "rule": "Type 'null' is not assignable to type 'number'." }, { - "line": 28, + "line": 27, "column": 19, "problem": "StrictDiagnostic", "suggest": "Function lacks ending return statement and return type does not include 'undefined'.", "rule": "Function lacks ending return statement and return type does not include 'undefined'." }, { - "line": 39, + "line": 36, "column": 14, "problem": "StrictDiagnostic", "suggest": "Function lacks ending return statement and return type does not include 'undefined'.", "rule": "Function lacks ending return statement and return type does not include 'undefined'." }, { - "line": 42, - "column": 32, + "line": 41, + "column": 29, "problem": "StrictDiagnostic", "suggest": "Function lacks ending return statement and return type does not include 'undefined'.", "rule": "Function lacks ending return statement and return type does not include 'undefined'." }, { - "line": 44, - "column": 14, + "line": 45, + "column": 12, "problem": "StrictDiagnostic", "suggest": "Function lacks ending return statement and return type does not include 'undefined'.", "rule": "Function lacks ending return statement and return type does not include 'undefined'." } ] -} +} \ No newline at end of file diff --git a/ets2panda/linter-4.2/test_rules/rule37.ts b/ets2panda/linter-4.2/test_rules/rule37.ts deleted file mode 100644 index 1ab2f2733dfdedeb85646586ddd162480f1e4fb7..0000000000000000000000000000000000000000 --- a/ets2panda/linter-4.2/test_rules/rule37.ts +++ /dev/null @@ -1,36 +0,0 @@ -let regex: RegExp = /bc*d/ - -let regex2: RegExp = new RegExp("/bc*d/") - -const regex3 = /^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*(\.[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*)*$/; -const regex4: RegExp = new RegExp('^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*(\.[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*)*$'); - -class A { - static readonly classregex0: RegExp = /bc*d/ - - static readonly classregex2 = /^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*(\.[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*)*$/; - - classregex3: RegExp = new RegExp("bc*d"); - - static staticMethodOne() { - let regex = /bc*d/ - } - - static staticMethodTwo() { - let regex: RegExp = new RegExp("/bc*d/"); - } - - methodOne() { - let regex = /bc*d/ - } - - methodTwo() { - let regex: RegExp = new RegExp("/bc*d/"); - } - - methodRet(): RegExp { - return /^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*(\.[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*)*$/; - } -} - -const regexLambda = () => /^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*(\.[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*)*$/; diff --git a/ets2panda/linter-4.2/test_rules/rule37.ts.autofix.json b/ets2panda/linter-4.2/test_rules/rule37.ts.autofix.json deleted file mode 100644 index 4f840a64a318a8705de1c692864054470cfe911f..0000000000000000000000000000000000000000 --- a/ets2panda/linter-4.2/test_rules/rule37.ts.autofix.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "nodes": [ - { - "line": 1, - "column": 21, - "problem": "RegexLiteral", - "autofixable": false, - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 5, - "column": 16, - "problem": "RegexLiteral", - "autofixable": false, - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 9, - "column": 43, - "problem": "RegexLiteral", - "autofixable": false, - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 11, - "column": 35, - "problem": "RegexLiteral", - "autofixable": false, - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 16, - "column": 21, - "problem": "RegexLiteral", - "autofixable": false, - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 24, - "column": 21, - "problem": "RegexLiteral", - "autofixable": false, - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 32, - "column": 16, - "problem": "RegexLiteral", - "autofixable": false, - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 36, - "column": 27, - "problem": "RegexLiteral", - "autofixable": false, - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - } - ] -} diff --git a/ets2panda/linter-4.2/test_rules/rule37.ts.relax.json b/ets2panda/linter-4.2/test_rules/rule37.ts.relax.json deleted file mode 100644 index a214861d2978d451e65f49ef3964077b361903d5..0000000000000000000000000000000000000000 --- a/ets2panda/linter-4.2/test_rules/rule37.ts.relax.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "nodes": [ - { - "line": 1, - "column": 21, - "problem": "RegexLiteral", - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 5, - "column": 16, - "problem": "RegexLiteral", - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 9, - "column": 43, - "problem": "RegexLiteral", - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 11, - "column": 35, - "problem": "RegexLiteral", - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 16, - "column": 21, - "problem": "RegexLiteral", - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 24, - "column": 21, - "problem": "RegexLiteral", - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 32, - "column": 16, - "problem": "RegexLiteral", - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 36, - "column": 27, - "problem": "RegexLiteral", - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - } - ] -} diff --git a/ets2panda/linter-4.2/test_rules/rule37.ts.strict.json b/ets2panda/linter-4.2/test_rules/rule37.ts.strict.json deleted file mode 100644 index a214861d2978d451e65f49ef3964077b361903d5..0000000000000000000000000000000000000000 --- a/ets2panda/linter-4.2/test_rules/rule37.ts.strict.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "nodes": [ - { - "line": 1, - "column": 21, - "problem": "RegexLiteral", - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 5, - "column": 16, - "problem": "RegexLiteral", - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 9, - "column": 43, - "problem": "RegexLiteral", - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 11, - "column": 35, - "problem": "RegexLiteral", - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 16, - "column": 21, - "problem": "RegexLiteral", - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 24, - "column": 21, - "problem": "RegexLiteral", - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 32, - "column": 16, - "problem": "RegexLiteral", - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 36, - "column": 27, - "problem": "RegexLiteral", - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - } - ] -} diff --git a/ets2panda/linter-4.2/test_rules/rule93.ts.autofix.json b/ets2panda/linter-4.2/test_rules/rule93.ts.autofix.json index 872acd394066cbc9e387b56a855037bb74ddc8ab..819c25194284cf48a8701462d1c1c31871221d4f 100644 --- a/ets2panda/linter-4.2/test_rules/rule93.ts.autofix.json +++ b/ets2panda/linter-4.2/test_rules/rule93.ts.autofix.json @@ -1,8 +1,8 @@ { "nodes": [ { - "line": 1, - "column": 1, + "line": 2, + "column": 5, "problem": "FunctionContainsThis", "autofixable": false, "suggest": "", diff --git a/ets2panda/linter-4.2/test_rules/rule93.ts.relax.json b/ets2panda/linter-4.2/test_rules/rule93.ts.relax.json index a16283a186b51f9545d9a78a95a673bbd7acbd15..772a1f628e47461607ed2422e22212586394fbc1 100644 --- a/ets2panda/linter-4.2/test_rules/rule93.ts.relax.json +++ b/ets2panda/linter-4.2/test_rules/rule93.ts.relax.json @@ -1,8 +1,8 @@ { "nodes": [ { - "line": 1, - "column": 1, + "line": 2, + "column": 5, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" diff --git a/ets2panda/linter-4.2/test_rules/rule93.ts.strict.json b/ets2panda/linter-4.2/test_rules/rule93.ts.strict.json index a16283a186b51f9545d9a78a95a673bbd7acbd15..772a1f628e47461607ed2422e22212586394fbc1 100644 --- a/ets2panda/linter-4.2/test_rules/rule93.ts.strict.json +++ b/ets2panda/linter-4.2/test_rules/rule93.ts.strict.json @@ -1,8 +1,8 @@ { "nodes": [ { - "line": 1, - "column": 1, + "line": 2, + "column": 5, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" diff --git a/ets2panda/linter-4.2/utils/logger.ts b/ets2panda/linter-4.2/utils/logger.ts index 88bf50d16882271a9373fdb8bc09270d747ac64f..ed6a6d7a7cbca18bad872a50d4e818f45564b532 100644 --- a/ets2panda/linter-4.2/utils/logger.ts +++ b/ets2panda/linter-4.2/utils/logger.ts @@ -13,35 +13,35 @@ * limitations under the License. */ -import { Logger } from 'log4js'; +import type { Logger } from 'log4js'; import { configure, getLogger } from 'log4js'; export default class ConsoleLogger { private static isConfigured = false; - public static configure(): void { + static configure(): void { configure({ appenders: { console: { type: 'console', layout: { type: 'pattern', - pattern: '%m', - }, - }, + pattern: '%m' + } + } }, categories: { default: { appenders: ['console'], - level: 'all', - }, - }, + level: 'all' + } + } }); ConsoleLogger.isConfigured = true; } - public static getLogger(): Logger { + static getLogger(): Logger { if (!ConsoleLogger.isConfigured) { ConsoleLogger.configure(); } diff --git a/ets2panda/linter/.editorconfig b/ets2panda/linter/.editorconfig new file mode 100644 index 0000000000000000000000000000000000000000..dfacac7983bb536bc361d7ffea5b465c14559dee --- /dev/null +++ b/ets2panda/linter/.editorconfig @@ -0,0 +1,6 @@ +root = true + +[**] +end_of_line = lf +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/ets2panda/linter/.eslintignore b/ets2panda/linter/.eslintignore new file mode 100644 index 0000000000000000000000000000000000000000..38c3349124a75b5a2b3c91416b59489ebe34fe49 --- /dev/null +++ b/ets2panda/linter/.eslintignore @@ -0,0 +1,28 @@ +# +# Copyright (c) 2022-2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +/bin/** +/build/** +/bundle/** +/dist/** +/docs/** +/node_modules/** +/scripts/** +/test/** +/test_extended_features/** +/test_rules/** +/test_regression/** +**.json +**.js diff --git a/ets2panda/linter/.eslintrc.json b/ets2panda/linter/.eslintrc.json new file mode 100644 index 0000000000000000000000000000000000000000..b5c073b28d693df46812d1e747ac67bdf901c315 --- /dev/null +++ b/ets2panda/linter/.eslintrc.json @@ -0,0 +1,193 @@ +{ + "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": "latest", + "sourceType": "module", + "project": true + }, + "plugins": ["@typescript-eslint", "@stylistic"], + "rules": { + // suggestions + "arrow-body-style": ["error", "always"], + "camelcase": "off", // we use naming-convention rule to enforce naming scheme + "class-methods-use-this": ["error", { "exceptMethods": [], "enforceForClassFields": true }], + "complexity": ["error", { "max": 15 }], + "consistent-return": ["error", { "treatUndefinedAsUnspecified": false }], + "curly": ["error", "all"], + "dot-notation": "error", + "eqeqeq": ["error", "always"], + "max-depth": ["error", { "max": 4 }], + "multiline-comment-style": ["error", "starred-block"], + "@stylistic/no-confusing-arrow": "error", + "no-else-return": ["error", { "allowElseIf": true }], + "no-extra-bind": "error", + "@stylistic/no-floating-decimal": "error", + "no-lonely-if": "error", + "no-unneeded-ternary": "error", + "no-useless-return": "error", + "no-var": "error", + "prefer-const": "error", + "spaced-comment": ["error", "always"], + "one-var": ["error", "never"], + "max-lines-per-function": ["error", { "max": 50 }], + + // style + "@stylistic/array-bracket-newline": ["error", "consistent"], + "@stylistic/array-bracket-spacing": ["error", "never"], + "@stylistic/array-element-newline": ["error", "consistent"], + "@stylistic/arrow-parens": ["error", "always"], + "@stylistic/arrow-spacing": ["error", { "before": true, "after": true }], + "@stylistic/block-spacing": ["error", "always"], + "@stylistic/brace-style": ["error", "1tbs", { "allowSingleLine": false }], + "@stylistic/comma-dangle": [ + "error", + { + "arrays": "never", + "objects": "never", + "imports": "never", + "exports": "never", + "functions": "never" + } + ], + "@stylistic/comma-spacing": ["error", { "before": false, "after": true }], + "@stylistic/comma-style": ["error", "last"], + "@stylistic/computed-property-spacing": ["error", "never", { "enforceForClassMembers": true }], + "@stylistic/dot-location": ["error", "object"], + "@stylistic/eol-last": ["error", "always"], + "@stylistic/func-call-spacing": ["error", "never"], + "@stylistic/function-call-argument-newline": ["error", "consistent"], + "@stylistic/function-paren-newline": ["error", "consistent"], + "@stylistic/generator-star-spacing": ["error", { "before": true, "after": false }], + "@stylistic/implicit-arrow-linebreak": ["error", "beside"], + "@stylistic/indent": [ + "error", + 2, + { + "ignoredNodes": [], + "SwitchCase": 1, + "VariableDeclarator": 1, + "outerIIFEBody": 1, + "MemberExpression": 1, + "FunctionDeclaration": { + "parameters": 1, + "body": 1 + }, + "FunctionExpression": { + "parameters": 1, + "body": 1 + }, + "CallExpression": { + "arguments": 1 + }, + "ArrayExpression": 1, + "ObjectExpression": 1, + "ImportDeclaration": 1, + "flatTernaryExpressions": true, + "offsetTernaryExpressions": false, + "ignoreComments": false + } + ], + "@stylistic/jsx-quotes": ["error", "prefer-double"], + "@stylistic/keyword-spacing": ["error", { "before": true, "after": true }], + "line-comment-position": ["error", { "position": "above" }], + "@stylistic/linebreak-style": ["error", "unix"], + "@stylistic/lines-around-comment": ["error", { "beforeBlockComment": true }], + "@stylistic/lines-between-class-members": [ + "error", + { + "enforce": [ + { "blankLine": "always", "prev": "*", "next": "method" }, + { "blankLine": "always", "prev": "method", "next": "*" } + ] + } + ], + "@stylistic/max-len": ["error", { "code": 120, "tabWidth": 2, "ignoreComments": true, "ignoreStrings": true }], + "@stylistic/max-statements-per-line": ["error", { "max": 1 }], + "@stylistic/multiline-ternary": ["error", "always-multiline"], + "@stylistic/new-parens": ["error", "always"], + "@stylistic/newline-per-chained-call": ["error", { "ignoreChainWithDepth": 2 }], + "@stylistic/no-extra-parens": ["error", "all"], + "@stylistic/no-mixed-spaces-and-tabs": "error", + "@stylistic/no-multi-spaces": "error", + "@stylistic/no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 1 }], + "@stylistic/no-tabs": "error", + "@stylistic/no-trailing-spaces": ["error", { "skipBlankLines": false, "ignoreComments": false }], + "@stylistic/no-whitespace-before-property": "error", + "@stylistic/nonblock-statement-body-position": ["error", "beside"], + "@stylistic/object-curly-newline": ["error", { "consistent": true }], + "@stylistic/object-curly-spacing": ["error", "always"], + "@stylistic/operator-linebreak": ["error", "after"], + // disable due to conflict with required rule 'lines-around-comment' + "@stylistic/padded-blocks": "off", + "@stylistic/quotes": ["error", "single"], + "@stylistic/rest-spread-spacing": ["error", "never"], + "@stylistic/semi": ["error", "always"], + "@stylistic/semi-spacing": ["error", { "before": false, "after": true }], + "@stylistic/semi-style": ["error", "last"], + "@stylistic/space-before-blocks": ["error", "always"], + "@stylistic/space-before-function-paren": ["error", "never"], + "@stylistic/space-in-parens": ["error", "never"], + "@stylistic/space-infix-ops": ["error"], + "@stylistic/space-unary-ops": ["error", { "words": true, "nonwords": false, "overrides": {} }], + "@stylistic/switch-colon-spacing": ["error", { "after": true, "before": false }], + "@stylistic/template-curly-spacing": ["error", "never"], + "@stylistic/template-tag-spacing": ["error", "never"], + "unicode-bom": ["error", "never"], + "@stylistic/wrap-iife": ["error", "outside"], + "@stylistic/wrap-regex": "error", + "@stylistic/yield-star-spacing": ["error", { "before": true, "after": false }], + + // typescript + "@typescript-eslint/explicit-function-return-type": "error", + "@typescript-eslint/adjacent-overload-signatures": "error", + "@typescript-eslint/explicit-member-accessibility": [ + "error", + { + "accessibility": "no-public" + } + ], + "@typescript-eslint/method-signature-style": "error", + "@typescript-eslint/no-confusing-non-null-assertion": "error", + "@typescript-eslint/no-confusing-void-expression": "error", + // FIXME(knazarov) + // need to do something about this + "@typescript-eslint/no-explicit-any": "warn", + "@typescript-eslint/no-extra-non-null-assertion": "error", + "@typescript-eslint/no-meaningless-void-operator": "error", + "@typescript-eslint/no-unnecessary-boolean-literal-compare": "error", + // FIXME(knazarov) + // disabled due to many cases, where typescript deduces non-undefined value, but we can recieve one from the api + "@typescript-eslint/no-unnecessary-condition": "off", + "@typescript-eslint/no-unnecessary-type-assertion": "error", + "@typescript-eslint/prefer-as-const": "error", + "@typescript-eslint/prefer-optional-chain": "error", + "@typescript-eslint/prefer-readonly": "error", + "@typescript-eslint/consistent-type-imports": "error", + // FIXME(knazarov) + // need to change metadata in cookbook accordingly. so do it later + "@typescript-eslint/naming-convention": [ + "error", + { + "selector": "default", + "format": ["camelCase"] + }, + { + "selector": "enumMember", + "format": ["PascalCase", "UPPER_CASE"] + }, + { + "selector": "variable", + "format": ["camelCase", "UPPER_CASE"] + }, + { + "selector": "typeLike", + "format": ["PascalCase"] + }, + { + "selector": "memberLike", + "format": ["camelCase"] + } + ] + } +} diff --git a/ets2panda/linter/.prettierignore b/ets2panda/linter/.prettierignore new file mode 100644 index 0000000000000000000000000000000000000000..38c3349124a75b5a2b3c91416b59489ebe34fe49 --- /dev/null +++ b/ets2panda/linter/.prettierignore @@ -0,0 +1,28 @@ +# +# Copyright (c) 2022-2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +/bin/** +/build/** +/bundle/** +/dist/** +/docs/** +/node_modules/** +/scripts/** +/test/** +/test_extended_features/** +/test_rules/** +/test_regression/** +**.json +**.js diff --git a/ets2panda/linter/.prettierrc.json b/ets2panda/linter/.prettierrc.json new file mode 100644 index 0000000000000000000000000000000000000000..813963a8211a5529c7f71beb13f34a8e349c2ddf --- /dev/null +++ b/ets2panda/linter/.prettierrc.json @@ -0,0 +1,10 @@ +{ + "singleQuote": true, + "jsxSingleQuote": true, + "arrowParens": "always", + "printWidth": 120, + "tabWidth": 2, + "useTabs": false, + "semi": true, + "trailingComma": "none" +} diff --git a/ets2panda/linter/README.md b/ets2panda/linter/README.md index 3983b1f55123e84c069cdba99b25d334aaae64ed..9c637a534ebb9f839d09fc10853aaf72337a5fbe 100644 --- a/ets2panda/linter/README.md +++ b/ets2panda/linter/README.md @@ -1,14 +1,17 @@ # TypeScript linter -Typescript linter ( further mentioned as 'linter' ) is a tool to check typescript sources and find language elements -and constructions which are deprecated to use in a purpose to migrate sources to STS. + +Typescript linter ( further mentioned as 'linter' ) is a tool to check typescript sources and find language elements +and constructions which are deprecated to use in a purpose to migrate sources to STS. The linter is currently under development. ## Prerequisits ### Visual Studio Code + For development, it's recommended to use `VS Code`, as it has a full built-in support for TypeScript language. ### NodeJS and NPM + Install the latest stable version of `NodeJS` and `NPM`. It is recommended using a `Node version manager` to install Node and NPM ([nvm](https://github.com/nvm-sh/nvm) for Linux; [nvm-windows](https://github.com/coreybutler/nvm-windows) for windows - v1.1.9 is the most stable). You can also follow the [official guide](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm). ## Building @@ -26,7 +29,9 @@ npm run build ``` ## Running + Run the following command from the same directory: + ```bash node dist/tslinter.js [options] [input files] ``` @@ -56,6 +61,7 @@ To prevent command line buffer overflow, response file may be used. It is specif ## Running tests Run the following command: + ```bash npm test ``` diff --git a/ets2panda/linter/cookbook_convertor/README.md b/ets2panda/linter/cookbook_convertor/README.md index d54c35d4a7d0515d8a2809dcfd0daee54d366b31..3a63ff481687d542e91f5f944037a3af1e909b64 100644 --- a/ets2panda/linter/cookbook_convertor/README.md +++ b/ets2panda/linter/cookbook_convertor/README.md @@ -1,4 +1,5 @@ # Cookbook Convertor + Cookbook convertor is a tool that generates rule descriptions for TS Linter from Cookbook receipts. Convertor accepts RST file that contains the sources for Cookbook receipts. ## Prerequisits @@ -20,7 +21,9 @@ npm run build ``` ## Running + Run the following command from the same directory: + ```bash node build/cookbook_convertor.js [rst_file] ``` diff --git a/ets2panda/linter/cookbook_convertor/src/cookbook_convertor.ts b/ets2panda/linter/cookbook_convertor/src/cookbook_convertor.ts index 20d861b62b9ced7fd24673ca89842bb76732aec5..58932f507750bde7b477bf56ac0aad2ff76a4306 100644 --- a/ets2panda/linter/cookbook_convertor/src/cookbook_convertor.ts +++ b/ets2panda/linter/cookbook_convertor/src/cookbook_convertor.ts @@ -16,483 +16,473 @@ import { readFileSync, writeFileSync } from 'fs'; import { join } from 'path'; - -const COPYRIGHT_HEADER = "/* \n\ +const COPYRIGHT_HEADER = + '/* \n\ * Copyright (c) 2022-2023 Huawei Device Co., Ltd. \n\ - * Licensed under the Apache License, Version 2.0 (the \"License\"); \n\ + * Licensed under the Apache License, Version 2.0 (the "License"); \n\ * you may not use this file except in compliance with the License. \n\ * You may obtain a copy of the License at \n\ * \n\ * http://www.apache.org/licenses/LICENSE-2.0 \n\ * \n\ * Unless required by applicable law or agreed to in writing, software \n\ - * distributed under the License is distributed on an \"AS IS\" BASIS, \n\ + * distributed under the License is distributed on an "AS IS" BASIS, \n\ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. \n\ * See the License for the specific language governing permissions and \n\ * limitations under the License. \n\ */ \n\ -"; +'; -const CODE_PROLOGUE = "export const cookBookMsg: string[] = [];\n\ +const CODE_PROLOGUE = + 'export const cookBookMsg: string[] = [];\n\ export const cookBookTag: string[] = [];\n\ \n\ for( let i = 0; i <= 150; i++) {\n\ - cookBookMsg[ i ] = '';\n\ + cookBookMsg[ i ] = \'\';\n\ }\n\ -"; - -// HTML tegs -const T_BR = "
"; -const T_UNDERLINE = ""; -const T_END_UNDERLINE = ""; -const T_BOLD = ""; -const T_END_BOLD = ""; -const T_ITALIC = ""; -const T_END_ITALIC = ""; -const T_CODE = ""; -const T_END_CODE = ""; -const T_NBSP = " "; +'; + +// HTML tags +const T_BR = '
'; +const T_BOLD = ''; +const T_END_BOLD = ''; +const T_CODE = ''; +const T_END_CODE = ''; +const T_NBSP = ' '; const T_HR = '
'; // RST substititions -const CB_ = "|CB_"; -const CB_R = "|CB_R|"; -const CB_RULE = "|CB_RULE|"; -const CB_BAD = "|CB_BAD|"; -const CB_OK = "|CB_OK|"; -const CB_ERROR = "|CB_ERROR|"; //replace:: **Severity: error** -const CB_WARNING = "|CB_WARNING|"; //replace:: **Severity: warning** -const CB_SEE = "|CB_SEE|"; -const CB_REF = ":ref:"; -const CB_META = ".. meta"; -const CB_FIX = ":fix:"; +const CB_PREFIX = '|CB_PREFIX'; +const CB_R = '|CB_R|'; +const CB_RULE = '|CB_RULE|'; +const CB_BAD = '|CB_BAD|'; +const CB_OK = '|CB_OK|'; +// replace:: **Severity: error** +const CB_ERROR = '|CB_ERROR|'; +// replace:: **Severity: warning** +const CB_WARNING = '|CB_WARNING|'; +const CB_SEE = '|CB_SEE|'; +const CB_REF = ':ref:'; +const CB_META = '.. meta'; +const CB_FIX = ':fix:'; const NEW_REC_HEADER = /.. _R\d\d\d:/; -const CODE_BLOCK = ".. code"; // should be ".. code-block" but in some places there is error in doc file - -let MAKE_MD = false; // flag to generate .md files +// should be ".. code-block" but in some places there is error in doc file +const CODE_BLOCK = '.. code'; -let doc_lines: string[]; -let _line:number +let docLines: string[]; +let curLine: number; let recNum: number; -let tegs: string[] = []; -let ruleNames: string[] = []; -let cooks: string[] = []; +const tegs: string[] = []; +const ruleNames: string[] = []; let mdText: string[] = []; -let fixTitles: Map = new Map(); - +const fixTitles: Map = new Map(); -const CL = " \\"; // continue line -const STR_DLMTR = "\'"; +// continue line +const CL = ' \\'; +const STR_DLMTR = '\''; - -function syncReadFile(filename: string) { +function syncReadFile(filename: string): string[] { const contents = readFileSync(filename, 'utf-8'); - doc_lines = contents.split(/\r?\n/); + docLines = contents.split(/\r?\n/); -// make table of rule names - _line = 0; + // make table of rule names + curLine = 0; let ruleNum = -1; - while( _line < doc_lines.length ) { - const line = doc_lines[ _line ] - if(NEW_REC_HEADER.test(line)) { - ruleNum = Number(line.replace(/\D/g, '')) -console.log(">>>>>>> START RULE " + ruleNum + ":") -console.log(" NUMBER: " + ruleNum) - } - if( doc_lines[ _line ].startsWith( CB_R ) ) { - let line = doc_lines[ _line ].split( CB_R )[1]; - //let tegNumStr = line.split(':')[0]; - //let ruleNum = Number(tegNumStr.split('#')[1]); - ruleNames[ ruleNum ] = line; //line.split(':')[1]; - _line++; + while (curLine < docLines.length) { + const line = docLines[curLine]; + if (NEW_REC_HEADER.test(line)) { + ruleNum = Number(line.replace(/\D/g, '')); + console.log('>>>>>>> START RULE ' + ruleNum + ':'); + console.log(' NUMBER: ' + ruleNum); + } + if (docLines[curLine].startsWith(CB_R)) { + let line = docLines[curLine].split(CB_R)[1]; + ruleNames[ruleNum] = line; + curLine++; needHeader(); - if( doc_lines[ _line ].startsWith( CB_RULE ) ) { - line = doc_lines[ _line ].trim().replace( CB_RULE, "").trim(); - ruleNames[ ruleNum ] = ruleNames[ ruleNum ] + " (" + line + ")"; + if (docLines[curLine].startsWith(CB_RULE)) { + line = docLines[curLine].trim().replace(CB_RULE, ''). + trim(); + ruleNames[ruleNum] = ruleNames[ruleNum] + ' (' + line + ')'; } } - _line ++; + curLine++; } // scan text - _line = 0; - while( _line < doc_lines.length ) { - skipEmptyLines(); - const line = doc_lines[_line] - if(NEW_REC_HEADER.test(line)) { - makeRecept(); - } - else - _line++; + curLine = 0; + while (curLine < docLines.length) { + skipEmptyLines(); + const line = docLines[curLine]; + if (NEW_REC_HEADER.test(line)) { + makeRecipe(); + } else { + curLine++; + } } - return doc_lines; + return docLines; } +/* + * + * utility functions + * + */ -// -// utility functions -// - -function replaceAll( s: string, from: string, to: string): string { - let ss = s.split(from); - let outStr = ""; - ss.forEach( (line) => { outStr += to + line; }); - - return outStr.replace( to, ""); // remove 1st 'to' substring -} +function replaceAll(s: string, from: string, to: string): string { + const ss = s.split(from); + let outStr = ''; + ss.forEach((line) => { + outStr += to + line; + }); -function translateLine( s: string ) : string { - let line = s; - line = line.replace( CB_BAD, "TypeScript"); - line = line.replace( CB_OK, "ArkTS"); - //line = line.replace( "|CB_R|", "Recipe"); - //.. |CB_RULE| replace:: Rule - line = line.replace( CB_ERROR, "**Severity: error**" ); - line = line.replace( CB_WARNING, "**Severity: warning**" ); - line = line.replace(CB_SEE, "## See also" ); - - line = replaceAll(line, "|JS|", "JavaScript"); - line = replaceAll(line, "|LANG|", "ArkTS"); //.. |LANG| replace:: {lang} - line = replaceAll(line, "|TS|", "TypeScript"); - - return line; + // remove 1st 'to' substring + return outStr.replace(to, ''); } +function translateLine(s: string): string { + let line = s; + line = line.replace(CB_BAD, 'TypeScript'); + line = line.replace(CB_OK, 'ArkTS'); -function translateTeg( s: string) :string { - return replaceAll( s, "\`\`", '"' ).trim(); -} + line = line.replace(CB_ERROR, '**Severity: error**'); + line = line.replace(CB_WARNING, '**Severity: warning**'); + line = line.replace(CB_SEE, '## See also'); + line = replaceAll(line, '|JS|', 'JavaScript'); + line = replaceAll(line, '|LANG|', 'ArkTS'); + line = replaceAll(line, '|TS|', 'TypeScript'); -function makeHdr( s: string) :string { - return replaceAll( s, "\`\`", "\'" ); + return line; } +function translateTeg(s: string): string { + return replaceAll(s, '``', '"').trim(); +} -function highlightCode( s: string ): string { - let ss = s.split("\`\`"); - let line = ss[0]; - for( let i = 1; i < ss.length; i++ ) { - if( (i % 2) === 0 ) - line += T_END_CODE; - else - line += T_CODE; - line += ss[i]; - } - return line; +function highlightCode(s: string): string { + const ss = s.split('``'); + let line = ss[0]; + for (let i = 1; i < ss.length; i++) { + if (i % 2 === 0) { + line += T_END_CODE; + } else { + line += T_CODE; + } + line += ss[i]; + } + return line; } -function escapeSym( s: string ): string { - let ss = replaceAll(s, "\'", "\\\'"); - return replaceAll(ss, "\"", "\\\""); +function escapeSym(s: string): string { + const ss = replaceAll(s, '\'', '\\\''); + return replaceAll(ss, '"', '\\"'); } -function setNBSP( s: string ): string { - let ss = ""; - let flag = true; - for( let ch of s ) { - if( ch !== " " && ch !== "\t" ) - flag = false; - if( flag && ch === " " ) - ss += T_NBSP; - else if( flag && ch ==="\t" ) - ss += T_NBSP + T_NBSP + T_NBSP + T_NBSP + T_NBSP + T_NBSP + T_NBSP + T_NBSP ; - else - ss += ch; - } - return ss; +function setNBSP(s: string): string { + let ss = ''; + let flag = true; + for (const ch of s) { + if (ch !== ' ' && ch !== '\t') { + flag = false; + } + if (flag && ch === ' ') { + ss += T_NBSP; + } else if (flag && ch === '\t') { + ss += T_NBSP + T_NBSP + T_NBSP + T_NBSP + T_NBSP + T_NBSP + T_NBSP + T_NBSP; + } else { + ss += ch; + } + } + return ss; } -function skipEmptyLines() { - while( _line < doc_lines.length ) { - let s = doc_lines[_line]; - s = s.trim(); - if( s !== "") - break; - _line++; - } +function skipEmptyLines(): void { + while (curLine < docLines.length) { + let s = docLines[curLine]; + s = s.trim(); + if (s !== '') { + break; + } + curLine++; + } } function isHeader(): boolean { - return doc_lines[ _line ].startsWith( CB_ ) || doc_lines[ _line ].startsWith( ".." ) ; + return docLines[curLine].startsWith(CB_PREFIX) || docLines[curLine].startsWith('..'); } -function needHeader() { - while ( _line < doc_lines.length && !isHeader() ) - _line++; +function needHeader(): void { + while (curLine < docLines.length && !isHeader()) { + curLine++; + } } function isFixTitle(): boolean { - return doc_lines[_line].trimStart().startsWith(CB_FIX) + return docLines[curLine].trimStart().startsWith(CB_FIX); } -// -// parsing functions -// +/* + * + * parsing functions + * + */ -function makeFixTitle() { - while (_line < doc_lines.length && !isHeader() && !isFixTitle()) { - _line++; - } +function makeFixTitle(): void { + while (curLine < docLines.length && !isHeader() && !isFixTitle()) { + curLine++; + } - if (isFixTitle()) { - const title = doc_lines[_line].split(CB_FIX)[1].trim(); - fixTitles.set(recNum, escapeSym(title)); - } + if (isFixTitle()) { + const title = docLines[curLine].split(CB_FIX)[1].trim(); + fixTitles.set(recNum, escapeSym(title)); + } } -function makeRecept() { - const line = doc_lines[_line] - recNum = Number(line.replace(/\D/g, '')) - console.log("cookBookMsg[ " + recNum + " ] = " + STR_DLMTR + CL); - _line++; - mdText = []; - makeTeg(); - makeBody(); - makeBad(); - makeOk(); - makeSee(); - - // emit .md file - let mdFileName = join("./md", "recipe" + recNum + ".md" ); - writeFileSync( mdFileName, "", { flag: 'w', }); - mdText.forEach((mdLine) => { -console.error("MD> " + mdLine); - writeFileSync(mdFileName, mdLine + '\n', { flag: "a+"} ) - }); - - console.log(STR_DLMTR + ";"); - console.log(""); +function makeRecipe(): void { + const line = docLines[curLine]; + recNum = Number(line.replace(/\D/g, '')); + console.log('cookBookMsg[ ' + recNum + ' ] = ' + STR_DLMTR + CL); + curLine++; + mdText = []; + makeTag(); + makeBody(); + makeBad(); + makeOk(); + makeSee(); + + // emit .md file + const mdFileName = join('./md', 'recipe' + recNum + '.md'); + writeFileSync(mdFileName, '', { flag: 'w' }); + mdText.forEach((mdLine) => { + console.error('MD> ' + mdLine); + writeFileSync(mdFileName, mdLine + '\n', { flag: 'a+' }); + }); + + console.log(STR_DLMTR + ';'); + console.log(''); } +function makeTag(): void { + needHeader(); + console.error('>>>TEG>>>: ' + curLine + ' -> ' + docLines[curLine]); + if (!docLines[curLine].startsWith(CB_R)) { + return; + } + let line = docLines[curLine].split(CB_R)[1]; -function makeTeg() { - needHeader(); -console.error(">>>TEG>>>: " + _line + " -> " + doc_lines[_line]); - if( ! doc_lines[ _line ].startsWith( CB_R ) ) - return; - let line = doc_lines[ _line ].split( CB_R )[1]; - - mdText.push("# " + translateLine( line )); //.split(':')[1] ); - mdText.push(""); + mdText.push('# ' + translateLine(line)); + mdText.push(''); - line = escapeSym( translateLine(line) ); - let teg = translateTeg( line ); - let hdr = highlightCode(line); - console.log(hdr + T_BR + CL); - tegs[ recNum ] = teg; //.split(':')[1]; - _line++; + line = escapeSym(translateLine(line)); + const teg = translateTeg(line); + const hdr = highlightCode(line); + console.log(hdr + T_BR + CL); + tegs[recNum] = teg; + curLine++; } - function makeBody(): string { - let body = ""; + let body = ''; + needHeader(); + console.error('>>>BODY HDR>>>: ' + +curLine + ' -> ' + docLines[curLine]); + if (!docLines[curLine].startsWith(CB_RULE)) { + return ''; + } - needHeader(); -console.error(">>>BODY HDR>>>: " + + _line + " -> " + doc_lines[_line]); - if( !doc_lines[ _line ].startsWith( CB_RULE ) ) - return ""; + let line = docLines[curLine].trim(); + const mdLine = line; + line = line.replace(CB_RULE, ''); + line = escapeSym(translateLine(line)); + tegs[recNum] = tegs[recNum].trim() + ' (' + replaceAll(translateTeg(line), '"', '') + ')'; + curLine++; + + // skip underline + curLine++; + console.log(T_HR + T_BOLD + 'Rule' + T_END_BOLD + T_BR + CL); + + // ("## Rule"); + mdText.push(mdLine.replace(CB_RULE, 'Rule')); + mdText.push(''); + needHeader(); + console.error('>>>BODY 2 HDR>>>: ' + +curLine + ' -> ' + docLines[curLine]); + + if (docLines[curLine].startsWith(CB_META)) { + curLine++; + makeFixTitle(); + needHeader(); + console.error('>>>BODY 3 HDR>>>: ' + +curLine + ' -> ' + docLines[curLine]); + } - let line = doc_lines[ _line ].trim(); - let md_line = line; - line = line.replace( CB_RULE, ""); - line = escapeSym( translateLine(line) ); - tegs[ recNum ] = tegs[ recNum ].trim() + " (" + replaceAll(translateTeg(line), '"', '') + ")"; + while (!isHeader() || docLines[curLine].startsWith(CB_ERROR) || docLines[curLine].startsWith(CB_WARNING)) { + let s = translateLine(docLines[curLine]); - _line++; _line++; // skip underline - console.log( T_HR + T_BOLD + "Rule" + T_END_BOLD + T_BR + CL ); + mdText.push(s); + s = highlightCode(s); + s = escapeSym(s); + console.log(s + CL); + body += s; + curLine++; + } - mdText.push( md_line.replace( CB_RULE, "Rule" ) ); //("## Rule"); - mdText.push(""); + console.log(T_BR + CL); + mdText.push(''); - needHeader(); -console.error(">>>BODY 2 HDR>>>: " + + _line + " -> " + doc_lines[_line]); - if( doc_lines[ _line ].startsWith(CB_META) ) { - _line++; - makeFixTitle(); - needHeader(); -console.error(">>>BODY 3 HDR>>>: " + + _line + " -> " + doc_lines[_line]); - } - //_line++; - while( !isHeader() || doc_lines[ _line ].startsWith( CB_ERROR ) || doc_lines[ _line ].startsWith( CB_WARNING ) ) { - //skipEmptyLines(); - let s = translateLine( doc_lines[_line] ); + return body; +} - mdText.push(s); +function makeBad(): string { + let badCode = ''; - s = highlightCode( s ); - s = escapeSym( s ); - console.log(s + CL); - - body += s; - _line++; - } - console.log(T_BR + CL); + needHeader(); + console.error('>>>makeBAD HDR>>>: ' + docLines[curLine]); + if (!docLines[curLine].startsWith(CB_BAD)) { + return ''; + } + curLine++; + // skip underline + curLine++; - mdText.push(""); + console.log(T_HR + T_BOLD + 'TypeScript' + T_END_BOLD + T_BR + CL); - return body; -} + mdText.push('## TypeScript'); + mdText.push(''); + while (curLine < docLines.length && !isHeader()) { + let s = translateLine(docLines[curLine]); + mdText.push(s); + s = highlightCode(s); + console.log(s + CL); -function makeBad(): string { - let badCode =""; - - needHeader(); -console.error(">>>makeBAD HDR>>>: " + doc_lines[_line]); - if( ! doc_lines[_line].startsWith( CB_BAD ) ) { - return ""; - } - _line++; _line++; // skip underline - - console.log( T_HR + T_BOLD + "TypeScript" + T_END_BOLD + T_BR + CL ); - - mdText.push("## TypeScript"); - mdText.push(""); - - while( _line < doc_lines.length && !isHeader() ) { - //skipEmptyLines(); - let s = translateLine( doc_lines[_line] ); - mdText.push( s ); - - s = highlightCode( s ); - console.log(s + CL); - - badCode += s; - _line++; + badCode += s; + curLine++; + } + + skipEmptyLines(); + if (docLines[curLine++].startsWith(CODE_BLOCK)) { + mdText.push('```'); + console.log(T_CODE + CL); + while (curLine < docLines.length && !isHeader()) { + mdText.push(docLines[curLine]); + console.log(setNBSP(escapeSym(docLines[curLine])) + T_BR + CL); + curLine++; } + console.log(T_END_CODE + T_BR + CL); - skipEmptyLines(); - if( doc_lines[_line++].startsWith( CODE_BLOCK ) ) { - mdText.push("```"); - console.log( T_CODE + CL ); - while( _line < doc_lines.length && !isHeader() ) { - mdText.push( doc_lines[_line] ); - console.log( setNBSP( escapeSym(doc_lines[_line]) ) + T_BR + CL ); - _line++; - } - console.log( T_END_CODE + T_BR + CL ); - - mdText.push("```"); - } - mdText.push(""); - - return badCode; -} + mdText.push('```'); + } + mdText.push(''); + return badCode; +} function makeOk(): string { - let goodCode = ""; + let goodCode = ''; - needHeader(); -console.error( ">>>makeOK HDR>>>: " + doc_lines[ _line ] ); - if( _line >= doc_lines.length || !doc_lines[_line].startsWith(CB_OK) ) { - return ""; - } - _line++; _line++; // skip underline - console.log( T_HR + T_BOLD + "ArkTS" + T_END_BOLD + T_BR + CL ); - - mdText.push("## ArkTS"); - mdText.push(""); - - while( _line < doc_lines.length && !isHeader() ) { - //skipEmptyLines(); - let s = translateLine( doc_lines[ _line ] ); - - mdText.push( s ); - - s = highlightCode( s ); - console.log(s + CL); - - goodCode += s; - _line++; - } + needHeader(); + console.error('>>>makeOK HDR>>>: ' + docLines[curLine]); + if (curLine >= docLines.length || !docLines[curLine].startsWith(CB_OK)) { + return ''; + } + curLine++; + // skip underline + curLine++; + console.log(T_HR + T_BOLD + 'ArkTS' + T_END_BOLD + T_BR + CL); - skipEmptyLines(); - if( doc_lines[ _line++ ].startsWith( CODE_BLOCK ) ) { - console.log( T_CODE + CL ); + mdText.push('## ArkTS'); + mdText.push(''); - mdText.push("```"); + while (curLine < docLines.length && !isHeader()) { + let s = translateLine(docLines[curLine]); - while( _line < doc_lines.length && !isHeader() ) { - mdText.push( doc_lines[_line] ); - console.log( setNBSP( escapeSym(doc_lines[ _line ]) ) + T_BR + CL ); - _line++; - } - console.log( T_END_CODE + T_BR + CL); + mdText.push(s); - mdText.push("```"); - } + s = highlightCode(s); + console.log(s + CL); - mdText.push(""); + goodCode += s; + curLine++; + } - return goodCode; -} + skipEmptyLines(); + if (docLines[curLine++].startsWith(CODE_BLOCK)) { + console.log(T_CODE + CL); + mdText.push('```'); -function makeSee( ): string { - //mdText.push("## See also"); - //mdText.push(""); - const RECIPE = "Recipe "; -console.error(">>> #" + recNum + " PASSED: " + doc_lines[_line]); - while( _line < doc_lines.length && !doc_lines[ _line ].startsWith( ".." ) ) { + while (curLine < docLines.length && !isHeader()) { + mdText.push(docLines[curLine]); + console.log(setNBSP(escapeSym(docLines[curLine])) + T_BR + CL); + curLine++; + } + console.log(T_END_CODE + T_BR + CL); - let s = translateLine( doc_lines[_line] ); + mdText.push('```'); + } - if( s.split(CB_REF)[1] ) { - s = s.replace("*", "-") - s = s.replace( CB_REF, RECIPE); - s = s.replace("`R", ""); - let ruleNum = Number( s.replace("`", "").split(RECIPE)[1]); -console.error(">>>RULE in SEE " + ruleNum + " " + s.replace("`", "") + " -> " + ruleNames[ruleNum] ); - s = s.replace("`", ":"); - s += ' ' + ruleNames[ruleNum]; - } + mdText.push(''); - mdText.push( s ); + return goodCode; +} - if( doc_lines[_line].startsWith(CB_SEE) ) - _line++; - _line++; - } +function makeSee(): string { + const RECIPE = 'Recipe '; + console.error('>>> #' + recNum + ' PASSED: ' + docLines[curLine]); + while (curLine < docLines.length && !docLines[curLine].startsWith('..')) { + let s = translateLine(docLines[curLine]); + + if (s.split(CB_REF)[1]) { + s = s.replace('*', '-'); + s = s.replace(CB_REF, RECIPE); + s = s.replace('`R', ''); + const ruleNum = Number(s.replace('`', '').split(RECIPE)[1]); + console.error('>>>RULE in SEE ' + ruleNum + ' ' + s.replace('`', '') + ' -> ' + ruleNames[ruleNum]); + s = s.replace('`', ':'); + s += ' ' + ruleNames[ruleNum]; + } - mdText.push(""); + mdText.push(s); - return ""; -} + if (docLines[curLine].startsWith(CB_SEE)) { + curLine++; + } + curLine++; + } + mdText.push(''); + + return ''; +} -// -// Main routine -// +/* + * + * Main routine + * + */ let commandLineArgs = process.argv.slice(2); if (commandLineArgs.length === 0) { - console.error(">>> Command line error: no arguments"); - process.exit(-1); + console.error('>>> Command line error: no arguments'); + process.exit(-1); } -if( commandLineArgs[0] == '-md') { - commandLineArgs = process.argv.slice(3); - MAKE_MD = true; +if (commandLineArgs[0] === '-md') { + commandLineArgs = process.argv.slice(3); } -let inFileName = commandLineArgs[0]; -//console.error(inFileName); +const inFileName = commandLineArgs[0]; console.log(COPYRIGHT_HEADER); -//console.log("export const cookBookMsg: string[] = []; \n"); -//console.log("export const cookBookTag: string[] = []; \n"); -console.log( CODE_PROLOGUE ); -syncReadFile( inFileName); +console.log(CODE_PROLOGUE); +syncReadFile(inFileName); -for( recNum = 1; recNum < tegs.length; recNum++ ) { - console.log( "cookBookTag[ " + recNum + " ] = " + STR_DLMTR + ( tegs[ recNum ] ? tegs[ recNum ] : "" ) + STR_DLMTR + ";" ); +for (recNum = 1; recNum < tegs.length; recNum++) { + console.log('cookBookTag[ ' + recNum + ' ] = ' + STR_DLMTR + (tegs[recNum] ? tegs[recNum] : '') + STR_DLMTR + ';'); } console.log('\nexport const cookBookRefToFixTitle: Map = new Map(['); for (const num of fixTitles.keys()) { - console.log(` [${num}, '${fixTitles.get(num)}'],`); + console.log(` [${num}, '${fixTitles.get(num)}'],`); } console.log(']);'); diff --git a/ets2panda/linter/docs/rules/recipe118.md b/ets2panda/linter/docs/rules/recipe118.md deleted file mode 100644 index f625bc3e982353617c9dd28f7c6d67ff886e1e6d..0000000000000000000000000000000000000000 --- a/ets2panda/linter/docs/rules/recipe118.md +++ /dev/null @@ -1,39 +0,0 @@ -# Special import type declarations are not supported - -Rule ``arkts-no-special-imports`` - -**Severity: error** - -ArkTS does not have a special notation for importing types. -Use ordinary import instead. - - -## TypeScript - - -``` - - // Re-using the same import - import { APIResponseType } from "api" - - // Explicitly use import type - import type { APIResponseType } from "api" - -``` - -## ArkTS - - -``` - - import { APIResponseType } from "api" - -``` - -## See also - -- Recipe 119: Importing a module for side-effects only is not supported (``arkts-no-side-effects-imports``) -- Recipe 120: ``import default as ...`` is not supported (``arkts-no-import-default-as``) -- Recipe 121: ``require`` and ``import`` assignment are not supported (``arkts-no-require``) - - diff --git a/ets2panda/linter/docs/rules/recipe127.md b/ets2panda/linter/docs/rules/recipe127.md deleted file mode 100644 index c89d24fbac4b50b1e68937ea99e7e979ae8c1452..0000000000000000000000000000000000000000 --- a/ets2panda/linter/docs/rules/recipe127.md +++ /dev/null @@ -1,48 +0,0 @@ -# Special ``export type`` declarations are not supported - -Rule ``arkts-no-special-exports`` - -**Severity: error** - -ArkTS does not have a special notation for exporting types through -``export type ...``. Use ordinary export instead. - - -## TypeScript - - -``` - - // Explicitly exported class: - export class Class1 { - // ... - } - - // Declared class later exported through export type ... - class Class2 { - // ... - } - - // This is not supported: - export type { Class2 } - -``` - -## ArkTS - - -``` - - // Explicitly exported class: - export class Class1 { - // ... - } - - // Explicitly exported class: - export class Class2 { - // ... - } - -``` - - diff --git a/ets2panda/linter/docs/rules/recipe144.md b/ets2panda/linter/docs/rules/recipe144.md index 0bbcfefd413a1b7fc5b4e07cf9bb7d5487c669b7..371513d07946355b28a85f8bd976626c553e2f92 100644 --- a/ets2panda/linter/docs/rules/recipe144.md +++ b/ets2panda/linter/docs/rules/recipe144.md @@ -30,8 +30,6 @@ Properties and functions of the global object: ``eval`` ``handler.has()``, ``handler.isExtensible()``, ``handler.ownKeys()``, ``handler.preventExtensions()``, ``handler.set()``, ``handler.setPrototypeOf()`` -``ArrayBuffer``: ``isView`` - ## See also diff --git a/ets2panda/linter/docs/rules/recipe151.md b/ets2panda/linter/docs/rules/recipe151.md index 0b00ba64328b8da460cba56c63a6dd63fd050e23..2e7fd2d46a7c1a86d1c9ac02edd38945f5cfd0b1 100644 --- a/ets2panda/linter/docs/rules/recipe151.md +++ b/ets2panda/linter/docs/rules/recipe151.md @@ -1,17 +1,18 @@ # Usage of ``ESObject`` type is restricted -Rule ``arkts-limited-esobject`` +Rule ``arkts-limited-esobj`` **Severity: warning** -ArkTS does not allow using ``ESObject`` type in some cases. The most part of limitations -are put in place in order to prevent spread of dynamic objects in the static codebase. -The only scenario where it is permited to use ``ESObject`` as type specifier is in local -variable declaration. Initialization of variables with ``ESObject`` type is also limited. -Such variables can only be initialized with values that originate from interop: -other ``ESObject`` typed variables, any, unknown, variables with anonymous type, etc. -It is prohibited to initialize ``ESObject`` typed variable with statically typed value. -Varaible of type ``ESObject`` can only be passed to interop calls and assigned to other +ArkTS does not allow using ``ESObject`` type in some cases. The most part of +limitations are put in place in order to prevent spread of dynamic objects in +the static codebase. The only scenario where it is permited to use ``ESObject`` +as type specifier is in local variable declaration. Initialization of variables +with ``ESObject`` type is also limited. Such variables can only be initialized +with values that originate from interop: other ``ESObject`` typed variables, +any, unknown, variables with anonymous type, etc. It is prohibited to +initialize ``ESObject`` typed variable with statically typed value. Varaible +of type ``ESObject`` can only be passed to interop calls and assigned to other variables of type ``ESObject``. @@ -32,12 +33,13 @@ variables of type ``ESObject``. let e3: ESObject = {}; // CTE - can't initialize ESObject with not dynamic values let e4: ESObject = []; // CTE - can't initialize ESObject with not dynamic values let e5: ESObject = ""; // CTE - can't initialize ESObject with not dynamic values + e5['prop'] // CTE - can't access dynamic properties of ESObject + e5[1] // CTE - can't access dynamic properties of ESObject + e5.prop // CTE - can't access dynamic properties of ESObject + let e6: ESObject = foo(); // OK - explicitly annotaded as ESObject let e7 = e6; // OK - initialize ESObject with ESObject - e6['prop'] // CTE - can't access dynamic properties of ESObject - e6[1] // CTE - can't access dynamic properties of ESObject - e6.prop // CTE - can't access dynamic properties of ESObject - bar(e6) // OK - ESObject is passed to interop call + bar(e7) // OK - ESObject is passed to interop call } ``` @@ -51,4 +53,3 @@ variables of type ``ESObject``. - Recipe 066: ``in`` operator is not supported (``arkts-no-in``) - Recipe 137: ``globalThis`` is not supported (``arkts-no-globalthis``) - diff --git a/ets2panda/linter/docs/rules/recipe29.md b/ets2panda/linter/docs/rules/recipe29.md index c7be978d6962ce635842df26f13c20591f59b060..7af61c43e6cc5976617e17be86598978444d8ebe 100644 --- a/ets2panda/linter/docs/rules/recipe29.md +++ b/ets2panda/linter/docs/rules/recipe29.md @@ -10,9 +10,13 @@ that are either declared in the class, or accessible via inheritance. Accessing any other fields is prohibited, and causes compile-time errors. To access a field, use ``obj.field`` syntax, indexed access (``obj["field"]``) -is not supported. An exception are all typed arrays from the standard library -(for example, ``Int32Array``), which support access to their elements through -``container[index]`` syntax. +is not supported. An exception are: + +- All typed arrays from the standard library (for example, ``Int32Array``), which +support access to their elements through ``container[index]`` syntax. +- Tuples. +- Records. +- Enums. ## TypeScript diff --git a/ets2panda/linter/src/Autofixer.ts b/ets2panda/linter/lib/Autofixer.ts similarity index 39% rename from ets2panda/linter/src/Autofixer.ts rename to ets2panda/linter/lib/Autofixer.ts index 59c1cc2d36a1986ab0400c64cec971b706faaa36..c2aaf55c80bb257602a5720ae529db5f3af228fb 100644 --- a/ets2panda/linter/src/Autofixer.ts +++ b/ets2panda/linter/lib/Autofixer.ts @@ -14,20 +14,24 @@ */ import * as ts from 'typescript'; -import { AutofixInfo } from './autofixes/AutofixInfo'; -import { FaultID } from './utils/consts/Problems'; +import type { AutofixInfo } from './autofixes/AutofixInfo'; +import { FaultID } from './Problems'; import { isAssignmentOperator } from './utils/functions/isAssignmentOperator'; export const AUTOFIX_ALL: AutofixInfo = { - problemID: '', start: -1, end: -1 -} + problemID: '', + start: -1, + end: -1 +}; -// Some fixes are potentially risky and may break source code if fixes -// are applied separately. -// Temporary solution is to disable all risky autofixes, until the -// algorithm is improved to guarantee that fixes can be applied -// safely and won't break program code. -const UNSAFE_FIXES: FaultID[] = [ FaultID.LiteralAsPropertyName, FaultID.PropertyAccessByIndex ]; +/* + * Some fixes are potentially risky and may break source code if fixes + * are applied separately. + * Temporary solution is to disable all risky autofixes, until the + * algorithm is improved to guarantee that fixes can be applied + * safely and won't break program code. + */ +const UNSAFE_FIXES: FaultID[] = [FaultID.LiteralAsPropertyName, FaultID.PropertyAccessByIndex]; export interface Autofix { replacementText: string; @@ -36,155 +40,162 @@ export interface Autofix { } export class AutofixInfoSet { - private autofixInfo: AutofixInfo[]; + private readonly autofixInfo: AutofixInfo[]; constructor(autofixInfo: AutofixInfo[] | undefined) { this.autofixInfo = autofixInfo ? autofixInfo : []; } - public shouldAutofix(node: ts.Node, faultID: FaultID): boolean { - if (UNSAFE_FIXES.includes(faultID)) return false; - if (this.autofixInfo.length === 0) return false; - if (this.autofixInfo.length === 1 && this.autofixInfo[0] == AUTOFIX_ALL) return true; - return this.autofixInfo.findIndex( - value => value.start === node.getStart() && value.end === node.getEnd() && value.problemID === FaultID[faultID] - ) !== -1; + shouldAutofix(node: ts.Node, faultID: FaultID): boolean { + if (UNSAFE_FIXES.includes(faultID)) { + return false; + } + if (this.autofixInfo.length === 0) { + return false; + } + if (this.autofixInfo.length === 1 && this.autofixInfo[0] === AUTOFIX_ALL) { + return true; + } + return ( + this.autofixInfo.findIndex((value) => { + return value.start === node.getStart() && value.end === node.getEnd() && value.problemID === FaultID[faultID]; + }) !== -1 + ); } } export function fixLiteralAsPropertyName(node: ts.Node): Autofix[] | undefined { if (ts.isPropertyDeclaration(node) || ts.isPropertyAssignment(node)) { - let propName = (node as (ts.PropertyDeclaration | ts.PropertyAssignment)).name; - let identName = propertyName2IdentifierName(propName); - if (identName) + const propName = node.name; + const identName = propertyName2IdentifierName(propName); + if (identName) { return [{ replacementText: identName, start: propName.getStart(), end: propName.getEnd() }]; + } } return undefined; } export function fixPropertyAccessByIndex(node: ts.Node): Autofix[] | undefined { if (ts.isElementAccessExpression(node)) { - let elemAccess = node as ts.ElementAccessExpression; - let identifierName = indexExpr2IdentifierName(elemAccess.argumentExpression); - if (identifierName) - return [{ - replacementText: elemAccess.expression.getText() + '.' + identifierName, - start: elemAccess.getStart(), end: elemAccess.getEnd() - }]; + const elemAccess = node; + const identifierName = indexExpr2IdentifierName(elemAccess.argumentExpression); + if (identifierName) { + return [ + { + replacementText: elemAccess.expression.getText() + '.' + identifierName, + start: elemAccess.getStart(), + end: elemAccess.getEnd() + } + ]; + } } return undefined; } -export function fixFunctionExpression(funcExpr: ts.FunctionExpression, - params: ts.NodeArray = funcExpr.parameters, +export function fixFunctionExpression( + funcExpr: ts.FunctionExpression, + params: ts.NodeArray = funcExpr.parameters, retType: ts.TypeNode | undefined = funcExpr.type, - modifiers: readonly ts.Modifier[] | undefined): Autofix { + modifiers: readonly ts.Modifier[] | undefined +): Autofix { let arrowFunc: ts.Expression = ts.factory.createArrowFunction( - modifiers, undefined, params, retType, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), + modifiers, + undefined, + params, + retType, + ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), funcExpr.body ); if (needsParentheses(funcExpr)) { arrowFunc = ts.factory.createParenthesizedExpression(arrowFunc); } - let text = printer.printNode(ts.EmitHint.Unspecified, arrowFunc, funcExpr.getSourceFile()); + const text = printer.printNode(ts.EmitHint.Unspecified, arrowFunc, funcExpr.getSourceFile()); return { start: funcExpr.getStart(), end: funcExpr.getEnd(), replacementText: text }; } export function fixReturnType(funcLikeDecl: ts.FunctionLikeDeclaration, typeNode: ts.TypeNode): Autofix { - let text = ': ' + printer.printNode(ts.EmitHint.Unspecified, typeNode, funcLikeDecl.getSourceFile()); - let pos = getReturnTypePosition(funcLikeDecl); + const text = ': ' + printer.printNode(ts.EmitHint.Unspecified, typeNode, funcLikeDecl.getSourceFile()); + const pos = getReturnTypePosition(funcLikeDecl); return { start: pos, end: pos, replacementText: text }; } export function dropTypeOnVarDecl(varDecl: ts.VariableDeclaration): Autofix { - let newVarDecl = ts.factory.createVariableDeclaration(varDecl.name, undefined, undefined, undefined); - let text = printer.printNode(ts.EmitHint.Unspecified, newVarDecl, varDecl.getSourceFile()); - return { start: varDecl.getStart(), end: varDecl.getEnd(), replacementText: text}; + const newVarDecl = ts.factory.createVariableDeclaration(varDecl.name, undefined, undefined, undefined); + const text = printer.printNode(ts.EmitHint.Unspecified, newVarDecl, varDecl.getSourceFile()); + return { start: varDecl.getStart(), end: varDecl.getEnd(), replacementText: text }; } -export function dropTypeOnlyFlag( - impExpNode: ts.ImportClause | ts.ImportSpecifier | ts.ExportDeclaration | ts.ExportSpecifier +export function fixDefaultImport( + importClause: ts.ImportClause, + defaultSpec: ts.ImportSpecifier, + nonDefaultSpecs: ts.ImportSpecifier[] ): Autofix { - let text: string; - if (ts.isImportClause(impExpNode)) { - let newImportClause = ts.factory.createImportClause(false, impExpNode.name, impExpNode.namedBindings); - text = printer.printNode(ts.EmitHint.Unspecified, newImportClause, impExpNode.getSourceFile()); - } - else if (ts.isImportSpecifier(impExpNode)) { - let newImportSpec = ts.factory.createImportSpecifier(false, impExpNode.propertyName, impExpNode.name); - text = printer.printNode(ts.EmitHint.Unspecified, newImportSpec, impExpNode.getSourceFile()); - } - else if (ts.isExportDeclaration(impExpNode)) { - let newExportDecl = ts.factory.createExportDeclaration(impExpNode.modifiers, false, impExpNode.exportClause, - impExpNode.moduleSpecifier, impExpNode.assertClause); - text = printer.printNode(ts.EmitHint.Unspecified, newExportDecl, impExpNode.getSourceFile()); - } - else { - let newExportSpec = ts.factory.createExportSpecifier(false, impExpNode.propertyName, impExpNode.name); - text = printer.printNode(ts.EmitHint.Unspecified, newExportSpec, impExpNode.getSourceFile()); - } - return { start: impExpNode.getStart(), end: impExpNode.getEnd(), replacementText: text }; -} - -export function fixDefaultImport(importClause: ts.ImportClause, - defaultSpec: ts.ImportSpecifier, nonDefaultSpecs: ts.ImportSpecifier[]): Autofix { - let nameBindings = nonDefaultSpecs.length > 0 ? ts.factory.createNamedImports(nonDefaultSpecs) : undefined; - let newImportClause = ts.factory.createImportClause(importClause.isTypeOnly, defaultSpec.name, nameBindings); - let text = printer.printNode(ts.EmitHint.Unspecified, newImportClause, importClause.getSourceFile()); + const nameBindings = nonDefaultSpecs.length > 0 ? ts.factory.createNamedImports(nonDefaultSpecs) : undefined; + const newImportClause = ts.factory.createImportClause(importClause.isTypeOnly, defaultSpec.name, nameBindings); + const text = printer.printNode(ts.EmitHint.Unspecified, newImportClause, importClause.getSourceFile()); return { start: importClause.getStart(), end: importClause.getEnd(), replacementText: text }; } export function fixTypeAssertion(typeAssertion: ts.TypeAssertion): Autofix { const asExpr = ts.factory.createAsExpression(typeAssertion.expression, typeAssertion.type); - let text = printer.printNode(ts.EmitHint.Unspecified, asExpr, typeAssertion.getSourceFile()); + const text = printer.printNode(ts.EmitHint.Unspecified, asExpr, typeAssertion.getSourceFile()); return { start: typeAssertion.getStart(), end: typeAssertion.getEnd(), replacementText: text }; } const printer: ts.Printer = ts.createPrinter(); -function numericLiteral2IdentifierName(numeric: ts.NumericLiteral) { +function numericLiteral2IdentifierName(numeric: ts.NumericLiteral): string { return '__' + numeric.getText(); } -function stringLiteral2IdentifierName(str: ts.StringLiteral) { - let text = (str as ts.StringLiteral).getText(); - return text.substring(1, text.length-1); // cut out starting and ending quoters. +function stringLiteral2IdentifierName(str: ts.StringLiteral): string { + const text = str.getText(); + // cut out starting and ending quoters. + return text.substring(1, text.length - 1); } function propertyName2IdentifierName(name: ts.PropertyName): string { - if (name.kind === ts.SyntaxKind.NumericLiteral) - return numericLiteral2IdentifierName(name as ts.NumericLiteral); + if (name.kind === ts.SyntaxKind.NumericLiteral) { + return numericLiteral2IdentifierName(name); + } + + if (name.kind === ts.SyntaxKind.StringLiteral) { + return stringLiteral2IdentifierName(name); + } - if (name.kind === ts.SyntaxKind.StringLiteral) - return stringLiteral2IdentifierName(name as ts.StringLiteral); - return ''; } -function indexExpr2IdentifierName(index: ts.Expression) { - if (index.kind === ts.SyntaxKind.NumericLiteral) +function indexExpr2IdentifierName(index: ts.Expression): string { + if (index.kind === ts.SyntaxKind.NumericLiteral) { return numericLiteral2IdentifierName(index as ts.NumericLiteral); + } - if (index.kind === ts.SyntaxKind.StringLiteral) + if (index.kind === ts.SyntaxKind.StringLiteral) { return stringLiteral2IdentifierName(index as ts.StringLiteral); - + } + return ''; } function getReturnTypePosition(funcLikeDecl: ts.FunctionLikeDeclaration): number { if (funcLikeDecl.body) { - // Find position of the first node or token that follows parameters. - // After that, iterate over child nodes in reverse order, until found - // first closing parenthesis. - let postParametersPosition = ts.isArrowFunction(funcLikeDecl) - ? funcLikeDecl.equalsGreaterThanToken.getStart() - : funcLikeDecl.body.getStart(); - + + /* + * Find position of the first node or token that follows parameters. + * After that, iterate over child nodes in reverse order, until found + * first closing parenthesis. + */ + const postParametersPosition = ts.isArrowFunction(funcLikeDecl) ? + funcLikeDecl.equalsGreaterThanToken.getStart() : + funcLikeDecl.body.getStart(); + const children = funcLikeDecl.getChildren(); for (let i = children.length - 1; i >= 0; i--) { const child = children[i]; - if (child.kind === ts.SyntaxKind.CloseParenToken && child.getEnd() <= postParametersPosition) + if (child.kind === ts.SyntaxKind.CloseParenToken && child.getEnd() <= postParametersPosition) { return child.getEnd(); + } } } @@ -194,9 +205,15 @@ function getReturnTypePosition(funcLikeDecl: ts.FunctionLikeDeclaration): number function needsParentheses(node: ts.FunctionExpression): boolean { const parent = node.parent; - return ts.isPrefixUnaryExpression(parent) || ts.isPostfixUnaryExpression(parent) || - ts.isPropertyAccessExpression(parent) || ts.isElementAccessExpression(parent) || - ts.isTypeOfExpression(parent) || ts.isVoidExpression(parent) || ts.isAwaitExpression(parent) || - (ts.isCallExpression(parent) && node === parent.expression) || - (ts.isBinaryExpression(parent) && !isAssignmentOperator(parent.operatorToken)); -} \ No newline at end of file + return ( + ts.isPrefixUnaryExpression(parent) || + ts.isPostfixUnaryExpression(parent) || + ts.isPropertyAccessExpression(parent) || + ts.isElementAccessExpression(parent) || + ts.isTypeOfExpression(parent) || + ts.isVoidExpression(parent) || + ts.isAwaitExpression(parent) || + ts.isCallExpression(parent) && node === parent.expression || + ts.isBinaryExpression(parent) && !isAssignmentOperator(parent.operatorToken) + ); +} diff --git a/ets2panda/linter/src/CommandLineOptions.ts b/ets2panda/linter/lib/CommandLineOptions.ts similarity index 89% rename from ets2panda/linter/src/CommandLineOptions.ts rename to ets2panda/linter/lib/CommandLineOptions.ts index fab0ed3720e6d68ff922888013fb85836c4ea6ce..7e174d625f365732bf70de6af034f3a36392a373 100644 --- a/ets2panda/linter/src/CommandLineOptions.ts +++ b/ets2panda/linter/lib/CommandLineOptions.ts @@ -13,8 +13,8 @@ * limitations under the License. */ -import * as ts from 'typescript'; -import { AutofixInfo } from './autofixes/AutofixInfo'; +import type * as ts from 'typescript'; +import type { AutofixInfo } from './autofixes/AutofixInfo'; export interface CommandLineOptions { testMode?: boolean; diff --git a/ets2panda/linter/src/CookBookMsg.ts b/ets2panda/linter/lib/CookBookMsg.ts similarity index 86% rename from ets2panda/linter/src/CookBookMsg.ts rename to ets2panda/linter/lib/CookBookMsg.ts index e977c8e19103a387bf789f4e820ab671e73ac312..98c11d8e38b87f412bacf4df95366af836c55a27 100644 --- a/ets2panda/linter/src/CookBookMsg.ts +++ b/ets2panda/linter/lib/CookBookMsg.ts @@ -1,26 +1,27 @@ -/* - * Copyright (c) 2022-2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. +/* + * Copyright (c) 2022-2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ export const cookBookMsg: string[] = []; export const cookBookTag: string[] = []; -for (let i = 0; i <= 150; i++) { +for (let i = 0; i <= 151; i++) { cookBookMsg[i] = ''; } -cookBookTag[1] = 'Objects with property names that are not identifiers are not supported (arkts-identifiers-as-prop-names)'; +cookBookTag[1] = + 'Objects with property names that are not identifiers are not supported (arkts-identifiers-as-prop-names)'; cookBookTag[2] = '"Symbol()" API is not supported (arkts-no-symbol)'; cookBookTag[3] = 'Private \'#\' identifiers are not supported (arkts-no-private-identifiers)'; cookBookTag[4] = 'Use unique names for types and namespaces. (arkts-unique-names)'; @@ -56,8 +57,9 @@ cookBookTag[33] = ''; cookBookTag[34] = 'Type inference in case of generic function calls is limited (arkts-no-inferred-generic-params)'; cookBookTag[35] = ''; cookBookTag[36] = ''; -cookBookTag[37] = 'RegExp literals are not supported (arkts-no-regexp-literals)'; -cookBookTag[38] = 'Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)'; +cookBookTag[37] = ''; +cookBookTag[38] = + 'Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)'; cookBookTag[39] = ''; cookBookTag[40] = 'Object literals cannot be used as type declarations (arkts-no-obj-literals-as-types)'; cookBookTag[41] = ''; @@ -118,7 +120,8 @@ cookBookTag[95] = ''; cookBookTag[96] = 'Type guarding is supported with "instanceof" and "as" (arkts-no-is)'; cookBookTag[97] = ''; cookBookTag[98] = ''; -cookBookTag[99] = 'It is possible to spread only arrays or classes derived from arrays into the rest parameter or array literals (arkts-no-spread)'; +cookBookTag[99] = + 'It is possible to spread only arrays or classes derived from arrays into the rest parameter or array literals (arkts-no-spread)'; cookBookTag[100] = ''; cookBookTag[101] = ''; cookBookTag[102] = 'Interface can not extend interfaces with the same method (arkts-no-extend-same-prop)'; @@ -130,14 +133,16 @@ cookBookTag[107] = ''; cookBookTag[108] = ''; cookBookTag[109] = ''; cookBookTag[110] = ''; -cookBookTag[111] = 'Enumeration members can be initialized only with compile time expressions of the same type (arkts-no-enum-mixed-types)'; +cookBookTag[111] = + 'Enumeration members can be initialized only with compile time expressions of the same type (arkts-no-enum-mixed-types)'; cookBookTag[112] = ''; cookBookTag[113] = '"enum" declaration merging is not supported (arkts-no-enum-merging)'; cookBookTag[114] = 'Namespaces cannot be used as objects (arkts-no-ns-as-obj)'; cookBookTag[115] = ''; -cookBookTag[116] = 'Non-declaration statements in namespaces are not supported (arkts-no-ns-statements)'; +cookBookTag[116] = + 'Non-declaration statements in namespaces are not supported (single semicolons are considered as empty non-declaration statements) (arkts-no-ns-statements)'; cookBookTag[117] = ''; -cookBookTag[118] = 'Special import type declarations are not supported (arkts-no-special-imports)'; +cookBookTag[118] = ''; cookBookTag[119] = 'Importing a module for side-effects only is not supported (arkts-no-side-effects-imports)'; cookBookTag[120] = '"import default as ..." is not supported (arkts-no-import-default-as)'; cookBookTag[121] = '"require" and "import" assignment are not supported (arkts-no-require)'; @@ -146,7 +151,7 @@ cookBookTag[123] = ''; cookBookTag[124] = ''; cookBookTag[125] = ''; cookBookTag[126] = '"export = ..." assignment is not supported (arkts-no-export-assignment)'; -cookBookTag[127] = 'Special "export type" declarations are not supported (arkts-no-special-exports)'; +cookBookTag[127] = ''; cookBookTag[128] = 'Ambient module declaration is not supported (arkts-no-ambient-decls)'; cookBookTag[129] = 'Wildcards in module names are not supported (arkts-no-module-wildcards)'; cookBookTag[130] = 'Universal module definitions (UMD) are not supported (arkts-no-umd)'; @@ -159,7 +164,8 @@ cookBookTag[136] = 'Prototype assignment is not supported (arkts-no-prototype-as cookBookTag[137] = '"globalThis" is not supported (arkts-no-globalthis)'; cookBookTag[138] = 'Some of utility types are not supported (arkts-no-utility-types)'; cookBookTag[139] = 'Declaring properties on functions is not supported (arkts-no-func-props)'; -cookBookTag[140] = '"Function.apply", "Function.bind", "Function.call" are not supported (arkts-no-func-apply-bind-call)'; +cookBookTag[140] = + '"Function.apply", "Function.bind", "Function.call" are not supported (arkts-no-func-apply-bind-call)'; cookBookTag[141] = ''; cookBookTag[142] = '"as const" assertions are not supported (arkts-no-as-const)'; cookBookTag[143] = 'Import assertions are not supported (arkts-no-import-assertions)'; @@ -170,4 +176,4 @@ cookBookTag[147] = 'No dependencies on TypeScript code are currently allowed (ar cookBookTag[148] = 'No decorators except ArkUI decorators are currently allowed (arkts-no-decorators-except-arkui)'; cookBookTag[149] = 'Classes cannot be used as objects (arkts-no-classes-as-obj)'; cookBookTag[150] = '"import" statements after other statements are not allowed (arkts-no-misplaced-imports)'; -cookBookTag[151] = 'Usage of "ESObject" type is restricted (arkts-limited-esobject)'; +cookBookTag[151] = 'Usage of "ESObject" type is restricted (arkts-limited-esobj)'; diff --git a/ets2panda/linter/lib/FaultAttrs.ts b/ets2panda/linter/lib/FaultAttrs.ts new file mode 100644 index 0000000000000000000000000000000000000000..da7a46442506b1430854f7b4b0b12a153049060a --- /dev/null +++ b/ets2panda/linter/lib/FaultAttrs.ts @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2022-2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { FaultID } from './Problems'; +import { ProblemSeverity } from './ProblemSeverity'; + +export class FaultAttributes { + constructor( + public cookBookRef: number, + public migratable: boolean = false, + public severity: ProblemSeverity = ProblemSeverity.ERROR + ) {} +} + +export const faultsAttrs: FaultAttributes[] = []; + +faultsAttrs[FaultID.LiteralAsPropertyName] = new FaultAttributes(1, true); +faultsAttrs[FaultID.ComputedPropertyName] = new FaultAttributes(1); +faultsAttrs[FaultID.SymbolType] = new FaultAttributes(2); +faultsAttrs[FaultID.PrivateIdentifier] = new FaultAttributes(3, true); +faultsAttrs[FaultID.DeclWithDuplicateName] = new FaultAttributes(4, true); +faultsAttrs[FaultID.VarDeclaration] = new FaultAttributes(5, true); +faultsAttrs[FaultID.AnyType] = new FaultAttributes(8); +faultsAttrs[FaultID.UnknownType] = new FaultAttributes(8); +faultsAttrs[FaultID.CallSignature] = new FaultAttributes(14); +faultsAttrs[FaultID.ConstructorType] = new FaultAttributes(15); +faultsAttrs[FaultID.MultipleStaticBlocks] = new FaultAttributes(16); +faultsAttrs[FaultID.IndexMember] = new FaultAttributes(17); +faultsAttrs[FaultID.IntersectionType] = new FaultAttributes(19); +faultsAttrs[FaultID.ThisType] = new FaultAttributes(21); +faultsAttrs[FaultID.ConditionalType] = new FaultAttributes(22); +faultsAttrs[FaultID.ParameterProperties] = new FaultAttributes(25, true); +faultsAttrs[FaultID.ConstructorIface] = new FaultAttributes(27); +faultsAttrs[FaultID.IndexedAccessType] = new FaultAttributes(28); +faultsAttrs[FaultID.PropertyAccessByIndex] = new FaultAttributes(29, true); +faultsAttrs[FaultID.StructuralIdentity] = new FaultAttributes(30); +faultsAttrs[FaultID.GenericCallNoTypeArgs] = new FaultAttributes(34); +faultsAttrs[FaultID.ObjectLiteralNoContextType] = new FaultAttributes(38); +faultsAttrs[FaultID.ObjectTypeLiteral] = new FaultAttributes(40); +faultsAttrs[FaultID.ArrayLiteralNoContextType] = new FaultAttributes(43); +faultsAttrs[FaultID.FunctionExpression] = new FaultAttributes(46, true); +faultsAttrs[FaultID.LambdaWithTypeParameters] = new FaultAttributes(49, true); +faultsAttrs[FaultID.ClassExpression] = new FaultAttributes(50, true); +faultsAttrs[FaultID.ImplementsClass] = new FaultAttributes(51); +faultsAttrs[FaultID.MethodReassignment] = new FaultAttributes(52); +faultsAttrs[FaultID.TypeAssertion] = new FaultAttributes(53, true); +faultsAttrs[FaultID.JsxElement] = new FaultAttributes(54); +faultsAttrs[FaultID.UnaryArithmNotNumber] = new FaultAttributes(55); +faultsAttrs[FaultID.DeleteOperator] = new FaultAttributes(59); +faultsAttrs[FaultID.TypeQuery] = new FaultAttributes(60); +faultsAttrs[FaultID.InstanceofUnsupported] = new FaultAttributes(65); +faultsAttrs[FaultID.InOperator] = new FaultAttributes(66); +faultsAttrs[FaultID.DestructuringAssignment] = new FaultAttributes(69, true); +faultsAttrs[FaultID.CommaOperator] = new FaultAttributes(71); +faultsAttrs[FaultID.DestructuringDeclaration] = new FaultAttributes(74, true); +faultsAttrs[FaultID.CatchWithUnsupportedType] = new FaultAttributes(79, true); +faultsAttrs[FaultID.ForInStatement] = new FaultAttributes(80); +faultsAttrs[FaultID.MappedType] = new FaultAttributes(83); +faultsAttrs[FaultID.WithStatement] = new FaultAttributes(84); +faultsAttrs[FaultID.ThrowStatement] = new FaultAttributes(87, true); +faultsAttrs[FaultID.LimitedReturnTypeInference] = new FaultAttributes(90, true); +faultsAttrs[FaultID.DestructuringParameter] = new FaultAttributes(91); +faultsAttrs[FaultID.LocalFunction] = new FaultAttributes(92, true); +faultsAttrs[FaultID.FunctionContainsThis] = new FaultAttributes(93); +faultsAttrs[FaultID.GeneratorFunction] = new FaultAttributes(94); +faultsAttrs[FaultID.YieldExpression] = new FaultAttributes(94); +faultsAttrs[FaultID.IsOperator] = new FaultAttributes(96); +faultsAttrs[FaultID.SpreadOperator] = new FaultAttributes(99); +faultsAttrs[FaultID.IntefaceExtendDifProps] = new FaultAttributes(102); +faultsAttrs[FaultID.InterfaceMerging] = new FaultAttributes(103); +faultsAttrs[FaultID.InterfaceExtendsClass] = new FaultAttributes(104); +faultsAttrs[FaultID.ConstructorFuncs] = new FaultAttributes(106); +faultsAttrs[FaultID.EnumMemberNonConstInit] = new FaultAttributes(111); +faultsAttrs[FaultID.EnumMerging] = new FaultAttributes(113); +faultsAttrs[FaultID.NamespaceAsObject] = new FaultAttributes(114); +faultsAttrs[FaultID.NonDeclarationInNamespace] = new FaultAttributes(116); +faultsAttrs[FaultID.ImportFromPath] = new FaultAttributes(119); +faultsAttrs[FaultID.DefaultImport] = new FaultAttributes(120, true); +faultsAttrs[FaultID.ImportAssignment] = new FaultAttributes(121); +faultsAttrs[FaultID.ExportAssignment] = new FaultAttributes(126); +faultsAttrs[FaultID.ShorthandAmbientModuleDecl] = new FaultAttributes(128); +faultsAttrs[FaultID.WildcardsInModuleName] = new FaultAttributes(129); +faultsAttrs[FaultID.UMDModuleDefinition] = new FaultAttributes(130); +faultsAttrs[FaultID.NewTarget] = new FaultAttributes(132); +faultsAttrs[FaultID.DefiniteAssignment] = new FaultAttributes(134, false, ProblemSeverity.WARNING); +faultsAttrs[FaultID.Prototype] = new FaultAttributes(136); +faultsAttrs[FaultID.GlobalThis] = new FaultAttributes(137, false, ProblemSeverity.WARNING); +faultsAttrs[FaultID.UtilityType] = new FaultAttributes(138); +faultsAttrs[FaultID.PropertyDeclOnFunction] = new FaultAttributes(139); +faultsAttrs[FaultID.FunctionApplyCall] = new FaultAttributes(140); +faultsAttrs[FaultID.FunctionBind] = new FaultAttributes(140, false, ProblemSeverity.WARNING); +faultsAttrs[FaultID.ConstAssertion] = new FaultAttributes(142); +faultsAttrs[FaultID.ImportAssertion] = new FaultAttributes(143); +faultsAttrs[FaultID.LimitedStdLibApi] = new FaultAttributes(144); +faultsAttrs[FaultID.StrictDiagnostic] = new FaultAttributes(145); +faultsAttrs[FaultID.ErrorSuppression] = new FaultAttributes(146); +faultsAttrs[FaultID.UnsupportedDecorators] = new FaultAttributes(148, false, ProblemSeverity.WARNING); +faultsAttrs[FaultID.ClassAsObject] = new FaultAttributes(149, false, ProblemSeverity.WARNING); +faultsAttrs[FaultID.ImportAfterStatement] = new FaultAttributes(150); +faultsAttrs[FaultID.EsObjectType] = new FaultAttributes(151, false, ProblemSeverity.WARNING); diff --git a/ets2panda/linter/src/FaultDesc.ts b/ets2panda/linter/lib/FaultDesc.ts similarity index 95% rename from ets2panda/linter/src/FaultDesc.ts rename to ets2panda/linter/lib/FaultDesc.ts index c61b136e7b1aabf1c36ce96c92a0b4c5bf039df3..36d2eb652ba12839ad05f7f002fff722b7d2cfac 100644 --- a/ets2panda/linter/src/FaultDesc.ts +++ b/ets2panda/linter/lib/FaultDesc.ts @@ -13,7 +13,7 @@ * limitations under the License. */ -import { FaultID } from './utils/consts/Problems'; +import { FaultID } from './Problems'; export const faultDesc: string[] = []; @@ -24,7 +24,6 @@ faultDesc[FaultID.ArrayLiteralNoContextType] = 'Array literals with no context A faultDesc[FaultID.ComputedPropertyName] = 'Computed properties'; faultDesc[FaultID.LiteralAsPropertyName] = 'String or integer literal as property name'; faultDesc[FaultID.TypeQuery] = '"typeof" operations'; -faultDesc[FaultID.RegexLiteral] = 'regex literals'; faultDesc[FaultID.IsOperator] = '"is" operations'; faultDesc[FaultID.DestructuringParameter] = 'destructuring parameters'; faultDesc[FaultID.YieldExpression] = '"yield" operations'; @@ -76,8 +75,6 @@ faultDesc[FaultID.MultipleStaticBlocks] = 'Multiple static blocks'; faultDesc[FaultID.ThisType] = '"this" type'; faultDesc[FaultID.IntefaceExtendDifProps] = 'Extends same properties with different types'; faultDesc[FaultID.StructuralIdentity] = 'Use of type structural identity'; -faultDesc[FaultID.TypeOnlyImport] = 'Type-only imports'; -faultDesc[FaultID.TypeOnlyExport] = 'Type-only exports'; faultDesc[FaultID.DefaultImport] = 'Default import declarations'; faultDesc[FaultID.ExportAssignment] = 'Export assignments (export = ..)'; faultDesc[FaultID.ImportAssignment] = 'Import assignments (import = ..)'; @@ -93,7 +90,8 @@ faultDesc[FaultID.Prototype] = 'Prototype assignment'; faultDesc[FaultID.GlobalThis] = 'Use of globalThis'; faultDesc[FaultID.UtilityType] = 'Standard Utility types'; faultDesc[FaultID.PropertyDeclOnFunction] = 'Property declaration on function'; -faultDesc[FaultID.FunctionApplyBindCall] = 'Invoking methods of function objects'; +faultDesc[FaultID.FunctionApplyCall] = 'Invoking methods of function objects'; +faultDesc[FaultID.FunctionBind] = 'Invoking methods of function objects'; faultDesc[FaultID.ConstAssertion] = '"as const" assertion'; faultDesc[FaultID.ImportAssertion] = 'Import assertion'; faultDesc[FaultID.SpreadOperator] = 'Spread operation'; diff --git a/ets2panda/linter/src/IncrementalLintInfo.ts b/ets2panda/linter/lib/IncrementalLintInfo.ts similarity index 88% rename from ets2panda/linter/src/IncrementalLintInfo.ts rename to ets2panda/linter/lib/IncrementalLintInfo.ts index f1039696b841a81debc4a042d6edea50b9629d7a..610b78409d2239b6f0cf637464f3a6c7028dc572 100644 --- a/ets2panda/linter/src/IncrementalLintInfo.ts +++ b/ets2panda/linter/lib/IncrementalLintInfo.ts @@ -13,8 +13,8 @@ * limitations under the License. */ -import * as ts from 'typescript'; +import type * as ts from 'typescript'; export interface IncrementalLintInfo { - shouldSkipCheck(node: ts.Node): boolean; + shouldSkipCheck: (node: ts.Node) => boolean; } diff --git a/ets2panda/linter/lib/IsFileFromModuleCallback.ts b/ets2panda/linter/lib/IsFileFromModuleCallback.ts new file mode 100644 index 0000000000000000000000000000000000000000..46ed92b861d4da1be3221f33ded3a68d9166a41d --- /dev/null +++ b/ets2panda/linter/lib/IsFileFromModuleCallback.ts @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2023-2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export type IsFileFromModuleCallback = (fileFsPath: string) => boolean; diff --git a/ets2panda/linter/src/LintOptions.ts b/ets2panda/linter/lib/LintOptions.ts similarity index 64% rename from ets2panda/linter/src/LintOptions.ts rename to ets2panda/linter/lib/LintOptions.ts index 621b43dc22c89263632bdf73aaed55357b091b88..7f50442831d4412e4487b05196ab0b4292e1a570 100644 --- a/ets2panda/linter/src/LintOptions.ts +++ b/ets2panda/linter/lib/LintOptions.ts @@ -13,18 +13,19 @@ * limitations under the License. */ -import * as ts from 'typescript'; -import { CommandLineOptions } from './CommandLineOptions'; -import { IncrementalLintInfo } from './IncrementalLintInfo'; -import { ReportAutofixCallback } from './autofixes/ReportAutofixCallback'; +import type * as ts from 'typescript'; +import type { CommandLineOptions } from './CommandLineOptions'; +import type { IncrementalLintInfo } from './IncrementalLintInfo'; +import type { ReportAutofixCallback } from './autofixes/ReportAutofixCallback'; +import type { IsFileFromModuleCallback } from './IsFileFromModuleCallback'; +import type { TSCCompiledProgram } from './ts-diagnostics/TSCCompiledProgram'; // common options interface, additional fields may be used by plugins export interface LintOptions { cmdOptions: CommandLineOptions; - realtimeLint: boolean; + tscCompiledProgram: TSCCompiledProgram; cancellationToken?: ts.CancellationToken; incrementalLintInfo?: IncrementalLintInfo; - tsProgram?: ts.Program; reportAutofixCb?: ReportAutofixCallback; - [key: string]: any; + isFileFromModuleCb?: IsFileFromModuleCallback; } diff --git a/ets2panda/linter/src/LintRunResult.ts b/ets2panda/linter/lib/LintRunResult.ts similarity index 93% rename from ets2panda/linter/src/LintRunResult.ts rename to ets2panda/linter/lib/LintRunResult.ts index 0b28219491ce1f9db577f1370caf7417ee1054a4..751cc8d6e80148bd6d248441440480058f680102 100644 --- a/ets2panda/linter/src/LintRunResult.ts +++ b/ets2panda/linter/lib/LintRunResult.ts @@ -13,7 +13,7 @@ * limitations under the License. */ -import { ProblemInfo } from './ProblemInfo'; +import type { ProblemInfo } from './ProblemInfo'; export interface LintRunResult { errorNodes: number; diff --git a/ets2panda/linter/src/LinterRunner.ts b/ets2panda/linter/lib/LinterRunner.ts similarity index 48% rename from ets2panda/linter/src/LinterRunner.ts rename to ets2panda/linter/lib/LinterRunner.ts index 0e701f2baa717d06dc4c66a680990d66d8c508c9..21d6aab1fbb63124abfd8f4a09b991b8b9243811 100644 --- a/ets2panda/linter/src/LinterRunner.ts +++ b/ets2panda/linter/lib/LinterRunner.ts @@ -13,62 +13,107 @@ * limitations under the License. */ -import * as ts from 'typescript'; -import { ProblemInfo } from './ProblemInfo'; +import type * as ts from 'typescript'; +import type { ProblemInfo } from './ProblemInfo'; import { TypeScriptLinter, consoleLog } from './TypeScriptLinter'; -import { FaultID } from './utils/consts/Problems'; +import { FaultID } from './Problems'; import { faultDesc } from './FaultDesc'; import { faultsAttrs } from './FaultAttrs'; -import { LintRunResult } from './LintRunResult'; +import type { LintRunResult } from './LintRunResult'; import * as path from 'node:path'; -import { compile } from './CompilerWrapper'; -import { LintOptions } from './LintOptions'; +import type { LintOptions } from './LintOptions'; +import type { CommandLineOptions } from './CommandLineOptions'; import { AutofixInfoSet } from './Autofixer'; -import { TSCCompiledProgram, TSCCompiledProgramSimple, TSCCompiledProgramWithDiagnostics, getStrictOptions } from './ts-diagnostics/TSCCompiledProgram'; import { mergeArrayMaps } from './utils/functions/MergeArrayMaps'; import { getTscDiagnostics } from './ts-diagnostics/GetTscDiagnostics'; import { transformTscDiagnostics } from './ts-diagnostics/TransformTscDiagnostics'; -import { ARKTS_IGNORE_DIRS, ARKTS_IGNORE_FILES } from './utils/consts/ArktsIgnorePaths'; +import { + ARKTS_IGNORE_DIRS_NO_OH_MODULES, + ARKTS_IGNORE_DIRS_OH_MODULES, + ARKTS_IGNORE_FILES +} from './utils/consts/ArktsIgnorePaths'; import { pathContainsDirectory } from './utils/functions/PathHelper'; +import { ProblemSeverity } from './ProblemSeverity'; -export function lint(options: LintOptions): LintRunResult { - const cmdOptions = options.cmdOptions; - const cancellationToken = options.cancellationToken; - - const tscDiagnosticsLinter = createLinter(options); - const tsProgram = tscDiagnosticsLinter.getOriginalProgram(); +function toFixedPercent(part: number): string { + const percentiles = 100; + const precision = 2; + return (part * percentiles).toFixed(precision); +} - // Prepare list of input files for linter and retrieve AST for those files. - let inputFiles: string[] = cmdOptions.inputFiles; +function prepareInputFilesList(cmdOptions: CommandLineOptions): string[] { + let inputFiles = cmdOptions.inputFiles; if (cmdOptions.parsedConfigFile) { inputFiles = cmdOptions.parsedConfigFile.fileNames; if (cmdOptions.inputFiles.length > 0) { - // Apply linter only to the project source files that are specified - // as a command-line arguments. Other source files will be discarded. - const cmdInputsResolvedPaths = cmdOptions.inputFiles.map((x) => path.resolve(x)); - const configInputsResolvedPaths = inputFiles.map((x) => path.resolve(x)); - inputFiles = configInputsResolvedPaths.filter((x) => cmdInputsResolvedPaths.some((y) => x === y)); + + /* + * Apply linter only to the project source files that are specified + * as a command-line arguments. Other source files will be discarded. + */ + const cmdInputsResolvedPaths = cmdOptions.inputFiles.map((x) => { + return path.resolve(x); + }); + const configInputsResolvedPaths = inputFiles.map((x) => { + return path.resolve(x); + }); + inputFiles = configInputsResolvedPaths.filter((x) => { + return cmdInputsResolvedPaths.some((y) => { + return x === y; + }); + }); } } - // #13436: ignore-list for ArkTS projects. - inputFiles = inputFiles.filter(input => - !ARKTS_IGNORE_FILES.some(ignore => path.basename(input) === ignore) && - !ARKTS_IGNORE_DIRS.some(ignore => pathContainsDirectory(path.resolve(input), ignore))); + return inputFiles; +} +function countProblems(linter: TypeScriptLinter): [number, number] { + let errorNodesTotal = 0; + let warningNodes = 0; + for (let i = 0; i < FaultID.LAST_ID; i++) { + // if Strict mode - count all cases + if (!linter.strictMode && faultsAttrs[i].migratable) { + // In relax mode skip migratable + continue; + } + switch (faultsAttrs[i].severity) { + case ProblemSeverity.ERROR: + errorNodesTotal += linter.nodeCounters[i]; + break; + case ProblemSeverity.WARNING: + warningNodes += linter.nodeCounters[i]; + break; + } + } + + return [errorNodesTotal, warningNodes]; +} + +export function lint(options: LintOptions): LintRunResult { + const cmdOptions = options.cmdOptions; + const cancellationToken = options.cancellationToken; + const tscCompiledProgram = options.tscCompiledProgram; + const tsProgram = tscCompiledProgram.getProgram(); + + // Prepare list of input files for linter and retrieve AST for those files. + let inputFiles = prepareInputFilesList(cmdOptions); + inputFiles = inputFiles.filter((input) => { + return shouldProcessFile(options, input); + }); const srcFiles: ts.SourceFile[] = []; for (const inputFile of inputFiles) { const srcFile = tsProgram.getSourceFile(inputFile); - if (srcFile) srcFiles.push(srcFile); + if (srcFile) { + srcFiles.push(srcFile); + } } - const tscStrictDiagnostics = getTscDiagnostics(tscDiagnosticsLinter, srcFiles); - + const tscStrictDiagnostics = getTscDiagnostics(tscCompiledProgram, srcFiles); const linter = new TypeScriptLinter( tsProgram.getTypeChecker(), new AutofixInfoSet(cmdOptions.autofixInfo), !!cmdOptions.strictMode, - cmdOptions.warningsAsErrors, cancellationToken, options.incrementalLintInfo, tscStrictDiagnostics, @@ -79,37 +124,20 @@ export function lint(options: LintOptions): LintRunResult { consoleLog('\n\n\nFiles scanned: ', srcFiles.length); consoleLog('\nFiles with problems: ', errorNodes); - let errorNodesTotal = 0, warningNodes = 0; - for (let i = 0; i < FaultID.LAST_ID; i++) { - // if Strict mode - count all cases - if (!linter.strictMode && faultsAttrs[i].migratable) // In relax mode skip migratable - continue; + const [errorNodesTotal, warningNodes] = countProblems(linter); - if (faultsAttrs[i].warning) warningNodes += linter.nodeCounters[i]; - else errorNodesTotal += linter.nodeCounters[i]; - } logTotalProblemsInfo(errorNodesTotal, warningNodes, linter); logProblemsPercentageByFeatures(linter); + return { errorNodes: errorNodesTotal, - problemsInfos: mergeArrayMaps(problemsInfos, transformTscDiagnostics(tscStrictDiagnostics)), + problemsInfos: mergeArrayMaps(problemsInfos, transformTscDiagnostics(tscStrictDiagnostics)) }; } -export function createLinter(options: LintOptions): TSCCompiledProgram { - if (options.tscDiagnosticsLinter) { - return options.tscDiagnosticsLinter; - } - const tsProgram = options.tsProgram ?? compile(options, getStrictOptions()); - if (options.realtimeLint) { - return new TSCCompiledProgramSimple(tsProgram); - } - return new TSCCompiledProgramWithDiagnostics(tsProgram, options); -} - function lintFiles(srcFiles: ts.SourceFile[], linter: TypeScriptLinter): LintRunResult { let problemFiles = 0; - let problemsInfos: Map = new Map(); + const problemsInfos: Map = new Map(); for (const srcFile of srcFiles) { const prevVisitedNodes = linter.totalVisitedNodes; @@ -119,12 +147,13 @@ function lintFiles(srcFiles: ts.SourceFile[], linter: TypeScriptLinter): LintRun linter.warningLineNumbersString = ''; const nodeCounters: number[] = []; - for (let i = 0; i < FaultID.LAST_ID; i++) + for (let i = 0; i < FaultID.LAST_ID; i++) { nodeCounters[i] = linter.nodeCounters[i]; + } linter.lint(srcFile); // save results and clear problems array - problemsInfos.set( path.normalize(srcFile.fileName), [...linter.problemsInfos]); + problemsInfos.set(path.normalize(srcFile.fileName), [...linter.problemsInfos]); linter.problemsInfos.length = 0; // print results for current file @@ -133,60 +162,105 @@ function lintFiles(srcFiles: ts.SourceFile[], linter: TypeScriptLinter): LintRun const fileWarningLines = linter.totalWarningLines - prevWarningLines; problemFiles = countProblemFiles( - nodeCounters, problemFiles, srcFile, fileVisitedNodes, fileErrorLines, fileWarningLines, linter + nodeCounters, + problemFiles, + srcFile, + fileVisitedNodes, + fileErrorLines, + fileWarningLines, + linter ); } return { errorNodes: problemFiles, - problemsInfos: problemsInfos, + problemsInfos: problemsInfos }; } function countProblemFiles( - nodeCounters: number[], filesNumber: number, tsSrcFile: ts.SourceFile, - fileNodes: number, fileErrorLines: number, fileWarningLines: number, linter: TypeScriptLinter, -) { - let errorNodes = 0, warningNodes = 0; + nodeCounters: number[], + filesNumber: number, + tsSrcFile: ts.SourceFile, + fileNodes: number, + fileErrorLines: number, + fileWarningLines: number, + linter: TypeScriptLinter +): number { + let errorNodes = 0; + let warningNodes = 0; for (let i = 0; i < FaultID.LAST_ID; i++) { - let nodeCounterDiff = linter.nodeCounters[i] - nodeCounters[i]; - if (faultsAttrs[i].warning) warningNodes += nodeCounterDiff; - else errorNodes += nodeCounterDiff; + const nodeCounterDiff = linter.nodeCounters[i] - nodeCounters[i]; + switch (faultsAttrs[i].severity) { + case ProblemSeverity.ERROR: + errorNodes += nodeCounterDiff; + break; + case ProblemSeverity.WARNING: + warningNodes += nodeCounterDiff; + break; + } } - if (errorNodes > 0) { filesNumber++; - let errorRate = ((errorNodes / fileNodes) * 100).toFixed(2); - let warningRate = ((warningNodes / fileNodes) * 100).toFixed(2); + const errorRate = toFixedPercent(errorNodes / fileNodes); + const warningRate = toFixedPercent(warningNodes / fileNodes); consoleLog(tsSrcFile.fileName, ': ', '\n\tError lines: ', linter.errorLineNumbersString); consoleLog(tsSrcFile.fileName, ': ', '\n\tWarning lines: ', linter.warningLineNumbersString); - consoleLog('\n\tError constructs (%): ', errorRate, '\t[ of ', fileNodes, ' constructs ], \t', fileErrorLines, ' lines'); - consoleLog('\n\tWarning constructs (%): ', warningRate, '\t[ of ', fileNodes, ' constructs ], \t', fileWarningLines, ' lines'); + consoleLog(`\n\tError constructs (%): ${errorRate}\t[ of ${fileNodes} constructs ], \t${fileErrorLines} lines`); + consoleLog( + `\n\tWarning constructs (%): ${warningRate}\t[ of ${fileNodes} constructs ], \t${fileWarningLines} lines` + ); } return filesNumber; } -function logTotalProblemsInfo(errorNodes: number, warningNodes: number, linter: TypeScriptLinter) { - let errorRate = ((errorNodes / linter.totalVisitedNodes) * 100).toFixed(2); - let warningRate = ((warningNodes / linter.totalVisitedNodes) * 100).toFixed(2); +function logTotalProblemsInfo(errorNodes: number, warningNodes: number, linter: TypeScriptLinter): void { + const errorRate = toFixedPercent(errorNodes / linter.totalVisitedNodes); + const warningRate = toFixedPercent(warningNodes / linter.totalVisitedNodes); consoleLog('\nTotal error constructs (%): ', errorRate); consoleLog('\nTotal warning constructs (%): ', warningRate); consoleLog('\nTotal error lines:', linter.totalErrorLines, ' lines\n'); consoleLog('\nTotal warning lines:', linter.totalWarningLines, ' lines\n'); } -function logProblemsPercentageByFeatures(linter: TypeScriptLinter) { +function logProblemsPercentageByFeatures(linter: TypeScriptLinter): void { consoleLog('\nPercent by features: '); + const paddingPercentage = 7; + const paddingFaultDescr = 55; for (let i = 0; i < FaultID.LAST_ID; i++) { // if Strict mode - count all cases - if (!linter.strictMode && faultsAttrs[i].migratable) + if (!linter.strictMode && faultsAttrs[i].migratable) { continue; - - let nodes = linter.nodeCounters[i]; - let lines = linter.lineCounters[i]; - let pecentage = ((nodes / linter.totalVisitedNodes) * 100).toFixed(2).padEnd(7, ' '); + } - consoleLog(faultDesc[i].padEnd(55, ' '), pecentage, '[', nodes, ' constructs / ', lines, ' lines]'); + const nodes = linter.nodeCounters[i]; + const lines = linter.lineCounters[i]; + const pecentage = toFixedPercent(nodes / linter.totalVisitedNodes).padEnd(paddingPercentage, ' '); + + consoleLog(faultDesc[i].padEnd(paddingFaultDescr, ' '), pecentage, '[', nodes, ' constructs / ', lines, ' lines]'); } } + +function shouldProcessFile(options: LintOptions, fileFsPath: string): boolean { + if ( + ARKTS_IGNORE_FILES.some((ignore) => { + return path.basename(fileFsPath) === ignore; + }) + ) { + return false; + } + + if ( + ARKTS_IGNORE_DIRS_NO_OH_MODULES.some((ignore) => { + return pathContainsDirectory(path.resolve(fileFsPath), ignore); + }) + ) { + return false; + } + + return ( + !pathContainsDirectory(path.resolve(fileFsPath), ARKTS_IGNORE_DIRS_OH_MODULES) || + options.isFileFromModuleCb !== undefined && options.isFileFromModuleCb(fileFsPath) + ); +} diff --git a/ets2panda/linter/lib/Logger.ts b/ets2panda/linter/lib/Logger.ts new file mode 100644 index 0000000000000000000000000000000000000000..85e1eb9ef8f91cf9c9e193d92dd272e646e27602 --- /dev/null +++ b/ets2panda/linter/lib/Logger.ts @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2023-2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export abstract class Logger { + static init(instance: Logger): void { + this.instance = instance; + } + + static trace(message: string): void { + this.getInstance().doTrace(message); + } + + static debug(message: string): void { + this.getInstance().doDebug(message); + } + + static info(message: string): void { + this.getInstance().doInfo(message); + } + + static warn(message: string): void { + this.getInstance().doWarn(message); + } + + static error(message: string): void { + this.getInstance().doError(message); + } + + protected abstract doTrace(message: string): void; + protected abstract doDebug(message: string): void; + protected abstract doInfo(message: string): void; + protected abstract doWarn(message: string): void; + protected abstract doError(message: string): void; + + private static getInstance(): Logger { + if (!this.instance) { + throw new Error('Not initialized'); + } + return this.instance; + } + + private static instance?: Logger; +} diff --git a/ets2panda/linter/src/ProblemInfo.ts b/ets2panda/linter/lib/ProblemInfo.ts similarity index 95% rename from ets2panda/linter/src/ProblemInfo.ts rename to ets2panda/linter/lib/ProblemInfo.ts index ff4543a328f65aa93fd7ac96491fde3b1b533eac..97e82432e55182967a1f5547abbab3d99461e8b7 100644 --- a/ets2panda/linter/src/ProblemInfo.ts +++ b/ets2panda/linter/lib/ProblemInfo.ts @@ -13,7 +13,7 @@ * limitations under the License. */ -import { Autofix } from './Autofixer'; +import type { Autofix } from './Autofixer'; export interface ProblemInfo { line: number; diff --git a/ets2panda/linter/src/ProblemSeverity.ts b/ets2panda/linter/lib/ProblemSeverity.ts similarity index 91% rename from ets2panda/linter/src/ProblemSeverity.ts rename to ets2panda/linter/lib/ProblemSeverity.ts index 3736c692ef46726f5ecced3be6290c18c371f039..e89dc4a78f69f1c70d9e3c4341cd7d8e8e71b917 100644 --- a/ets2panda/linter/src/ProblemSeverity.ts +++ b/ets2panda/linter/lib/ProblemSeverity.ts @@ -13,4 +13,7 @@ * limitations under the License. */ -export enum ProblemSeverity { WARNING = 1, ERROR = 2 } +export enum ProblemSeverity { + WARNING = 1, + ERROR = 2 +} diff --git a/ets2panda/linter/src/utils/consts/Problems.ts b/ets2panda/linter/lib/Problems.ts similarity index 94% rename from ets2panda/linter/src/utils/consts/Problems.ts rename to ets2panda/linter/lib/Problems.ts index 3d1def20dcb9e9f650dd85af93e81eff6643be04..07262b77e0f29059e23a651a32e077d0483af2db 100644 --- a/ets2panda/linter/src/utils/consts/Problems.ts +++ b/ets2panda/linter/lib/Problems.ts @@ -21,7 +21,6 @@ export enum FaultID { ComputedPropertyName, LiteralAsPropertyName, TypeQuery, - RegexLiteral, IsOperator, DestructuringParameter, YieldExpression, @@ -73,8 +72,6 @@ export enum FaultID { ThisType, IntefaceExtendDifProps, StructuralIdentity, - TypeOnlyImport, - TypeOnlyExport, DefaultImport, ExportAssignment, ImportAssignment, @@ -90,7 +87,8 @@ export enum FaultID { GlobalThis, UtilityType, PropertyDeclOnFunction, - FunctionApplyBindCall, + FunctionApplyCall, + FunctionBind, ConstAssertion, ImportAssertion, SpreadOperator, @@ -100,5 +98,6 @@ export enum FaultID { UnsupportedDecorators, ImportAfterStatement, EsObjectType, - LAST_ID, // this should always be last enum` + // this should always be last enum + LAST_ID } diff --git a/ets2panda/linter/src/TypeScriptLinter.ts b/ets2panda/linter/lib/TypeScriptLinter.ts similarity index 50% rename from ets2panda/linter/src/TypeScriptLinter.ts rename to ets2panda/linter/lib/TypeScriptLinter.ts index 15d018ed5999142bb38be27095e9792ba21f180b..d7d4d272e9f94a4a52804aaa00176f22af19b0b8 100644 --- a/ets2panda/linter/src/TypeScriptLinter.ts +++ b/ets2panda/linter/lib/TypeScriptLinter.ts @@ -15,44 +15,46 @@ import * as ts from 'typescript'; import * as path from 'node:path'; -import { TsUtils, CheckType } from './utils/TsUtils'; -import { FaultID } from './utils/consts/Problems'; +import { TsUtils } from './utils/TsUtils'; +import { FaultID } from './Problems'; import { faultsAttrs } from './FaultAttrs'; import { faultDesc } from './FaultDesc'; import { cookBookMsg, cookBookTag } from './CookBookMsg'; import { LinterConfig } from './TypeScriptLinterConfig'; -import { Autofix, AutofixInfoSet } from './Autofixer'; -import * as Autofixer from './Autofixer'; -import { ProblemInfo } from './ProblemInfo'; +import type { Autofix, AutofixInfoSet } from './Autofixer'; +import * as autofixer from './Autofixer'; +import type { ProblemInfo } from './ProblemInfo'; import { ProblemSeverity } from './ProblemSeverity'; -import Logger from '../utils/logger'; +import { Logger } from './Logger'; import { ARKUI_DECORATORS } from './utils/consts/ArkUIDecorators'; -import { NON_INITIALIZABLE_PROPERTY_DECORATORS, - NON_INITIALIZABLE_PROPERTY_CLASS_DECORATORS } from './utils/consts/NonInitializablePropertyDecorators'; +import { + NON_INITIALIZABLE_PROPERTY_DECORATORS, + NON_INITIALIZABLE_PROPERTY_CLASS_DECORATORS +} from './utils/consts/NonInitializablePropertyDecorators'; import { NON_RETURN_FUNCTION_DECORATORS } from './utils/consts/NonReturnFunctionDecorators'; import { LIMITED_STANDARD_UTILITY_TYPES } from './utils/consts/LimitedStandardUtilityTypes'; import { PROPERTY_HAS_NO_INITIALIZER_ERROR_CODE } from './utils/consts/PropertyHasNoInitializerErrorCode'; import { FUNCTION_HAS_NO_RETURN_ERROR_CODE } from './utils/consts/FunctionHasNoReturnErrorCode'; -import { identiferUseInValueContext } from './utils/functions/identiferUseInValueContext' -import { hasPredecessor } from './utils/functions/HasPredecessor' +import { identiferUseInValueContext } from './utils/functions/identiferUseInValueContext'; +import { hasPredecessor } from './utils/functions/HasPredecessor'; import { scopeContainsThis } from './utils/functions/ContainsThis'; import { isStructDeclaration, isStruct } from './utils/functions/IsStruct'; import { isAssignmentOperator } from './utils/functions/isAssignmentOperator'; -import { IncrementalLintInfo } from './IncrementalLintInfo'; +import type { IncrementalLintInfo } from './IncrementalLintInfo'; import { cookBookRefToFixTitle } from './autofixes/AutofixTitles'; import { isStdLibraryType } from './utils/functions/IsStdLibrary'; -import { ReportAutofixCallback } from './autofixes/ReportAutofixCallback'; -import { DiagnosticChecker } from './utils/functions/DiagnosticChecker'; +import type { ReportAutofixCallback } from './autofixes/ReportAutofixCallback'; +import type { DiagnosticChecker } from './utils/functions/DiagnosticChecker'; import { ARGUMENT_OF_TYPE_0_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_ERROR_CODE, + NO_OVERLOAD_MATCHES_THIS_CALL_ERROR_CODE, TYPE_0_IS_NOT_ASSIGNABLE_TO_TYPE_1_ERROR_CODE, LibraryTypeCallDiagnosticChecker } from './utils/functions/LibraryTypeCallDiagnosticChecker'; -import { LIMITED_STD_API, LIMITED_STD_GLOBAL_API } from './utils/consts/LimitedStdApi'; - -const logger = Logger.getLogger(); +import { forEachNodeInSubtree } from './utils/functions/ForEachNodeInSubtree'; +import { LIMITED_STD_API } from './utils/consts/LimitedStdAPI'; -export function consoleLog(...args: any[]): void { +export function consoleLog(...args: unknown[]): void { if (TypeScriptLinter.ideMode) { return; } @@ -60,7 +62,7 @@ export function consoleLog(...args: any[]): void { for (let k = 0; k < args.length; k++) { outLine += `${args[k]} `; } - logger.info(outLine); + Logger.info(outLine); } export class TypeScriptLinter { @@ -94,27 +96,45 @@ export class TypeScriptLinter { TypeScriptLinter.filteredDiagnosticMessages = new Set(); } + private initEtsHandlers(): void { + + /* + * some syntax elements are ArkTs-specific and are only implemented inside patched + * compiler, so we initialize those handlers if corresponding properties do exist + */ + const etsComponentExpression: ts.SyntaxKind | undefined = (ts.SyntaxKind as any).EtsComponentExpression; + if (etsComponentExpression) { + this.handlersMap.set(etsComponentExpression, this.handleEtsComponentExpression); + } + } + + private initCounters(): void { + for (let i = 0; i < FaultID.LAST_ID; i++) { + this.nodeCounters[i] = 0; + this.lineCounters[i] = 0; + } + } + constructor( - private tsTypeChecker: ts.TypeChecker, - private autofixesInfo: AutofixInfoSet, - public strictMode: boolean, - public warningsAsErrors: boolean, - private cancellationToken?: ts.CancellationToken, - private incrementalLintInfo?: IncrementalLintInfo, - private tscStrictDiagnostics?: Map, - private reportAutofixCb?: ReportAutofixCallback + private readonly tsTypeChecker: ts.TypeChecker, + private readonly autofixesInfo: AutofixInfoSet, + readonly strictMode: boolean, + private readonly cancellationToken?: ts.CancellationToken, + private readonly incrementalLintInfo?: IncrementalLintInfo, + private readonly tscStrictDiagnostics?: Map, + private readonly reportAutofixCb?: ReportAutofixCallback ) { this.tsUtils = new TsUtils(this.tsTypeChecker, TypeScriptLinter.testMode, TypeScriptLinter.advancedClassChecks); this.currentErrorLine = 0; this.currentWarningLine = 0; this.staticBlocks = new Set(); this.walkedComments = new Set(); - this.libraryTypeCallDiagnosticChecker = new LibraryTypeCallDiagnosticChecker(TypeScriptLinter.filteredDiagnosticMessages); + this.libraryTypeCallDiagnosticChecker = new LibraryTypeCallDiagnosticChecker( + TypeScriptLinter.filteredDiagnosticMessages + ); - for (let i = 0; i < FaultID.LAST_ID; i++) { - this.nodeCounters[i] = 0; - this.lineCounters[i] = 0; - } + this.initEtsHandlers(); + this.initCounters(); } readonly handlersMap = new Map([ @@ -154,7 +174,6 @@ export class TypeScriptLinter { [ts.SyntaxKind.ElementAccessExpression, this.handleElementAccessExpression], [ts.SyntaxKind.EnumMember, this.handleEnumMember], [ts.SyntaxKind.TypeReference, this.handleTypeReference], - [ts.SyntaxKind.ExportDeclaration, this.handleExportDeclaration], [ts.SyntaxKind.ExportAssignment, this.handleExportAssignment], [ts.SyntaxKind.CallExpression, this.handleCallExpression], [ts.SyntaxKind.MetaProperty, this.handleMetaProperty], @@ -166,104 +185,142 @@ export class TypeScriptLinter { [ts.SyntaxKind.SetAccessor, this.handleSetAccessor], [ts.SyntaxKind.ConstructSignature, this.handleConstructSignature], [ts.SyntaxKind.ExpressionWithTypeArguments, this.handleExpressionWithTypeArguments], + [ts.SyntaxKind.ComputedPropertyName, this.handleComputedPropertyName] ]); - public incrementCounters(node: ts.Node | ts.CommentRange, faultId: number, autofixable: boolean = false, autofix?: Autofix[],) { - if (!this.strictMode && faultsAttrs[faultId].migratable) { // In relax mode skip migratable + private getLineAndCharacterOfNode(node: ts.Node | ts.CommentRange): ts.LineAndCharacter { + const startPos = TsUtils.getStartPos(node); + const { line, character } = this.sourceFile!.getLineAndCharacterOfPosition(startPos); + // TSC counts lines and columns from zero + return { line: line + 1, character: character + 1 }; + } + + incrementCounters( + node: ts.Node | ts.CommentRange, + faultId: number, + autofixable: boolean = false, + autofix?: Autofix[] + ): void { + if (!this.strictMode && faultsAttrs[faultId].migratable) { + // In relax mode skip migratable return; } - const startPos = this.tsUtils.getStartPos(node); - const endPos = this.tsUtils.getEndPos(node); - const pos = this.sourceFile!.getLineAndCharacterOfPosition(endPos); this.nodeCounters[faultId]++; - // TSC counts lines and columns from zero - let { line, character } = this.sourceFile!.getLineAndCharacterOfPosition(startPos); - ++line; - ++character; - let faultDescr = faultDesc[faultId]; - let faultType = LinterConfig.tsSyntaxKindNames[node.kind]; + const { line, character } = this.getLineAndCharacterOfNode(node); if (TypeScriptLinter.ideMode) { - const cookBookMsgNum = faultsAttrs[faultId] ? Number(faultsAttrs[faultId].cookBookRef) : 0; - const cookBookTg = cookBookTag[cookBookMsgNum]; - let severity = ProblemSeverity.ERROR; - if (faultsAttrs[faultId] && faultsAttrs[faultId].warning) { - severity = ProblemSeverity.WARNING; - } - const isMsgNumValid = cookBookMsgNum > 0; - const badNodeInfo: ProblemInfo = { - line: line, - column: character, - endLine: pos.line + 1, - endColumn: pos.character + 1, - start: startPos, - end: endPos, - type: faultType, - severity: severity, - problem: FaultID[faultId], - suggest: isMsgNumValid ? cookBookMsg[cookBookMsgNum] : '', - rule: isMsgNumValid && cookBookTg !== '' ? cookBookTg : faultDescr ? faultDescr : faultType, - ruleTag: cookBookMsgNum, - autofixable: autofixable, - autofix: autofix, - autofixTitle: isMsgNumValid && autofixable ? cookBookRefToFixTitle.get(cookBookMsgNum) : undefined, - }; - this.problemsInfos.push(badNodeInfo); - // problems with autofixes might be collected separately - if (this.reportAutofixCb && badNodeInfo.autofix) { - this.reportAutofixCb(badNodeInfo); - } + this.incrementCountersIdeMode(node, faultId, line, character, autofixable, autofix); } else { - logger.info( + const faultDescr = faultDesc[faultId]; + const faultType = LinterConfig.tsSyntaxKindNames[node.kind]; + Logger.info( `Warning: ${this.sourceFile!.fileName} (${line}, ${character}): ${faultDescr ? faultDescr : faultType}` ); } this.lineCounters[faultId]++; - if (faultsAttrs[faultId].warning) { - if (line != this.currentWarningLine) { + switch (faultsAttrs[faultId].severity) { + case ProblemSeverity.ERROR: { + this.currentErrorLine = line; + ++this.totalErrorLines; + this.errorLineNumbersString += line + ', '; + break; + } + case ProblemSeverity.WARNING: { + if (line === this.currentWarningLine) { + break; + } this.currentWarningLine = line; ++this.totalWarningLines; - this.warningLineNumbersString += line + ', ' + this.warningLineNumbersString += line + ', '; + break; } - } else if (line != this.currentErrorLine) { - this.currentErrorLine = line; - ++this.totalErrorLines; - this.errorLineNumbersString += line + ', '; } } - private visitTSNode(node: ts.Node): void { - const self = this; - visitTSNodeImpl(node); - function visitTSNodeImpl(node: ts.Node): void { - if (node === null || node.kind === null) { - return; - } - if (self.incrementalLintInfo?.shouldSkipCheck(node)) { - return; - } + private incrementCountersIdeMode( + node: ts.Node | ts.CommentRange, + faultId: number, + line: number, + character: number, + autofixable: boolean, + autofix?: Autofix[] + ): void { + if (!TypeScriptLinter.ideMode) { + return; + } + const startPos = TsUtils.getStartPos(node); + const endPos = TsUtils.getEndPos(node); + const pos = this.sourceFile!.getLineAndCharacterOfPosition(endPos); - self.totalVisitedNodes++; + const faultDescr = faultDesc[faultId]; + const faultType = LinterConfig.tsSyntaxKindNames[node.kind]; + + const cookBookMsgNum = faultsAttrs[faultId] ? faultsAttrs[faultId].cookBookRef : 0; + const cookBookTg = cookBookTag[cookBookMsgNum]; + const severity = faultsAttrs[faultId]?.severity ?? ProblemSeverity.ERROR; + const isMsgNumValid = cookBookMsgNum > 0; + const badNodeInfo: ProblemInfo = { + line: line, + column: character, + endLine: pos.line + 1, + endColumn: pos.character + 1, + start: startPos, + end: endPos, + type: faultType, + severity: severity, + problem: FaultID[faultId], + suggest: isMsgNumValid ? cookBookMsg[cookBookMsgNum] : '', + rule: isMsgNumValid && cookBookTg !== '' ? cookBookTg : faultDescr ? faultDescr : faultType, + ruleTag: cookBookMsgNum, + autofixable: autofixable, + autofix: autofix, + autofixTitle: isMsgNumValid && autofixable ? cookBookRefToFixTitle.get(cookBookMsgNum) : undefined + }; + this.problemsInfos.push(badNodeInfo); + // problems with autofixes might be collected separately + if (this.reportAutofixCb && badNodeInfo.autofix) { + this.reportAutofixCb(badNodeInfo); + } + } + + private visitSourceFile(sf: ts.SourceFile): void { + const callback = (node: ts.Node): void => { + this.totalVisitedNodes++; if (isStructDeclaration(node)) { - self.handleStructDeclaration(node); - return; + // early exit via exception if cancellation was requested + this.cancellationToken?.throwIfCancellationRequested(); } - self.handleComments(node); - if (LinterConfig.terminalTokens.has(node.kind)) { - return; - } - let incrementedType = LinterConfig.incrementOnlyTokens.get(node.kind); + const incrementedType = LinterConfig.incrementOnlyTokens.get(node.kind); if (incrementedType !== undefined) { - self.incrementCounters(node, incrementedType); + this.incrementCounters(node, incrementedType); } else { - let handler = self.handlersMap.get(node.kind); + const handler = this.handlersMap.get(node.kind); if (handler !== undefined) { - // possibly requested cancellation will be checked in a limited number of handlers - // checked nodes are selected as construct nodes, similar to how TSC does - handler.call(self, node); + + /* + * possibly requested cancellation will be checked in a limited number of handlers + * checked nodes are selected as construct nodes, similar to how TSC does + */ + handler.call(this, node); } } - ts.forEachChild(node, visitTSNodeImpl); - } + }; + const stopCondition = (node: ts.Node): boolean => { + if (!node) { + return true; + } + if (this.incrementalLintInfo?.shouldSkipCheck(node)) { + return true; + } + // Skip synthetic constructor in Struct declaration. + if (node.parent && isStructDeclaration(node.parent) && ts.isConstructorDeclaration(node)) { + return true; + } + if (LinterConfig.terminalTokens.has(node.kind)) { + return true; + } + return false; + }; + forEachNodeInSubtree(sf, callback, stopCondition); } private countInterfaceExtendsDifferentPropertyTypes( @@ -271,7 +328,7 @@ export class TypeScriptLinter { prop2type: Map, propName: string, type: ts.TypeNode | undefined - ) { + ): void { if (type) { const methodType = type.getText(); const propType = prop2type.get(propName); @@ -283,56 +340,70 @@ export class TypeScriptLinter { } } - private countDeclarationsWithDuplicateName(tsNode: ts.Node, tsDeclNode: ts.Node, tsDeclKind?: ts.SyntaxKind - ): void { - let symbol = this.tsTypeChecker.getSymbolAtLocation(tsNode); - // If specific declaration kind is provided, check against it. - // Otherwise, use syntax kind of corresponding declaration node. - if (!!symbol && this.tsUtils.symbolHasDuplicateName(symbol, tsDeclKind ?? tsDeclNode.kind)) { + private countDeclarationsWithDuplicateName(tsNode: ts.Node, tsDeclNode: ts.Node, tsDeclKind?: ts.SyntaxKind): void { + const symbol = this.tsTypeChecker.getSymbolAtLocation(tsNode); + + /* + * If specific declaration kind is provided, check against it. + * Otherwise, use syntax kind of corresponding declaration node. + */ + if (!!symbol && TsUtils.symbolHasDuplicateName(symbol, tsDeclKind ?? tsDeclNode.kind)) { this.incrementCounters(tsDeclNode, FaultID.DeclWithDuplicateName); } } + private static isPrivateIdentifierDuplicateOfIdentifier( + ident1: ts.Identifier | ts.PrivateIdentifier, + ident2: ts.Identifier | ts.PrivateIdentifier + ): boolean { + if (ts.isIdentifier(ident1) && ts.isPrivateIdentifier(ident2)) { + return ident1.text === ident2.text.substring(1); + } + if (ts.isIdentifier(ident2) && ts.isPrivateIdentifier(ident1)) { + return ident2.text === ident1.text.substring(1); + } + return false; + } + + private static isIdentifierOrPrivateIdentifier(node?: ts.PropertyName): node is ts.Identifier | ts.PrivateIdentifier { + if (!node) { + return false; + } + return ts.isIdentifier(node) || ts.isPrivateIdentifier(node); + } + private countClassMembersWithDuplicateName(tsClassDecl: ts.ClassDeclaration): void { - for (const tsCurrentMember of tsClassDecl.members) { - if ( - !tsCurrentMember.name || - !(ts.isIdentifier(tsCurrentMember.name) || ts.isPrivateIdentifier(tsCurrentMember.name)) - ) { + const nClassMembers = tsClassDecl.members.length; + const isNodeReported = new Array(nClassMembers); + for (let curIdx = 0; curIdx < nClassMembers; ++curIdx) { + const tsCurrentMember = tsClassDecl.members[curIdx]; + if (!TypeScriptLinter.isIdentifierOrPrivateIdentifier(tsCurrentMember.name)) { continue; } - for (const tsClassMember of tsClassDecl.members) { - if (tsCurrentMember === tsClassMember) { - continue; - } - if ( - !tsClassMember.name || - !(ts.isIdentifier(tsClassMember.name) || ts.isPrivateIdentifier(tsClassMember.name)) - ) { + if (isNodeReported[curIdx]) { + continue; + } + for (let idx = curIdx + 1; idx < nClassMembers; ++idx) { + const tsClassMember = tsClassDecl.members[idx]; + if (!TypeScriptLinter.isIdentifierOrPrivateIdentifier(tsClassMember.name)) { continue; } - if ( - ts.isIdentifier(tsCurrentMember.name) && - ts.isPrivateIdentifier(tsClassMember.name) && - tsCurrentMember.name.text === tsClassMember.name.text.substring(1) - ) { - this.incrementCounters(tsCurrentMember, FaultID.DeclWithDuplicateName); - break; - } - if ( - ts.isPrivateIdentifier(tsCurrentMember.name) && - ts.isIdentifier(tsClassMember.name) && - tsCurrentMember.name.text.substring(1) === tsClassMember.name.text - ) { + if (TypeScriptLinter.isPrivateIdentifierDuplicateOfIdentifier(tsCurrentMember.name, tsClassMember.name)) { this.incrementCounters(tsCurrentMember, FaultID.DeclWithDuplicateName); + this.incrementCounters(tsClassMember, FaultID.DeclWithDuplicateName); + isNodeReported[idx] = true; break; } } } } - private isPrototypePropertyAccess(tsPropertyAccess: ts.PropertyAccessExpression, propAccessSym: ts.Symbol | undefined, - baseExprSym: ts.Symbol | undefined, baseExprType: ts.Type): boolean { + private isPrototypePropertyAccess( + tsPropertyAccess: ts.PropertyAccessExpression, + propAccessSym: ts.Symbol | undefined, + baseExprSym: ts.Symbol | undefined, + baseExprType: ts.Type + ): boolean { if (!(ts.isIdentifier(tsPropertyAccess.name) && tsPropertyAccess.name.text === 'prototype')) { return false; } @@ -349,25 +420,28 @@ export class TypeScriptLinter { if (ts.isIdentifier(curPropAccess) && curPropAccess.text !== 'prototype') { const type = this.tsTypeChecker.getTypeAtLocation(curPropAccess); - if (this.tsUtils.isAnyType(type)) { + if (TsUtils.isAnyType(type)) { return false; } } // Check if property symbol is 'Prototype' - if (this.tsUtils.isPrototypeSymbol(propAccessSym)) { + if (TsUtils.isPrototypeSymbol(propAccessSym)) { return true; } // Check if symbol of LHS-expression is Class or Function. - if (this.tsUtils.isTypeSymbol(baseExprSym) || this.tsUtils.isFunctionSymbol(baseExprSym)) { + if (TsUtils.isTypeSymbol(baseExprSym) || TsUtils.isFunctionSymbol(baseExprSym)) { return true; } - // Check if type of LHS expression Function type or Any type. - // The latter check is to cover cases with multiple prototype - // chain (as the 'Prototype' property should be 'Any' type): - // X.prototype.prototype.prototype = ... + + /* + * Check if type of LHS expression Function type or Any type. + * The latter check is to cover cases with multiple prototype + * chain (as the 'Prototype' property should be 'Any' type): + * X.prototype.prototype.prototype = ... + */ const baseExprTypeNode = this.tsTypeChecker.typeToTypeNode(baseExprType, undefined, ts.NodeBuilderFlags.None); - return ((baseExprTypeNode && ts.isFunctionTypeNode(baseExprTypeNode)) || this.tsUtils.isAnyType(baseExprType)); + return baseExprTypeNode && ts.isFunctionTypeNode(baseExprTypeNode) || TsUtils.isAnyType(baseExprType); } private interfaceInheritanceLint(node: ts.Node, heritageClauses: ts.NodeArray): void { @@ -388,7 +462,9 @@ export class TypeScriptLinter { } private lintForInterfaceExtendsDifferentPorpertyTypes( - node: ts.Node, tsExprType: ts.Type, prop2type: Map + node: ts.Node, + tsExprType: ts.Type, + prop2type: Map ): void { const props = tsExprType.getProperties(); for (const p of props) { @@ -404,37 +480,47 @@ export class TypeScriptLinter { } } - private handleObjectLiteralExpression(node: ts.Node) { - let objectLiteralExpr = node as ts.ObjectLiteralExpression; + private handleObjectLiteralExpression(node: ts.Node): void { + const objectLiteralExpr = node as ts.ObjectLiteralExpression; // If object literal is a part of destructuring assignment, then don't process it further. - if (this.tsUtils.isDestructuringAssignmentLHS(objectLiteralExpr)) { + if (TsUtils.isDestructuringAssignmentLHS(objectLiteralExpr)) { return; } // issue 13082: Allow initializing struct instances with object literal. - let objectLiteralType = this.tsTypeChecker.getContextualType(objectLiteralExpr); - if (!this.tsUtils.isStructObjectInitializer(objectLiteralExpr) && - !this.tsUtils.isDynamicLiteralInitializer(objectLiteralExpr) && - !this.tsUtils.isObjectLiteralAssignable(objectLiteralType, objectLiteralExpr)) { + const objectLiteralType = this.tsTypeChecker.getContextualType(objectLiteralExpr); + if ( + !this.tsUtils.isStructObjectInitializer(objectLiteralExpr) && + !this.tsUtils.isDynamicLiteralInitializer(objectLiteralExpr) && + !this.tsUtils.isObjectLiteralAssignable(objectLiteralType, objectLiteralExpr) + ) { this.incrementCounters(node, FaultID.ObjectLiteralNoContextType); } } - private handleArrayLiteralExpression(node: ts.Node) { - // If array literal is a part of destructuring assignment, then - // don't process it further. - if (this.tsUtils.isDestructuringAssignmentLHS(node as ts.ArrayLiteralExpression)) { + private handleArrayLiteralExpression(node: ts.Node): void { + + /* + * If array literal is a part of destructuring assignment, then + * don't process it further. + */ + if (TsUtils.isDestructuringAssignmentLHS(node as ts.ArrayLiteralExpression)) { return; } - let arrayLitNode = node as ts.ArrayLiteralExpression; + const arrayLitNode = node as ts.ArrayLiteralExpression; let noContextTypeForArrayLiteral = false; - // check that array literal consists of inferrable types - // e.g. there is no element which is untyped object literals - let arrayLitElements = arrayLitNode.elements; - for(let element of arrayLitElements ) { - if(ts.isObjectLiteralExpression(element)) { - let objectLiteralType = this.tsTypeChecker.getContextualType(element); - if (!this.tsUtils.isDynamicLiteralInitializer(arrayLitNode) && - !this.tsUtils.isObjectLiteralAssignable(objectLiteralType, element)) { + + /* + * check that array literal consists of inferrable types + * e.g. there is no element which is untyped object literals + */ + const arrayLitElements = arrayLitNode.elements; + for (const element of arrayLitElements) { + if (ts.isObjectLiteralExpression(element)) { + const objectLiteralType = this.tsTypeChecker.getContextualType(element); + if ( + !this.tsUtils.isDynamicLiteralInitializer(arrayLitNode) && + !this.tsUtils.isObjectLiteralAssignable(objectLiteralType, element) + ) { noContextTypeForArrayLiteral = true; break; } @@ -445,18 +531,18 @@ export class TypeScriptLinter { } } - private handleParameter(node: ts.Node) { - let tsParam = node as ts.ParameterDeclaration; + private handleParameter(node: ts.Node): void { + const tsParam = node as ts.ParameterDeclaration; if (ts.isArrayBindingPattern(tsParam.name) || ts.isObjectBindingPattern(tsParam.name)) { this.incrementCounters(node, FaultID.DestructuringParameter); } - let tsParamMods = ts.getModifiers(tsParam); + const tsParamMods = ts.getModifiers(tsParam); if ( tsParamMods && - (this.tsUtils.hasModifier(tsParamMods, ts.SyntaxKind.PublicKeyword) || - this.tsUtils.hasModifier(tsParamMods, ts.SyntaxKind.ProtectedKeyword) || - this.tsUtils.hasModifier(tsParamMods, ts.SyntaxKind.ReadonlyKeyword) || - this.tsUtils.hasModifier(tsParamMods, ts.SyntaxKind.PrivateKeyword)) + (TsUtils.hasModifier(tsParamMods, ts.SyntaxKind.PublicKeyword) || + TsUtils.hasModifier(tsParamMods, ts.SyntaxKind.ProtectedKeyword) || + TsUtils.hasModifier(tsParamMods, ts.SyntaxKind.ReadonlyKeyword) || + TsUtils.hasModifier(tsParamMods, ts.SyntaxKind.PrivateKeyword)) ) { this.incrementCounters(node, FaultID.ParameterProperties); } @@ -464,45 +550,55 @@ export class TypeScriptLinter { this.handleDeclarationInferredType(tsParam); } - private handleEnumDeclaration(node: ts.Node) { - let enumNode = node as ts.EnumDeclaration; + private handleEnumDeclaration(node: ts.Node): void { + const enumNode = node as ts.EnumDeclaration; this.countDeclarationsWithDuplicateName(enumNode.name, enumNode); - let enumSymbol = this.tsUtils.trueSymbolAtLocation(enumNode.name); + const enumSymbol = this.tsUtils.trueSymbolAtLocation(enumNode.name); if (!enumSymbol) { return; } - let enumDecls = enumSymbol.getDeclarations(); + const enumDecls = enumSymbol.getDeclarations(); if (!enumDecls) { return; } - // Since type checker merges all declarations with the same name - // into one symbol, we need to check that there's more than one - // enum declaration related to that specific symbol. - // See 'countDeclarationsWithDuplicateName' method for details. + + /* + * Since type checker merges all declarations with the same name + * into one symbol, we need to check that there's more than one + * enum declaration related to that specific symbol. + * See 'countDeclarationsWithDuplicateName' method for details. + */ let enumDeclCount = 0; for (const decl of enumDecls) { - if (decl.kind === ts.SyntaxKind.EnumDeclaration) enumDeclCount++; + if (decl.kind === ts.SyntaxKind.EnumDeclaration) { + enumDeclCount++; + } } if (enumDeclCount > 1) { this.incrementCounters(node, FaultID.EnumMerging); } } - private handleInterfaceDeclaration(node: ts.Node) { + private handleInterfaceDeclaration(node: ts.Node): void { // early exit via exception if cancellation was requested this.cancellationToken?.throwIfCancellationRequested(); - let interfaceNode = node as ts.InterfaceDeclaration; - let iSymbol = this.tsUtils.trueSymbolAtLocation(interfaceNode.name); - let iDecls = iSymbol ? iSymbol.getDeclarations() : null; + const interfaceNode = node as ts.InterfaceDeclaration; + const iSymbol = this.tsUtils.trueSymbolAtLocation(interfaceNode.name); + const iDecls = iSymbol ? iSymbol.getDeclarations() : null; if (iDecls) { - // Since type checker merges all declarations with the same name - // into one symbol, we need to check that there's more than one - // interface declaration related to that specific symbol. - // See 'countDeclarationsWithDuplicateName' method for details. + + /* + * Since type checker merges all declarations with the same name + * into one symbol, we need to check that there's more than one + * interface declaration related to that specific symbol. + * See 'countDeclarationsWithDuplicateName' method for details. + */ let iDeclCount = 0; for (const decl of iDecls) { - if (decl.kind === ts.SyntaxKind.InterfaceDeclaration) iDeclCount++; + if (decl.kind === ts.SyntaxKind.InterfaceDeclaration) { + iDeclCount++; + } } if (iDeclCount > 1) { this.incrementCounters(node, FaultID.InterfaceMerging); @@ -514,44 +610,47 @@ export class TypeScriptLinter { this.countDeclarationsWithDuplicateName(interfaceNode.name, interfaceNode); } - private handleThrowStatement(node: ts.Node) { - let throwStmt = node as ts.ThrowStatement; - let throwExprType = this.tsTypeChecker.getTypeAtLocation(throwStmt.expression); - if (!throwExprType.isClassOrInterface() || !this.tsUtils.isDerivedFrom(throwExprType, CheckType.Error)) { + private handleThrowStatement(node: ts.Node): void { + const throwStmt = node as ts.ThrowStatement; + const throwExprType = this.tsTypeChecker.getTypeAtLocation(throwStmt.expression); + if ( + !throwExprType.isClassOrInterface() || + !this.tsUtils.isOrDerivedFrom(throwExprType, this.tsUtils.isStdErrorType) + ) { this.incrementCounters(node, FaultID.ThrowStatement, false, undefined); } } - private handleForStatement(node: ts.Node) { - let tsForStmt = node as ts.ForStatement; - let tsForInit = tsForStmt.initializer; + private handleForStatement(node: ts.Node): void { + const tsForStmt = node as ts.ForStatement; + const tsForInit = tsForStmt.initializer; if (tsForInit && (ts.isArrayLiteralExpression(tsForInit) || ts.isObjectLiteralExpression(tsForInit))) { this.incrementCounters(tsForInit, FaultID.DestructuringAssignment); } } - private handleForInStatement(node: ts.Node) { - let tsForInStmt = node as ts.ForInStatement; - let tsForInInit = tsForInStmt.initializer; + private handleForInStatement(node: ts.Node): void { + const tsForInStmt = node as ts.ForInStatement; + const tsForInInit = tsForInStmt.initializer; if (ts.isArrayLiteralExpression(tsForInInit) || ts.isObjectLiteralExpression(tsForInInit)) { this.incrementCounters(tsForInInit, FaultID.DestructuringAssignment); } this.incrementCounters(node, FaultID.ForInStatement); } - private handleForOfStatement(node: ts.Node) { - let tsForOfStmt = node as ts.ForOfStatement; - let tsForOfInit = tsForOfStmt.initializer; + private handleForOfStatement(node: ts.Node): void { + const tsForOfStmt = node as ts.ForOfStatement; + const tsForOfInit = tsForOfStmt.initializer; if (ts.isArrayLiteralExpression(tsForOfInit) || ts.isObjectLiteralExpression(tsForOfInit)) { this.incrementCounters(tsForOfInit, FaultID.DestructuringAssignment); } } - private handleImportDeclaration(node: ts.Node) { + private handleImportDeclaration(node: ts.Node): void { // early exit via exception if cancellation was requested this.cancellationToken?.throwIfCancellationRequested(); - let importDeclNode = node as ts.ImportDeclaration; + const importDeclNode = node as ts.ImportDeclaration; for (const stmt of importDeclNode.parent.statements) { if (stmt === importDeclNode) { break; @@ -561,7 +660,7 @@ export class TypeScriptLinter { break; } } - let expr1 = importDeclNode.moduleSpecifier; + const expr1 = importDeclNode.moduleSpecifier; if (expr1.kind === ts.SyntaxKind.StringLiteral) { if (!importDeclNode.importClause) { this.incrementCounters(node, FaultID.ImportFromPath); @@ -572,40 +671,47 @@ export class TypeScriptLinter { } } - private handlePropertyAccessExpression(node: ts.Node) { - let propertyAccessNode = node as ts.PropertyAccessExpression; + private handlePropertyAccessExpression(node: ts.Node): void { + if (ts.isCallExpression(node.parent) && node === node.parent.expression) { + return; + } + + const propertyAccessNode = node as ts.PropertyAccessExpression; const exprSym = this.tsUtils.trueSymbolAtLocation(propertyAccessNode); const baseExprSym = this.tsUtils.trueSymbolAtLocation(propertyAccessNode.expression); const baseExprType = this.tsTypeChecker.getTypeAtLocation(propertyAccessNode.expression); - if (!!baseExprSym && this.tsUtils.symbolHasEsObjectType(baseExprSym)) { - this.incrementCounters(propertyAccessNode, FaultID.EsObjectType); - } - if (this.isPrototypePropertyAccess(propertyAccessNode, exprSym, baseExprSym, baseExprType)) { + if (this.isPrototypePropertyAccess(propertyAccessNode, exprSym, baseExprSym, baseExprType)) { this.incrementCounters(propertyAccessNode.name, FaultID.Prototype); } if (TypeScriptLinter.advancedClassChecks && this.tsUtils.isClassObjectExpression(propertyAccessNode.expression)) { // missing exact rule this.incrementCounters(propertyAccessNode.expression, FaultID.ClassAsObject); } + if (!!baseExprSym && TsUtils.symbolHasEsObjectType(baseExprSym)) { + this.incrementCounters(propertyAccessNode, FaultID.EsObjectType); + } } - private handlePropertyAssignmentOrDeclaration(node: ts.Node) { - let propName = (node as ts.PropertyAssignment | ts.PropertyDeclaration).name; + private handlePropertyAssignmentOrDeclaration(node: ts.Node): void { + const propName = (node as ts.PropertyAssignment | ts.PropertyDeclaration).name; if (propName && (propName.kind === ts.SyntaxKind.NumericLiteral || propName.kind === ts.SyntaxKind.StringLiteral)) { // We can use literals as property names only when creating Record or any interop instances. let isRecordObjectInitializer = false; - let isDynamicLiteralInitializer = false; + let isLibraryType = false; + let isDynamic = false; if (ts.isPropertyAssignment(node)) { - let objectLiteralType = this.tsTypeChecker.getContextualType(node.parent); - isRecordObjectInitializer = !!objectLiteralType && this.tsUtils.isStdRecordType(objectLiteralType); - isDynamicLiteralInitializer = this.tsUtils.isDynamicLiteralInitializer(node.parent); + const objectLiteralType = this.tsTypeChecker.getContextualType(node.parent); + if (objectLiteralType) { + isRecordObjectInitializer = this.tsUtils.checkTypeSet(objectLiteralType, this.tsUtils.isStdRecordType); + isLibraryType = this.tsUtils.isLibraryType(objectLiteralType); + } + isDynamic = isLibraryType || this.tsUtils.isDynamicLiteralInitializer(node.parent); } - - if (!isRecordObjectInitializer && !isDynamicLiteralInitializer) { - let autofix : Autofix[] | undefined = Autofixer.fixLiteralAsPropertyName(node); - let autofixable = autofix != undefined; + if (!isRecordObjectInitializer && !isDynamic) { + let autofix: Autofix[] | undefined = autofixer.fixLiteralAsPropertyName(node); + const autofixable = autofix !== undefined; if (!this.autofixesInfo.shouldAutofix(node, FaultID.LiteralAsPropertyName)) { autofix = undefined; } @@ -615,13 +721,21 @@ export class TypeScriptLinter { if (ts.isPropertyDeclaration(node)) { const decorators = ts.getDecorators(node); this.handleDecorators(decorators); - this.filterOutDecoratorsDiagnostics(decorators, NON_INITIALIZABLE_PROPERTY_DECORATORS, - {begin: propName.getStart(), end: propName.getStart()}, PROPERTY_HAS_NO_INITIALIZER_ERROR_CODE); - + this.filterOutDecoratorsDiagnostics( + decorators, + NON_INITIALIZABLE_PROPERTY_DECORATORS, + { begin: propName.getStart(), end: propName.getStart() }, + PROPERTY_HAS_NO_INITIALIZER_ERROR_CODE + ); const classDecorators = ts.getDecorators(node.parent); - const propType = (node as ts.PropertyDeclaration).type?.getText(); - this.filterOutDecoratorsDiagnostics(classDecorators, NON_INITIALIZABLE_PROPERTY_CLASS_DECORATORS, - {begin: propName.getStart(), end: propName.getStart()}, PROPERTY_HAS_NO_INITIALIZER_ERROR_CODE, propType); + const propType = node.type?.getText(); + this.filterOutDecoratorsDiagnostics( + classDecorators, + NON_INITIALIZABLE_PROPERTY_CLASS_DECORATORS, + { begin: propName.getStart(), end: propName.getStart() }, + PROPERTY_HAS_NO_INITIALIZER_ERROR_CODE, + propType + ); this.handleDeclarationInferredType(node); this.handleDefiniteAssignmentAssertion(node); } @@ -630,56 +744,52 @@ export class TypeScriptLinter { private filterOutDecoratorsDiagnostics( decorators: readonly ts.Decorator[] | undefined, expectedDecorators: readonly string[], - range: {begin: number, end: number}, + range: { begin: number; end: number }, code: number, propType?: string - ) { + ): void { // Filter out non-initializable property decorators from strict diagnostics. if (this.tscStrictDiagnostics && this.sourceFile) { - if (decorators?.some(x => { - let decoratorName = ''; - if (ts.isIdentifier(x.expression)) { - decoratorName = x.expression.text; - } else if (ts.isCallExpression(x.expression) && ts.isIdentifier(x.expression.expression)) { - decoratorName = x.expression.expression.text; - } - // special case for property of type CustomDialogController of the @CustomDialog-decorated class - if (expectedDecorators.includes(NON_INITIALIZABLE_PROPERTY_CLASS_DECORATORS[0])) { - return expectedDecorators.includes(decoratorName) && propType === 'CustomDialogController' - } - return expectedDecorators.includes(decoratorName); - })) { - let file = path.normalize(this.sourceFile.fileName); - let tscDiagnostics = this.tscStrictDiagnostics.get(file) + if ( + decorators?.some((x) => { + let decoratorName = ''; + if (ts.isIdentifier(x.expression)) { + decoratorName = x.expression.text; + } else if (ts.isCallExpression(x.expression) && ts.isIdentifier(x.expression.expression)) { + decoratorName = x.expression.expression.text; + } + // special case for property of type CustomDialogController of the @CustomDialog-decorated class + if (expectedDecorators.includes(NON_INITIALIZABLE_PROPERTY_CLASS_DECORATORS[0])) { + return expectedDecorators.includes(decoratorName) && propType === 'CustomDialogController'; + } + return expectedDecorators.includes(decoratorName); + }) + ) { + const file = path.normalize(this.sourceFile.fileName); + const tscDiagnostics = this.tscStrictDiagnostics.get(file); if (tscDiagnostics) { - let filteredDiagnostics = tscDiagnostics.filter( - (val, idx, array) => { - if (val.code !== code) { - return true; - } - - if (val.start === undefined) { - return true; - } - - if (val.start < range.begin) { - return true; - } - - if (val.start > range.end) { - return true; - } - - return false; + const filteredDiagnostics = tscDiagnostics.filter((val) => { + if (val.code !== code) { + return true; + } + if (val.start === undefined) { + return true; } - ); + if (val.start < range.begin) { + return true; + } + if (val.start > range.end) { + return true; + } + return false; + }); this.tscStrictDiagnostics.set(file, filteredDiagnostics); } } } } - private checkInRange(rangesToFilter: { begin: number, end: number }[], pos: number): boolean { + private static checkInRange(rangesToFilter: { begin: number; end: number }[], pos: number): boolean { for (let i = 0; i < rangesToFilter.length; i++) { if (pos >= rangesToFilter[i].begin && pos < rangesToFilter[i].end) { return false; @@ -688,18 +798,20 @@ export class TypeScriptLinter { return true; } - private filterStrictDiagnostics(filters: { [code: number]: (pos: number) => boolean }, - diagnosticChecker: DiagnosticChecker): boolean { + private filterStrictDiagnostics( + filters: { [code: number]: (pos: number) => boolean }, + diagnosticChecker: DiagnosticChecker + ): boolean { if (!this.tscStrictDiagnostics || !this.sourceFile) { return false; } - let file = path.normalize(this.sourceFile.fileName); - let tscDiagnostics = this.tscStrictDiagnostics.get(file) + const file = path.normalize(this.sourceFile.fileName); + const tscDiagnostics = this.tscStrictDiagnostics.get(file); if (!tscDiagnostics) { return false; } - const checkDiagnostic = (val: ts.Diagnostic) => { + const checkDiagnostic = (val: ts.Diagnostic): boolean => { const checkInRange = filters[val.code]; if (!checkInRange) { return true; @@ -717,43 +829,44 @@ export class TypeScriptLinter { return true; } - private handleFunctionExpression(node: ts.Node) { + private static isClassLikeOrIface(node: ts.Node): boolean { + return ts.isClassLike(node) || ts.isInterfaceDeclaration(node); + } + + private handleFunctionExpression(node: ts.Node): void { const funcExpr = node as ts.FunctionExpression; - const isGenerator = funcExpr.asteriskToken !== undefined; - const containsThis = scopeContainsThis(funcExpr.body); - const hasValidContext = hasPredecessor(funcExpr, ts.isClassLike) || - hasPredecessor(funcExpr, ts.isInterfaceDeclaration); const isGeneric = funcExpr.typeParameters !== undefined && funcExpr.typeParameters.length > 0; + const isGenerator = funcExpr.asteriskToken !== undefined; + const hasThisKeyword = scopeContainsThis(funcExpr.body); const isCalledRecursively = this.tsUtils.isFunctionCalledRecursively(funcExpr); const [hasUnfixableReturnType, newRetTypeNode] = this.handleMissingReturnType(funcExpr); - const autofixable = !isGeneric && !isGenerator && !containsThis && !hasUnfixableReturnType && - !isCalledRecursively; + const autofixable = + !isGeneric && !isGenerator && !hasThisKeyword && !isCalledRecursively && !hasUnfixableReturnType; let autofix: Autofix[] | undefined; - if (autofixable && this.autofixesInfo.shouldAutofix(node, FaultID.FunctionExpression)) { - autofix = [ Autofixer.fixFunctionExpression(funcExpr, funcExpr.parameters, newRetTypeNode, ts.getModifiers(funcExpr)) ]; + if (autofixable && this.autofixesInfo.shouldAutofix(funcExpr, FaultID.FunctionExpression)) { + autofix = [ + autofixer.fixFunctionExpression(funcExpr, funcExpr.parameters, newRetTypeNode, ts.getModifiers(funcExpr)) + ]; } - this.incrementCounters(node, FaultID.FunctionExpression, autofixable, autofix); + this.incrementCounters(funcExpr, FaultID.FunctionExpression, autofixable, autofix); if (isGeneric) { this.incrementCounters(funcExpr, FaultID.LambdaWithTypeParameters); } if (isGenerator) { this.incrementCounters(funcExpr, FaultID.GeneratorFunction); } - if (containsThis && !hasValidContext) { - this.incrementCounters(funcExpr, FaultID.FunctionContainsThis); + if (!hasPredecessor(funcExpr, TypeScriptLinter.isClassLikeOrIface)) { + this.reportThisKeywordsInScope(funcExpr.body); } if (hasUnfixableReturnType) { this.incrementCounters(funcExpr, FaultID.LimitedReturnTypeInference); } } - private handleArrowFunction(node: ts.Node) { + private handleArrowFunction(node: ts.Node): void { const arrowFunc = node as ts.ArrowFunction; - const containsThis = scopeContainsThis(arrowFunc.body); - const hasValidContext = hasPredecessor(arrowFunc, ts.isClassLike) || - hasPredecessor(arrowFunc, ts.isInterfaceDeclaration); - if (containsThis && !hasValidContext) { - this.incrementCounters(arrowFunc, FaultID.FunctionContainsThis); + if (!hasPredecessor(arrowFunc, TypeScriptLinter.isClassLikeOrIface)) { + this.reportThisKeywordsInScope(arrowFunc.body); } const contextType = this.tsTypeChecker.getContextualType(arrowFunc); if (!(contextType && this.tsUtils.isLibraryType(contextType))) { @@ -766,27 +879,28 @@ export class TypeScriptLinter { } } - private handleClassExpression(node: ts.Node) { - let tsClassExpr = node as ts.ClassExpression; + private handleClassExpression(node: ts.Node): void { + const tsClassExpr = node as ts.ClassExpression; this.incrementCounters(node, FaultID.ClassExpression); this.handleDecorators(ts.getDecorators(tsClassExpr)); } - private handleFunctionDeclaration(node: ts.Node) { + private handleFunctionDeclaration(node: ts.Node): void { // early exit via exception if cancellation was requested this.cancellationToken?.throwIfCancellationRequested(); - let tsFunctionDeclaration = node as ts.FunctionDeclaration; + const tsFunctionDeclaration = node as ts.FunctionDeclaration; if (!tsFunctionDeclaration.type) { this.handleMissingReturnType(tsFunctionDeclaration); } if (tsFunctionDeclaration.name) { this.countDeclarationsWithDuplicateName(tsFunctionDeclaration.name, tsFunctionDeclaration); } - if (tsFunctionDeclaration.body && scopeContainsThis(tsFunctionDeclaration.body)) { - this.incrementCounters(node, FaultID.FunctionContainsThis); + if (tsFunctionDeclaration.body) { + this.reportThisKeywordsInScope(tsFunctionDeclaration.body); } - if (!ts.isSourceFile(tsFunctionDeclaration.parent) && !ts.isModuleBlock(tsFunctionDeclaration.parent)) { + const funcDeclParent = tsFunctionDeclaration.parent; + if (!ts.isSourceFile(funcDeclParent) && !ts.isModuleBlock(funcDeclParent)) { this.incrementCounters(tsFunctionDeclaration, FaultID.LocalFunction); } if (tsFunctionDeclaration.asteriskToken) { @@ -794,99 +908,120 @@ export class TypeScriptLinter { } } - private handleMissingReturnType(funcLikeDecl: ts.FunctionLikeDeclaration | ts.MethodSignature): [boolean, ts.TypeNode | undefined] { + private handleMissingReturnType( + funcLikeDecl: ts.FunctionLikeDeclaration | ts.MethodSignature + ): [boolean, ts.TypeNode | undefined] { // Note: Return type can't be inferred for function without body. const isSignature = ts.isMethodSignature(funcLikeDecl); if (isSignature || !funcLikeDecl.body) { // Ambient flag is not exposed, so we apply dirty hack to make it visible - const isAmbientDeclaration = !!(funcLikeDecl.flags & (ts.NodeFlags as any)['Ambient']); + const isAmbientDeclaration = !!(funcLikeDecl.flags & (ts.NodeFlags as any).Ambient); if ((isSignature || isAmbientDeclaration) && !funcLikeDecl.type) { this.incrementCounters(funcLikeDecl, FaultID.LimitedReturnTypeInference); } return [false, undefined]; } + + return this.tryAutofixMissingReturnType(funcLikeDecl); + } + + private tryAutofixMissingReturnType(funcLikeDecl: ts.FunctionLikeDeclaration): [boolean, ts.TypeNode | undefined] { + if (!funcLikeDecl.body) { + return [false, undefined]; + } + let autofixable = false; - let autofix : Autofix[] | undefined; + let autofix: Autofix[] | undefined; let newRetTypeNode: ts.TypeNode | undefined; - let isFuncExpr = ts.isFunctionExpression(funcLikeDecl); - // Currently, ArkTS can't infer return type of function, when expression - // in the return statement is a call to a function or method whose return - // value type is omitted. In that case, we attempt to prepare an autofix. + const isFuncExpr = ts.isFunctionExpression(funcLikeDecl); + + /* + * Currently, ArkTS can't infer return type of function, when expression + * in the return statement is a call to a function or method whose return + * value type is omitted. In that case, we attempt to prepare an autofix. + */ let hasLimitedRetTypeInference = this.hasLimitedTypeInferenceFromReturnExpr(funcLikeDecl.body); - let tsSignature = this.tsTypeChecker.getSignatureFromDeclaration(funcLikeDecl); + const tsSignature = this.tsTypeChecker.getSignatureFromDeclaration(funcLikeDecl); if (tsSignature) { - let tsRetType = this.tsTypeChecker.getReturnTypeOfSignature(tsSignature); - if (!tsRetType || this.tsUtils.isUnsupportedType(tsRetType)) { + const tsRetType = this.tsTypeChecker.getReturnTypeOfSignature(tsSignature); + if (!tsRetType || TsUtils.isUnsupportedType(tsRetType)) { hasLimitedRetTypeInference = true; } else if (hasLimitedRetTypeInference) { newRetTypeNode = this.tsTypeChecker.typeToTypeNode(tsRetType, funcLikeDecl, ts.NodeBuilderFlags.None); if (newRetTypeNode && !isFuncExpr) { autofixable = true; if (this.autofixesInfo.shouldAutofix(funcLikeDecl, FaultID.LimitedReturnTypeInference)) { - autofix = [Autofixer.fixReturnType(funcLikeDecl, newRetTypeNode)]; + autofix = [autofixer.fixReturnType(funcLikeDecl, newRetTypeNode)]; } } } } - // Don't report here if in function expression context. - // See handleFunctionExpression for details. + + /* + * Don't report here if in function expression context. + * See handleFunctionExpression for details. + */ if (hasLimitedRetTypeInference && !isFuncExpr) { this.incrementCounters(funcLikeDecl, FaultID.LimitedReturnTypeInference, autofixable, autofix); } + return [hasLimitedRetTypeInference && !newRetTypeNode, newRetTypeNode]; } private hasLimitedTypeInferenceFromReturnExpr(funBody: ts.ConciseBody): boolean { let hasLimitedTypeInference = false; - const self = this; - function visitNode(tsNode: ts.Node): void { + const callback = (node: ts.Node): void => { if (hasLimitedTypeInference) { return; } if ( - ts.isReturnStatement(tsNode) && tsNode.expression && - self.tsUtils.isCallToFunctionWithOmittedReturnType(self.tsUtils.unwrapParenthesized(tsNode.expression)) + ts.isReturnStatement(node) && + node.expression && + this.tsUtils.isCallToFunctionWithOmittedReturnType(TsUtils.unwrapParenthesized(node.expression)) ) { hasLimitedTypeInference = true; - return; } - // Visit children nodes. Don't traverse other nested function-like declarations. - if ( - !ts.isFunctionDeclaration(tsNode) && - !ts.isFunctionExpression(tsNode) && - !ts.isMethodDeclaration(tsNode) && - !ts.isAccessor(tsNode) && - !ts.isArrowFunction(tsNode) - ) - tsNode.forEachChild(visitNode); - } + }; + // Don't traverse other nested function-like declarations. + const stopCondition = (node: ts.Node): boolean => { + return ( + ts.isFunctionDeclaration(node) || + ts.isFunctionExpression(node) || + ts.isMethodDeclaration(node) || + ts.isAccessor(node) || + ts.isArrowFunction(node) + ); + }; if (ts.isBlock(funBody)) { - visitNode(funBody); + forEachNodeInSubtree(funBody, callback, stopCondition); } else { - const tsExpr = this.tsUtils.unwrapParenthesized(funBody); + const tsExpr = TsUtils.unwrapParenthesized(funBody); hasLimitedTypeInference = this.tsUtils.isCallToFunctionWithOmittedReturnType(tsExpr); } return hasLimitedTypeInference; } - private handlePrefixUnaryExpression(node: ts.Node) { - let tsUnaryArithm = node as ts.PrefixUnaryExpression; - let tsUnaryOp = tsUnaryArithm.operator; + private handlePrefixUnaryExpression(node: ts.Node): void { + const tsUnaryArithm = node as ts.PrefixUnaryExpression; + const tsUnaryOp = tsUnaryArithm.operator; if ( tsUnaryOp === ts.SyntaxKind.PlusToken || tsUnaryOp === ts.SyntaxKind.MinusToken || tsUnaryOp === ts.SyntaxKind.TildeToken ) { const tsOperatndType = this.tsTypeChecker.getTypeAtLocation(tsUnaryArithm.operand); - if (!(tsOperatndType.getFlags() & (ts.TypeFlags.NumberLike | ts.TypeFlags.BigIntLiteral)) || - ( tsUnaryOp === ts.SyntaxKind.TildeToken && tsUnaryArithm.operand.kind === ts.SyntaxKind.NumericLiteral && - !this.tsUtils.isIntegerConstantValue(tsUnaryArithm.operand as ts.NumericLiteral) ) - ) + if ( + !(tsOperatndType.getFlags() & (ts.TypeFlags.NumberLike | ts.TypeFlags.BigIntLiteral)) || + tsUnaryOp === ts.SyntaxKind.TildeToken && + tsUnaryArithm.operand.kind === ts.SyntaxKind.NumericLiteral && + !this.tsUtils.isIntegerConstantValue(tsUnaryArithm.operand as ts.NumericLiteral) + ) { this.incrementCounters(node, FaultID.UnaryArithmNotNumber); + } } } - private handleBinaryExpression(node: ts.Node) { + private handleBinaryExpression(node: ts.Node): void { const tsBinaryExpr = node as ts.BinaryExpression; const tsLhsExpr = tsBinaryExpr.left; const tsRhsExpr = tsBinaryExpr.right; @@ -904,6 +1039,9 @@ export class TypeScriptLinter { case ts.SyntaxKind.InstanceOfKeyword: this.processBinaryInstanceOf(node, tsLhsExpr, leftOperandType); break; + case ts.SyntaxKind.InKeyword: + this.incrementCounters(tsBinaryExpr.operatorToken, FaultID.InOperator); + break; case ts.SyntaxKind.EqualsToken: if (this.tsUtils.needToDeduceStructuralIdentity(leftOperandType, rightOperandType, tsRhsExpr)) { this.incrementCounters(tsBinaryExpr, FaultID.StructuralIdentity); @@ -911,29 +1049,30 @@ export class TypeScriptLinter { this.handleEsObjectAssignment(tsBinaryExpr, typeNode, tsRhsExpr); break; default: - return; } } - private processBinaryAssignment(node: ts.Node, tsLhsExpr: ts.Expression) { + private processBinaryAssignment(node: ts.Node, tsLhsExpr: ts.Expression): void { if (ts.isObjectLiteralExpression(tsLhsExpr) || ts.isArrayLiteralExpression(tsLhsExpr)) { this.incrementCounters(node, FaultID.DestructuringAssignment); } if (ts.isPropertyAccessExpression(tsLhsExpr)) { const tsLhsSymbol = this.tsUtils.trueSymbolAtLocation(tsLhsExpr); const tsLhsBaseSymbol = this.tsUtils.trueSymbolAtLocation(tsLhsExpr.expression); - if (tsLhsSymbol && (tsLhsSymbol.flags & ts.SymbolFlags.Method)) { + if (tsLhsSymbol && tsLhsSymbol.flags & ts.SymbolFlags.Method) { this.incrementCounters(tsLhsExpr, FaultID.MethodReassignment); } if ( - this.tsUtils.isMethodAssignment(tsLhsSymbol) && tsLhsBaseSymbol && + TsUtils.isMethodAssignment(tsLhsSymbol) && + tsLhsBaseSymbol && (tsLhsBaseSymbol.flags & ts.SymbolFlags.Function) !== 0 - ) + ) { this.incrementCounters(tsLhsExpr, FaultID.PropertyDeclOnFunction); + } } } - private processBinaryComma(tsBinaryExpr: ts.BinaryExpression) { + private processBinaryComma(tsBinaryExpr: ts.BinaryExpression): void { // CommaOpertor is allowed in 'for' statement initalizer and incrementor let tsExprNode: ts.Node = tsBinaryExpr; let tsParentNode = tsExprNode.parent; @@ -943,45 +1082,54 @@ export class TypeScriptLinter { } if (tsParentNode && tsParentNode.kind === ts.SyntaxKind.ForStatement) { const tsForNode = tsParentNode as ts.ForStatement; - if (tsExprNode === tsForNode.initializer || tsExprNode === tsForNode.incrementor) return; + if (tsExprNode === tsForNode.initializer || tsExprNode === tsForNode.incrementor) { + return; + } } this.incrementCounters(tsBinaryExpr as ts.Node, FaultID.CommaOperator); } - private processBinaryInstanceOf(node: ts.Node, tsLhsExpr: ts.Expression, leftOperandType: ts.Type) { - const leftExpr = this.tsUtils.unwrapParenthesized(tsLhsExpr); + private processBinaryInstanceOf(node: ts.Node, tsLhsExpr: ts.Expression, leftOperandType: ts.Type): void { + const leftExpr = TsUtils.unwrapParenthesized(tsLhsExpr); const leftSymbol = this.tsUtils.trueSymbolAtLocation(leftExpr); - // In STS, the left-hand side expression may be of any reference type, otherwise - // a compile-time error occurs. In addition, the left operand in STS cannot be a type. + + /* + * In STS, the left-hand side expression may be of any reference type, otherwise + * a compile-time error occurs. In addition, the left operand in STS cannot be a type. + */ if (tsLhsExpr.kind === ts.SyntaxKind.ThisKeyword) { return; } - if (this.tsUtils.isPrimitiveType(leftOperandType) || ts.isTypeNode(leftExpr) || this.tsUtils.isTypeSymbol(leftSymbol)) { + if (TsUtils.isPrimitiveType(leftOperandType) || ts.isTypeNode(leftExpr) || TsUtils.isTypeSymbol(leftSymbol)) { this.incrementCounters(node, FaultID.InstanceofUnsupported); } } - private handleVariableDeclarationList(node: ts.Node) { - let varDeclFlags = ts.getCombinedNodeFlags(node); - if (!(varDeclFlags & (ts.NodeFlags.Let | ts.NodeFlags.Const))) + private handleVariableDeclarationList(node: ts.Node): void { + const varDeclFlags = ts.getCombinedNodeFlags(node); + if (!(varDeclFlags & (ts.NodeFlags.Let | ts.NodeFlags.Const))) { this.incrementCounters(node, FaultID.VarDeclaration); + } } - private handleVariableDeclaration(node: ts.Node) { - let tsVarDecl = node as ts.VariableDeclaration; - if (ts.isArrayBindingPattern(tsVarDecl.name) || ts.isObjectBindingPattern(tsVarDecl.name)) + private handleVariableDeclaration(node: ts.Node): void { + const tsVarDecl = node as ts.VariableDeclaration; + if (ts.isArrayBindingPattern(tsVarDecl.name) || ts.isObjectBindingPattern(tsVarDecl.name)) { this.incrementCounters(node, FaultID.DestructuringDeclaration); + } { // Check variable declaration for duplicate name. - const visitBindingPatternNames = (tsBindingName: ts.BindingName) => { + const visitBindingPatternNames = (tsBindingName: ts.BindingName): void => { if (ts.isIdentifier(tsBindingName)) { // The syntax kind of the declaration is defined here by the parent of 'BindingName' node. this.countDeclarationsWithDuplicateName(tsBindingName, tsBindingName, tsBindingName.parent.kind); return; } for (const tsBindingElem of tsBindingName.elements) { - if (ts.isOmittedExpression(tsBindingElem)) continue; + if (ts.isOmittedExpression(tsBindingElem)) { + continue; + } visitBindingPatternNames(tsBindingElem.name); } @@ -989,9 +1137,9 @@ export class TypeScriptLinter { visitBindingPatternNames(tsVarDecl.name); } if (tsVarDecl.type && tsVarDecl.initializer) { - let tsVarInit = tsVarDecl.initializer; - let tsVarType = this.tsTypeChecker.getTypeAtLocation(tsVarDecl.type); - let tsInitType = this.tsTypeChecker.getTypeAtLocation(tsVarInit); + const tsVarInit = tsVarDecl.initializer; + const tsVarType = this.tsTypeChecker.getTypeAtLocation(tsVarDecl.type); + const tsInitType = this.tsTypeChecker.getTypeAtLocation(tsVarInit); if (this.tsUtils.needToDeduceStructuralIdentity(tsVarType, tsInitType, tsVarInit)) { this.incrementCounters(tsVarDecl, FaultID.StructuralIdentity); } @@ -1001,11 +1149,11 @@ export class TypeScriptLinter { this.handleDefiniteAssignmentAssertion(tsVarDecl); } - private handleEsObjectDelaration(node: ts.VariableDeclaration) { - const isDeclaredESObject = !!node.type && this.tsUtils.isEsObjectType(node.type); + private handleEsObjectDelaration(node: ts.VariableDeclaration): void { + const isDeclaredESObject = !!node.type && TsUtils.isEsObjectType(node.type); const initalizerTypeNode = node.initializer && this.tsUtils.getVariableDeclarationTypeNode(node.initializer); - const isInitializedWithESObject = !!initalizerTypeNode && this.tsUtils.isEsObjectType(initalizerTypeNode); - const isLocal = this.tsUtils.isInsideBlock(node) + const isInitializedWithESObject = !!initalizerTypeNode && TsUtils.isEsObjectType(initalizerTypeNode); + const isLocal = TsUtils.isInsideBlock(node); if ((isDeclaredESObject || isInitializedWithESObject) && !isLocal) { this.incrementCounters(node, FaultID.EsObjectType); return; @@ -1016,11 +1164,11 @@ export class TypeScriptLinter { } } - private handleEsObjectAssignment(node: ts.Node, nodeDeclType: ts.TypeNode | undefined, initializer: ts.Node) { + private handleEsObjectAssignment(node: ts.Node, nodeDeclType: ts.TypeNode | undefined, initializer: ts.Node): void { const isTypeAnnotated = !!nodeDeclType; - const isDeclaredESObject = isTypeAnnotated && this.tsUtils.isEsObjectType(nodeDeclType); + const isDeclaredESObject = isTypeAnnotated && TsUtils.isEsObjectType(nodeDeclType); const initalizerTypeNode = this.tsUtils.getVariableDeclarationTypeNode(initializer); - const isInitializedWithESObject = !!initalizerTypeNode && this.tsUtils.isEsObjectType(initalizerTypeNode); + const isInitializedWithESObject = !!initalizerTypeNode && TsUtils.isEsObjectType(initalizerTypeNode); if (isTypeAnnotated && !isDeclaredESObject && isInitializedWithESObject) { this.incrementCounters(node, FaultID.EsObjectType); return; @@ -1031,35 +1179,39 @@ export class TypeScriptLinter { } } - private handleCatchClause(node: ts.Node) { - let tsCatch = node as ts.CatchClause; - // In TS catch clause doesn't permit specification of the exception varible type except 'any' or 'unknown'. - // It is not compatible with STS 'catch' where the exception variable has to be of type - // Error or derived from it. - // So each 'catch' which has explicit type for the exception object goes to problems in strict mode. - if (tsCatch.variableDeclaration && tsCatch.variableDeclaration.type) { + private handleCatchClause(node: ts.Node): void { + const tsCatch = node as ts.CatchClause; + + /* + * In TS catch clause doesn't permit specification of the exception varible type except 'any' or 'unknown'. + * It is not compatible with STS 'catch' where the exception variable has to be of type + * Error or derived from it. + * So each 'catch' which has explicit type for the exception object goes to problems in strict mode. + */ + if (tsCatch.variableDeclaration?.type) { let autofix: Autofix[] | undefined; - if (this.autofixesInfo.shouldAutofix(tsCatch, FaultID.CatchWithUnsupportedType)) - autofix = [ Autofixer.dropTypeOnVarDecl(tsCatch.variableDeclaration) ]; + if (this.autofixesInfo.shouldAutofix(tsCatch, FaultID.CatchWithUnsupportedType)) { + autofix = [autofixer.dropTypeOnVarDecl(tsCatch.variableDeclaration)]; + } this.incrementCounters(node, FaultID.CatchWithUnsupportedType, true, autofix); } } - private handleClassDeclaration(node: ts.Node) { + private handleClassDeclaration(node: ts.Node): void { // early exit via exception if cancellation was requested this.cancellationToken?.throwIfCancellationRequested(); - let tsClassDecl = node as ts.ClassDeclaration; + const tsClassDecl = node as ts.ClassDeclaration; this.staticBlocks.clear(); if (tsClassDecl.name) { this.countDeclarationsWithDuplicateName(tsClassDecl.name, tsClassDecl); } this.countClassMembersWithDuplicateName(tsClassDecl); - const visitHClause = (hClause: ts.HeritageClause) => { + const visitHClause = (hClause: ts.HeritageClause): void => { for (const tsTypeExpr of hClause.types) { const tsExprType = this.tsTypeChecker.getTypeAtLocation(tsTypeExpr.expression); - if (tsExprType.isClass() && hClause.token == ts.SyntaxKind.ImplementsKeyword) { + if (tsExprType.isClass() && hClause.token === ts.SyntaxKind.ImplementsKeyword) { this.incrementCounters(tsTypeExpr, FaultID.ImplementsClass); } } @@ -1076,123 +1228,121 @@ export class TypeScriptLinter { this.handleDecorators(ts.getDecorators(tsClassDecl)); } - private handleModuleDeclaration(node: ts.Node) { + private handleModuleDeclaration(node: ts.Node): void { // early exit via exception if cancellation was requested this.cancellationToken?.throwIfCancellationRequested(); - let tsModuleDecl = node as ts.ModuleDeclaration; + const tsModuleDecl = node as ts.ModuleDeclaration; this.countDeclarationsWithDuplicateName(tsModuleDecl.name, tsModuleDecl); - let tsModuleBody = tsModuleDecl.body; - let tsModifiers = ts.getModifiers(tsModuleDecl); + const tsModuleBody = tsModuleDecl.body; + const tsModifiers = ts.getModifiers(tsModuleDecl); if (tsModuleBody) { if (ts.isModuleBlock(tsModuleBody)) { - for (const tsModuleStmt of tsModuleBody.statements) { - switch (tsModuleStmt.kind) { - case ts.SyntaxKind.VariableStatement: - case ts.SyntaxKind.FunctionDeclaration: - case ts.SyntaxKind.ClassDeclaration: - case ts.SyntaxKind.InterfaceDeclaration: - case ts.SyntaxKind.TypeAliasDeclaration: - case ts.SyntaxKind.EnumDeclaration: - case ts.SyntaxKind.ExportDeclaration: - break; - // Nested namespace declarations are prohibited - // but there is no cookbook recipe for it! - case ts.SyntaxKind.ModuleDeclaration: - break; - default: - this.incrementCounters(tsModuleStmt, FaultID.NonDeclarationInNamespace); - break; - } - } + this.handleModuleBlock(tsModuleBody); } } - if (!(tsModuleDecl.flags & ts.NodeFlags.Namespace) && - this.tsUtils.hasModifier(tsModifiers, ts.SyntaxKind.DeclareKeyword)) { + if ( + !(tsModuleDecl.flags & ts.NodeFlags.Namespace) && + TsUtils.hasModifier(tsModifiers, ts.SyntaxKind.DeclareKeyword) + ) { this.incrementCounters(tsModuleDecl, FaultID.ShorthandAmbientModuleDecl); } - if (ts.isStringLiteral(tsModuleDecl.name) && tsModuleDecl.name.text.includes('*')) + if (ts.isStringLiteral(tsModuleDecl.name) && tsModuleDecl.name.text.includes('*')) { this.incrementCounters(tsModuleDecl, FaultID.WildcardsInModuleName); + } + } + + private handleModuleBlock(moduleBlock: ts.ModuleBlock): void { + for (const tsModuleStmt of moduleBlock.statements) { + switch (tsModuleStmt.kind) { + case ts.SyntaxKind.VariableStatement: + case ts.SyntaxKind.FunctionDeclaration: + case ts.SyntaxKind.ClassDeclaration: + case ts.SyntaxKind.InterfaceDeclaration: + case ts.SyntaxKind.TypeAliasDeclaration: + case ts.SyntaxKind.EnumDeclaration: + case ts.SyntaxKind.ExportDeclaration: + break; + + /* + * Nested namespace declarations are prohibited + * but there is no cookbook recipe for it! + */ + case ts.SyntaxKind.ModuleDeclaration: + break; + default: + this.incrementCounters(tsModuleStmt, FaultID.NonDeclarationInNamespace); + break; + } + } } - private handleTypeAliasDeclaration(node: ts.Node) { - let tsTypeAlias = node as ts.TypeAliasDeclaration; + private handleTypeAliasDeclaration(node: ts.Node): void { + const tsTypeAlias = node as ts.TypeAliasDeclaration; this.countDeclarationsWithDuplicateName(tsTypeAlias.name, tsTypeAlias); } - private handleImportClause(node: ts.Node) { - let tsImportClause = node as ts.ImportClause; + private handleImportClause(node: ts.Node): void { + const tsImportClause = node as ts.ImportClause; if (tsImportClause.name) { this.countDeclarationsWithDuplicateName(tsImportClause.name, tsImportClause); } if (tsImportClause.namedBindings && ts.isNamedImports(tsImportClause.namedBindings)) { - let nonDefaultSpecs: ts.ImportSpecifier[] = []; + const nonDefaultSpecs: ts.ImportSpecifier[] = []; let defaultSpec: ts.ImportSpecifier | undefined; for (const importSpec of tsImportClause.namedBindings.elements) { - if (this.tsUtils.isDefaultImport(importSpec)) defaultSpec = importSpec; - else nonDefaultSpecs.push(importSpec); + if (TsUtils.isDefaultImport(importSpec)) { + defaultSpec = importSpec; + } else { + nonDefaultSpecs.push(importSpec); + } } if (defaultSpec) { let autofix: Autofix[] | undefined; - if (this.autofixesInfo.shouldAutofix(defaultSpec, FaultID.DefaultImport)) - autofix = [ Autofixer.fixDefaultImport(tsImportClause, defaultSpec, nonDefaultSpecs) ]; + if (this.autofixesInfo.shouldAutofix(defaultSpec, FaultID.DefaultImport)) { + autofix = [autofixer.fixDefaultImport(tsImportClause, defaultSpec, nonDefaultSpecs)]; + } this.incrementCounters(defaultSpec, FaultID.DefaultImport, true, autofix); } } - if (tsImportClause.isTypeOnly) { - let autofix: Autofix[] | undefined; - if (this.autofixesInfo.shouldAutofix(node, FaultID.TypeOnlyImport)) - autofix = [ Autofixer.dropTypeOnlyFlag(tsImportClause) ]; - this.incrementCounters(node, FaultID.TypeOnlyImport, true, autofix); - } } - private handleImportSpecifier(node: ts.Node) { - let importSpec = node as ts.ImportSpecifier; + private handleImportSpecifier(node: ts.Node): void { + const importSpec = node as ts.ImportSpecifier; this.countDeclarationsWithDuplicateName(importSpec.name, importSpec); - // Don't report or autofix type-only flag on default import if the latter has been autofixed already. - if ( - importSpec.isTypeOnly && - (!this.tsUtils.isDefaultImport(importSpec) || !this.autofixesInfo.shouldAutofix(importSpec, FaultID.DefaultImport)) - ) { - let autofix: Autofix[] | undefined; - if (this.autofixesInfo.shouldAutofix(node, FaultID.TypeOnlyImport)) - autofix = [ Autofixer.dropTypeOnlyFlag(importSpec) ]; - this.incrementCounters(node, FaultID.TypeOnlyImport, true, autofix); - } } - private handleNamespaceImport(node: ts.Node) { - let tsNamespaceImport = node as ts.NamespaceImport; + private handleNamespaceImport(node: ts.Node): void { + const tsNamespaceImport = node as ts.NamespaceImport; this.countDeclarationsWithDuplicateName(tsNamespaceImport.name, tsNamespaceImport); } - private handleTypeAssertionExpression(node: ts.Node) { - let tsTypeAssertion = node as ts.TypeAssertion; - if (tsTypeAssertion.type.getText() === 'const') + private handleTypeAssertionExpression(node: ts.Node): void { + const tsTypeAssertion = node as ts.TypeAssertion; + if (tsTypeAssertion.type.getText() === 'const') { this.incrementCounters(tsTypeAssertion, FaultID.ConstAssertion); - else - this.incrementCounters(node, FaultID.TypeAssertion, true, [ Autofixer.fixTypeAssertion(tsTypeAssertion) ]); + } else { + this.incrementCounters(node, FaultID.TypeAssertion, true, [autofixer.fixTypeAssertion(tsTypeAssertion)]); + } } - private handleMethodDeclaration(node: ts.Node) { + private handleMethodDeclaration(node: ts.Node): void { const tsMethodDecl = node as ts.MethodDeclaration; - const hasThis = scopeContainsThis(tsMethodDecl); - let isStatic = false + let isStatic = false; if (tsMethodDecl.modifiers) { - for (let mod of tsMethodDecl.modifiers) { + for (const mod of tsMethodDecl.modifiers) { if (mod.kind === ts.SyntaxKind.StaticKeyword) { isStatic = true; break; } } } - if (isStatic && hasThis) { - this.incrementCounters(node, FaultID.FunctionContainsThis); + if (tsMethodDecl.body && isStatic) { + this.reportThisKeywordsInScope(tsMethodDecl.body); } if (!tsMethodDecl.type) { this.handleMissingReturnType(tsMethodDecl); @@ -1201,38 +1351,40 @@ export class TypeScriptLinter { this.incrementCounters(node, FaultID.GeneratorFunction); } this.handleDecorators(ts.getDecorators(tsMethodDecl)); - this.filterOutDecoratorsDiagnostics(ts.getDecorators(tsMethodDecl), NON_RETURN_FUNCTION_DECORATORS, - {begin: tsMethodDecl.parameters.end, end: tsMethodDecl.body?.getStart() ?? tsMethodDecl.parameters.end}, - FUNCTION_HAS_NO_RETURN_ERROR_CODE); + this.filterOutDecoratorsDiagnostics( + ts.getDecorators(tsMethodDecl), + NON_RETURN_FUNCTION_DECORATORS, + { begin: tsMethodDecl.parameters.end, end: tsMethodDecl.body?.getStart() ?? tsMethodDecl.parameters.end }, + FUNCTION_HAS_NO_RETURN_ERROR_CODE + ); } - private handleMethodSignature(node: ts.MethodSignature) { - const tsMethodSign = node as ts.MethodSignature; + private handleMethodSignature(node: ts.MethodSignature): void { + const tsMethodSign = node; if (!tsMethodSign.type) { this.handleMissingReturnType(tsMethodSign); } } - private handleClassStaticBlockDeclaration(node: ts.Node) { - if (ts.isClassDeclaration(node.parent)) { - const tsClassDecl = node.parent as ts.ClassDeclaration; - let className = ''; - if (tsClassDecl.name) - // May be undefined in `export default class { ... }`. - className = tsClassDecl.name.text; - if (scopeContainsThis(node)) { - this.incrementCounters(node, FaultID.FunctionContainsThis); - } - if (this.staticBlocks.has(className)) - this.incrementCounters(node, FaultID.MultipleStaticBlocks); - else - this.staticBlocks.add(className); + private handleClassStaticBlockDeclaration(node: ts.Node): void { + const classStaticBlockDecl = node as ts.ClassStaticBlockDeclaration; + const parent = classStaticBlockDecl.parent; + if (!ts.isClassDeclaration(parent)) { + return; + } + this.reportThisKeywordsInScope(classStaticBlockDecl.body); + // May be undefined in `export default class { ... }`. + const className = parent.name?.text ?? ''; + if (this.staticBlocks.has(className)) { + this.incrementCounters(classStaticBlockDecl, FaultID.MultipleStaticBlocks); + } else { + this.staticBlocks.add(className); } } - private handleIdentifier(node: ts.Node) { - let tsIdentifier = node as ts.Identifier; - let tsIdentSym = this.tsUtils.trueSymbolAtLocation(tsIdentifier); + private handleIdentifier(node: ts.Node): void { + const tsIdentifier = node as ts.Identifier; + const tsIdentSym = this.tsUtils.trueSymbolAtLocation(tsIdentifier); if (!tsIdentSym) { return; } @@ -1241,7 +1393,7 @@ export class TypeScriptLinter { (tsIdentSym.flags & ts.SymbolFlags.Transient) !== 0 && tsIdentifier.text === 'globalThis' ) { - this.incrementCounters(tsIdentifier, FaultID.GlobalThis); + this.incrementCounters(node, FaultID.GlobalThis); } else { this.checkLimitedStdLib(tsIdentifier, tsIdentSym); this.handleRestrictedValues(tsIdentifier, tsIdentSym); @@ -1249,7 +1401,7 @@ export class TypeScriptLinter { } // hard-coded alternative to TypeScriptLinter.advancedClassChecks - private isAllowedClassValueContext(tsIdentifier: ts.Identifier, tsIdentSym: ts.Symbol): boolean { + private isAllowedClassValueContext(tsIdentifier: ts.Identifier): boolean { let ctx: ts.Node = tsIdentifier; while (ts.isPropertyAccessExpression(ctx.parent) || ts.isQualifiedName(ctx.parent)) { ctx = ctx.parent; @@ -1257,37 +1409,48 @@ export class TypeScriptLinter { if (ts.isPropertyAssignment(ctx.parent) && ts.isObjectLiteralExpression(ctx.parent.parent)) { ctx = ctx.parent.parent; } - if (ts.isArrowFunction(ctx.parent) && ctx.parent.body == ctx) { + if (ts.isArrowFunction(ctx.parent) && ctx.parent.body === ctx) { ctx = ctx.parent; } if (ts.isCallExpression(ctx.parent) || ts.isNewExpression(ctx.parent)) { - let callee = ctx.parent.expression; - if (callee != ctx && this.tsUtils.hasLibraryType(callee)) { + const callee = ctx.parent.expression; + const isAny = TsUtils.isAnyType(this.tsTypeChecker.getTypeAtLocation(callee)); + const isDynamic = isAny || this.tsUtils.hasLibraryType(callee); + if (callee !== ctx && isDynamic) { return true; } } return false; } - private handleRestrictedValues(tsIdentifier: ts.Identifier, tsIdentSym: ts.Symbol) { - const illegalValues = ts.SymbolFlags.ConstEnum | ts.SymbolFlags.RegularEnum | ts.SymbolFlags.ValueModule | - (TypeScriptLinter.advancedClassChecks? 0 : ts.SymbolFlags.Class); - - // If module name is duplicated by another declaration, this increases the possibility - // of finding a lot of false positives. Thus, do not check further in that case. - if ((tsIdentSym.flags & ts.SymbolFlags.ValueModule) != 0) { - if (!!tsIdentSym && this.tsUtils.symbolHasDuplicateName(tsIdentSym, ts.SyntaxKind.ModuleDeclaration)) { + private handleRestrictedValues(tsIdentifier: ts.Identifier, tsIdentSym: ts.Symbol): void { + const illegalValues = + ts.SymbolFlags.ConstEnum | + ts.SymbolFlags.RegularEnum | + ts.SymbolFlags.ValueModule | + (TypeScriptLinter.advancedClassChecks ? 0 : ts.SymbolFlags.Class); + + /* + * If module name is duplicated by another declaration, this increases the possibility + * of finding a lot of false positives. Thus, do not check further in that case. + */ + if ((tsIdentSym.flags & ts.SymbolFlags.ValueModule) !== 0) { + if (!!tsIdentSym && TsUtils.symbolHasDuplicateName(tsIdentSym, ts.SyntaxKind.ModuleDeclaration)) { return; } } - if ((tsIdentSym.flags & illegalValues) == 0 || isStruct(tsIdentSym) || !identiferUseInValueContext(tsIdentifier, tsIdentSym)) { + if ( + (tsIdentSym.flags & illegalValues) === 0 || + isStruct(tsIdentSym) || + !identiferUseInValueContext(tsIdentifier, tsIdentSym) + ) { return; } - if ((tsIdentSym.flags & ts.SymbolFlags.Class) != 0) { - if (!TypeScriptLinter.advancedClassChecks && this.isAllowedClassValueContext(tsIdentifier, tsIdentSym)) { + if ((tsIdentSym.flags & ts.SymbolFlags.Class) !== 0) { + if (!TypeScriptLinter.advancedClassChecks && this.isAllowedClassValueContext(tsIdentifier)) { return; } } @@ -1300,28 +1463,48 @@ export class TypeScriptLinter { } } - private handleElementAccessExpression(node: ts.Node) { - const tsElementAccessExpr = node as ts.ElementAccessExpression; - const tsElemAccessBaseExprType = this.tsTypeChecker.getTypeAtLocation(tsElementAccessExpr.expression); - const tsElemAccessBaseExprTypeNode = this.tsTypeChecker.typeToTypeNode(tsElemAccessBaseExprType, undefined, ts.NodeBuilderFlags.None); + private isElementAcessAllowed(type: ts.Type): boolean { + if (type.isUnion()) { + for (const t of type.types) { + if (!this.isElementAcessAllowed(t)) { + return false; + } + } + return true; + } - const checkClassOrInterface = tsElemAccessBaseExprType.isClassOrInterface() && - !this.tsUtils.isGenericArrayType(tsElemAccessBaseExprType) && - !this.tsUtils.isDerivedFrom(tsElemAccessBaseExprType, CheckType.Array); - const checkThisOrSuper = this.tsUtils.isThisOrSuperExpr(tsElementAccessExpr.expression) && - !this.tsUtils.isDerivedFrom(tsElemAccessBaseExprType, CheckType.Array); + const typeNode = this.tsTypeChecker.typeToTypeNode(type, undefined, ts.NodeBuilderFlags.None); - // implement check for enum argument expression + return ( + this.tsUtils.isLibraryType(type) || + TsUtils.isAnyType(type) || + this.tsUtils.isOrDerivedFrom(type, this.tsUtils.isArray) || + this.tsUtils.isOrDerivedFrom(type, TsUtils.isTuple) || + this.tsUtils.isOrDerivedFrom(type, this.tsUtils.isStdRecordType) || + TsUtils.isEnumType(type) || + // we allow EsObject here beacuse it is reported later using FaultId.EsObjectType + TsUtils.isEsObjectType(typeNode) + ); + } + + private handleElementAccessExpression(node: ts.Node): void { + const tsElementAccessExpr = node as ts.ElementAccessExpression; + const tsElementAccessExprSymbol = this.tsUtils.trueSymbolAtLocation(tsElementAccessExpr.expression); + const tsElemAccessBaseExprType = TsUtils.getNonNullableType( + this.tsUtils.getTypeOrTypeConstraintAtLocation(tsElementAccessExpr.expression) + ); if ( - !this.tsUtils.isLibraryType(tsElemAccessBaseExprType) && !this.tsUtils.isTypedArray(tsElemAccessBaseExprTypeNode) && - (checkClassOrInterface || this.tsUtils.isObjectLiteralType(tsElemAccessBaseExprType) || checkThisOrSuper) + // unnamed types do not have symbol, so need to check that explicitly + !this.tsUtils.isLibrarySymbol(tsElementAccessExprSymbol) && + !ts.isArrayLiteralExpression(tsElementAccessExpr.expression) && + !this.isElementAcessAllowed(tsElemAccessBaseExprType) ) { - let autofix = Autofixer.fixPropertyAccessByIndex(node); - const autofixable = autofix != undefined; - if (!this.autofixesInfo.shouldAutofix(node, FaultID.PropertyAccessByIndex)) + let autofix = autofixer.fixPropertyAccessByIndex(node); + const autofixable = autofix !== undefined; + if (!this.autofixesInfo.shouldAutofix(node, FaultID.PropertyAccessByIndex)) { autofix = undefined; - + } this.incrementCounters(node, FaultID.PropertyAccessByIndex, autofixable, autofix); } @@ -1330,69 +1513,62 @@ export class TypeScriptLinter { } } - private handleEnumMember(node: ts.Node) { - let tsEnumMember = node as ts.EnumMember; - let tsEnumMemberType = this.tsTypeChecker.getTypeAtLocation(tsEnumMember); - let constVal = this.tsTypeChecker.getConstantValue(tsEnumMember); - if (tsEnumMember.initializer && !this.tsUtils.isValidEnumMemberInit(tsEnumMember.initializer)) + private handleEnumMember(node: ts.Node): void { + const tsEnumMember = node as ts.EnumMember; + const tsEnumMemberType = this.tsTypeChecker.getTypeAtLocation(tsEnumMember); + const constVal = this.tsTypeChecker.getConstantValue(tsEnumMember); + if (tsEnumMember.initializer && !this.tsUtils.isValidEnumMemberInit(tsEnumMember.initializer)) { this.incrementCounters(node, FaultID.EnumMemberNonConstInit); + } // check for type - all members should be of same type - let enumDecl = tsEnumMember.parent; - let firstEnumMember = enumDecl.members[0]; - let firstEnumMemberType = this.tsTypeChecker.getTypeAtLocation(firstEnumMember); - let firstElewmVal = this.tsTypeChecker.getConstantValue(firstEnumMember); - // each string enum member has its own type - // so check that value type is string - if( constVal !==undefined && typeof constVal === 'string' && - firstElewmVal !==undefined && typeof firstElewmVal === 'string' ) + const enumDecl = tsEnumMember.parent; + const firstEnumMember = enumDecl.members[0]; + const firstEnumMemberType = this.tsTypeChecker.getTypeAtLocation(firstEnumMember); + const firstElewmVal = this.tsTypeChecker.getConstantValue(firstEnumMember); + + /* + * each string enum member has its own type + * so check that value type is string + */ + if ( + constVal !== undefined && + typeof constVal === 'string' && + firstElewmVal !== undefined && + typeof firstElewmVal === 'string' + ) { return; - if( constVal !==undefined && typeof constVal === 'number' && - firstElewmVal !==undefined && typeof firstElewmVal === 'number' ) + } + if ( + constVal !== undefined && + typeof constVal === 'number' && + firstElewmVal !== undefined && + typeof firstElewmVal === 'number' + ) { return; - if(firstEnumMemberType !== tsEnumMemberType) { - this.incrementCounters(node, FaultID.EnumMemberNonConstInit); } - } - - private handleExportDeclaration(node: ts.Node) { - let tsExportDecl = node as ts.ExportDeclaration; - if (tsExportDecl.isTypeOnly) { - let autofix: Autofix[] | undefined; - if (this.autofixesInfo.shouldAutofix(node, FaultID.TypeOnlyExport)) - autofix = [ Autofixer.dropTypeOnlyFlag(tsExportDecl) ]; - this.incrementCounters(node, FaultID.TypeOnlyExport, true, autofix); - } - let exportClause = tsExportDecl.exportClause; - if (exportClause && ts.isNamedExports(exportClause)) { - for (const exportSpec of exportClause.elements) { - if (exportSpec.isTypeOnly) { - let autofix: Autofix[] | undefined; - if (this.autofixesInfo.shouldAutofix(exportSpec, FaultID.TypeOnlyExport)) - autofix = [ Autofixer.dropTypeOnlyFlag(exportSpec) ]; - this.incrementCounters(exportSpec, FaultID.TypeOnlyExport, true, autofix); - } - } + if (firstEnumMemberType !== tsEnumMemberType) { + this.incrementCounters(node, FaultID.EnumMemberNonConstInit); } } - private handleExportAssignment(node: ts.Node) { + private handleExportAssignment(node: ts.Node): void { const exportAssignment = node as ts.ExportAssignment; if (exportAssignment.isExportEquals) { this.incrementCounters(node, FaultID.ExportAssignment); } } - private handleCallExpression(node: ts.Node) { - let tsCallExpr = node as ts.CallExpression; + private handleCallExpression(node: ts.Node): void { + const tsCallExpr = node as ts.CallExpression; const calleeSym = this.tsUtils.trueSymbolAtLocation(tsCallExpr.expression); - const calleeType = this.tsTypeChecker.getTypeAtLocation(tsCallExpr.expression); const callSignature = this.tsTypeChecker.getResolvedSignature(tsCallExpr); this.handleImportCall(tsCallExpr); this.handleRequireCall(tsCallExpr); - if (!!calleeSym) { - if (this.tsUtils.symbolHasEsObjectType(calleeSym)) { + // NOTE: Keep handleFunctionApplyBindPropCall above handleGenericCallWithNoTypeArgs here!!! + if (calleeSym !== undefined) { + if (TsUtils.symbolHasEsObjectType(calleeSym)) { this.incrementCounters(tsCallExpr, FaultID.EsObjectType); } // need to process Symbol call separatey in order to not report two times when using Symbol API @@ -1400,16 +1576,29 @@ export class TypeScriptLinter { this.incrementCounters(tsCallExpr, FaultID.SymbolType); } } - if (!!callSignature) { + if (callSignature !== undefined) { if (!this.tsUtils.isLibrarySymbol(calleeSym)) { this.handleGenericCallWithNoTypeArgs(tsCallExpr, callSignature); } this.handleStructIdentAndUndefinedInArgs(tsCallExpr, callSignature); } - this.handleLibraryTypeCall(tsCallExpr, calleeType); + this.handleLibraryTypeCall(tsCallExpr); + + if ( + ts.isPropertyAccessExpression(tsCallExpr.expression) && + this.tsUtils.hasEsObjectType(tsCallExpr.expression.expression) + ) { + this.incrementCounters(node, FaultID.EsObjectType); + } } - private handleImportCall(tsCallExpr: ts.CallExpression) { + private handleEtsComponentExpression(node: ts.Node): void { + // for all the checks we make EtsComponentExpression is compatible with the CallExpression + const etsComponentExpression = node as ts.CallExpression; + this.handleLibraryTypeCall(etsComponentExpression); + } + + private handleImportCall(tsCallExpr: ts.CallExpression): void { if (tsCallExpr.expression.kind === ts.SyntaxKind.ImportKeyword) { // relax rule#133 "arkts-no-runtime-import" const tsArgs = tsCallExpr.arguments; @@ -1427,27 +1616,41 @@ export class TypeScriptLinter { } } - private handleRequireCall(tsCallExpr: ts.CallExpression) { + private handleRequireCall(tsCallExpr: ts.CallExpression): void { if ( ts.isIdentifier(tsCallExpr.expression) && tsCallExpr.expression.text === 'require' && ts.isVariableDeclaration(tsCallExpr.parent) ) { - let tsType = this.tsTypeChecker.getTypeAtLocation(tsCallExpr.expression); - if (this.tsUtils.isInterfaceType(tsType) && tsType.symbol.name === 'NodeRequire') + const tsType = this.tsTypeChecker.getTypeAtLocation(tsCallExpr.expression); + if (TsUtils.isInterfaceType(tsType) && tsType.symbol.name === 'NodeRequire') { this.incrementCounters(tsCallExpr.parent, FaultID.ImportAssignment); + } } } - private handleGenericCallWithNoTypeArgs(callLikeExpr: ts.CallExpression | ts.NewExpression, callSignature: ts.Signature) { - // Note: The PR!716 has led to a significant performance degradation. - // Since initial problem was fixed in a more general way, this change - // became redundant. Therefore, it was reverted. See #13721 comments - // for a detailed analysis. - const tsSyntaxKind = ts.isNewExpression(callLikeExpr) ? ts.SyntaxKind.Constructor : ts.SyntaxKind.FunctionDeclaration; + private handleGenericCallWithNoTypeArgs( + callLikeExpr: ts.CallExpression | ts.NewExpression, + callSignature: ts.Signature + ): void { + + /* + * Note: The PR!716 has led to a significant performance degradation. + * Since initial problem was fixed in a more general way, this change + * became redundant. Therefore, it was reverted. See #13721 comments + * for a detailed analysis. + */ + const tsSyntaxKind = ts.isNewExpression(callLikeExpr) ? + ts.SyntaxKind.Constructor : + ts.SyntaxKind.FunctionDeclaration; const signFlags = ts.NodeBuilderFlags.WriteTypeArgumentsOfSignature | ts.NodeBuilderFlags.IgnoreErrors; - const signDecl = this.tsTypeChecker.signatureToSignatureDeclaration(callSignature, tsSyntaxKind, undefined, signFlags); + const signDecl = this.tsTypeChecker.signatureToSignatureDeclaration( + callSignature, + tsSyntaxKind, + undefined, + signFlags + ); if (!signDecl?.typeArguments) { return; } @@ -1455,37 +1658,43 @@ export class TypeScriptLinter { const startTypeArg = callLikeExpr.typeArguments?.length ?? 0; for (let i = startTypeArg; i < resolvedTypeArgs.length; ++i) { const typeNode = resolvedTypeArgs[i]; - // if compiler infers 'unknown' type there are 2 possible cases: - // 1. Compiler unable to infer type from arguments and use 'unknown' - // 2. Compiler infer 'unknown' from arguments - // We report error in both cases. It is ok because we cannot use 'unknown' - // in ArkTS and already have separate check for it. - if (typeNode.kind == ts.SyntaxKind.UnknownKeyword) { + + /* + * if compiler infers 'unknown' type there are 2 possible cases: + * 1. Compiler unable to infer type from arguments and use 'unknown' + * 2. Compiler infer 'unknown' from arguments + * We report error in both cases. It is ok because we cannot use 'unknown' + * in ArkTS and already have separate check for it. + */ + if (typeNode.kind === ts.SyntaxKind.UnknownKeyword) { this.incrementCounters(callLikeExpr, FaultID.GenericCallNoTypeArgs); break; } } } - private handleStructIdentAndUndefinedInArgs(tsCallOrNewExpr: ts.CallExpression | ts.NewExpression, callSignature: ts.Signature) { + private handleStructIdentAndUndefinedInArgs( + tsCallOrNewExpr: ts.CallExpression | ts.NewExpression, + callSignature: ts.Signature + ): void { if (!tsCallOrNewExpr.arguments) { return; } for (let argIndex = 0; argIndex < tsCallOrNewExpr.arguments.length; ++argIndex) { - let tsArg = tsCallOrNewExpr.arguments[argIndex]; - let tsArgType = this.tsTypeChecker.getTypeAtLocation(tsArg); + const tsArg = tsCallOrNewExpr.arguments[argIndex]; + const tsArgType = this.tsTypeChecker.getTypeAtLocation(tsArg); if (!tsArgType) { continue; } - let paramIndex = argIndex < callSignature.parameters.length ? argIndex : callSignature.parameters.length-1; - let tsParamSym = callSignature.parameters[paramIndex]; + const paramIndex = argIndex < callSignature.parameters.length ? argIndex : callSignature.parameters.length - 1; + const tsParamSym = callSignature.parameters[paramIndex]; if (!tsParamSym) { continue; } - let tsParamDecl = tsParamSym.valueDeclaration; + const tsParamDecl = tsParamSym.valueDeclaration; if (tsParamDecl && ts.isParameter(tsParamDecl)) { let tsParamType = this.tsTypeChecker.getTypeOfSymbolAtLocation(tsParamSym, tsParamDecl); - if (tsParamDecl.dotDotDotToken && this.tsUtils.isGenericArrayType(tsParamType) && tsParamType.typeArguments) { + if (tsParamDecl.dotDotDotToken && TsUtils.isGenericArrayType(tsParamType) && tsParamType.typeArguments) { tsParamType = tsParamType.typeArguments[0]; } if (!tsParamType) { @@ -1498,112 +1707,122 @@ export class TypeScriptLinter { } } - private checkLimitedStdLib(node: ts.Node, symbol: ts.Symbol) { + private checkLimitedStdLib(node: ts.Node, symbol: ts.Symbol): void { const parName = this.tsUtils.getParentSymbolName(symbol); - const res = parName ? LIMITED_STD_API.get(parName) : undefined; - if (res && res.arr.includes(symbol.name)) { - this.incrementCounters(node, res.fault); + const entries = LIMITED_STD_API.get(parName); + if (!entries) { return; } - const name = this.tsTypeChecker.getFullyQualifiedName(symbol); - if (LIMITED_STD_GLOBAL_API.includes(name)) { - this.incrementCounters(node, FaultID.LimitedStdLibApi) - return; + for (const entry of entries) { + if (entry.api.includes(symbol.name)) { + this.incrementCounters(node, entry.faultId); + return; + } } } - private findNonFilteringRangesFunctionCalls(callExpr: ts.CallExpression): { begin: number, end: number }[] { - let args = callExpr.arguments; - let result: { begin: number, end: number }[] = []; + private static findNonFilteringRangesFunctionCalls(callExpr: ts.CallExpression): { begin: number; end: number }[] { + const args = callExpr.arguments; + const result: { begin: number; end: number }[] = []; for (const arg of args) { if (ts.isArrowFunction(arg)) { - let arrowFuncExpr = arg as ts.ArrowFunction; - result.push({begin: arrowFuncExpr.body.pos, end: arrowFuncExpr.body.end}); + const arrowFuncExpr = arg; + result.push({ begin: arrowFuncExpr.body.pos, end: arrowFuncExpr.body.end }); } else if (ts.isCallExpression(arg)) { - result.push({begin: arg.arguments.pos, end: arg.arguments.end}); + result.push({ begin: arg.arguments.pos, end: arg.arguments.end }); } // there may be other cases } return result; } - private handleLibraryTypeCall(callExpr: ts.CallExpression, calleeType: ts.Type) { - let inLibCall = this.tsUtils.isLibraryType(calleeType); - const diagnosticMessages: Array = [] + private handleLibraryTypeCall(callExpr: ts.CallExpression): void { + const calleeType = this.tsTypeChecker.getTypeAtLocation(callExpr.expression); + const inLibCall = this.tsUtils.isLibraryType(calleeType); + const diagnosticMessages: Array = []; + this.libraryTypeCallDiagnosticChecker.configure(inLibCall, diagnosticMessages); - let nonFilteringRanges = this.findNonFilteringRangesFunctionCalls(callExpr); - let rangesToFilter: { begin: number, end: number }[] = []; + const nonFilteringRanges = TypeScriptLinter.findNonFilteringRangesFunctionCalls(callExpr); + const rangesToFilter: { begin: number; end: number }[] = []; if (nonFilteringRanges.length !== 0) { - let rangesSize = nonFilteringRanges.length; - rangesToFilter.push({ begin: callExpr.pos, end: nonFilteringRanges[0].begin }) - rangesToFilter.push({ begin: nonFilteringRanges[rangesSize - 1].end, end: callExpr.end }) + const rangesSize = nonFilteringRanges.length; + rangesToFilter.push({ begin: callExpr.arguments.pos, end: nonFilteringRanges[0].begin }); + rangesToFilter.push({ begin: nonFilteringRanges[rangesSize - 1].end, end: callExpr.arguments.end }); for (let i = 0; i < rangesSize - 1; i++) { - rangesToFilter.push({ begin: nonFilteringRanges[i].end, end: nonFilteringRanges[i + 1].begin }) + rangesToFilter.push({ begin: nonFilteringRanges[i].end, end: nonFilteringRanges[i + 1].begin }); } } else { - rangesToFilter.push({ begin: callExpr.pos, end: callExpr.end }) + rangesToFilter.push({ begin: callExpr.arguments.pos, end: callExpr.arguments.end }); } - this.filterStrictDiagnostics({ + this.filterStrictDiagnostics( + { [ARGUMENT_OF_TYPE_0_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_ERROR_CODE]: (pos: number) => { - return this.checkInRange(rangesToFilter, pos); + return TypeScriptLinter.checkInRange(rangesToFilter, pos); + }, + [NO_OVERLOAD_MATCHES_THIS_CALL_ERROR_CODE]: (pos: number) => { + return TypeScriptLinter.checkInRange(rangesToFilter, pos); }, [TYPE_0_IS_NOT_ASSIGNABLE_TO_TYPE_1_ERROR_CODE]: (pos: number) => { - return this.checkInRange(rangesToFilter, pos); + return TypeScriptLinter.checkInRange(rangesToFilter, pos); } }, this.libraryTypeCallDiagnosticChecker ); for (const msgChain of diagnosticMessages) { - TypeScriptLinter.filteredDiagnosticMessages.add(msgChain) + TypeScriptLinter.filteredDiagnosticMessages.add(msgChain); } } - private handleNewExpression(node: ts.Node) { - let tsNewExpr = node as ts.NewExpression; + private handleNewExpression(node: ts.Node): void { + const tsNewExpr = node as ts.NewExpression; if (TypeScriptLinter.advancedClassChecks) { - let calleeExpr = tsNewExpr.expression; - let calleeType = this.tsTypeChecker.getTypeAtLocation(calleeExpr); - if (!this.tsUtils.isClassTypeExrepssion(calleeExpr) && !isStdLibraryType(calleeType) && - !this.tsUtils.isLibraryType(calleeType) && !this.tsUtils.hasEsObjectType(calleeExpr)) { + const calleeExpr = tsNewExpr.expression; + const calleeType = this.tsTypeChecker.getTypeAtLocation(calleeExpr); + if ( + !this.tsUtils.isClassTypeExrepssion(calleeExpr) && + !isStdLibraryType(calleeType) && + !this.tsUtils.isLibraryType(calleeType) && + !this.tsUtils.hasEsObjectType(calleeExpr) + ) { // missing exact rule this.incrementCounters(calleeExpr, FaultID.ClassAsObject); } } - let callSignature = this.tsTypeChecker.getResolvedSignature(tsNewExpr); + const callSignature = this.tsTypeChecker.getResolvedSignature(tsNewExpr); if (callSignature !== undefined) { this.handleStructIdentAndUndefinedInArgs(tsNewExpr, callSignature); this.handleGenericCallWithNoTypeArgs(tsNewExpr, callSignature); } } - private handleAsExpression(node: ts.Node) { - let tsAsExpr = node as ts.AsExpression; + private handleAsExpression(node: ts.Node): void { + const tsAsExpr = node as ts.AsExpression; if (tsAsExpr.type.getText() === 'const') { this.incrementCounters(node, FaultID.ConstAssertion); } - let targetType = this.tsTypeChecker.getTypeAtLocation(tsAsExpr.type).getNonNullableType(); - let exprType = this.tsTypeChecker.getTypeAtLocation(tsAsExpr.expression).getNonNullableType(); + const targetType = this.tsTypeChecker.getTypeAtLocation(tsAsExpr.type).getNonNullableType(); + const exprType = this.tsTypeChecker.getTypeAtLocation(tsAsExpr.expression).getNonNullableType(); if (this.tsUtils.needToDeduceStructuralIdentity(targetType, exprType, tsAsExpr.expression, true)) { this.incrementCounters(tsAsExpr, FaultID.StructuralIdentity); } // check for rule#65: 'number as Number' and 'boolean as Boolean' are disabled if ( - ( this.tsUtils.isNumberType(exprType) && targetType.getSymbol()?.getName() === 'Number' ) || - ( this.tsUtils.isBooleanType(exprType) && targetType.getSymbol()?.getName() === 'Boolean' ) + TsUtils.isNumberType(exprType) && targetType.getSymbol()?.getName() === 'Number' || + TsUtils.isBooleanType(exprType) && targetType.getSymbol()?.getName() === 'Boolean' ) { this.incrementCounters(node, FaultID.TypeAssertion); } } - private handleTypeReference(node: ts.Node) { + private handleTypeReference(node: ts.Node): void { const typeRef = node as ts.TypeReferenceNode; - const isESObject = this.tsUtils.isEsObjectType(typeRef); - const isPossiblyValidContext = this.tsUtils.isEsObjectPossiblyAllowed(typeRef); + const isESObject = TsUtils.isEsObjectType(typeRef); + const isPossiblyValidContext = TsUtils.isEsObjectPossiblyAllowed(typeRef); if (isESObject && !isPossiblyValidContext) { this.incrementCounters(node, FaultID.EsObjectType); return; @@ -1622,39 +1841,28 @@ export class TypeScriptLinter { const argType = hasSingleTypeArgument && this.tsTypeChecker.getTypeFromTypeNode(typeRef.typeArguments[0]); if (isStdPartial && argType && !argType.isClassOrInterface()) { this.incrementCounters(node, FaultID.UtilityType); - return; } } - private handleMetaProperty(node: ts.Node) { - let tsMetaProperty = node as ts.MetaProperty; + private handleMetaProperty(node: ts.Node): void { + const tsMetaProperty = node as ts.MetaProperty; if (tsMetaProperty.name.text === 'target') { this.incrementCounters(node, FaultID.NewTarget); } } - private handleStructDeclaration(node: ts.Node) { - // early exit via exception if cancellation was requested - this.cancellationToken?.throwIfCancellationRequested(); + private handleSpreadOp(node: ts.Node): void { - node.forEachChild(child => { - // Skip synthetic constructor in Struct declaration. - if (!ts.isConstructorDeclaration(child)) this.visitTSNode(child); - }); - } - - private handleSpreadOp(node: ts.Node) { - // spread assignment is disabled - // spread element is allowed only for arrays as rest parameter - if( ts.isSpreadElement(node) ) { - let spreadElemNode = node as ts.SpreadElement; - let spreadExprType = this.tsTypeChecker.getTypeAtLocation(spreadElemNode.expression); + /* + * spread assignment is disabled + * spread element is allowed only for arrays as rest parameter + */ + if (ts.isSpreadElement(node)) { + const spreadExprType = this.tsTypeChecker.getTypeAtLocation(node.expression); if (spreadExprType) { - const spreadExprTypeNode = this.tsTypeChecker.typeToTypeNode(spreadExprType, undefined, ts.NodeBuilderFlags.None); - if (spreadExprTypeNode !== undefined && (ts.isCallLikeExpression(node.parent) || ts.isArrayLiteralExpression(node.parent))) { - if (ts.isArrayTypeNode(spreadExprTypeNode) || this.tsUtils.isTypedArray(spreadExprTypeNode) || - this.tsUtils.isDerivedFrom(spreadExprType, CheckType.Array)) { - return + if (ts.isCallLikeExpression(node.parent) || ts.isArrayLiteralExpression(node.parent)) { + if (this.tsUtils.isOrDerivedFrom(spreadExprType, this.tsUtils.isArray)) { + return; } } } @@ -1662,7 +1870,7 @@ export class TypeScriptLinter { this.incrementCounters(node, FaultID.SpreadOperator); } - private handleConstructSignature(node: ts.Node) { + private handleConstructSignature(node: ts.Node): void { switch (node.parent.kind) { case ts.SyntaxKind.TypeLiteral: this.incrementCounters(node, FaultID.ConstructorType); @@ -1671,61 +1879,31 @@ export class TypeScriptLinter { this.incrementCounters(node, FaultID.ConstructorIface); break; default: - return; - } - } - - private handleComments(node: ts.Node) { - // Note: Same comment may be owned by several nodes if their - // start/end position matches. Thus, look for the most parental - // owner of the specific comment (by the node's position). - const srcText = node.getSourceFile().getFullText(); - const parent = node.parent; - if (!parent || parent.getFullStart() !== node.getFullStart()) { - let leadingComments = ts.getLeadingCommentRanges(srcText, node.getFullStart()); - if (leadingComments) { - for (const comment of leadingComments) { - // In the real-time linter comment from the first line is double proccessed. - // It may be caused by tsc, but it should be investigated. This is a workaround - if (!this.walkedComments.has(comment.pos) && comment.pos !== comment.end) { - this.walkedComments.add(comment.pos); - this.checkErrorSuppressingAnnotation(comment, srcText); - } - } - } - } - if (!parent || parent.getEnd() !== node.getEnd()) { - let trailingComments = ts.getTrailingCommentRanges(srcText, node.getEnd()); - if (trailingComments) { - for (const comment of trailingComments) { - // In the real-time linter comment from the first line is double proccessed. - // It may be caused by tsc, but it should be investigated. This is a workaround - if (!this.walkedComments.has(comment.pos) && comment.pos !== comment.end) { - this.walkedComments.add(comment.pos); - this.checkErrorSuppressingAnnotation(comment, srcText); - } - } - } } } - private handleExpressionWithTypeArguments(node: ts.Node) { - let tsTypeExpr = node as ts.ExpressionWithTypeArguments; - let symbol = this.tsUtils.trueSymbolAtLocation(tsTypeExpr.expression); - if (!!symbol && this.tsUtils.isEsObjectSymbol(symbol)) { + private handleExpressionWithTypeArguments(node: ts.Node): void { + const tsTypeExpr = node as ts.ExpressionWithTypeArguments; + const symbol = this.tsUtils.trueSymbolAtLocation(tsTypeExpr.expression); + if (!!symbol && TsUtils.isEsObjectSymbol(symbol)) { this.incrementCounters(tsTypeExpr, FaultID.EsObjectType); } } - private checkErrorSuppressingAnnotation(comment: ts.CommentRange, srcText: string) { - const commentContent = comment.kind === ts.SyntaxKind.MultiLineCommentTrivia - ? srcText.slice(comment.pos + 2, comment.end - 2) - : srcText.slice(comment.pos + 2, comment.end); - const trimmedContent = commentContent.trim() - if (trimmedContent.startsWith('@ts-ignore') || - trimmedContent.startsWith('@ts-nocheck') || - trimmedContent.startsWith('@ts-expect-error')) - this.incrementCounters(comment, FaultID.ErrorSuppression); + private handleComputedPropertyName(node: ts.Node): void { + const computedProperty = node as ts.ComputedPropertyName; + const symbol = this.tsUtils.trueSymbolAtLocation(computedProperty.expression); + if (!!symbol && this.tsUtils.isSymbolIterator(symbol)) { + return; + } + const isEnumMember = !!symbol && !!(symbol.flags & ts.SymbolFlags.EnumMember); + const type = this.tsTypeChecker.getTypeAtLocation(computedProperty.expression); + const isStringEnumLiteral = TsUtils.isEnumType(type) && !!(type.flags & ts.TypeFlags.StringLiteral); + // we allow computed property names if exporession is string Enum member + if (isEnumMember && isStringEnumLiteral) { + return; + } + this.incrementCounters(node, FaultID.ComputedPropertyName); } private handleDecorators(decorators: readonly ts.Decorator[] | undefined): void { @@ -1745,26 +1923,29 @@ export class TypeScriptLinter { } } - private handleGetAccessor(node: ts.Node) { + private handleGetAccessor(node: ts.Node): void { this.handleDecorators(ts.getDecorators(node as ts.GetAccessorDeclaration)); } - private handleSetAccessor(node: ts.Node) { + private handleSetAccessor(node: ts.Node): void { this.handleDecorators(ts.getDecorators(node as ts.SetAccessorDeclaration)); } private handleDeclarationInferredType( decl: ts.VariableDeclaration | ts.PropertyDeclaration | ts.ParameterDeclaration - ) { + ): void { // The type is explicitly specified, no need to check inferred type. if (decl.type) { return; } - // issue 13161: - // In TypeScript, the catch clause variable must be 'any' or 'unknown' type. Since - // ArkTS doesn't support these types, the type for such variable is simply omitted, - // and we don't report it as an error. See TypeScriptLinter.handleCatchClause() - // for reference. + + /* + * issue 13161: + * In TypeScript, the catch clause variable must be 'any' or 'unknown' type. Since + * ArkTS doesn't support these types, the type for such variable is simply omitted, + * and we don't report it as an error. See TypeScriptLinter.handleCatchClause() + * for reference. + */ if (ts.isCatchClause(decl.parent)) { return; } @@ -1773,14 +1954,19 @@ export class TypeScriptLinter { return; } - // issue 13987: - // When variable have no type annotation and no initial value, and 'noImplicitAny' - // option is enabled, compiler attempts to infer type from variable references: - // see https://github.com/microsoft/TypeScript/pull/11263. - // In this case, we still want to report the error, since ArkTS doesn't allow - // to omit both type annotation and initializer. - if (((ts.isVariableDeclaration(decl) && ts.isVariableStatement(decl.parent.parent)) || ts.isPropertyDeclaration(decl)) - && !decl.initializer) { + /* + * issue 13987: + * When variable have no type annotation and no initial value, and 'noImplicitAny' + * option is enabled, compiler attempts to infer type from variable references: + * see https://github.com/microsoft/TypeScript/pull/11263. + * In this case, we still want to report the error, since ArkTS doesn't allow + * to omit both type annotation and initializer. + */ + if ( + (ts.isVariableDeclaration(decl) && ts.isVariableStatement(decl.parent.parent) || + ts.isPropertyDeclaration(decl)) && + !decl.initializer + ) { this.incrementCounters(decl, FaultID.AnyType); return; } @@ -1791,20 +1977,19 @@ export class TypeScriptLinter { } } - private handleDefiniteAssignmentAssertion(decl: ts.VariableDeclaration | ts.PropertyDeclaration) { + private handleDefiniteAssignmentAssertion(decl: ts.VariableDeclaration | ts.PropertyDeclaration): void { if (decl.exclamationToken !== undefined) { this.incrementCounters(decl, FaultID.DefiniteAssignment); } } - private validatedTypesSet = new Set(); + private readonly validatedTypesSet = new Set(); private checkAnyOrUnknownChildNode(node: ts.Node): boolean { - if (node.kind === ts.SyntaxKind.AnyKeyword || - node.kind === ts.SyntaxKind.UnknownKeyword) { + if (node.kind === ts.SyntaxKind.AnyKeyword || node.kind === ts.SyntaxKind.UnknownKeyword) { return true; } - for (let child of node.getChildren()) { + for (const child of node.getChildren()) { if (this.checkAnyOrUnknownChildNode(child)) { return true; } @@ -1815,7 +2000,7 @@ export class TypeScriptLinter { private handleInferredObjectreference( type: ts.Type, decl: ts.VariableDeclaration | ts.PropertyDeclaration | ts.ParameterDeclaration - ) { + ): void { const typeArgs = this.tsTypeChecker.getTypeArguments(type as ts.TypeReference); if (typeArgs) { const haveAnyOrUnknownNodes = this.checkAnyOrUnknownChildNode(decl); @@ -1831,12 +2016,10 @@ export class TypeScriptLinter { type: ts.Type, decl: ts.VariableDeclaration | ts.PropertyDeclaration | ts.ParameterDeclaration ): void { - if (type.aliasSymbol != undefined) { + if (type.aliasSymbol !== undefined) { return; } - const isObject = type.flags & ts.TypeFlags.Object; - const isReference = (type as ts.ObjectType).objectFlags & ts.ObjectFlags.Reference; - if (isObject && isReference) { + if (TsUtils.isObjectType(type) && !!(type.objectFlags & ts.ObjectFlags.Reference)) { this.handleInferredObjectreference(type, decl); return; } @@ -1845,20 +2028,93 @@ export class TypeScriptLinter { } if (type.isUnion()) { this.validatedTypesSet.add(type); - for (let unionElem of type.types) { - this.validateDeclInferredType(unionElem, decl) + for (const unionElem of type.types) { + this.validateDeclInferredType(unionElem, decl); } } - if (this.tsUtils.isAnyType(type)) { + if (TsUtils.isAnyType(type)) { this.incrementCounters(decl, FaultID.AnyType); - } else if (this.tsUtils.isUnknownType(type)) { + } else if (TsUtils.isUnknownType(type)) { this.incrementCounters(decl, FaultID.UnknownType); } } - public lint(sourceFile: ts.SourceFile) { + private handleCommentDirectives(sourceFile: ts.SourceFile): void { + + /* + * We use a dirty hack to retrieve list of parsed comment directives by accessing + * internal properties of SourceFile node. + */ + + // Handle comment directive '@ts-nocheck' + const pragmas = (sourceFile as any).pragmas; + if (pragmas && pragmas instanceof Map) { + const noCheckPragma = pragmas.get('ts-nocheck'); + if (noCheckPragma) { + + /* + * The value is either a single entry or an array of entries. + * Wrap up single entry with array to simplify processing. + */ + const noCheckEntries: any[] = Array.isArray(noCheckPragma) ? noCheckPragma : [noCheckPragma]; + for (const entry of noCheckEntries) { + this.processNoCheckEntry(entry); + } + } + } + + // Handle comment directives '@ts-ignore' and '@ts-expect-error' + const commentDirectives = (sourceFile as any).commentDirectives; + if (commentDirectives && Array.isArray(commentDirectives)) { + for (const directive of commentDirectives) { + if (directive.range?.pos === undefined || directive.range?.end === undefined) { + continue; + } + + const range = directive.range as ts.TextRange; + const commentOpen = '/*'; + const kind: ts.SyntaxKind = + sourceFile.text.slice(range.pos, range.pos + commentOpen.length) === commentOpen ? + ts.SyntaxKind.MultiLineCommentTrivia : + ts.SyntaxKind.SingleLineCommentTrivia; + const commentRange: ts.CommentRange = { + pos: range.pos, + end: range.end, + kind + }; + + this.incrementCounters(commentRange, FaultID.ErrorSuppression); + } + } + } + + private processNoCheckEntry(entry: any): void { + if (entry.range?.kind === undefined || entry.range?.pos === undefined || entry.range?.end === undefined) { + return; + } + + this.incrementCounters(entry.range as ts.CommentRange, FaultID.ErrorSuppression); + } + + private reportThisKeywordsInScope(scope: ts.Block | ts.Expression): void { + const callback = (node: ts.Node): void => { + if (node.kind === ts.SyntaxKind.ThisKeyword) { + this.incrementCounters(node, FaultID.FunctionContainsThis); + } + }; + const stopCondition = (node: ts.Node): boolean => { + const isClassLike = ts.isClassDeclaration(node) || ts.isClassExpression(node); + const isFunctionLike = ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node); + const isModuleDecl = ts.isModuleDeclaration(node); + return isClassLike || isFunctionLike || isModuleDecl; + }; + forEachNodeInSubtree(scope, callback, stopCondition); + } + + lint(sourceFile: ts.SourceFile): void { this.walkedComments.clear(); this.sourceFile = sourceFile; - this.visitTSNode(this.sourceFile); + this.visitSourceFile(this.sourceFile); + this.handleCommentDirectives(this.sourceFile); } } diff --git a/ets2panda/linter/src/TypeScriptLinterConfig.ts b/ets2panda/linter/lib/TypeScriptLinterConfig.ts similarity index 34% rename from ets2panda/linter/src/TypeScriptLinterConfig.ts rename to ets2panda/linter/lib/TypeScriptLinterConfig.ts index 58f32df628769245048013901d2624b994e3946b..a153d9f6828ec30d12144fbc1e331eee820b0a0e 100644 --- a/ets2panda/linter/src/TypeScriptLinterConfig.ts +++ b/ets2panda/linter/lib/TypeScriptLinterConfig.ts @@ -14,17 +14,20 @@ */ import * as ts from 'typescript'; -import { FaultID } from './utils/consts/Problems'; +import { FaultID } from './Problems'; export class LinterConfig { - // The SyntaxKind enum defines additional elements at the end of the enum - // that serve as markers (FirstX/LastX). Those elements are initialized - // with indices of the previously defined elements. As result, the enum - // may return incorrect name for a certain kind index (e.g. 'FirstStatement' - // instead of 'VariableStatement'). - // The following code creates a map with correct syntax kind names. - // It can be used when need to print name of syntax kind of certain - // AST node in diagnostic messages. + + /* + * The SyntaxKind enum defines additional elements at the end of the enum + * that serve as markers (FirstX/LastX). Those elements are initialized + * with indices of the previously defined elements. As result, the enum + * may return incorrect name for a certain kind index (e.g. 'FirstStatement' + * instead of 'VariableStatement'). + * The following code creates a map with correct syntax kind names. + * It can be used when need to print name of syntax kind of certain + * AST node in diagnostic messages. + */ static tsSyntaxKindNames: string[] = []; static { @@ -46,48 +49,96 @@ export class LinterConfig { // must detect terminals during parsing static terminalTokens: Set = new Set([ - ts.SyntaxKind.OpenBraceToken, ts.SyntaxKind.CloseBraceToken, ts.SyntaxKind.OpenParenToken, - ts.SyntaxKind.CloseParenToken, ts.SyntaxKind.OpenBracketToken, ts.SyntaxKind.CloseBracketToken, - ts.SyntaxKind.DotToken, ts.SyntaxKind.DotDotDotToken, ts.SyntaxKind.SemicolonToken, ts.SyntaxKind.CommaToken, - ts.SyntaxKind.QuestionDotToken, ts.SyntaxKind.LessThanToken, ts.SyntaxKind.LessThanSlashToken, - ts.SyntaxKind.GreaterThanToken, ts.SyntaxKind.LessThanEqualsToken, ts.SyntaxKind.GreaterThanEqualsToken, - ts.SyntaxKind.EqualsEqualsToken, ts.SyntaxKind.ExclamationEqualsToken, ts.SyntaxKind.EqualsEqualsEqualsToken, - ts.SyntaxKind.ExclamationEqualsEqualsToken, ts.SyntaxKind.EqualsGreaterThanToken, ts.SyntaxKind.PlusToken, - ts.SyntaxKind.MinusToken, ts.SyntaxKind.AsteriskToken, ts.SyntaxKind.AsteriskAsteriskToken, - ts.SyntaxKind.SlashToken, ts.SyntaxKind.PercentToken, ts.SyntaxKind.PlusPlusToken, ts.SyntaxKind.MinusMinusToken, - ts.SyntaxKind.LessThanLessThanToken, ts.SyntaxKind.GreaterThanGreaterThanToken, - ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken, ts.SyntaxKind.AmpersandToken, ts.SyntaxKind.BarToken, - ts.SyntaxKind.CaretToken, ts.SyntaxKind.ExclamationToken, ts.SyntaxKind.TildeToken, - ts.SyntaxKind.AmpersandAmpersandToken, ts.SyntaxKind.BarBarToken, ts.SyntaxKind.QuestionQuestionToken, - ts.SyntaxKind.QuestionToken, ts.SyntaxKind.ColonToken, ts.SyntaxKind.AtToken, ts.SyntaxKind.BacktickToken, - ts.SyntaxKind.HashToken, ts.SyntaxKind.EqualsToken, ts.SyntaxKind.PlusEqualsToken, ts.SyntaxKind.MinusEqualsToken, - ts.SyntaxKind.AsteriskEqualsToken, ts.SyntaxKind.AsteriskAsteriskEqualsToken, ts.SyntaxKind.SlashEqualsToken, - ts.SyntaxKind.PercentEqualsToken, ts.SyntaxKind.LessThanLessThanEqualsToken, - ts.SyntaxKind.GreaterThanGreaterThanEqualsToken, ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken, - ts.SyntaxKind.AmpersandEqualsToken, ts.SyntaxKind.BarEqualsToken, ts.SyntaxKind.CaretEqualsToken, - ts.SyntaxKind.EndOfFileToken, ts.SyntaxKind.SingleLineCommentTrivia, - ts.SyntaxKind.MultiLineCommentTrivia, ts.SyntaxKind.NewLineTrivia, ts.SyntaxKind.WhitespaceTrivia, - ts.SyntaxKind.ShebangTrivia, /* We detect and preserve #! on the first line */ ts.SyntaxKind.ConflictMarkerTrivia, + ts.SyntaxKind.OpenBraceToken, + ts.SyntaxKind.CloseBraceToken, + ts.SyntaxKind.OpenParenToken, + ts.SyntaxKind.CloseParenToken, + ts.SyntaxKind.OpenBracketToken, + ts.SyntaxKind.CloseBracketToken, + ts.SyntaxKind.DotToken, + ts.SyntaxKind.DotDotDotToken, + ts.SyntaxKind.SemicolonToken, + ts.SyntaxKind.CommaToken, + ts.SyntaxKind.QuestionDotToken, + ts.SyntaxKind.LessThanToken, + ts.SyntaxKind.LessThanSlashToken, + ts.SyntaxKind.GreaterThanToken, + ts.SyntaxKind.LessThanEqualsToken, + ts.SyntaxKind.GreaterThanEqualsToken, + ts.SyntaxKind.EqualsEqualsToken, + ts.SyntaxKind.ExclamationEqualsToken, + ts.SyntaxKind.EqualsEqualsEqualsToken, + ts.SyntaxKind.ExclamationEqualsEqualsToken, + ts.SyntaxKind.EqualsGreaterThanToken, + ts.SyntaxKind.PlusToken, + ts.SyntaxKind.MinusToken, + ts.SyntaxKind.AsteriskToken, + ts.SyntaxKind.AsteriskAsteriskToken, + ts.SyntaxKind.SlashToken, + ts.SyntaxKind.PercentToken, + ts.SyntaxKind.PlusPlusToken, + ts.SyntaxKind.MinusMinusToken, + ts.SyntaxKind.LessThanLessThanToken, + ts.SyntaxKind.GreaterThanGreaterThanToken, + ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken, + ts.SyntaxKind.AmpersandToken, + ts.SyntaxKind.BarToken, + ts.SyntaxKind.CaretToken, + ts.SyntaxKind.ExclamationToken, + ts.SyntaxKind.TildeToken, + ts.SyntaxKind.AmpersandAmpersandToken, + ts.SyntaxKind.BarBarToken, + ts.SyntaxKind.QuestionQuestionToken, + ts.SyntaxKind.QuestionToken, + ts.SyntaxKind.ColonToken, + ts.SyntaxKind.AtToken, + ts.SyntaxKind.BacktickToken, + ts.SyntaxKind.HashToken, + ts.SyntaxKind.EqualsToken, + ts.SyntaxKind.PlusEqualsToken, + ts.SyntaxKind.MinusEqualsToken, + ts.SyntaxKind.AsteriskEqualsToken, + ts.SyntaxKind.AsteriskAsteriskEqualsToken, + ts.SyntaxKind.SlashEqualsToken, + ts.SyntaxKind.PercentEqualsToken, + ts.SyntaxKind.LessThanLessThanEqualsToken, + ts.SyntaxKind.GreaterThanGreaterThanEqualsToken, + ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken, + ts.SyntaxKind.AmpersandEqualsToken, + ts.SyntaxKind.BarEqualsToken, + ts.SyntaxKind.CaretEqualsToken, + ts.SyntaxKind.EndOfFileToken, + ts.SyntaxKind.SingleLineCommentTrivia, + ts.SyntaxKind.MultiLineCommentTrivia, + ts.SyntaxKind.NewLineTrivia, + ts.SyntaxKind.WhitespaceTrivia, + ts.SyntaxKind.ShebangTrivia, + /* We detect and preserve #! on the first line */ ts.SyntaxKind.ConflictMarkerTrivia ]); // tokens which can be reported without additional parsing static incrementOnlyTokens: Map = new Map([ - [ts.SyntaxKind.AnyKeyword, FaultID.AnyType], [ts.SyntaxKind.SymbolKeyword, FaultID.SymbolType], + [ts.SyntaxKind.AnyKeyword, FaultID.AnyType], + [ts.SyntaxKind.SymbolKeyword, FaultID.SymbolType], [ts.SyntaxKind.ThisType, FaultID.ThisType], - [ts.SyntaxKind.ComputedPropertyName, FaultID.ComputedPropertyName], [ts.SyntaxKind.TypeQuery, FaultID.TypeQuery], [ts.SyntaxKind.DeleteExpression, FaultID.DeleteOperator], - [ts.SyntaxKind.RegularExpressionLiteral, FaultID.RegexLiteral], - [ts.SyntaxKind.TypePredicate, FaultID.IsOperator], [ts.SyntaxKind.YieldExpression, FaultID.YieldExpression], - [ts.SyntaxKind.IndexSignature, FaultID.IndexMember], [ts.SyntaxKind.WithStatement, FaultID.WithStatement], - [ts.SyntaxKind.IndexedAccessType, FaultID.IndexedAccessType], [ts.SyntaxKind.UnknownKeyword, FaultID.UnknownType], - [ts.SyntaxKind.InKeyword, FaultID.InOperator], [ts.SyntaxKind.CallSignature, FaultID.CallSignature], + [ts.SyntaxKind.TypePredicate, FaultID.IsOperator], + [ts.SyntaxKind.YieldExpression, FaultID.YieldExpression], + [ts.SyntaxKind.IndexSignature, FaultID.IndexMember], + [ts.SyntaxKind.WithStatement, FaultID.WithStatement], + [ts.SyntaxKind.IndexedAccessType, FaultID.IndexedAccessType], + [ts.SyntaxKind.UnknownKeyword, FaultID.UnknownType], + [ts.SyntaxKind.CallSignature, FaultID.CallSignature], [ts.SyntaxKind.IntersectionType, FaultID.IntersectionType], - [ts.SyntaxKind.TypeLiteral, FaultID.ObjectTypeLiteral], [ts.SyntaxKind.ConstructorType, FaultID.ConstructorFuncs], + [ts.SyntaxKind.TypeLiteral, FaultID.ObjectTypeLiteral], + [ts.SyntaxKind.ConstructorType, FaultID.ConstructorFuncs], [ts.SyntaxKind.PrivateIdentifier, FaultID.PrivateIdentifier], - [ts.SyntaxKind.ConditionalType, FaultID.ConditionalType], [ts.SyntaxKind.MappedType, FaultID.MappedType], - [ts.SyntaxKind.JsxElement, FaultID.JsxElement], [ts.SyntaxKind.JsxSelfClosingElement, FaultID.JsxElement], + [ts.SyntaxKind.ConditionalType, FaultID.ConditionalType], + [ts.SyntaxKind.MappedType, FaultID.MappedType], + [ts.SyntaxKind.JsxElement, FaultID.JsxElement], + [ts.SyntaxKind.JsxSelfClosingElement, FaultID.JsxElement], [ts.SyntaxKind.ImportEqualsDeclaration, FaultID.ImportAssignment], - [ts.SyntaxKind.NamespaceExportDeclaration, FaultID.UMDModuleDefinition], + [ts.SyntaxKind.NamespaceExportDeclaration, FaultID.UMDModuleDefinition] ]); } diff --git a/ets2panda/linter/src/autofixes/AutofixInfo.ts b/ets2panda/linter/lib/autofixes/AutofixInfo.ts similarity index 100% rename from ets2panda/linter/src/autofixes/AutofixInfo.ts rename to ets2panda/linter/lib/autofixes/AutofixInfo.ts diff --git a/ets2panda/linter/src/autofixes/AutofixTitles.ts b/ets2panda/linter/lib/autofixes/AutofixTitles.ts similarity index 74% rename from ets2panda/linter/src/autofixes/AutofixTitles.ts rename to ets2panda/linter/lib/autofixes/AutofixTitles.ts index 89bad0381fe8112ffdb123e32e89b96142010523..91b7ff1f02e3bd961088772b450e8096f8f71685 100644 --- a/ets2panda/linter/src/autofixes/AutofixTitles.ts +++ b/ets2panda/linter/lib/autofixes/AutofixTitles.ts @@ -1,17 +1,17 @@ -/* - * Copyright (c) 2022-2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* + * Copyright (c) 2022-2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ // generated from recipes.rst export const cookBookRefToFixTitle: Map = new Map([ @@ -24,5 +24,5 @@ export const cookBookRefToFixTitle: Map = new Map([ [90, 'Annotate return type'], [118, 'Replace with ordinary import'], [120, 'Replace with explicit import'], - [127, 'Replace with ordinary export'], + [127, 'Replace with ordinary export'] ]); diff --git a/ets2panda/linter/src/autofixes/ReportAutofixCallback.ts b/ets2panda/linter/lib/autofixes/ReportAutofixCallback.ts similarity index 50% rename from ets2panda/linter/src/autofixes/ReportAutofixCallback.ts rename to ets2panda/linter/lib/autofixes/ReportAutofixCallback.ts index 0338538bcbacae5af05b6edb5878a0b41122c2d0..62e03bff42bbc8263f2bd82e94b5354ddbc249e5 100644 --- a/ets2panda/linter/src/autofixes/ReportAutofixCallback.ts +++ b/ets2panda/linter/lib/autofixes/ReportAutofixCallback.ts @@ -1,24 +1,18 @@ -/* - * Copyright (c) 2022-2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* + * Copyright (c) 2022-2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ -import { ProblemInfo } from '../ProblemInfo'; +import type { ProblemInfo } from '../ProblemInfo'; export type ReportAutofixCallback = (p: ProblemInfo) => void; - -export function getDefaultCallback(withAutofixes: ProblemInfo[]): ReportAutofixCallback { - return (p: ProblemInfo) => { - withAutofixes.push(p); - }; -} diff --git a/ets2panda/linter/src/ts-diagnostics/GetTscDiagnostics.ts b/ets2panda/linter/lib/ts-diagnostics/GetTscDiagnostics.ts similarity index 87% rename from ets2panda/linter/src/ts-diagnostics/GetTscDiagnostics.ts rename to ets2panda/linter/lib/ts-diagnostics/GetTscDiagnostics.ts index 3b75a69aa48a899d9c67fbab01af16349f8960ed..4c7d640ff4f730f9709bcf1534e0a815f3ca9f0d 100644 --- a/ets2panda/linter/src/ts-diagnostics/GetTscDiagnostics.ts +++ b/ets2panda/linter/lib/ts-diagnostics/GetTscDiagnostics.ts @@ -13,9 +13,9 @@ * limitations under the License. */ -import * as ts from 'typescript'; +import type * as ts from 'typescript'; import * as path from 'node:path'; -import { TSCCompiledProgram } from './TSCCompiledProgram'; +import type { TSCCompiledProgram } from './TSCCompiledProgram'; /** * Extracts TSC diagnostics emitted by strict checks. @@ -26,12 +26,12 @@ import { TSCCompiledProgram } from './TSCCompiledProgram'; */ export function getTscDiagnostics( tscDiagnosticsLinter: TSCCompiledProgram, - sourceFiles: ts.SourceFile[], + sourceFiles: ts.SourceFile[] ): Map { const strictDiagnostics = new Map(); - sourceFiles.forEach(file => { + sourceFiles.forEach((file) => { const diagnostics = tscDiagnosticsLinter.getStrictDiagnostics(file.fileName); - if (diagnostics.length != 0) { + if (diagnostics.length > 0) { strictDiagnostics.set(path.normalize(file.fileName), diagnostics); } }); diff --git a/ets2panda/linter/src/ts-diagnostics/TSCCompiledProgram.ts b/ets2panda/linter/lib/ts-diagnostics/TSCCompiledProgram.ts similarity index 42% rename from ets2panda/linter/src/ts-diagnostics/TSCCompiledProgram.ts rename to ets2panda/linter/lib/ts-diagnostics/TSCCompiledProgram.ts index 36faaf7c35e1906207c214a9a0c7958dbc002593..7dd10de28e716389a33c6cba257709d4bd92b9a7 100644 --- a/ets2panda/linter/src/ts-diagnostics/TSCCompiledProgram.ts +++ b/ets2panda/linter/lib/ts-diagnostics/TSCCompiledProgram.ts @@ -14,93 +14,87 @@ */ import * as ts from 'typescript'; -import { ProblemInfo } from '../ProblemInfo'; +import type { ProblemInfo } from '../ProblemInfo'; import { ProblemSeverity } from '../ProblemSeverity'; -import { LintOptions } from '../LintOptions'; -import { TypeScriptDiagnosticsExtractor } from './TypeScriptDiagnosticsExtractor'; -import { compile } from '../CompilerWrapper'; -import { FaultID } from '../utils/consts/Problems'; +import { getStrictDiagnostics } from './TypeScriptDiagnosticsExtractor'; +import { FaultID } from '../Problems'; import { faultsAttrs } from '../FaultAttrs'; export interface TSCCompiledProgram { - getOriginalProgram(): ts.Program; - getStrictDiagnostics(fileName: string): ts.Diagnostic[]; + getProgram: () => ts.Program; + getStrictDiagnostics: (fileName: string) => ts.Diagnostic[]; } export class TSCCompiledProgramSimple implements TSCCompiledProgram { - private tsProgram: ts.Program; + private readonly program: ts.Program; constructor(program: ts.Program) { - this.tsProgram = program; + this.program = program; } - public getOriginalProgram(): ts.Program { - return this.tsProgram; + getProgram(): ts.Program { + return this.program; } - public getStrictDiagnostics(fileName: string): ts.Diagnostic[] { + getStrictDiagnostics(fileName: string): ts.Diagnostic[] { + void this; + void fileName; return []; } } -export class TSCCompiledProgramWithDiagnostics implements TSCCompiledProgram { - private diagnosticsExtractor: TypeScriptDiagnosticsExtractor; - private wasStrict: boolean; - - constructor(program: ts.Program, options: LintOptions) { - const { strict, nonStrict, wasStrict } = getTwoCompiledVersions(program, options); - this.diagnosticsExtractor = new TypeScriptDiagnosticsExtractor(strict, nonStrict); - this.wasStrict = wasStrict; - } +export type ProgressCallback = (message: string) => void; - public getOriginalProgram(): ts.Program { - return this.wasStrict ? this.diagnosticsExtractor.strictProgram : this.diagnosticsExtractor.nonStrictProgram; +export class TSCCompiledProgramWithDiagnostics implements TSCCompiledProgram { + private readonly program: ts.Program; + private readonly cachedDiagnostics: Map = new Map(); + + constructor( + strict: ts.Program, + nonStrict: ts.Program, + inputFiles: string[], + cancellationToken?: ts.CancellationToken, + progressCb?: ProgressCallback + ) { + this.program = strict; + + inputFiles.forEach((fileName) => { + progressCb?.(fileName); + + const sourceFile = this.program.getSourceFile(fileName); + if (sourceFile !== undefined) { + this.cachedDiagnostics.set( + sourceFile.fileName, + getStrictDiagnostics(strict, nonStrict, sourceFile.fileName, cancellationToken) + ); + } + }); } - public getStrictDiagnostics(fileName: string): ts.Diagnostic[] { - return this.diagnosticsExtractor.getStrictDiagnostics(fileName); + static getOverrideCompilerOptions(strict: boolean): ts.CompilerOptions { + return { + strict: false, + alwaysStrict: false, + noImplicitAny: false, + noImplicitThis: false, + strictBindCallApply: false, + useUnknownInCatchVariables: false, + strictNullChecks: strict, + strictFunctionTypes: strict, + strictPropertyInitialization: strict, + noImplicitReturns: strict + }; } -} -export function getStrictOptions(strict: boolean = true) { - return { - strictNullChecks: strict, - strictFunctionTypes: strict, - strictPropertyInitialization: strict, - noImplicitReturns: strict, + getProgram(): ts.Program { + return this.program; } -} - -function getTwoCompiledVersions( - program: ts.Program, - options: LintOptions, -): { strict: ts.Program; nonStrict: ts.Program; wasStrict: boolean } { - const compilerOptions = { ...program.getCompilerOptions()}; - const wasStrict = inverseStrictOptions(compilerOptions); - const inversedOptions = getStrictOptions(!wasStrict); - const withInversedOptions = compile(options, inversedOptions); - - return { - strict: wasStrict ? program : withInversedOptions, - nonStrict: wasStrict ? withInversedOptions : program, - wasStrict: wasStrict, + getStrictDiagnostics(fileName: string): ts.Diagnostic[] { + return this.cachedDiagnostics.get(fileName) ?? []; } } -/** - * Returns true if options were initially strict - */ -function inverseStrictOptions(compilerOptions: ts.CompilerOptions): boolean { - const strictOptions = getStrictOptions(); - let wasStrict = false; - Object.keys(strictOptions).forEach(x => { - wasStrict = wasStrict || !!compilerOptions[x]; - }); - // wasStrict evaluates true if any of the strict options was set - return wasStrict; -} - export function transformDiagnostic(diagnostic: ts.Diagnostic): ProblemInfo { const startPos = diagnostic.start!; const start = getLineAndColumn(diagnostic.file!, startPos); @@ -117,12 +111,13 @@ export function transformDiagnostic(diagnostic: ts.Diagnostic): ProblemInfo { start: startPos, end: endPos, type: 'StrictModeError', - severity: ProblemSeverity.ERROR, // expect strict options to always present + // expect strict options to always present + severity: ProblemSeverity.ERROR, problem: FaultID[faultId], suggest: messageText, rule: messageText, - ruleTag: faultsAttrs[faultId] ? Number(faultsAttrs[faultId].cookBookRef) : 0, - autofixable: false, + ruleTag: faultsAttrs[faultId] ? faultsAttrs[faultId].cookBookRef : 0, + autofixable: false }; } @@ -130,10 +125,10 @@ export function transformDiagnostic(diagnostic: ts.Diagnostic): ProblemInfo { * Returns line and column of the position, counts from 1 */ function getLineAndColumn(file: ts.SourceFile, position: number): { line: number; column: number } { - let { line, character } = file.getLineAndCharacterOfPosition(position); + const { line, character } = file.getLineAndCharacterOfPosition(position); // TSC counts lines and columns from zero return { line: line + 1, - column: character + 1, - } + column: character + 1 + }; } diff --git a/ets2panda/linter/src/ts-diagnostics/TransformTscDiagnostics.ts b/ets2panda/linter/lib/ts-diagnostics/TransformTscDiagnostics.ts similarity index 66% rename from ets2panda/linter/src/ts-diagnostics/TransformTscDiagnostics.ts rename to ets2panda/linter/lib/ts-diagnostics/TransformTscDiagnostics.ts index 9c644117e5455d062eb5960112039987fea1f106..718d3e5f0b4c9baefdb8623fbc473033a509d5fe 100644 --- a/ets2panda/linter/src/ts-diagnostics/TransformTscDiagnostics.ts +++ b/ets2panda/linter/lib/ts-diagnostics/TransformTscDiagnostics.ts @@ -13,16 +13,19 @@ * limitations under the License. */ -import * as ts from 'typescript'; -import { ProblemInfo } from '../ProblemInfo'; +import type * as ts from 'typescript'; +import type { ProblemInfo } from '../ProblemInfo'; import { transformDiagnostic } from './TSCCompiledProgram'; -export function transformTscDiagnostics( - strictDiagnostics: Map -): Map { +export function transformTscDiagnostics(strictDiagnostics: Map): Map { const problemsInfos = new Map(); - strictDiagnostics.forEach((diagnostics, file, map) => { - problemsInfos.set(file, diagnostics.map(x => transformDiagnostic(x))); + strictDiagnostics.forEach((diagnostics, file) => { + problemsInfos.set( + file, + diagnostics.map((x) => { + return transformDiagnostic(x); + }) + ); }); return problemsInfos; } diff --git a/ets2panda/linter/src/ts-diagnostics/TypeScriptDiagnosticsExtractor.ts b/ets2panda/linter/lib/ts-diagnostics/TypeScriptDiagnosticsExtractor.ts similarity index 41% rename from ets2panda/linter/src/ts-diagnostics/TypeScriptDiagnosticsExtractor.ts rename to ets2panda/linter/lib/ts-diagnostics/TypeScriptDiagnosticsExtractor.ts index 0d5106dd9f116531b356ca406fc9f4fff8ef053a..fdff281d54a2cbc94ae56bdc750c80ddbc4ddd7c 100644 --- a/ets2panda/linter/src/ts-diagnostics/TypeScriptDiagnosticsExtractor.ts +++ b/ets2panda/linter/lib/ts-diagnostics/TypeScriptDiagnosticsExtractor.ts @@ -13,42 +13,50 @@ * limitations under the License. */ -import * as ts from 'typescript'; +import type * as ts from 'typescript'; -export class TypeScriptDiagnosticsExtractor { - constructor(public strictProgram: ts.Program, public nonStrictProgram: ts.Program) { - } - - /** - * Returns diagnostics which appear in strict compilation mode only - */ - public getStrictDiagnostics(fileName: string): ts.Diagnostic[] { - // applying filter is a workaround for tsc bug - const strict = getAllDiagnostics(this.strictProgram, fileName) - .filter(diag => !(diag.length === 0 && diag.start === 0)); - const nonStrict = getAllDiagnostics(this.nonStrictProgram, fileName); +/** + * Returns diagnostics which appear in strict compilation mode only + */ +export function getStrictDiagnostics( + strictProgram: ts.Program, + nonStrictProgram: ts.Program, + fileName: string, + cancellationToken?: ts.CancellationToken +): ts.Diagnostic[] { + // applying filter is a workaround for tsc bug + const strict = getAllDiagnostics(strictProgram, fileName, cancellationToken).filter((diag) => { + return !(diag.length === 0 && diag.start === 0); + }); + const nonStrict = getAllDiagnostics(nonStrictProgram, fileName, cancellationToken); - // collect hashes for later easier comparison - const nonStrictHashes = nonStrict.reduce((result, value) => { - const hash = hashDiagnostic(value); - if (hash) { - result.add(hash); - } - return result; - }, new Set()); - // return diagnostics which weren't detected in non-strict mode - return strict.filter(value => { - const hash = hashDiagnostic(value); - return (hash && !nonStrictHashes.has(hash)); - }); - } + // collect hashes for later easier comparison + const nonStrictHashes = nonStrict.reduce((result, value) => { + const hash = hashDiagnostic(value); + if (hash) { + result.add(hash); + } + return result; + }, new Set()); + // return diagnostics which weren't detected in non-strict mode + return strict.filter((value) => { + const hash = hashDiagnostic(value); + return hash && !nonStrictHashes.has(hash); + }); } -function getAllDiagnostics(program: ts.Program, fileName: string): ts.Diagnostic[] { +function getAllDiagnostics( + program: ts.Program, + fileName: string, + cancellationToken?: ts.CancellationToken +): ts.Diagnostic[] { const sourceFile = program.getSourceFile(fileName); - return program.getSemanticDiagnostics(sourceFile) - .concat(program.getSyntacticDiagnostics(sourceFile)) - .filter(diag => diag.file === sourceFile); + return program. + getSemanticDiagnostics(sourceFile, cancellationToken). + concat(program.getSyntacticDiagnostics(sourceFile, cancellationToken)). + filter((diag) => { + return diag.file === sourceFile; + }); } function hashDiagnostic(diagnostic: ts.Diagnostic): string | undefined { diff --git a/ets2panda/linter/src/utils/TsUtils.ts b/ets2panda/linter/lib/utils/TsUtils.ts similarity index 38% rename from ets2panda/linter/src/utils/TsUtils.ts rename to ets2panda/linter/lib/utils/TsUtils.ts index c2ee9fef0a07aa042a99b1a72659d54ba6aa9ad1..3de62026330ca15d7d4ae9d226144f85e02d925c 100644 --- a/ets2panda/linter/src/utils/TsUtils.ts +++ b/ets2panda/linter/lib/utils/TsUtils.ts @@ -24,127 +24,63 @@ import { isStructDeclaration, isStructDeclarationKind } from './functions/IsStru import { pathContainsDirectory } from './functions/PathHelper'; import { ARKTS_IGNORE_DIRS, ARKTS_IGNORE_FILES } from './consts/ArktsIgnorePaths'; import { isAssignmentOperator } from './functions/isAssignmentOperator'; +import { forEachNodeInSubtree } from './functions/ForEachNodeInSubtree'; -export enum CheckType { - Array, - String = 'String', - Set = 'Set', - Map = 'Map', - Error = 'Error' -}; +export type CheckType = (this: TsUtils, t: ts.Type) => boolean; export class TsUtils { - constructor(private tsTypeChecker: ts.TypeChecker, private testMode: boolean, private advancedClassChecks: boolean) { - } - - public isTypedArray(tsType: ts.TypeNode | undefined): boolean { - if (tsType === undefined || !ts.isTypeReferenceNode(tsType)) { - return false; - } - return TYPED_ARRAYS.includes(this.entityNameToString(tsType.typeName)); - } + constructor( + private readonly tsTypeChecker: ts.TypeChecker, + private readonly testMode: boolean, + private readonly advancedClassChecks: boolean + ) {} - public isType(tsType: ts.TypeNode | undefined, checkType: string): boolean { - if (tsType === undefined || !ts.isTypeReferenceNode(tsType)) { - return false; - } - return this.entityNameToString(tsType.typeName) == checkType; - } - - public entityNameToString(name: ts.EntityName): string { + entityNameToString(name: ts.EntityName): string { if (ts.isIdentifier(name)) { return name.escapedText.toString(); - } else { - return this.entityNameToString(name.left) + this.entityNameToString(name.right); } + return this.entityNameToString(name.left) + this.entityNameToString(name.right); } - public isNumberType(tsType: ts.Type): boolean { + static isNumberType(tsType: ts.Type): boolean { if (tsType.isUnion()) { - for (let tsCompType of tsType.types) { - if ((tsCompType.flags & ts.TypeFlags.NumberLike) === 0) return false; + for (const tsCompType of tsType.types) { + if ((tsCompType.flags & ts.TypeFlags.NumberLike) === 0) { + return false; + } } return true; } return (tsType.getFlags() & ts.TypeFlags.NumberLike) !== 0; } - public isBooleanType(tsType: ts.Type): boolean { + static isBooleanType(tsType: ts.Type): boolean { return (tsType.getFlags() & ts.TypeFlags.BooleanLike) !== 0; } - public isStringLikeType(tsType: ts.Type): boolean { - if (tsType.isUnion()) { - for (let tsCompType of tsType.types) { - if ((tsCompType.flags & ts.TypeFlags.StringLike) === 0) return false; - } - return true; - } - return (tsType.getFlags() & ts.TypeFlags.StringLike) !== 0; - } - - public isStringType(type: ts.Type): boolean { - return (type.getFlags() & ts.TypeFlags.String) !== 0; - } - - public isPrimitiveEnumType(type: ts.Type, primitiveType: ts.TypeFlags): boolean { - const isNonPrimitive = (type.flags & ts.TypeFlags.NonPrimitive) !== 0; - if (!this.isEnumType(type) || !type.isUnion() || isNonPrimitive) { - return false; - } - for (const t of type.types) { - if ((t.flags & primitiveType) === 0) { - return false; - } - } - return true; - } - - public isPrimitiveEnumMemberType(type: ts.Type, primitiveType: ts.TypeFlags): boolean { - const isNonPrimitive = (type.flags & ts.TypeFlags.NonPrimitive) !== 0; - if (!this.isEnumMemberType(type) || isNonPrimitive) { - return false; - } - return (type.flags & primitiveType) !== 0; - } - - public unwrapParenthesizedType(tsType: ts.TypeNode): ts.TypeNode { - while (ts.isParenthesizedTypeNode(tsType)) { - tsType = tsType.type; - } - return tsType; - } - - public findParentIf(asExpr: ts.AsExpression): ts.IfStatement | null { - let node = asExpr.parent; - while (node) { - if (node.kind === ts.SyntaxKind.IfStatement) - return node as ts.IfStatement; - - node = node.parent; - } - - return null; - } + static isDestructuringAssignmentLHS(tsExpr: ts.ArrayLiteralExpression | ts.ObjectLiteralExpression): boolean { - public isDestructuringAssignmentLHS( - tsExpr: ts.ArrayLiteralExpression | ts.ObjectLiteralExpression - ): boolean { - // Check whether given expression is the LHS part of the destructuring - // assignment (or is a nested element of destructuring pattern). + /* + * Check whether given expression is the LHS part of the destructuring + * assignment (or is a nested element of destructuring pattern). + */ let tsParent = tsExpr.parent; let tsCurrentExpr: ts.Node = tsExpr; while (tsParent) { if ( - ts.isBinaryExpression(tsParent) && isAssignmentOperator(tsParent.operatorToken) && + ts.isBinaryExpression(tsParent) && + isAssignmentOperator(tsParent.operatorToken) && tsParent.left === tsCurrentExpr - ) + ) { return true; + } if ( (ts.isForStatement(tsParent) || ts.isForInStatement(tsParent) || ts.isForOfStatement(tsParent)) && - tsParent.initializer && tsParent.initializer === tsCurrentExpr - ) + tsParent.initializer && + tsParent.initializer === tsCurrentExpr + ) { return true; + } tsCurrentExpr = tsParent; tsParent = tsParent.parent; @@ -153,57 +89,53 @@ export class TsUtils { return false; } - public isEnumType(tsType: ts.Type): boolean { - // Note: For some reason, test (tsType.flags & ts.TypeFlags.Enum) != 0 doesn't work here. - // Must use SymbolFlags to figure out if this is an enum type. - return tsType.symbol && (tsType.symbol.flags & ts.SymbolFlags.Enum) !== 0; - } - - public isEnumMemberType(tsType: ts.Type): boolean { - // Note: For some reason, test (tsType.flags & ts.TypeFlags.Enum) != 0 doesn't work here. - // Must use SymbolFlags to figure out if this is an enum type. - return tsType.symbol && (tsType.symbol.flags & ts.SymbolFlags.EnumMember) !== 0; + static isEnumType(tsType: ts.Type): boolean { + // when type equals `typeof `, only symbol contains information about it's type. + const isEnumSymbol = tsType.symbol && this.isEnum(tsType.symbol); + // otherwise, we should analyze flags of the type itself + const isEnumType = !!(tsType.flags & ts.TypeFlags.Enum) || !!(tsType.flags & ts.TypeFlags.EnumLiteral); + return isEnumSymbol || isEnumType; } - public isObjectLiteralType(tsType: ts.Type): boolean { - return tsType.symbol && (tsType.symbol.flags & ts.SymbolFlags.ObjectLiteral) !== 0; + static isEnum(tsSymbol: ts.Symbol): boolean { + return !!(tsSymbol.flags & ts.SymbolFlags.Enum); } - public isNumberLikeType(tsType: ts.Type): boolean { - return (tsType.getFlags() & ts.TypeFlags.NumberLike) !== 0; - } - - public hasModifier(tsModifiers: readonly ts.Modifier[] | undefined, tsModifierKind: number): boolean { - // Sanity check. - if (!tsModifiers) return false; + static hasModifier(tsModifiers: readonly ts.Modifier[] | undefined, tsModifierKind: number): boolean { + if (!tsModifiers) { + return false; + } for (const tsModifier of tsModifiers) { - if (tsModifier.kind === tsModifierKind) return true; + if (tsModifier.kind === tsModifierKind) { + return true; + } } return false; } - public unwrapParenthesized(tsExpr: ts.Expression): ts.Expression { + static unwrapParenthesized(tsExpr: ts.Expression): ts.Expression { let unwrappedExpr = tsExpr; - while (ts.isParenthesizedExpression(unwrappedExpr)) + while (ts.isParenthesizedExpression(unwrappedExpr)) { unwrappedExpr = unwrappedExpr.expression; + } return unwrappedExpr; } - public followIfAliased(sym: ts.Symbol): ts.Symbol { + followIfAliased(sym: ts.Symbol): ts.Symbol { if ((sym.getFlags() & ts.SymbolFlags.Alias) !== 0) { return this.tsTypeChecker.getAliasedSymbol(sym); } return sym; } - - private trueSymbolAtLocationCache = new Map(); - public trueSymbolAtLocation(node: ts.Node): ts.Symbol | undefined { - let cache = this.trueSymbolAtLocationCache; - let val = cache.get(node); + private readonly trueSymbolAtLocationCache = new Map(); + + trueSymbolAtLocation(node: ts.Node): ts.Symbol | undefined { + const cache = this.trueSymbolAtLocationCache; + const val = cache.get(node); if (val !== undefined) { return val !== null ? val : undefined; } @@ -217,141 +149,163 @@ export class TsUtils { return sym; } - private isTypeDeclSyntaxKind(kind: ts.SyntaxKind) { - return isStructDeclarationKind(kind) || + private static isTypeDeclSyntaxKind(kind: ts.SyntaxKind): boolean { + return ( + isStructDeclarationKind(kind) || kind === ts.SyntaxKind.EnumDeclaration || kind === ts.SyntaxKind.ClassDeclaration || kind === ts.SyntaxKind.InterfaceDeclaration || - kind === ts.SyntaxKind.TypeAliasDeclaration; + kind === ts.SyntaxKind.TypeAliasDeclaration + ); } - public symbolHasDuplicateName(symbol: ts.Symbol, tsDeclKind: ts.SyntaxKind): boolean { - // Type Checker merges all declarations with the same name in one scope into one symbol. - // Thus, check whether the symbol of certain declaration has any declaration with - // different syntax kind. + static symbolHasDuplicateName(symbol: ts.Symbol, tsDeclKind: ts.SyntaxKind): boolean { + + /* + * Type Checker merges all declarations with the same name in one scope into one symbol. + * Thus, check whether the symbol of certain declaration has any declaration with + * different syntax kind. + */ const symbolDecls = symbol?.getDeclarations(); if (symbolDecls) { for (const symDecl of symbolDecls) { const declKind = symDecl.kind; // we relax arkts-unique-names for namespace collision with class/interface/enum/type/struct const isNamespaceTypeCollision = - (this.isTypeDeclSyntaxKind(declKind) && tsDeclKind === ts.SyntaxKind.ModuleDeclaration) || - (this.isTypeDeclSyntaxKind(tsDeclKind) && declKind === ts.SyntaxKind.ModuleDeclaration) - - // Don't count declarations with 'Identifier' syntax kind as those - // usually depict declaring an object's property through assignment. - if (declKind !== ts.SyntaxKind.Identifier && declKind !== tsDeclKind && !isNamespaceTypeCollision) return true; + TsUtils.isTypeDeclSyntaxKind(declKind) && tsDeclKind === ts.SyntaxKind.ModuleDeclaration || + TsUtils.isTypeDeclSyntaxKind(tsDeclKind) && declKind === ts.SyntaxKind.ModuleDeclaration; + + /* + * Don't count declarations with 'Identifier' syntax kind as those + * usually depict declaring an object's property through assignment. + */ + if (declKind !== ts.SyntaxKind.Identifier && declKind !== tsDeclKind && !isNamespaceTypeCollision) { + return true; + } } } return false; } - public isReferenceType(tsType: ts.Type): boolean { - const f = tsType.getFlags(); - return ( - (f & ts.TypeFlags.InstantiableNonPrimitive) != 0 || (f & ts.TypeFlags.Object) != 0 || - (f & ts.TypeFlags.Boolean) != 0 || (f & ts.TypeFlags.Enum) != 0 || (f & ts.TypeFlags.NonPrimitive) != 0 || - (f & ts.TypeFlags.Number) != 0 || (f & ts.TypeFlags.String) != 0 - ); - } - - public isPrimitiveType(type: ts.Type): boolean { + static isPrimitiveType(type: ts.Type): boolean { const f = type.getFlags(); return ( - (f & ts.TypeFlags.Boolean) != 0 || (f & ts.TypeFlags.BooleanLiteral) != 0 || - (f & ts.TypeFlags.Number) != 0 || (f & ts.TypeFlags.NumberLiteral) != 0 - // In ArkTS 'string' is not a primitive type. So for the common subset 'string' - // should be considered as a reference type. That is why next line is commented out. - //(f & ts.TypeFlags.String) != 0 || (f & ts.TypeFlags.StringLiteral) != 0 + (f & ts.TypeFlags.Boolean) !== 0 || + (f & ts.TypeFlags.BooleanLiteral) !== 0 || + (f & ts.TypeFlags.Number) !== 0 || + (f & ts.TypeFlags.NumberLiteral) !== 0 + + /* + * In ArkTS 'string' is not a primitive type. So for the common subset 'string' + * should be considered as a reference type. That is why next line is commented out. + * (f & ts.TypeFlags.String) != 0 || (f & ts.TypeFlags.StringLiteral) != 0 + */ ); } - public isTypeSymbol(symbol: ts.Symbol | undefined): boolean { + static isTypeSymbol(symbol: ts.Symbol | undefined): boolean { return ( - !!symbol && !!symbol.flags && + !!symbol && + !!symbol.flags && ((symbol.flags & ts.SymbolFlags.Class) !== 0 || (symbol.flags & ts.SymbolFlags.Interface) !== 0) ); } // Check whether type is generic 'Array' type defined in TypeScript standard library. - public isGenericArrayType(tsType: ts.Type): tsType is ts.TypeReference { + static isGenericArrayType(tsType: ts.Type): tsType is ts.TypeReference { return ( - this.isTypeReference(tsType) && tsType.typeArguments?.length === 1 && tsType.target.typeParameters?.length === 1 && + TsUtils.isTypeReference(tsType) && + tsType.typeArguments?.length === 1 && + tsType.target.typeParameters?.length === 1 && tsType.getSymbol()?.getName() === 'Array' ); } + isTypedArray(tsType: ts.Type): boolean { + const symbol = tsType.symbol; + if (!symbol) { + return false; + } + const name = this.tsTypeChecker.getFullyQualifiedName(symbol); + return this.isGlobalSymbol(symbol) && TYPED_ARRAYS.includes(name); + } + + isArray(tsType: ts.Type): boolean { + return TsUtils.isGenericArrayType(tsType) || this.isTypedArray(tsType); + } + + static isTuple(tsType: ts.Type): boolean { + return TsUtils.isTypeReference(tsType) && !!(tsType.objectFlags & ts.ObjectFlags.Tuple); + } + // does something similar to relatedByInheritanceOrIdentical function - public isDerivedFrom(tsType: ts.Type, checkType: CheckType): tsType is ts.TypeReference { - if (this.isTypeReference(tsType) && tsType.target !== tsType) tsType = tsType.target; + isOrDerivedFrom(tsType: ts.Type, checkType: CheckType): boolean { + tsType = TsUtils.reduceReference(tsType); - const tsTypeNode = this.tsTypeChecker.typeToTypeNode(tsType, undefined, ts.NodeBuilderFlags.None); - if (checkType == CheckType.Array && (this.isGenericArrayType(tsType) || this.isTypedArray(tsTypeNode))) + if (checkType.call(this, tsType)) { return true; - if (checkType != CheckType.Array && this.isType(tsTypeNode, checkType.toString())) - return true; - if (!tsType.symbol || !tsType.symbol.declarations) return false; + } - for (let tsTypeDecl of tsType.symbol.declarations) { - if ( - (!ts.isClassDeclaration(tsTypeDecl) && !ts.isInterfaceDeclaration(tsTypeDecl)) || - !tsTypeDecl.heritageClauses - ) continue; - for (let heritageClause of tsTypeDecl.heritageClauses) { - if (this.processParentTypesCheck(heritageClause.types, checkType)) return true; + if (!tsType.symbol?.declarations) { + return false; + } + + for (const tsTypeDecl of tsType.symbol.declarations) { + const isClassOrInterfaceDecl = ts.isClassDeclaration(tsTypeDecl) || ts.isInterfaceDeclaration(tsTypeDecl); + const isDerived = isClassOrInterfaceDecl && !!tsTypeDecl.heritageClauses; + if (!isDerived) { + continue; + } + for (const heritageClause of tsTypeDecl.heritageClauses) { + if (this.processParentTypesCheck(heritageClause.types, checkType)) { + return true; + } } } return false; } - public isTypeReference(tsType: ts.Type): tsType is ts.TypeReference { + static isTypeReference(tsType: ts.Type): tsType is ts.TypeReference { return ( (tsType.getFlags() & ts.TypeFlags.Object) !== 0 && ((tsType as ts.ObjectType).objectFlags & ts.ObjectFlags.Reference) !== 0 ); } - public isNullType(tsTypeNode: ts.TypeNode): boolean { - return (ts.isLiteralTypeNode(tsTypeNode) && tsTypeNode.literal.kind === ts.SyntaxKind.NullKeyword); - } - - public isThisOrSuperExpr(tsExpr: ts.Expression): boolean { - return (tsExpr.kind == ts.SyntaxKind.ThisKeyword || tsExpr.kind == ts.SyntaxKind.SuperKeyword); - } - - public isPrototypeSymbol(symbol: ts.Symbol | undefined): boolean { - return (!!symbol && !!symbol.flags && (symbol.flags & ts.SymbolFlags.Prototype) !== 0); + static isPrototypeSymbol(symbol: ts.Symbol | undefined): boolean { + return !!symbol && !!symbol.flags && (symbol.flags & ts.SymbolFlags.Prototype) !== 0; } - public isFunctionSymbol(symbol: ts.Symbol | undefined): boolean { - return (!!symbol && !!symbol.flags && (symbol.flags & ts.SymbolFlags.Function) !== 0); + static isFunctionSymbol(symbol: ts.Symbol | undefined): boolean { + return !!symbol && !!symbol.flags && (symbol.flags & ts.SymbolFlags.Function) !== 0; } - public isInterfaceType(tsType: ts.Type | undefined): boolean { + static isInterfaceType(tsType: ts.Type | undefined): boolean { return ( - !!tsType && !!tsType.symbol && !!tsType.symbol.flags && - (tsType.symbol.flags & ts.SymbolFlags.Interface) !== 0 + !!tsType && !!tsType.symbol && !!tsType.symbol.flags && (tsType.symbol.flags & ts.SymbolFlags.Interface) !== 0 ); } - public isAnyType(tsType: ts.Type): tsType is ts.TypeReference { + static isAnyType(tsType: ts.Type): tsType is ts.TypeReference { return (tsType.getFlags() & ts.TypeFlags.Any) !== 0; } - public isUnknownType(tsType: ts.Type): boolean { + static isUnknownType(tsType: ts.Type): boolean { return (tsType.getFlags() & ts.TypeFlags.Unknown) !== 0; } - public isUnsupportedType(tsType: ts.Type): boolean { + static isUnsupportedType(tsType: ts.Type): boolean { return ( - !!tsType.flags && ((tsType.flags & ts.TypeFlags.Any) !== 0 || (tsType.flags & ts.TypeFlags.Unknown) !== 0 || + !!tsType.flags && + ((tsType.flags & ts.TypeFlags.Any) !== 0 || + (tsType.flags & ts.TypeFlags.Unknown) !== 0 || (tsType.flags & ts.TypeFlags.Intersection) !== 0) ); } - public isNullableUnionType(type: ts.Type): boolean { + static isNullableUnionType(type: ts.Type): boolean { if (type.isUnion()) { for (const t of type.types) { if (!!(t.flags & ts.TypeFlags.Undefined) || !!(t.flags & ts.TypeFlags.Null)) { @@ -362,43 +316,64 @@ export class TsUtils { return false; } - public isFunctionOrMethod(tsSymbol: ts.Symbol | undefined): boolean { - return ( - !!tsSymbol && - ((tsSymbol.flags & ts.SymbolFlags.Function) !== 0 || (tsSymbol.flags & ts.SymbolFlags.Method) !== 0) - ); - } - - public isMethodAssignment(tsSymbol: ts.Symbol | undefined): boolean { + static isMethodAssignment(tsSymbol: ts.Symbol | undefined): boolean { return ( - !!tsSymbol && - ((tsSymbol.flags & ts.SymbolFlags.Method) !== 0 && (tsSymbol.flags & ts.SymbolFlags.Assignment) !== 0) + !!tsSymbol && (tsSymbol.flags & ts.SymbolFlags.Method) !== 0 && (tsSymbol.flags & ts.SymbolFlags.Assignment) !== 0 ); } - public getDeclaration(tsSymbol: ts.Symbol | undefined): ts.Declaration | undefined { - if (tsSymbol && tsSymbol.declarations && tsSymbol.declarations.length > 0) + static getDeclaration(tsSymbol: ts.Symbol | undefined): ts.Declaration | undefined { + if (tsSymbol?.declarations && tsSymbol.declarations.length > 0) { return tsSymbol.declarations[0]; + } return undefined; } - private isVarDeclaration(tsDecl: ts.Node): boolean { + private static isVarDeclaration(tsDecl: ts.Node): boolean { return ts.isVariableDeclaration(tsDecl) && ts.isVariableDeclarationList(tsDecl.parent); } - public isValidEnumMemberInit(tsExpr: ts.Expression): boolean { - if (this.isNumberConstantValue(tsExpr.parent as ts.EnumMember)) + isValidEnumMemberInit(tsExpr: ts.Expression): boolean { + if (this.isNumberConstantValue(tsExpr.parent as ts.EnumMember)) { return true; - if (this.isStringConstantValue(tsExpr.parent as ts.EnumMember)) + } + if (this.isStringConstantValue(tsExpr.parent as ts.EnumMember)) { return true; + } return this.isCompileTimeExpression(tsExpr); } - public isCompileTimeExpression(tsExpr: ts.Expression): boolean { + private isCompileTimeExpressionHandlePropertyAccess(tsExpr: ts.Expression): boolean { + if (!ts.isPropertyAccessExpression(tsExpr)) { + return false; + } + + /* + * if enum member is in current enum declaration try to get value + * if it comes from another enum consider as constant + */ + const propertyAccess = tsExpr; + if (this.isNumberConstantValue(propertyAccess)) { + return true; + } + const leftHandSymbol = this.trueSymbolAtLocation(propertyAccess.expression); + if (!leftHandSymbol) { + return false; + } + const decls = leftHandSymbol.getDeclarations(); + if (!decls || decls.length !== 1) { + return false; + } + return ts.isEnumDeclaration(decls[0]); + } + + isCompileTimeExpression(tsExpr: ts.Expression): boolean { if ( ts.isParenthesizedExpression(tsExpr) || - (ts.isAsExpression(tsExpr) && tsExpr.type.kind === ts.SyntaxKind.NumberKeyword)) + ts.isAsExpression(tsExpr) && tsExpr.type.kind === ts.SyntaxKind.NumberKeyword + ) { return this.isCompileTimeExpression(tsExpr.expression); + } switch (tsExpr.kind) { case ts.SyntaxKind.PrefixUnaryExpression: @@ -414,143 +389,135 @@ export class TsUtils { return true; case ts.SyntaxKind.StringLiteral: return true; - case ts.SyntaxKind.PropertyAccessExpression: { - // if enum member is in current enum declaration try to get value - // if it comes from another enum consider as constant - const propertyAccess = tsExpr as ts.PropertyAccessExpression; - if (this.isNumberConstantValue(propertyAccess)) - return true; - const leftHandSymbol = this.trueSymbolAtLocation(propertyAccess.expression); - if (!leftHandSymbol) - return false; - const decls = leftHandSymbol.getDeclarations(); - if (!decls || decls.length !== 1) - return false; - return ts.isEnumDeclaration(decls[0]); - } + case ts.SyntaxKind.PropertyAccessExpression: + return this.isCompileTimeExpressionHandlePropertyAccess(tsExpr); default: return false; } } private isPrefixUnaryExprValidEnumMemberInit(tsExpr: ts.PrefixUnaryExpression): boolean { - return (this.isUnaryOpAllowedForEnumMemberInit(tsExpr.operator) && this.isCompileTimeExpression(tsExpr.operand)); + return TsUtils.isUnaryOpAllowedForEnumMemberInit(tsExpr.operator) && this.isCompileTimeExpression(tsExpr.operand); } private isBinaryExprValidEnumMemberInit(tsExpr: ts.BinaryExpression): boolean { return ( - this.isBinaryOpAllowedForEnumMemberInit(tsExpr.operatorToken) && this.isCompileTimeExpression(tsExpr.left) && + TsUtils.isBinaryOpAllowedForEnumMemberInit(tsExpr.operatorToken) && + this.isCompileTimeExpression(tsExpr.left) && this.isCompileTimeExpression(tsExpr.right) ); } private isConditionalExprValidEnumMemberInit(tsExpr: ts.ConditionalExpression): boolean { - return (this.isCompileTimeExpression(tsExpr.whenTrue) && this.isCompileTimeExpression(tsExpr.whenFalse)); + return this.isCompileTimeExpression(tsExpr.whenTrue) && this.isCompileTimeExpression(tsExpr.whenFalse); } private isIdentifierValidEnumMemberInit(tsExpr: ts.Identifier): boolean { - let tsSymbol = this.trueSymbolAtLocation(tsExpr); - let tsDecl = this.getDeclaration(tsSymbol); - return (!!tsDecl && - ((this.isVarDeclaration(tsDecl) && this.isConst(tsDecl.parent)) || - (tsDecl.kind === ts.SyntaxKind.EnumMember) - ) + const tsSymbol = this.trueSymbolAtLocation(tsExpr); + const tsDecl = TsUtils.getDeclaration(tsSymbol); + return ( + !!tsDecl && + (TsUtils.isVarDeclaration(tsDecl) && TsUtils.isConst(tsDecl.parent) || tsDecl.kind === ts.SyntaxKind.EnumMember) ); } - private isUnaryOpAllowedForEnumMemberInit(tsPrefixUnaryOp: ts.PrefixUnaryOperator): boolean { + private static isUnaryOpAllowedForEnumMemberInit(tsPrefixUnaryOp: ts.PrefixUnaryOperator): boolean { return ( - tsPrefixUnaryOp === ts.SyntaxKind.PlusToken || tsPrefixUnaryOp === ts.SyntaxKind.MinusToken || + tsPrefixUnaryOp === ts.SyntaxKind.PlusToken || + tsPrefixUnaryOp === ts.SyntaxKind.MinusToken || tsPrefixUnaryOp === ts.SyntaxKind.TildeToken ); } - private isBinaryOpAllowedForEnumMemberInit(tsBinaryOp: ts.BinaryOperatorToken): boolean { + private static isBinaryOpAllowedForEnumMemberInit(tsBinaryOp: ts.BinaryOperatorToken): boolean { return ( - tsBinaryOp.kind === ts.SyntaxKind.AsteriskToken || tsBinaryOp.kind === ts.SyntaxKind.SlashToken || - tsBinaryOp.kind === ts.SyntaxKind.PercentToken || tsBinaryOp.kind === ts.SyntaxKind.MinusToken || - tsBinaryOp.kind === ts.SyntaxKind.PlusToken || tsBinaryOp.kind === ts.SyntaxKind.LessThanLessThanToken || - tsBinaryOp.kind === ts.SyntaxKind.GreaterThanGreaterThanToken || tsBinaryOp.kind === ts.SyntaxKind.BarBarToken || + tsBinaryOp.kind === ts.SyntaxKind.AsteriskToken || + tsBinaryOp.kind === ts.SyntaxKind.SlashToken || + tsBinaryOp.kind === ts.SyntaxKind.PercentToken || + tsBinaryOp.kind === ts.SyntaxKind.MinusToken || + tsBinaryOp.kind === ts.SyntaxKind.PlusToken || + tsBinaryOp.kind === ts.SyntaxKind.LessThanLessThanToken || + tsBinaryOp.kind === ts.SyntaxKind.GreaterThanGreaterThanToken || + tsBinaryOp.kind === ts.SyntaxKind.BarBarToken || tsBinaryOp.kind === ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken || - tsBinaryOp.kind === ts.SyntaxKind.AmpersandToken || tsBinaryOp.kind === ts.SyntaxKind.CaretToken || - tsBinaryOp.kind === ts.SyntaxKind.BarToken || tsBinaryOp.kind === ts.SyntaxKind.AmpersandAmpersandToken + tsBinaryOp.kind === ts.SyntaxKind.AmpersandToken || + tsBinaryOp.kind === ts.SyntaxKind.CaretToken || + tsBinaryOp.kind === ts.SyntaxKind.BarToken || + tsBinaryOp.kind === ts.SyntaxKind.AmpersandAmpersandToken ); } - public isConst(tsNode: ts.Node): boolean { + static isConst(tsNode: ts.Node): boolean { return !!(ts.getCombinedNodeFlags(tsNode) & ts.NodeFlags.Const); } - public isNumberConstantValue( + isNumberConstantValue( tsExpr: ts.EnumMember | ts.PropertyAccessExpression | ts.ElementAccessExpression | ts.NumericLiteral ): boolean { + const tsConstValue = + tsExpr.kind === ts.SyntaxKind.NumericLiteral ? + Number(tsExpr.getText()) : + this.tsTypeChecker.getConstantValue(tsExpr); - const tsConstValue = (tsExpr.kind === ts.SyntaxKind.NumericLiteral) ? - Number(tsExpr.getText()) : - this.tsTypeChecker.getConstantValue(tsExpr); - - return tsConstValue !== undefined && typeof tsConstValue === 'number'; + return tsConstValue !== undefined && typeof tsConstValue === 'number'; } - public isIntegerConstantValue( + isIntegerConstantValue( tsExpr: ts.EnumMember | ts.PropertyAccessExpression | ts.ElementAccessExpression | ts.NumericLiteral ): boolean { - - const tsConstValue = (tsExpr.kind === ts.SyntaxKind.NumericLiteral) ? - Number(tsExpr.getText()) : - this.tsTypeChecker.getConstantValue(tsExpr); + const tsConstValue = + tsExpr.kind === ts.SyntaxKind.NumericLiteral ? + Number(tsExpr.getText()) : + this.tsTypeChecker.getConstantValue(tsExpr); return ( - tsConstValue !== undefined && typeof tsConstValue === 'number' && + tsConstValue !== undefined && + typeof tsConstValue === 'number' && tsConstValue.toFixed(0) === tsConstValue.toString() ); } - public isStringConstantValue( - tsExpr: ts.EnumMember | ts.PropertyAccessExpression | ts.ElementAccessExpression - ): boolean { + isStringConstantValue(tsExpr: ts.EnumMember | ts.PropertyAccessExpression | ts.ElementAccessExpression): boolean { const tsConstValue = this.tsTypeChecker.getConstantValue(tsExpr); - return ( - tsConstValue !== undefined && typeof tsConstValue === 'string' - ); + return tsConstValue !== undefined && typeof tsConstValue === 'string'; } // Returns true iff typeA is a subtype of typeB - public relatedByInheritanceOrIdentical(typeA: ts.Type, typeB: ts.Type): boolean { - if (this.isTypeReference(typeA) && typeA.target !== typeA) typeA = typeA.target; - if (this.isTypeReference(typeB) && typeB.target !== typeB) typeB = typeB.target; + relatedByInheritanceOrIdentical(typeA: ts.Type, typeB: ts.Type): boolean { + typeA = TsUtils.reduceReference(typeA); + typeB = TsUtils.reduceReference(typeB); - if (typeA === typeB || this.isObject(typeB)) return true; - if (!typeA.symbol || !typeA.symbol.declarations) return false; + if (typeA === typeB || this.isObject(typeB)) { + return true; + } + if (!typeA.symbol?.declarations) { + return false; + } - for (let typeADecl of typeA.symbol.declarations) { - if ( - (!ts.isClassDeclaration(typeADecl) && !ts.isInterfaceDeclaration(typeADecl)) || - !typeADecl.heritageClauses - ) continue; - for (let heritageClause of typeADecl.heritageClauses) { - let processInterfaces = typeA.isClass() ? (heritageClause.token != ts.SyntaxKind.ExtendsKeyword) : true; - if (this.processParentTypes(heritageClause.types, typeB, processInterfaces)) return true; + for (const typeADecl of typeA.symbol.declarations) { + if (!ts.isClassDeclaration(typeADecl) && !ts.isInterfaceDeclaration(typeADecl) || !typeADecl.heritageClauses) { + continue; + } + for (const heritageClause of typeADecl.heritageClauses) { + const processInterfaces = typeA.isClass() ? heritageClause.token !== ts.SyntaxKind.ExtendsKeyword : true; + if (this.processParentTypes(heritageClause.types, typeB, processInterfaces)) { + return true; + } } } return false; } - // return true if two class types are not related by inheritance and structural identity check is needed - public needToDeduceStructuralIdentity(lhsType: ts.Type, rhsType: ts.Type, rhsExpr: ts.Expression, - allowPromotion: boolean = false): boolean { - // Compare non-nullable version of types. - lhsType = this.getNonNullableType(lhsType); - rhsType = this.getNonNullableType(rhsType); - - if (this.isLibraryType(lhsType)) { - return false; - } - - if (this.isDynamicObjectAssignedToStdType(lhsType, rhsExpr)) { - return false; - } + static reduceReference(t: ts.Type): ts.Type { + return TsUtils.isTypeReference(t) && t.target !== t ? t.target : t; + } + private needToDeduceStructuralIdentityHandleUnions( + lhsType: ts.Type, + rhsType: ts.Type, + rhsExpr: ts.Expression, + allowPromotion: boolean + ): boolean { if (rhsType.isUnion()) { // Each Class/Interface of the RHS union type must be compatible with LHS type. for (const compType of rhsType.types) { @@ -560,7 +527,6 @@ export class TsUtils { } return false; } - if (lhsType.isUnion()) { // RHS type needs to be compatible with at least one type of the LHS union. for (const compType of lhsType.types) { @@ -570,77 +536,122 @@ export class TsUtils { } return true; } + // should be unreachable + return false; + } - if (this.advancedClassChecks && this.isClassValueType(rhsType) && lhsType != rhsType && !this.isObjectType(lhsType)) { + // return true if two class types are not related by inheritance and structural identity check is needed + needToDeduceStructuralIdentity( + lhsType: ts.Type, + rhsType: ts.Type, + rhsExpr: ts.Expression, + allowPromotion: boolean = false + ): boolean { + lhsType = TsUtils.getNonNullableType(lhsType); + rhsType = TsUtils.getNonNullableType(rhsType); + if (this.isLibraryType(lhsType)) { + return false; + } + if (this.isDynamicObjectAssignedToStdType(lhsType, rhsExpr)) { + return false; + } + // #14569: Check for Function type. + if (this.areCompatibleFunctionals(lhsType, rhsType)) { + return false; + } + if (rhsType.isUnion() || lhsType.isUnion()) { + return this.needToDeduceStructuralIdentityHandleUnions(lhsType, rhsType, rhsExpr, allowPromotion); + } + if ( + this.advancedClassChecks && + TsUtils.isClassValueType(rhsType) && + lhsType !== rhsType && + !TsUtils.isObjectType(lhsType) + ) { // missing exact rule return true; } - - let res = lhsType.isClassOrInterface() && rhsType.isClassOrInterface() && !this.relatedByInheritanceOrIdentical(rhsType, lhsType); - + let res = + lhsType.isClassOrInterface() && + rhsType.isClassOrInterface() && + !this.relatedByInheritanceOrIdentical(rhsType, lhsType); if (allowPromotion) { res &&= !this.relatedByInheritanceOrIdentical(lhsType, rhsType); } - return res; } - private processParentTypes(parentTypes: ts.NodeArray, typeB: ts.Type, processInterfaces: boolean): boolean { - for (let baseTypeExpr of parentTypes) { - let baseType = this.tsTypeChecker.getTypeAtLocation(baseTypeExpr); - if (this.isTypeReference(baseType) && baseType.target !== baseType) baseType = baseType.target; - if (baseType && (baseType.isClass() != processInterfaces) && this.relatedByInheritanceOrIdentical(baseType, typeB)) return true; + private processParentTypes( + parentTypes: ts.NodeArray, + typeB: ts.Type, + processInterfaces: boolean + ): boolean { + for (const baseTypeExpr of parentTypes) { + const baseType = TsUtils.reduceReference(this.tsTypeChecker.getTypeAtLocation(baseTypeExpr)); + if ( + baseType && + baseType.isClass() !== processInterfaces && + this.relatedByInheritanceOrIdentical(baseType, typeB) + ) { + return true; + } } return false; } private processParentTypesCheck(parentTypes: ts.NodeArray, checkType: CheckType): boolean { - for (let baseTypeExpr of parentTypes) { - let baseType = this.tsTypeChecker.getTypeAtLocation(baseTypeExpr); - if (this.isTypeReference(baseType) && baseType.target !== baseType) baseType = baseType.target; - if (baseType && this.isDerivedFrom(baseType, checkType)) return true; + for (const baseTypeExpr of parentTypes) { + const baseType = TsUtils.reduceReference(this.tsTypeChecker.getTypeAtLocation(baseTypeExpr)); + if (baseType && this.isOrDerivedFrom(baseType, checkType)) { + return true; + } } return false; } - public isObject(tsType: ts.Type): boolean { + isObject(tsType: ts.Type): boolean { if (!tsType) { return false; } - if (tsType.symbol && (tsType.isClassOrInterface() && tsType.symbol.name === 'Object')) { + if (tsType.symbol && tsType.isClassOrInterface() && tsType.symbol.name === 'Object') { return true; } - let node = this.tsTypeChecker.typeToTypeNode(tsType, undefined, undefined); - return node != undefined && node.kind === ts.SyntaxKind.ObjectKeyword; + const node = this.tsTypeChecker.typeToTypeNode(tsType, undefined, undefined); + return node !== undefined && node.kind === ts.SyntaxKind.ObjectKeyword; } - public isCallToFunctionWithOmittedReturnType(tsExpr: ts.Expression): boolean { + isCallToFunctionWithOmittedReturnType(tsExpr: ts.Expression): boolean { if (ts.isCallExpression(tsExpr)) { - let tsCallSignature = this.tsTypeChecker.getResolvedSignature(tsExpr); + const tsCallSignature = this.tsTypeChecker.getResolvedSignature(tsExpr); if (tsCallSignature) { const tsSignDecl = tsCallSignature.getDeclaration(); // `tsSignDecl` is undefined when `getResolvedSignature` returns `unknownSignature` - if (!tsSignDecl || !tsSignDecl.type) return true; + if (!tsSignDecl?.type) { + return true; + } } } return false; } - private hasReadonlyFields(type: ts.Type): boolean { - if (type.symbol.members === undefined) return false; // No members -> no readonly fields + private static hasReadonlyFields(type: ts.Type): boolean { + // No members -> no readonly fields + if (type.symbol.members === undefined) { + return false; + } let result: boolean = false; - type.symbol.members.forEach((value, key) => { + type.symbol.members.forEach((value) => { if ( - value.declarations !== undefined && value.declarations.length > 0 && + value.declarations !== undefined && + value.declarations.length > 0 && ts.isPropertyDeclaration(value.declarations[0]) ) { - let propmMods = ts.getModifiers(value.declarations[0] as ts.PropertyDeclaration); - if (this.hasModifier(propmMods, ts.SyntaxKind.ReadonlyKeyword)) { + const propmMods = ts.getModifiers(value.declarations[0]); + if (TsUtils.hasModifier(propmMods, ts.SyntaxKind.ReadonlyKeyword)) { result = true; - return; } } }); @@ -648,56 +659,66 @@ export class TsUtils { return result; } - private hasDefaultCtor(type: ts.Type): boolean { - if (type.symbol.members === undefined) return true; // No members -> no explicite constructors -> there is default ctor + private static hasDefaultCtor(type: ts.Type): boolean { + // No members -> no explicit constructors -> there is default ctor + if (type.symbol.members === undefined) { + return true; + } - let hasCtor: boolean = false; // has any constructor - let hasDefaultCtor: boolean = false; // has default constructor + // has any constructor + let hasCtor: boolean = false; + // has default constructor + let hasDefaultCtor: boolean = false; - type.symbol.members.forEach((value, key) => { + type.symbol.members.forEach((value) => { if ((value.flags & ts.SymbolFlags.Constructor) !== 0) { hasCtor = true; if (value.declarations !== undefined && value.declarations.length > 0) { - let declCtor = value.declarations[0] as ts.ConstructorDeclaration; + const declCtor = value.declarations[0] as ts.ConstructorDeclaration; if (declCtor.parameters.length === 0) { hasDefaultCtor = true; - return; } } } }); - return !hasCtor || hasDefaultCtor; // Has no any explicite constructor -> has implicite default constructor. + // Has no any explicit constructor -> has implicit default constructor. + return !hasCtor || hasDefaultCtor; } - private isAbstractClass(type: ts.Type): boolean { + private static isAbstractClass(type: ts.Type): boolean { if (type.isClass() && type.symbol.declarations && type.symbol.declarations.length > 0) { - let declClass = type.symbol.declarations[0] as ts.ClassDeclaration; - let classMods = ts.getModifiers(declClass); - if (this.hasModifier(classMods, ts.SyntaxKind.AbstractKeyword)) + const declClass = type.symbol.declarations[0] as ts.ClassDeclaration; + const classMods = ts.getModifiers(declClass); + if (TsUtils.hasModifier(classMods, ts.SyntaxKind.AbstractKeyword)) { return true; + } } return false; } - public validateObjectLiteralType(type: ts.Type | undefined): boolean { + static validateObjectLiteralType(type: ts.Type | undefined): boolean { if (!type) { return false; } - type = this.getTargetType(type); + type = TsUtils.reduceReference(type); return ( - type.isClassOrInterface() && this.hasDefaultCtor(type) && - !this.hasReadonlyFields(type) && !this.isAbstractClass(type) + type.isClassOrInterface() && + TsUtils.hasDefaultCtor(type) && + !TsUtils.hasReadonlyFields(type) && + !TsUtils.isAbstractClass(type) ); } - public hasMethods(type: ts.Type): boolean { + hasMethods(type: ts.Type): boolean { const properties = this.tsTypeChecker.getPropertiesOfType(type); if (properties?.length) { for (const prop of properties) { - if (prop.getFlags() & ts.SymbolFlags.Method) return true; + if (prop.getFlags() & ts.SymbolFlags.Method) { + return true; + } } } return false; @@ -707,61 +728,69 @@ export class TsUtils { const properties = this.tsTypeChecker.getPropertiesOfType(type); if (properties?.length) { for (const prop of properties) { - if (prop.name === name) return prop; + if (prop.name === name) { + return prop; + } } } return undefined; } - public checkTypeSet(uType: ts.Type, predicate: (t: ts.Type) => boolean): boolean { - if (!uType.isUnionOrIntersection()) { - return predicate(uType); + checkTypeSet(typeSet: ts.Type, predicate: CheckType): boolean { + if (!typeSet.isUnionOrIntersection()) { + return predicate.call(this, typeSet); } - for (let elemType of uType.types) { - if (!this.checkTypeSet(elemType, predicate)) { - return false; + for (const elemType of typeSet.types) { + if (this.checkTypeSet(elemType, predicate)) { + return true; } } - return true; + return false; } - private getNonNullableType(t: ts.Type) { - if (this.isNullableUnionType(t)) { + static getNonNullableType(t: ts.Type): ts.Type { + if (TsUtils.isNullableUnionType(t)) { return t.getNonNullableType(); } return t; } - public isObjectLiteralAssignable(lhsType: ts.Type | undefined, rhsExpr: ts.ObjectLiteralExpression): boolean { + private isObjectLiteralAssignableToUnion(lhsType: ts.UnionType, rhsExpr: ts.ObjectLiteralExpression): boolean { + for (const compType of lhsType.types) { + if (this.isObjectLiteralAssignable(compType, rhsExpr)) { + return true; + } + } + return false; + } + + isObjectLiteralAssignable(lhsType: ts.Type | undefined, rhsExpr: ts.ObjectLiteralExpression): boolean { if (lhsType === undefined) { return false; } - // Always check with the non-nullable variant of lhs type. - lhsType = this.getNonNullableType(lhsType); - - if (lhsType.isUnion()) { - for (const compType of lhsType.types) { - if (this.isObjectLiteralAssignable(compType, rhsExpr)) { - return true; - } - } + lhsType = TsUtils.getNonNullableType(lhsType); + if (lhsType.isUnion() && this.isObjectLiteralAssignableToUnion(lhsType, rhsExpr)) { + return true; } - // Allow initializing with anything when the type - // originates from the library. - if (this.isAnyType(lhsType) || this.isLibraryType(lhsType)) { + /* + * Allow initializing with anything when the type + * originates from the library. + */ + if (TsUtils.isAnyType(lhsType) || this.isLibraryType(lhsType)) { return true; } - // issue 13412: - // Allow initializing with a dynamic object when the LHS type - // is primitive or defined in standard library. + /* + * issue 13412: + * Allow initializing with a dynamic object when the LHS type + * is primitive or defined in standard library. + */ if (this.isDynamicObjectAssignedToStdType(lhsType, rhsExpr)) { return true; } - // For Partial, Required, Readonly types, validate their argument type. if (this.isStdPartialType(lhsType) || this.isStdRequiredType(lhsType) || this.isStdReadonlyType(lhsType)) { if (lhsType.aliasTypeArguments && lhsType.aliasTypeArguments.length === 1) { @@ -771,28 +800,27 @@ export class TsUtils { } } - // Allow initializing Record objects with object initializer. - // Record supports any type for a its value, but the key value - // must be either a string or number literal. + /* + * Allow initializing Record objects with object initializer. + * Record supports any type for a its value, but the key value + * must be either a string or number literal. + */ if (this.isStdRecordType(lhsType)) { - return this.validateRecordObjectKeys(rhsExpr); + return TsUtils.validateRecordObjectKeys(rhsExpr); } - - return this.validateObjectLiteralType(lhsType) && !this.hasMethods(lhsType) && this.validateFields(lhsType, rhsExpr); - } - - isFunctionalType(type: ts.Type): boolean { - const callSigns = type.getCallSignatures(); - return callSigns && callSigns.length > 0; + return ( + TsUtils.validateObjectLiteralType(lhsType) && !this.hasMethods(lhsType) && this.validateFields(lhsType, rhsExpr) + ); } private isDynamicObjectAssignedToStdType(lhsType: ts.Type, rhsExpr: ts.Expression): boolean { - if (isStdLibraryType(lhsType) || this.isPrimitiveType(lhsType)) { - let rhsSym = ts.isCallExpression(rhsExpr) - ? this.getSymbolOfCallExpression(rhsExpr) - : this.trueSymbolAtLocation(rhsExpr); - if (rhsSym && this.isLibrarySymbol(rhsSym)) + if (isStdLibraryType(lhsType) || TsUtils.isPrimitiveType(lhsType)) { + const rhsSym = ts.isCallExpression(rhsExpr) ? + this.getSymbolOfCallExpression(rhsExpr) : + this.trueSymbolAtLocation(rhsExpr); + if (rhsSym && this.isLibrarySymbol(rhsSym)) { return true; + } } return false; } @@ -804,83 +832,112 @@ export class TsUtils { return false; } } - }; + } return true; } private validateField(type: ts.Type, prop: ts.PropertyAssignment): boolean { - const propName = prop.name.getText(); + const propNameSymbol = this.tsTypeChecker.getSymbolAtLocation(prop.name); + const propName = propNameSymbol?.escapedName.toString() ?? prop.name.getText(); const propSym = this.findProperty(type, propName); - if (!propSym || !propSym.declarations?.length) { + if (!propSym?.declarations?.length) { return false; } const propType = this.tsTypeChecker.getTypeOfSymbolAtLocation(propSym, propSym.declarations[0]); - const initExpr = this.unwrapParenthesized(prop.initializer); + const initExpr = TsUtils.unwrapParenthesized(prop.initializer); if (ts.isObjectLiteralExpression(initExpr)) { if (!this.isObjectLiteralAssignable(propType, initExpr)) { return false; - } - } else { - // Only check for structural sub-typing. - if (this.needToDeduceStructuralIdentity(propType, this.tsTypeChecker.getTypeAtLocation(initExpr), initExpr)) { - return false; } + } else if ( + this.needToDeduceStructuralIdentity(propType, this.tsTypeChecker.getTypeAtLocation(initExpr), initExpr) + ) { + // Only check for structural sub-typing. + return false; } return true; } - validateRecordObjectKeys(objectLiteral: ts.ObjectLiteralExpression): boolean { + static validateRecordObjectKeys(objectLiteral: ts.ObjectLiteralExpression): boolean { for (const prop of objectLiteral.properties) { - if (!prop.name || (!ts.isStringLiteral(prop.name) && !ts.isNumericLiteral(prop.name))) return false; + if (!prop.name || !ts.isStringLiteral(prop.name) && !ts.isNumericLiteral(prop.name)) { + return false; + } } return true; } - getTargetType(type: ts.Type): ts.Type { - return (type.getFlags() & ts.TypeFlags.Object) && - (type as ts.ObjectType).objectFlags & ts.ObjectFlags.Reference ? (type as ts.TypeReference).target : type; + private static isSupportedTypeNodeKind(kind: ts.SyntaxKind): boolean { + return ( + kind !== ts.SyntaxKind.AnyKeyword && + kind !== ts.SyntaxKind.UnknownKeyword && + kind !== ts.SyntaxKind.SymbolKeyword && + kind !== ts.SyntaxKind.IndexedAccessType && + kind !== ts.SyntaxKind.ConditionalType && + kind !== ts.SyntaxKind.MappedType && + kind !== ts.SyntaxKind.InferType + ); + } + + private isSupportedTypeHandleUnionTypeNode(typeNode: ts.UnionTypeNode): boolean { + for (const unionTypeElem of typeNode.types) { + if (!this.isSupportedType(unionTypeElem)) { + return false; + } + } + return true; } - private isSupportedTypeNodeKind(kind: ts.SyntaxKind): boolean { - return kind !== ts.SyntaxKind.AnyKeyword && kind !== ts.SyntaxKind.UnknownKeyword && - kind !== ts.SyntaxKind.SymbolKeyword && kind !== ts.SyntaxKind.IndexedAccessType && - kind !== ts.SyntaxKind.ConditionalType && kind !== ts.SyntaxKind.MappedType && - kind !== ts.SyntaxKind.InferType; + private isSupportedTypeHandleTupleTypeNode(typeNode: ts.TupleTypeNode): boolean { + for (const elem of typeNode.elements) { + if (ts.isTypeNode(elem) && !this.isSupportedType(elem)) { + return false; + } + if (ts.isNamedTupleMember(elem) && !this.isSupportedType(elem.type)) { + return false; + } + } + return true; } - public isSupportedType(typeNode: ts.TypeNode): boolean { - if (ts.isParenthesizedTypeNode(typeNode)) return this.isSupportedType(typeNode.type); + isSupportedType(typeNode: ts.TypeNode): boolean { + if (ts.isParenthesizedTypeNode(typeNode)) { + return this.isSupportedType(typeNode.type); + } - if (ts.isArrayTypeNode(typeNode)) return this.isSupportedType(typeNode.elementType); + if (ts.isArrayTypeNode(typeNode)) { + return this.isSupportedType(typeNode.elementType); + } if (ts.isTypeReferenceNode(typeNode) && typeNode.typeArguments) { - for (const typeArg of typeNode.typeArguments) - if (!this.isSupportedType(typeArg)) return false; + for (const typeArg of typeNode.typeArguments) { + if (!this.isSupportedType(typeArg)) { + return false; + } + } return true; } if (ts.isUnionTypeNode(typeNode)) { - for (const unionTypeElem of typeNode.types) - if (!this.isSupportedType(unionTypeElem)) return false; - return true; + return this.isSupportedTypeHandleUnionTypeNode(typeNode); } if (ts.isTupleTypeNode(typeNode)) { - for (const elem of typeNode.elements) { - if (ts.isTypeNode(elem) && !this.isSupportedType(elem)) return false; - if (ts.isNamedTupleMember(elem) && !this.isSupportedType(elem.type)) return false; - } - return true; + return this.isSupportedTypeHandleTupleTypeNode(typeNode); } - return !ts.isTypeLiteralNode(typeNode) && (this.advancedClassChecks || !ts.isTypeQueryNode(typeNode)) && - !ts.isIntersectionTypeNode(typeNode) && this.isSupportedTypeNodeKind(typeNode.kind); + return ( + !ts.isTypeLiteralNode(typeNode) && + (this.advancedClassChecks || !ts.isTypeQueryNode(typeNode)) && + !ts.isIntersectionTypeNode(typeNode) && + TsUtils.isSupportedTypeNodeKind(typeNode.kind) + ); } - public isStructObjectInitializer(objectLiteral: ts.ObjectLiteralExpression): boolean { + isStructObjectInitializer(objectLiteral: ts.ObjectLiteralExpression): boolean { if (ts.isCallLikeExpression(objectLiteral.parent)) { const signature = this.tsTypeChecker.getResolvedSignature(objectLiteral.parent); const signDecl = signature?.declaration; @@ -889,72 +946,56 @@ export class TsUtils { return false; } - public getParentSymbolName(symbol: ts.Symbol): string | undefined { + getParentSymbolName(symbol: ts.Symbol): string | undefined { const name = this.tsTypeChecker.getFullyQualifiedName(symbol); const dotPosition = name.lastIndexOf('.'); - return (dotPosition === -1) ? undefined : name.substring(0, dotPosition); + return dotPosition === -1 ? undefined : name.substring(0, dotPosition); } - public isGlobalSymbol(symbol: ts.Symbol): boolean { - let parentName = this.getParentSymbolName(symbol); + isGlobalSymbol(symbol: ts.Symbol): boolean { + const parentName = this.getParentSymbolName(symbol); return !parentName || parentName === 'global'; } - public isStdObjectAPI(symbol: ts.Symbol): boolean { - let parentName = this.getParentSymbolName(symbol); - return !!parentName && (parentName === 'Object'); - } - - public isStdReflectAPI(symbol: ts.Symbol): boolean { - let parentName = this.getParentSymbolName(symbol); - return !!parentName && (parentName === 'Reflect'); - } - - public isStdProxyHandlerAPI(symbol: ts.Symbol): boolean { - let parentName = this.getParentSymbolName(symbol); - return !!parentName && (parentName === 'ProxyHandler'); - } - - public isStdArrayAPI(symbol: ts.Symbol): boolean { - let parentName = this.getParentSymbolName(symbol); - return !!parentName && (parentName === 'Array'); - } - - public isStdArrayBufferAPI(symbol: ts.Symbol): boolean { - let parentName = this.getParentSymbolName(symbol); - return !!parentName && (parentName === 'ArrayBuffer'); + isStdSymbol(symbol: ts.Symbol): boolean { + const name = this.tsTypeChecker.getFullyQualifiedName(symbol); + return name === 'Symbol' || name === 'SymbolConstructor'; } - public isStdSymbol(symbol: ts.Symbol): boolean { - const name = this.tsTypeChecker.getFullyQualifiedName(symbol) - return name === 'Symbol'; + isStdSymbolAPI(symbol: ts.Symbol): boolean { + const parentName = this.getParentSymbolName(symbol); + return !!parentName && (parentName === 'Symbol' || parentName === 'SymbolConstructor'); } - public isStdSymbolAPI(symbol: ts.Symbol): boolean { - let parentName = this.getParentSymbolName(symbol); - return !!parentName && (parentName === 'Symbol'); + isSymbolIterator(symbol: ts.Symbol): boolean { + return this.isStdSymbolAPI(symbol) && symbol.name === 'iterator'; } - public isDefaultImport(importSpec: ts.ImportSpecifier): boolean { + static isDefaultImport(importSpec: ts.ImportSpecifier): boolean { return importSpec?.propertyName?.text === 'default'; } - public getStartPos(nodeOrComment: ts.Node | ts.CommentRange): number { - return (nodeOrComment.kind === ts.SyntaxKind.SingleLineCommentTrivia || nodeOrComment.kind === ts.SyntaxKind.MultiLineCommentTrivia) - ? (nodeOrComment as ts.CommentRange).pos - : (nodeOrComment as ts.Node).getStart(); + static getStartPos(nodeOrComment: ts.Node | ts.CommentRange): number { + return nodeOrComment.kind === ts.SyntaxKind.SingleLineCommentTrivia || + nodeOrComment.kind === ts.SyntaxKind.MultiLineCommentTrivia ? + (nodeOrComment as ts.CommentRange).pos : + (nodeOrComment as ts.Node).getStart(); } - public getEndPos(nodeOrComment: ts.Node | ts.CommentRange): number { - return (nodeOrComment.kind === ts.SyntaxKind.SingleLineCommentTrivia || nodeOrComment.kind === ts.SyntaxKind.MultiLineCommentTrivia) - ? (nodeOrComment as ts.CommentRange).end - : (nodeOrComment as ts.Node).getEnd(); + static getEndPos(nodeOrComment: ts.Node | ts.CommentRange): number { + return nodeOrComment.kind === ts.SyntaxKind.SingleLineCommentTrivia || + nodeOrComment.kind === ts.SyntaxKind.MultiLineCommentTrivia ? + (nodeOrComment as ts.CommentRange).end : + (nodeOrComment as ts.Node).getEnd(); } - public isStdRecordType(type: ts.Type): boolean { - // In TypeScript, 'Record' is defined as type alias to a mapped type. - // Thus, it should have 'aliasSymbol' and 'target' properties. The 'target' - // in this case will resolve to origin 'Record' symbol. + isStdRecordType(type: ts.Type): boolean { + + /* + * In TypeScript, 'Record' is defined as type alias to a mapped type. + * Thus, it should have 'aliasSymbol' and 'target' properties. The 'target' + * in this case will resolve to origin 'Record' symbol. + */ if (type.aliasSymbol) { const target = (type as ts.TypeReference).target; if (target) { @@ -966,22 +1007,31 @@ export class TsUtils { return false; } - public isStdPartialType(type: ts.Type): boolean { + isStdErrorType(type: ts.Type): boolean { + const symbol = type.symbol; + if (!symbol) { + return false; + } + const name = this.tsTypeChecker.getFullyQualifiedName(symbol); + return name === 'Error' && this.isGlobalSymbol(symbol); + } + + isStdPartialType(type: ts.Type): boolean { const sym = type.aliasSymbol; return !!sym && sym.getName() === 'Partial' && this.isGlobalSymbol(sym); } - public isStdRequiredType(type: ts.Type): boolean { + isStdRequiredType(type: ts.Type): boolean { const sym = type.aliasSymbol; return !!sym && sym.getName() === 'Required' && this.isGlobalSymbol(sym); } - - public isStdReadonlyType(type: ts.Type): boolean { + + isStdReadonlyType(type: ts.Type): boolean { const sym = type.aliasSymbol; return !!sym && sym.getName() === 'Readonly' && this.isGlobalSymbol(sym); } - public isLibraryType(type: ts.Type): boolean { + isLibraryType(type: ts.Type): boolean { const nonNullableType = type.getNonNullableType(); if (nonNullableType.isUnion()) { for (const componentType of nonNullableType.types) { @@ -994,57 +1044,66 @@ export class TsUtils { return this.isLibrarySymbol(nonNullableType.aliasSymbol ?? nonNullableType.getSymbol()); } - public hasLibraryType(node: ts.Node): boolean { + hasLibraryType(node: ts.Node): boolean { return this.isLibraryType(this.tsTypeChecker.getTypeAtLocation(node)); } - public isLibrarySymbol(sym: ts.Symbol | undefined): boolean { - if (sym && sym.declarations && sym.declarations.length > 0) { + isLibrarySymbol(sym: ts.Symbol | undefined): boolean { + if (sym?.declarations && sym.declarations.length > 0) { const srcFile = sym.declarations[0].getSourceFile(); if (!srcFile) { return false; } - const fileName = srcFile.fileName - // Symbols from both *.ts and *.d.ts files should obey interop rules. - // We disable such behavior for *.ts files in the test mode due to lack of 'ets' - // extension support. + const fileName = srcFile.fileName; + + /* + * Symbols from both *.ts and *.d.ts files should obey interop rules. + * We disable such behavior for *.ts files in the test mode due to lack of 'ets' + * extension support. + */ const ext = path.extname(fileName).toLowerCase(); const isThirdPartyCode = - ARKTS_IGNORE_DIRS.some(ignore => pathContainsDirectory(path.normalize(fileName), ignore)) || - ARKTS_IGNORE_FILES.some(ignore => path.basename(fileName) === ignore); - const isEts = (ext === '.ets'); - const isTs = (ext === '.ts' && !srcFile.isDeclarationFile); - const isStatic = (isEts || (isTs && this.testMode)) && !isThirdPartyCode; - // We still need to confirm support for certain API from the - // TypeScript standard library in ArkTS. Thus, for now do not - // count standard library modules. - return !isStatic && - !STANDARD_LIBRARIES.includes(path.basename(fileName).toLowerCase()); + ARKTS_IGNORE_DIRS.some((ignore) => { + return pathContainsDirectory(path.normalize(fileName), ignore); + }) || + ARKTS_IGNORE_FILES.some((ignore) => { + return path.basename(fileName) === ignore; + }); + const isEts = ext === '.ets'; + const isTs = ext === '.ts' && !srcFile.isDeclarationFile; + const isStatic = (isEts || isTs && this.testMode) && !isThirdPartyCode; + const isStdLib = STANDARD_LIBRARIES.includes(path.basename(fileName).toLowerCase()); + + /* + * We still need to confirm support for certain API from the + * TypeScript standard library in ArkTS. Thus, for now do not + * count standard library modules as dynamic. + */ + return !isStatic && !isStdLib; } return false; } - public isStdFunctionType(type: ts.Type) { - const sym = type.getSymbol(); - return sym && sym.getName() === 'Function' && this.isGlobalSymbol(sym); - } - - public isDynamicType(type: ts.Type | undefined): boolean | undefined { + isDynamicType(type: ts.Type | undefined): boolean | undefined { if (type === undefined) { return false; } - // Return 'true' if it is an object of library type initialization, otherwise - // return 'false' if it is not an object of standard library type one. - // In the case of standard library type we need to determine context. + /* + * Return 'true' if it is an object of library type initialization, otherwise + * return 'false' if it is not an object of standard library type one. + * In the case of standard library type we need to determine context. + */ - // Check the non-nullable version of type to eliminate 'undefined' type - // from the union type elements. + /* + * Check the non-nullable version of type to eliminate 'undefined' type + * from the union type elements. + */ type = type.getNonNullableType(); if (type.isUnion()) { - for (let compType of type.types) { - let isDynamic = this.isDynamicType(compType); + for (const compType of type.types) { + const isDynamic = this.isDynamicType(compType); if (isDynamic || isDynamic === undefined) { return isDynamic; } @@ -1056,36 +1115,64 @@ export class TsUtils { return true; } - if (!isStdLibraryType(type) && !isIntrinsicObjectType(type) && !this.isAnyType(type)) { + if (!isStdLibraryType(type) && !isIntrinsicObjectType(type) && !TsUtils.isAnyType(type)) { return false; } return undefined; } - public isObjectType(type: ts.Type): type is ts.ObjectType { - return !!(type.flags & ts.TypeFlags.Object) + static isObjectType(type: ts.Type): type is ts.ObjectType { + return !!(type.flags & ts.TypeFlags.Object); } - private isAnonymous(type: ts.Type): boolean { - if (this.isObjectType(type)) { + private static isAnonymous(type: ts.Type): boolean { + if (TsUtils.isObjectType(type)) { return !!(type.objectFlags & ts.ObjectFlags.Anonymous); } return false; } - public isDynamicLiteralInitializer(expr: ts.Expression): boolean { + private isDynamicLiteralInitializerHandleCallExpression(callExpr: ts.CallExpression): boolean { + const type = this.tsTypeChecker.getTypeAtLocation(callExpr.expression); + + if (TsUtils.isAnyType(type)) { + return true; + } + + let sym: ts.Symbol | undefined = type.symbol; + if (this.isLibrarySymbol(sym)) { + return true; + } + + /* + * #13483: + * x.foo({ ... }), where 'x' is exported from some library: + */ + if (ts.isPropertyAccessExpression(callExpr.expression)) { + sym = this.trueSymbolAtLocation(callExpr.expression.expression); + if (sym && this.isLibrarySymbol(sym)) { + return true; + } + } + + return false; + } + + isDynamicLiteralInitializer(expr: ts.Expression): boolean { if (!ts.isObjectLiteralExpression(expr) && !ts.isArrayLiteralExpression(expr)) { return false; } - // Handle nested literals: - // { f: { ... } } + /* + * Handle nested literals: + * { f: { ... } } + */ let curNode: ts.Node = expr; while (ts.isObjectLiteralExpression(curNode) || ts.isArrayLiteralExpression(curNode)) { const exprType = this.tsTypeChecker.getContextualType(curNode); - if (exprType !== undefined && !this.isAnonymous(exprType)) { - const res = this.isDynamicType(exprType) + if (exprType !== undefined && !TsUtils.isAnonymous(exprType)) { + const res = this.isDynamicType(exprType); if (res !== undefined) { return res; } @@ -1097,37 +1184,22 @@ export class TsUtils { } } - // Handle calls with literals: - // foo({ ... }) - if (ts.isCallExpression(curNode)) { - const callExpr = curNode as ts.CallExpression; - const type = this.tsTypeChecker.getTypeAtLocation(callExpr.expression) - - if (this.isAnyType(type)) { - return true; - } - - let sym: ts.Symbol | undefined = type.symbol; - if(this.isLibrarySymbol(sym)) { - return true; - } - - // #13483: - // x.foo({ ... }), where 'x' is exported from some library: - if (ts.isPropertyAccessExpression(callExpr.expression)) { - sym = this.trueSymbolAtLocation(callExpr.expression.expression); - if (sym && this.isLibrarySymbol(sym)) { - return true; - } - } + /* + * Handle calls with literals: + * foo({ ... }) + */ + if (ts.isCallExpression(curNode) && this.isDynamicLiteralInitializerHandleCallExpression(curNode)) { + return true; } - // Handle property assignments with literals: - // obj.f = { ... } + /* + * Handle property assignments with literals: + * obj.f = { ... } + */ if (ts.isBinaryExpression(curNode)) { - const binExpr = curNode as ts.BinaryExpression; + const binExpr = curNode; if (ts.isPropertyAccessExpression(binExpr.left)) { - const propAccessExpr = binExpr.left as ts.PropertyAccessExpression; + const propAccessExpr = binExpr.left; const type = this.tsTypeChecker.getTypeAtLocation(propAccessExpr.expression); return this.isLibrarySymbol(type.symbol); } @@ -1136,13 +1208,17 @@ export class TsUtils { return false; } - public isEsObjectType(typeNode: ts.TypeNode): boolean { - return ts.isTypeReferenceNode(typeNode) && ts.isIdentifier(typeNode.typeName) && - typeNode.typeName.text == ES_OBJECT; + static isEsObjectType(typeNode: ts.TypeNode | undefined): boolean { + return ( + !!typeNode && + ts.isTypeReferenceNode(typeNode) && + ts.isIdentifier(typeNode.typeName) && + typeNode.typeName.text === ES_OBJECT + ); } - public isInsideBlock(node: ts.Node): boolean { - let par = node.parent + static isInsideBlock(node: ts.Node): boolean { + let par = node.parent; while (par) { if (ts.isBlock(par)) { return true; @@ -1152,150 +1228,155 @@ export class TsUtils { return false; } - public isEsObjectPossiblyAllowed(typeRef: ts.TypeReferenceNode): boolean { + static isEsObjectPossiblyAllowed(typeRef: ts.TypeReferenceNode): boolean { return ts.isVariableDeclaration(typeRef.parent); } - public isValueAssignableToESObject(node: ts.Node): boolean { + isValueAssignableToESObject(node: ts.Node): boolean { if (ts.isArrayLiteralExpression(node) || ts.isObjectLiteralExpression(node)) { return false; } const valueType = this.tsTypeChecker.getTypeAtLocation(node); - return this.isUnsupportedType(valueType) || this.isAnonymousType(valueType) + return TsUtils.isUnsupportedType(valueType) || TsUtils.isAnonymousType(valueType); } - public getVariableDeclarationTypeNode(node: ts.Node): ts.TypeNode | undefined { - let sym = this.trueSymbolAtLocation(node); + getVariableDeclarationTypeNode(node: ts.Node): ts.TypeNode | undefined { + const sym = this.trueSymbolAtLocation(node); if (sym === undefined) { return undefined; } - return this.getSymbolDeclarationTypeNode(sym); + return TsUtils.getSymbolDeclarationTypeNode(sym); } - public getSymbolDeclarationTypeNode(sym: ts.Symbol): ts.TypeNode | undefined { - const decl = this.getDeclaration(sym); + static getSymbolDeclarationTypeNode(sym: ts.Symbol): ts.TypeNode | undefined { + const decl = TsUtils.getDeclaration(sym); if (!!decl && ts.isVariableDeclaration(decl)) { return decl.type; } return undefined; } - public hasEsObjectType(node: ts.Node): boolean { - const typeNode = this.getVariableDeclarationTypeNode(node) - return typeNode !== undefined && this.isEsObjectType(typeNode); + hasEsObjectType(node: ts.Node): boolean { + const typeNode = this.getVariableDeclarationTypeNode(node); + return typeNode !== undefined && TsUtils.isEsObjectType(typeNode); } - public symbolHasEsObjectType(sym: ts.Symbol): boolean { - const typeNode = this.getSymbolDeclarationTypeNode(sym); - return typeNode !== undefined && this.isEsObjectType(typeNode); + static symbolHasEsObjectType(sym: ts.Symbol): boolean { + const typeNode = TsUtils.getSymbolDeclarationTypeNode(sym); + return typeNode !== undefined && TsUtils.isEsObjectType(typeNode); } - public isEsObjectSymbol(sym: ts.Symbol): boolean { - let decl = this.getDeclaration(sym); - return !!decl && ts.isTypeAliasDeclaration(decl) && decl.name.escapedText == ES_OBJECT && - decl.type.kind === ts.SyntaxKind.AnyKeyword; + static isEsObjectSymbol(sym: ts.Symbol): boolean { + const decl = TsUtils.getDeclaration(sym); + return ( + !!decl && + ts.isTypeAliasDeclaration(decl) && + decl.name.escapedText === ES_OBJECT && + decl.type.kind === ts.SyntaxKind.AnyKeyword + ); } - public isAnonymousType(type: ts.Type): boolean { + static isAnonymousType(type: ts.Type): boolean { if (type.isUnionOrIntersection()) { - for (let compType of type.types) { - if (this.isAnonymousType(compType)) { + for (const compType of type.types) { + if (TsUtils.isAnonymousType(compType)) { return true; } } return false; } - return (type.flags & ts.TypeFlags.Object) !== 0 && - ((type as ts.ObjectType).objectFlags & ts.ObjectFlags.Anonymous) !== 0; + return ( + (type.flags & ts.TypeFlags.Object) !== 0 && ((type as ts.ObjectType).objectFlags & ts.ObjectFlags.Anonymous) !== 0 + ); } - public getSymbolOfCallExpression(callExpr: ts.CallExpression): ts.Symbol | undefined { + getSymbolOfCallExpression(callExpr: ts.CallExpression): ts.Symbol | undefined { const signature = this.tsTypeChecker.getResolvedSignature(callExpr); const signDecl = signature?.getDeclaration(); - if (signDecl && signDecl.name) { + if (signDecl?.name) { return this.trueSymbolAtLocation(signDecl.name); } return undefined; } - // has to be re-implemented with local loop detection - public typeIsRecursive(topType: ts.Type, type: ts.Type | undefined = undefined): boolean { - if (type == undefined) { - type = topType; - } else if (type == topType) { - return true; - } else if (type.aliasSymbol) { - return false; - } - - if (type.isUnion()) { - for (let unionElem of type.types) { - if (this.typeIsRecursive(topType, unionElem)) { - return true; - } - } - } - if (type.flags & ts.TypeFlags.Object && (type as ts.ObjectType).objectFlags & ts.ObjectFlags.Reference) { - const typeArgs = this.tsTypeChecker.getTypeArguments(type as ts.TypeReference); - if (typeArgs) { - for (const typeArg of typeArgs) { - if (this.typeIsRecursive(topType, typeArg)) { - return true; - } - } - } - } - return false; - } - - public isClassValueType(type: ts.Type): boolean { - if ((type.flags & ts.TypeFlags.Object) === 0 || ((type as ts.ObjectType).objectFlags & ts.ObjectFlags.Anonymous) === 0) { + static isClassValueType(type: ts.Type): boolean { + if ( + (type.flags & ts.TypeFlags.Object) === 0 || + ((type as ts.ObjectType).objectFlags & ts.ObjectFlags.Anonymous) === 0 + ) { return false; } return type.symbol && (type.symbol.flags & ts.SymbolFlags.Class) !== 0; } - public isClassObjectExpression(expr: ts.Expression): boolean { - if (!this.isClassValueType(this.tsTypeChecker.getTypeAtLocation(expr))) { + isClassObjectExpression(expr: ts.Expression): boolean { + if (!TsUtils.isClassValueType(this.tsTypeChecker.getTypeAtLocation(expr))) { return false; } - let symbol = this.trueSymbolAtLocation(expr); + const symbol = this.trueSymbolAtLocation(expr); return !symbol || (symbol.flags & ts.SymbolFlags.Class) === 0; } - public isClassTypeExrepssion(expr: ts.Expression): boolean { - let sym = this.trueSymbolAtLocation(expr); + isClassTypeExrepssion(expr: ts.Expression): boolean { + const sym = this.trueSymbolAtLocation(expr); return sym !== undefined && (sym.flags & ts.SymbolFlags.Class) !== 0; } - public isFunctionCalledRecursively(funcExpr: ts.FunctionExpression): boolean { - if (!funcExpr.name) return false; + isFunctionCalledRecursively(funcExpr: ts.FunctionExpression): boolean { + if (!funcExpr.name) { + return false; + } const sym = this.tsTypeChecker.getSymbolAtLocation(funcExpr.name); - if (!sym) return false; + if (!sym) { + return false; + } let found = false; - const self = this; - function visitNode(tsNode: ts.Node) { - // Stop visiting child nodes if finished searching. - if (found) { - return; - } - - if (ts.isCallExpression(tsNode) && ts.isIdentifier(tsNode.expression)) { - const callSym = self.tsTypeChecker.getSymbolAtLocation(tsNode.expression); + const callback = (node: ts.Node): void => { + if (ts.isCallExpression(node) && ts.isIdentifier(node.expression)) { + const callSym = this.tsTypeChecker.getSymbolAtLocation(node.expression); if (callSym && callSym === sym) { found = true; - return; } } + }; - // Visit children nodes. - tsNode.forEachChild(visitNode); - } + const stopCondition = (node: ts.Node): boolean => { + void node; + return found; + }; - visitNode(funcExpr); + forEachNodeInSubtree(funcExpr, callback, stopCondition); return found; } + + getTypeOrTypeConstraintAtLocation(expr: ts.Expression): ts.Type { + const type = this.tsTypeChecker.getTypeAtLocation(expr); + if (type.isTypeParameter()) { + const constraint = type.getConstraint(); + if (constraint) { + return constraint; + } + } + return type; + } + + private areCompatibleFunctionals(lhsType: ts.Type, rhsType: ts.Type): boolean { + return ( + (this.isStdFunctionType(lhsType) || TsUtils.isFunctionalType(lhsType)) && + (this.isStdFunctionType(rhsType) || TsUtils.isFunctionalType(rhsType)) + ); + } + + private static isFunctionalType(type: ts.Type): boolean { + const callSigns = type.getCallSignatures(); + return callSigns && callSigns.length > 0; + } + + private isStdFunctionType(type: ts.Type): boolean { + const sym = type.getSymbol(); + return !!sym && sym.getName() === 'Function' && this.isGlobalSymbol(sym); + } } diff --git a/ets2panda/linter/src/utils/consts/ArkUIDecorators.ts b/ets2panda/linter/lib/utils/consts/ArkUIDecorators.ts similarity index 98% rename from ets2panda/linter/src/utils/consts/ArkUIDecorators.ts rename to ets2panda/linter/lib/utils/consts/ArkUIDecorators.ts index b60a1f522bf9b166840b4fe37124c60b291db6a5..295314fe4c6abbf366143617695c99752ed77632 100644 --- a/ets2panda/linter/src/utils/consts/ArkUIDecorators.ts +++ b/ets2panda/linter/lib/utils/consts/ArkUIDecorators.ts @@ -36,5 +36,5 @@ export const ARKUI_DECORATORS = [ 'StorageLink', 'StorageProp', 'Styles', - 'Watch', + 'Watch' ]; diff --git a/ets2panda/linter/src/utils/consts/ArktsIgnorePaths.ts b/ets2panda/linter/lib/utils/consts/ArktsIgnorePaths.ts similarity index 75% rename from ets2panda/linter/src/utils/consts/ArktsIgnorePaths.ts rename to ets2panda/linter/lib/utils/consts/ArktsIgnorePaths.ts index ca29111acbd80c0ddef33c69535c82997937a355..73e063e4b8b001d5c6ec613c08e5dada90ade814 100644 --- a/ets2panda/linter/src/utils/consts/ArktsIgnorePaths.ts +++ b/ets2panda/linter/lib/utils/consts/ArktsIgnorePaths.ts @@ -13,5 +13,10 @@ * limitations under the License. */ -export const ARKTS_IGNORE_DIRS = ['node_modules', 'oh_modules', 'build', '.preview']; +export const ARKTS_IGNORE_DIRS_NO_OH_MODULES = ['node_modules', 'build', '.preview']; + +export const ARKTS_IGNORE_DIRS_OH_MODULES = 'oh_modules'; + +export const ARKTS_IGNORE_DIRS = [...ARKTS_IGNORE_DIRS_NO_OH_MODULES, ARKTS_IGNORE_DIRS_OH_MODULES]; + export const ARKTS_IGNORE_FILES = ['hvigorfile.ts']; diff --git a/ets2panda/linter/src/utils/consts/ESObject.ts b/ets2panda/linter/lib/utils/consts/ESObject.ts similarity index 94% rename from ets2panda/linter/src/utils/consts/ESObject.ts rename to ets2panda/linter/lib/utils/consts/ESObject.ts index e1c9a2b10a9a2bafb8b7a716699099f3e38ac319..2837d1ceacb950c2dbd3669cfff7e1f90e8a0461 100644 --- a/ets2panda/linter/src/utils/consts/ESObject.ts +++ b/ets2panda/linter/lib/utils/consts/ESObject.ts @@ -13,4 +13,4 @@ * limitations under the License. */ -export const ES_OBJECT = 'ESObject' +export const ES_OBJECT = 'ESObject'; diff --git a/ets2panda/linter/src/utils/consts/FunctionHasNoReturnErrorCode.ts b/ets2panda/linter/lib/utils/consts/FunctionHasNoReturnErrorCode.ts similarity index 100% rename from ets2panda/linter/src/utils/consts/FunctionHasNoReturnErrorCode.ts rename to ets2panda/linter/lib/utils/consts/FunctionHasNoReturnErrorCode.ts diff --git a/ets2panda/linter/src/utils/consts/LimitedStandardUtilityTypes.ts b/ets2panda/linter/lib/utils/consts/LimitedStandardUtilityTypes.ts similarity index 98% rename from ets2panda/linter/src/utils/consts/LimitedStandardUtilityTypes.ts rename to ets2panda/linter/lib/utils/consts/LimitedStandardUtilityTypes.ts index 259443d9933fed46f4f6ea269251428b9f40db93..a9853ecb4d5e8a2645fcc2be0cc4cd950aa1ecaf 100644 --- a/ets2panda/linter/src/utils/consts/LimitedStandardUtilityTypes.ts +++ b/ets2panda/linter/lib/utils/consts/LimitedStandardUtilityTypes.ts @@ -30,5 +30,5 @@ export const LIMITED_STANDARD_UTILITY_TYPES = [ 'Uppercase', 'Lowercase', 'Capitalize', - 'Uncapitalize', + 'Uncapitalize' ]; diff --git a/ets2panda/linter/lib/utils/consts/LimitedStdAPI.ts b/ets2panda/linter/lib/utils/consts/LimitedStdAPI.ts new file mode 100644 index 0000000000000000000000000000000000000000..e32d47e4d326f59fb6e5db479e8979acf473cac3 --- /dev/null +++ b/ets2panda/linter/lib/utils/consts/LimitedStdAPI.ts @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2023-2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { FaultID } from '../../Problems'; + +const LIMITED_STD_SYMBOL_API = [ + // properties + 'asyncIterator', + 'description', + 'hasInstance', + 'isConcatSpreadable', + 'match', + 'matchAll', + 'replace', + 'search', + 'species', + 'split', + 'toPrimitive', + 'toStringTag', + 'unscopables', + + // methods + 'for', + 'keyFor', + 'toString', + 'valueOf' +]; + +const LIMITED_STD_OBJECT_API = [ + // properties + '__proto__', + + // methods + '__defineGetter__', + '__defineSetter__', + '__lookupGetter__', + '__lookupSetter__', + 'assign', + 'create', + 'defineProperties', + 'defineProperty', + 'freeze', + 'fromEntries', + 'getOwnPropertyDescriptor', + 'getOwnPropertyDescriptors', + 'getOwnPropertySymbols', + 'getPrototypeOf', + 'hasOwnProperty', + 'is', + 'isExtensible', + 'isFrozen', + 'isPrototypeOf', + 'isSealed', + 'preventExtensions', + 'propertyIsEnumerable', + 'seal', + 'setPrototypeOf' +]; + +const LIMITED_STD_PROXYHANDLER_API = [ + // properties + + // methods + 'apply', + 'construct', + 'defineProperty', + 'deleteProperty', + 'get', + 'getOwnPropertyDescriptor', + 'getPrototypeOf', + 'has', + 'isExtensible', + 'ownKeys', + 'preventExtensions', + 'set', + 'setPrototypeOf' +]; + +const LIMITED_STD_REFLECT_API = [ + // properties + + // methods + 'apply', + 'construct', + 'defineProperty', + 'deleteProperty', + 'getOwnPropertyDescriptor', + 'getPrototypeOf', + 'isExtensible', + 'preventExtensions', + 'setPrototypeOf' +]; + +const LIMITED_STD_FUNCTION_API_APPLY_CALL = [ + // properties + + // methods + 'apply', + 'call' +]; + +const LIMITED_STD_FUNCTION_API_BIND = [ + // properties + + // methods + 'bind' +]; + +const LIMITED_STD_GLOBAL_API = [ + // properties + + // methods + 'eval' +]; + +const STD_SYMBOL_ENTRY = [{ api: LIMITED_STD_SYMBOL_API, faultId: FaultID.SymbolType }]; +const STD_OBJECT_ENTRY = [{ api: LIMITED_STD_OBJECT_API, faultId: FaultID.LimitedStdLibApi }]; +const STD_PROXYHANDLER_ENTRY = [{ api: LIMITED_STD_PROXYHANDLER_API, faultId: FaultID.LimitedStdLibApi }]; +const STD_REFLECT_ENTRY = [{ api: LIMITED_STD_REFLECT_API, faultId: FaultID.LimitedStdLibApi }]; +const STD_FUNCTION_ENTRY = [ + { api: LIMITED_STD_FUNCTION_API_APPLY_CALL, faultId: FaultID.FunctionApplyCall }, + { api: LIMITED_STD_FUNCTION_API_BIND, faultId: FaultID.FunctionBind } +]; +const STD_GLOBAL_ENTRY = [{ api: LIMITED_STD_GLOBAL_API, faultId: FaultID.LimitedStdLibApi }]; + +export type LimitedStdLibApiEntry = { api: string[]; faultId: FaultID }; + +export const LIMITED_STD_API = new Map([ + [undefined, STD_GLOBAL_ENTRY], + ['Object', STD_OBJECT_ENTRY], + ['ObjectConstructor', STD_OBJECT_ENTRY], + ['Reflect', STD_REFLECT_ENTRY], + ['ProxyHandler', STD_PROXYHANDLER_ENTRY], + ['Symbol', STD_SYMBOL_ENTRY], + ['SymbolConstructor', STD_SYMBOL_ENTRY], + ['Function', STD_FUNCTION_ENTRY], + ['CallableFunction', STD_FUNCTION_ENTRY] +]); diff --git a/ets2panda/linter/src/utils/consts/NonInitializablePropertyDecorators.ts b/ets2panda/linter/lib/utils/consts/NonInitializablePropertyDecorators.ts similarity index 98% rename from ets2panda/linter/src/utils/consts/NonInitializablePropertyDecorators.ts rename to ets2panda/linter/lib/utils/consts/NonInitializablePropertyDecorators.ts index 251272c1496fc7591d9033d2a9f84c9e7bb49f2a..670ccb67d907135c72742b33513ab74fb7fef729 100644 --- a/ets2panda/linter/src/utils/consts/NonInitializablePropertyDecorators.ts +++ b/ets2panda/linter/lib/utils/consts/NonInitializablePropertyDecorators.ts @@ -15,4 +15,4 @@ export const NON_INITIALIZABLE_PROPERTY_DECORATORS = ['Link', 'Consume', 'ObjectLink', 'Prop', 'BuilderParam']; -export const NON_INITIALIZABLE_PROPERTY_CLASS_DECORATORS = ['CustomDialog'] +export const NON_INITIALIZABLE_PROPERTY_CLASS_DECORATORS = ['CustomDialog']; diff --git a/ets2panda/linter/src/utils/consts/NonReturnFunctionDecorators.ts b/ets2panda/linter/lib/utils/consts/NonReturnFunctionDecorators.ts similarity index 100% rename from ets2panda/linter/src/utils/consts/NonReturnFunctionDecorators.ts rename to ets2panda/linter/lib/utils/consts/NonReturnFunctionDecorators.ts diff --git a/ets2panda/linter/src/utils/consts/PropertyHasNoInitializerErrorCode.ts b/ets2panda/linter/lib/utils/consts/PropertyHasNoInitializerErrorCode.ts similarity index 100% rename from ets2panda/linter/src/utils/consts/PropertyHasNoInitializerErrorCode.ts rename to ets2panda/linter/lib/utils/consts/PropertyHasNoInitializerErrorCode.ts diff --git a/ets2panda/linter/lib/utils/consts/StandardLibraries.ts b/ets2panda/linter/lib/utils/consts/StandardLibraries.ts new file mode 100644 index 0000000000000000000000000000000000000000..2776e4ee79eae34c6f5904c1cb2c7cdf6fde4ef1 --- /dev/null +++ b/ets2panda/linter/lib/utils/consts/StandardLibraries.ts @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2023-2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export const STANDARD_LIBRARIES = [ + 'lib.dom.d.ts', + 'lib.dom.iterable.d.ts', + 'lib.webworker.d.ts', + 'lib.webworker.importscripts.d.ts', + 'lib.webworker.iterable.d.ts', + 'lib.scripthost.d.ts', + 'lib.decorators.d.ts', + 'lib.decorators.legacy.d.ts', + 'lib.es5.d.ts', + 'lib.es2015.core.d.ts', + 'lib.es2015.collection.d.ts', + 'lib.es2015.generator.d.ts', + 'lib.es2015.iterable.d.ts', + 'lib.es2015.promise.d.ts', + 'lib.es2015.proxy.d.ts', + 'lib.es2015.reflect.d.ts', + 'lib.es2015.symbol.d.ts', + 'lib.es2015.symbol.wellknown.d.ts', + 'lib.es2016.array.include.d.ts', + 'lib.es2017.object.d.ts', + 'lib.es2017.sharedmemory.d.ts', + 'lib.es2017.string.d.ts', + 'lib.es2017.intl.d.ts', + 'lib.es2017.typedarrays.d.ts', + 'lib.es2018.asyncgenerator.d.ts', + 'lib.es2018.asynciterable.d.ts', + 'lib.es2018.intl.d.ts', + 'lib.es2018.promise.d.ts', + 'lib.es2018.regexp.d.ts', + 'lib.es2019.array.d.ts', + 'lib.es2019.object.d.ts', + 'lib.es2019.string.d.ts', + 'lib.es2019.symbol.d.ts', + 'lib.es2019.intl.d.ts', + 'lib.es2020.bigint.d.ts', + 'lib.es2020.date.d.ts', + 'lib.es2020.promise.d.ts', + 'lib.es2020.sharedmemory.d.ts', + 'lib.es2020.string.d.ts', + 'lib.es2020.symbol.wellknown.d.ts', + 'lib.es2020.intl.d.ts', + 'lib.es2020.number.d.ts', + 'lib.es2021.promise.d.ts', + 'lib.es2021.string.d.ts', + 'lib.es2021.weakref.d.ts', + 'lib.es2021.intl.d.ts', + 'lib.es2022.array.d.ts', + 'lib.es2022.error.d.ts', + 'lib.es2022.intl.d.ts', + 'lib.es2022.object.d.ts', + 'lib.es2022.sharedmemory.d.ts', + 'lib.es2022.string.d.ts', + 'lib.es2022.regexp.d.ts', + 'lib.es2023.array.d.ts' +]; diff --git a/ets2panda/linter/src/utils/consts/TypedArrays.ts b/ets2panda/linter/lib/utils/consts/TypedArrays.ts similarity index 97% rename from ets2panda/linter/src/utils/consts/TypedArrays.ts rename to ets2panda/linter/lib/utils/consts/TypedArrays.ts index 9ef717311061b0620cf751533d6f0ac457a9ca88..c29dd2b73fa8f993c07818eb6fc77bcdb0a092a2 100644 --- a/ets2panda/linter/src/utils/consts/TypedArrays.ts +++ b/ets2panda/linter/lib/utils/consts/TypedArrays.ts @@ -24,5 +24,5 @@ export const TYPED_ARRAYS = [ 'Float32Array', 'Float64Array', 'BigInt64Array', - 'BigUint64Array', -] + 'BigUint64Array' +]; diff --git a/ets2panda/linter/lib/utils/functions/ContainsThis.ts b/ets2panda/linter/lib/utils/functions/ContainsThis.ts new file mode 100644 index 0000000000000000000000000000000000000000..1a720dad609f10ec0c202de1fbcd3e9e7f469ee7 --- /dev/null +++ b/ets2panda/linter/lib/utils/functions/ContainsThis.ts @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2022-2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as ts from 'typescript'; + +function scopeContainsThisVisitor(tsNode: ts.Node): boolean { + if (tsNode.kind === ts.SyntaxKind.ThisKeyword) { + return true; + } + + /* + * Visit children nodes. Skip any local declaration that defines + * its own scope as it needs to be checked separately. + */ + const isClassLike = ts.isClassDeclaration(tsNode) || ts.isClassExpression(tsNode); + const isFunctionLike = ts.isFunctionDeclaration(tsNode) || ts.isFunctionExpression(tsNode); + const isModuleDecl = ts.isModuleDeclaration(tsNode); + if (isClassLike || isFunctionLike || isModuleDecl) { + return false; + } + + for (const child of tsNode.getChildren()) { + if (scopeContainsThisVisitor(child)) { + return true; + } + } + + return false; +} + +export function scopeContainsThis(tsNode: ts.Expression | ts.Block): boolean { + return scopeContainsThisVisitor(tsNode); +} diff --git a/ets2panda/linter/src/utils/functions/DiagnosticChecker.ts b/ets2panda/linter/lib/utils/functions/DiagnosticChecker.ts similarity index 83% rename from ets2panda/linter/src/utils/functions/DiagnosticChecker.ts rename to ets2panda/linter/lib/utils/functions/DiagnosticChecker.ts index 841d0d5b4f390ea6cdd07da38bd2e58e9e00fa57..b64436f904739806768401678b388b84b09eea36 100644 --- a/ets2panda/linter/src/utils/functions/DiagnosticChecker.ts +++ b/ets2panda/linter/lib/utils/functions/DiagnosticChecker.ts @@ -13,8 +13,8 @@ * limitations under the License. */ -import * as ts from 'typescript'; +import type * as ts from 'typescript'; export interface DiagnosticChecker { - checkDiagnosticMessage(msgText: string | ts.DiagnosticMessageChain): boolean; -} \ No newline at end of file + checkDiagnosticMessage: (msgText: string | ts.DiagnosticMessageChain) => boolean; +} diff --git a/ets2panda/linter/src/utils/functions/ContainsThis.ts b/ets2panda/linter/lib/utils/functions/ForEachNodeInSubtree.ts similarity index 47% rename from ets2panda/linter/src/utils/functions/ContainsThis.ts rename to ets2panda/linter/lib/utils/functions/ForEachNodeInSubtree.ts index 958553f2808093b08b518cb2a1a7ec8a9928819c..08fd8e7148347756a44d6e2b653555b14444c54b 100644 --- a/ets2panda/linter/src/utils/functions/ContainsThis.ts +++ b/ets2panda/linter/lib/utils/functions/ForEachNodeInSubtree.ts @@ -15,29 +15,17 @@ import * as ts from 'typescript'; -export function scopeContainsThis(tsNode: ts.Node): boolean { - let found = false; - function visitNode(tsNode: ts.Node) { - // Stop visiting child nodes if finished searching. - if (found) { - return; - } - if (tsNode.kind === ts.SyntaxKind.ThisKeyword) { - found = true; - return; - } - // Visit children nodes. Skip any local declaration that defines - // its own scope as it needs to be checked separately. - if ( - !ts.isClassDeclaration(tsNode) && - !ts.isClassExpression(tsNode) && - !ts.isModuleDeclaration(tsNode) && - !ts.isFunctionDeclaration(tsNode) && - !ts.isFunctionExpression(tsNode) - ) - tsNode.forEachChild(visitNode); +export function forEachNodeInSubtree( + node: ts.Node, + cb: (n: ts.Node) => void, + stopCond?: (n: ts.Node) => boolean +): void { + cb(node); + if (stopCond && stopCond(node)) { + return; } - visitNode(tsNode); - return found; + + ts.forEachChild(node, (child) => { + forEachNodeInSubtree(child, cb, stopCond); + }); } - \ No newline at end of file diff --git a/ets2panda/linter/src/utils/functions/HasPredecessor.ts b/ets2panda/linter/lib/utils/functions/HasPredecessor.ts similarity index 95% rename from ets2panda/linter/src/utils/functions/HasPredecessor.ts rename to ets2panda/linter/lib/utils/functions/HasPredecessor.ts index 8c389cfa18e8015093195c5495ef653a039e3499..c3500a3cd74dab6e42fc41666a2677bdbf28aabe 100644 --- a/ets2panda/linter/src/utils/functions/HasPredecessor.ts +++ b/ets2panda/linter/lib/utils/functions/HasPredecessor.ts @@ -13,7 +13,7 @@ * limitations under the License. */ -import * as ts from 'typescript'; +import type * as ts from 'typescript'; export function hasPredecessor(node: ts.Node, predicate: (node: ts.Node) => boolean): boolean { let parent = node.parent; diff --git a/ets2panda/linter/src/utils/functions/IsStdLibrary.ts b/ets2panda/linter/lib/utils/functions/IsStdLibrary.ts similarity index 85% rename from ets2panda/linter/src/utils/functions/IsStdLibrary.ts rename to ets2panda/linter/lib/utils/functions/IsStdLibrary.ts index 95c15e4a47cfe719ddb299cac20d84607ea21ce3..3df8028eedece6d0c49427ddf7e25260224b0f17 100644 --- a/ets2panda/linter/src/utils/functions/IsStdLibrary.ts +++ b/ets2panda/linter/lib/utils/functions/IsStdLibrary.ts @@ -13,7 +13,7 @@ * limitations under the License. */ -import * as ts from 'typescript'; +import type * as ts from 'typescript'; import * as path from 'node:path'; import { STANDARD_LIBRARIES } from '../consts/StandardLibraries'; @@ -21,10 +21,10 @@ export function isStdLibraryType(type: ts.Type): boolean { return isStdLibrarySymbol(type.aliasSymbol ?? type.getSymbol()); } -export function isStdLibrarySymbol(sym: ts.Symbol | undefined) { - if (sym && sym.declarations && sym.declarations.length > 0) { +export function isStdLibrarySymbol(sym: ts.Symbol | undefined): boolean { + if (sym?.declarations && sym.declarations.length > 0) { const srcFile = sym.declarations[0].getSourceFile(); return srcFile && STANDARD_LIBRARIES.includes(path.basename(srcFile.fileName).toLowerCase()); } return false; -} \ No newline at end of file +} diff --git a/ets2panda/linter/src/utils/functions/IsStruct.ts b/ets2panda/linter/lib/utils/functions/IsStruct.ts similarity index 81% rename from ets2panda/linter/src/utils/functions/IsStruct.ts rename to ets2panda/linter/lib/utils/functions/IsStruct.ts index 4ba05fe20d8b1c01751cdc093d8dcd11b9675ccb..66ca6708a8ad6ba599535e154112a6cf7204f1b0 100644 --- a/ets2panda/linter/src/utils/functions/IsStruct.ts +++ b/ets2panda/linter/lib/utils/functions/IsStruct.ts @@ -13,10 +13,10 @@ * limitations under the License. */ -import * as ts from 'typescript'; +import type * as ts from 'typescript'; import { LinterConfig } from '../../TypeScriptLinterConfig'; -export function isStruct(symbol: ts.Symbol) { +export function isStruct(symbol: ts.Symbol): boolean { if (!symbol.declarations) { return false; } @@ -28,10 +28,10 @@ export function isStruct(symbol: ts.Symbol) { return false; } -export function isStructDeclarationKind(kind: ts.SyntaxKind) { +export function isStructDeclarationKind(kind: ts.SyntaxKind): boolean { return LinterConfig.tsSyntaxKindNames[kind] === 'StructDeclaration'; } -export function isStructDeclaration(node: ts.Node) { +export function isStructDeclaration(node: ts.Node): boolean { return isStructDeclarationKind(node.kind); } diff --git a/ets2panda/linter/src/utils/functions/LibraryTypeCallDiagnosticChecker.ts b/ets2panda/linter/lib/utils/functions/LibraryTypeCallDiagnosticChecker.ts similarity index 63% rename from ets2panda/linter/src/utils/functions/LibraryTypeCallDiagnosticChecker.ts rename to ets2panda/linter/lib/utils/functions/LibraryTypeCallDiagnosticChecker.ts index 72778922bfed4c2efc5e60cb04287f342e8344f2..2a9af9cfe3379247738bab5b144dae817a6ac751 100644 --- a/ets2panda/linter/src/utils/functions/LibraryTypeCallDiagnosticChecker.ts +++ b/ets2panda/linter/lib/utils/functions/LibraryTypeCallDiagnosticChecker.ts @@ -13,19 +13,26 @@ * limitations under the License. */ -import * as ts from 'typescript'; -import { DiagnosticChecker } from './DiagnosticChecker'; +import type * as ts from 'typescript'; +import type { DiagnosticChecker } from './DiagnosticChecker'; -// Current approach relates on error code and error message matching and it is quite fragile, -// so this place should be checked thoroughly in the case of typescript upgrade +/* + * Current approach relates on error code and error message matching and it is quite fragile, + * so this place should be checked thoroughly in the case of typescript upgrade + */ export const TYPE_0_IS_NOT_ASSIGNABLE_TO_TYPE_1_ERROR_CODE = 2322; -export const TYPE_UNKNOWN_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE = /^Type '(.*)\bunknown\b(.*)' is not assignable to type '.*'\.$/; -export const TYPE_NULL_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE = /^Type 'null' is not assignable to type '.*'\.$/; -export const TYPE_UNDEFINED_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE = /^Type 'undefined' is not assignable to type '.*'\.$/; +export const TYPE_UNKNOWN_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE = + /^Type '(.*)\bunknown\b(.*)' is not assignable to type '.*'\.$/; +export const TYPE_NULL_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE = /^Type '(.*)\bnull\b(.*)' is not assignable to type '.*'\.$/; +export const TYPE_UNDEFINED_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE = + /^Type '(.*)\bundefined\b(.*)' is not assignable to type '.*'\.$/; export const ARGUMENT_OF_TYPE_0_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_ERROR_CODE = 2345; -export const ARGUMENT_OF_TYPE_NULL_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_RE = /^Argument of type 'null' is not assignable to parameter of type '.*'\.$/; -export const ARGUMENT_OF_TYPE_UNDEFINED_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_RE = /^Argument of type 'undefined' is not assignable to parameter of type '.*'\.$/; +export const NO_OVERLOAD_MATCHES_THIS_CALL_ERROR_CODE = 2769; +export const ARGUMENT_OF_TYPE_NULL_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_RE = + /^Argument of type '(.*)\bnull\b(.*)' is not assignable to parameter of type '.*'\.$/; +export const ARGUMENT_OF_TYPE_UNDEFINED_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_RE = + /^Argument of type '(.*)\bundefined\b(.*)' is not assignable to parameter of type '.*'\.$/; export class LibraryTypeCallDiagnosticChecker implements DiagnosticChecker { inLibCall: boolean = false; @@ -35,15 +42,16 @@ export class LibraryTypeCallDiagnosticChecker implements DiagnosticChecker { constructor(filteredDiagnosticMessages: Set) { this.filteredDiagnosticMessages = filteredDiagnosticMessages; } - - configure(inLibCall: boolean, diagnosticMessages: Array) { + + configure(inLibCall: boolean, diagnosticMessages: Array): void { this.inLibCall = inLibCall; this.diagnosticMessages = diagnosticMessages; } checkMessageText(msg: string): boolean { if (this.inLibCall) { - const match = msg.match(ARGUMENT_OF_TYPE_NULL_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_RE) || + const match = + msg.match(ARGUMENT_OF_TYPE_NULL_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_RE) || msg.match(ARGUMENT_OF_TYPE_UNDEFINED_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_RE) || msg.match(TYPE_UNDEFINED_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE) || msg.match(TYPE_NULL_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE); @@ -53,7 +61,7 @@ export class LibraryTypeCallDiagnosticChecker implements DiagnosticChecker { } checkMessageChain(chain: ts.DiagnosticMessageChain): boolean { - if (chain.code == TYPE_0_IS_NOT_ASSIGNABLE_TO_TYPE_1_ERROR_CODE) { + if (chain.code === TYPE_0_IS_NOT_ASSIGNABLE_TO_TYPE_1_ERROR_CODE) { if (chain.messageText.match(TYPE_UNKNOWN_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE)) { return false; } @@ -64,11 +72,25 @@ export class LibraryTypeCallDiagnosticChecker implements DiagnosticChecker { return false; } } - return chain.next == undefined ? true : this.checkMessageChain(chain.next[0]); - }; + if (chain.code === ARGUMENT_OF_TYPE_0_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_ERROR_CODE) { + if ( + this.inLibCall && + chain.messageText.match(ARGUMENT_OF_TYPE_UNDEFINED_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_RE) + ) { + return false; + } + if ( + this.inLibCall && + chain.messageText.match(ARGUMENT_OF_TYPE_NULL_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_RE) + ) { + return false; + } + } + return chain.next === undefined ? true : this.checkMessageChain(chain.next[0]); + } - checkFilteredDiagnosticMessages(msgText: ts.DiagnosticMessageChain | string) { - if (this.filteredDiagnosticMessages.size == 0) { + checkFilteredDiagnosticMessages(msgText: ts.DiagnosticMessageChain | string): boolean { + if (this.filteredDiagnosticMessages.size === 0) { return true; } @@ -77,8 +99,8 @@ export class LibraryTypeCallDiagnosticChecker implements DiagnosticChecker { } for (const msgChain of this.filteredDiagnosticMessages) { - if (typeof msgText == 'string') { - if (msgText == msgChain.messageText) { + if (typeof msgText === 'string') { + if (msgText === msgChain.messageText) { return false; } continue; @@ -91,16 +113,16 @@ export class LibraryTypeCallDiagnosticChecker implements DiagnosticChecker { return true; } - if (curMsg.code != curFilteredMsg.code) { + if (curMsg.code !== curFilteredMsg.code) { return true; } - if (curMsg.messageText != curFilteredMsg.messageText) { + if (curMsg.messageText !== curFilteredMsg.messageText) { return true; } - curMsg = curMsg.next ? curMsg.next[0]: undefined; - curFilteredMsg = curFilteredMsg.next ? curFilteredMsg.next[0]: undefined; + curMsg = curMsg.next ? curMsg.next[0] : undefined; + curFilteredMsg = curFilteredMsg.next ? curFilteredMsg.next[0] : undefined; } return false; @@ -117,7 +139,7 @@ export class LibraryTypeCallDiagnosticChecker implements DiagnosticChecker { return false; } - if (typeof msgText == 'string') { + if (typeof msgText === 'string') { return this.checkMessageText(msgText); } @@ -127,4 +149,4 @@ export class LibraryTypeCallDiagnosticChecker implements DiagnosticChecker { } return true; } -} \ No newline at end of file +} diff --git a/ets2panda/linter/src/utils/functions/LogTscDiagnostic.ts b/ets2panda/linter/lib/utils/functions/LogTscDiagnostic.ts similarity index 95% rename from ets2panda/linter/src/utils/functions/LogTscDiagnostic.ts rename to ets2panda/linter/lib/utils/functions/LogTscDiagnostic.ts index a5b217420a46af4d5ef77b3d1f0c648ddb0c58bf..23a8ab166775c05bc05854c0e4c906a67d1f4dfe 100644 --- a/ets2panda/linter/src/utils/functions/LogTscDiagnostic.ts +++ b/ets2panda/linter/lib/utils/functions/LogTscDiagnostic.ts @@ -15,7 +15,7 @@ import * as ts from 'typescript'; -export function logTscDiagnostic(diagnostics: readonly ts.Diagnostic[], log: (message: any, ...args: any[]) => void) { +export function logTscDiagnostic(diagnostics: readonly ts.Diagnostic[], log: (message: string) => void): void { diagnostics.forEach((diagnostic) => { let message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'); diff --git a/ets2panda/linter/src/utils/functions/MergeArrayMaps.ts b/ets2panda/linter/lib/utils/functions/MergeArrayMaps.ts similarity index 83% rename from ets2panda/linter/src/utils/functions/MergeArrayMaps.ts rename to ets2panda/linter/lib/utils/functions/MergeArrayMaps.ts index 7eb70c55a3ea0301d6fa0cc8498d49d012278f18..fa37711e02d2c660d105af1665edb33ee0f66862 100644 --- a/ets2panda/linter/src/utils/functions/MergeArrayMaps.ts +++ b/ets2panda/linter/lib/utils/functions/MergeArrayMaps.ts @@ -13,19 +13,16 @@ * limitations under the License. */ -export function mergeArrayMaps( - lhs: Map, - rhs: Map -): Map { - if (lhs.size == 0) { +export function mergeArrayMaps(lhs: Map, rhs: Map): Map { + if (lhs.size === 0) { return rhs; } - if (rhs.size == 0) { + if (rhs.size === 0) { return lhs; } rhs.forEach((values, key) => { - if (values.length != 0) { + if (values.length !== 0) { if (lhs.has(key)) { lhs.get(key)!.push(...values); } else { diff --git a/ets2panda/linter/src/utils/functions/PathHelper.ts b/ets2panda/linter/lib/utils/functions/PathHelper.ts similarity index 99% rename from ets2panda/linter/src/utils/functions/PathHelper.ts rename to ets2panda/linter/lib/utils/functions/PathHelper.ts index d4e3f566aa20b82a2470eac7df01b162c9fec015..46b5ff253c16e022db8d7c428dce0750374a2240 100644 --- a/ets2panda/linter/src/utils/functions/PathHelper.ts +++ b/ets2panda/linter/lib/utils/functions/PathHelper.ts @@ -22,4 +22,4 @@ export function pathContainsDirectory(path: string, dir: string): boolean { } } return false; -} \ No newline at end of file +} diff --git a/ets2panda/linter/lib/utils/functions/identiferUseInValueContext.ts b/ets2panda/linter/lib/utils/functions/identiferUseInValueContext.ts new file mode 100644 index 0000000000000000000000000000000000000000..0ebd1ed973f32db1c527876c1b949b60d9991f8a --- /dev/null +++ b/ets2panda/linter/lib/utils/functions/identiferUseInValueContext.ts @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2022-2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as ts from 'typescript'; + +function isInstanceofContext(tsIdentStart: ts.Node): boolean { + return ( + ts.isBinaryExpression(tsIdentStart.parent) && + tsIdentStart.parent.operatorToken.kind === ts.SyntaxKind.InstanceOfKeyword + ); +} + +function isNewExpressionContext(tsIdentStart: ts.Node): boolean { + return ts.isNewExpression(tsIdentStart.parent) && tsIdentStart === tsIdentStart.parent.expression; +} + +/* + * If identifier is the right-most name of Property Access chain or Qualified name, + * or it's a separate identifier expression, then identifier is being referenced as an value. + */ +function isQualifiedNameContext(tsIdentStart: ts.Node, tsIdentifier: ts.Identifier): boolean { + // rightmost in AST is rightmost in qualified name chain + return ts.isQualifiedName(tsIdentStart) && tsIdentifier !== tsIdentStart.right; +} + +function isPropertyAccessContext(tsIdentStart: ts.Node, tsIdentifier: ts.Identifier): boolean { + // rightmost in AST is rightmost in qualified name chain + return ts.isPropertyAccessExpression(tsIdentStart) && tsIdentifier !== tsIdentStart.name; +} + +function getQualifiedStart(ident: ts.Node): ts.Node { + let qualifiedStart: ts.Node = ident; + while (ts.isPropertyAccessExpression(qualifiedStart.parent) || ts.isQualifiedName(qualifiedStart.parent)) { + qualifiedStart = qualifiedStart.parent; + } + return qualifiedStart; +} + +function isEnumPropAccess(ident: ts.Identifier, tsSym: ts.Symbol, context: ts.Node): boolean { + return ( + ts.isElementAccessExpression(context) && + !!(tsSym.flags & ts.SymbolFlags.Enum) && + (context.expression === ident || + ts.isPropertyAccessExpression(context.expression) && context.expression.name === ident) + ); +} + +function isValidParent(parent: ts.Node): boolean { + // treat TypeQuery as valid because it's already forbidden (FaultID.TypeQuery) + return ( + ts.isTypeNode(parent) && !ts.isTypeOfExpression(parent) || + ts.isExpressionWithTypeArguments(parent) || + ts.isExportAssignment(parent) || + ts.isExportSpecifier(parent) || + ts.isMetaProperty(parent) || + ts.isImportClause(parent) || + ts.isClassLike(parent) || + ts.isInterfaceDeclaration(parent) || + ts.isModuleDeclaration(parent) || + ts.isEnumDeclaration(parent) || + ts.isNamespaceImport(parent) || + ts.isImportSpecifier(parent) || + ts.isImportEqualsDeclaration(parent) + ); +} + +export function identiferUseInValueContext(ident: ts.Identifier, tsSym: ts.Symbol): boolean { + const qualifiedStart = getQualifiedStart(ident); + const parent = qualifiedStart.parent; + const isValidUse = + isValidParent(parent) || + isEnumPropAccess(ident, tsSym, parent) || + isQualifiedNameContext(qualifiedStart, ident) || + isPropertyAccessContext(qualifiedStart, ident) || + isNewExpressionContext(qualifiedStart) || + isInstanceofContext(qualifiedStart); + return !isValidUse; +} diff --git a/ets2panda/linter/src/utils/functions/isAssignmentOperator.ts b/ets2panda/linter/lib/utils/functions/isAssignmentOperator.ts similarity index 99% rename from ets2panda/linter/src/utils/functions/isAssignmentOperator.ts rename to ets2panda/linter/lib/utils/functions/isAssignmentOperator.ts index d2476dc2f328b5d80192d5bef8fae56d07dc7958..4fde599e8d9fb2b0fd2b2b7e3585e593177b0a88 100644 --- a/ets2panda/linter/src/utils/functions/isAssignmentOperator.ts +++ b/ets2panda/linter/lib/utils/functions/isAssignmentOperator.ts @@ -17,4 +17,4 @@ import * as ts from 'typescript'; export function isAssignmentOperator(tsBinOp: ts.BinaryOperatorToken): boolean { return tsBinOp.kind >= ts.SyntaxKind.FirstAssignment && tsBinOp.kind <= ts.SyntaxKind.LastAssignment; -} \ No newline at end of file +} diff --git a/ets2panda/linter/src/utils/functions/isIntrinsicObjectType.ts b/ets2panda/linter/lib/utils/functions/isIntrinsicObjectType.ts similarity index 100% rename from ets2panda/linter/src/utils/functions/isIntrinsicObjectType.ts rename to ets2panda/linter/lib/utils/functions/isIntrinsicObjectType.ts diff --git a/ets2panda/linter/package.json b/ets2panda/linter/package.json index b9f16d7bfea4cd002a7886685cd4458a0a90d685..e8aee43aac63855e7973d7c46ddef26d4e3077b2 100644 --- a/ets2panda/linter/package.json +++ b/ets2panda/linter/package.json @@ -16,27 +16,32 @@ "build": "npm run clean && npm run compile && npm run webpack && npm run pack:linter", "postinstall": "npm run build", "pack:linter": "rimraf bundle && mkdir bundle && npm pack --pack-destination bundle", - "test": "npm run compile && rimraf test/results test_rules/results && node build/src/TestRunner.js test test_rules", + "pretest": "npm run eslint-check", + "test": "npm run test_main && npm run test_rules && npm run test_regression", "test_main": "npm run compile && rimraf test/results && node build/src/TestRunner.js test", + "test_regression": "npm run compile && rimraf test_regression/results && node build/src/TestRunner.js test_regression", "test_rules": "npm run compile && rimraf test_rules/results && node build/src/TestRunner.js test_rules", - "update-tests": "node scripts/update-test-results.mjs test test_rules" + "update-tests": "node scripts/update-test-results.mjs test test_rules test_regression", + "eslint-check": "npx eslint --ext .ts .", + "eslint-fix": "npm run eslint-check -- --fix", + "prettier-fix": "npx prettier --write .", + "fix": "npm run prettier-fix && npm run eslint-fix" }, "dependencies": { - "typescript": "4.8.4", + "commander": "9.4.0", "log4js": "6.4.0", - "commander": "9.4.0" + "typescript": "4.8.4" }, "devDependencies": { + "@stylistic/eslint-plugin": "latest", "@types/node": "18.11.7", + "@typescript-eslint/eslint-plugin": "latest", + "@typescript-eslint/parser": "latest", + "eslint": "latest", + "prettier": "latest", "rimraf": "^3.0.2", "webpack": "^5.75.0", - "webpack-cli": "^5.0.1", - "@typescript-eslint/eslint-plugin": "5.51.0", - "@typescript-eslint/parser": "5.51.0", - "eslint": "7.32.0", - "eslint-config-prettier": "^6.15.0", - "eslint-plugin-prettier": "^3.1.4", - "prettier": "^2.0.5" + "webpack-cli": "^5.0.1" }, "bundledDependencies": [ "typescript", diff --git a/ets2panda/linter/scripts/update-test-results.mjs b/ets2panda/linter/scripts/update-test-results.mjs index dc579e42453a4416215504af7d0e3133221a4f33..45029c37eb3f2858d3a3848b5eebc2453598d268 100644 --- a/ets2panda/linter/scripts/update-test-results.mjs +++ b/ets2panda/linter/scripts/update-test-results.mjs @@ -37,7 +37,7 @@ const RESULTS_DIR = 'results' let testDirs = []; // forces to update all tests regardless of whether there was diff in a test result -const force_update = false; +let force_update = false; for (let arg of process.argv.slice(2)) { if (arg === '--force') @@ -72,33 +72,37 @@ function readTestFile(filePath) { function updateTest(testDir, testFile, mode) { let resultExt = RESULT_EXT[mode]; - let testFileWithExt = testFile + resultExt; + let resultFileWithExt = testFile + resultExt; + let resultFilePath = path.join(testDir, resultFileWithExt); // Do not update autofix result if test is skipped - if (mode === Mode.AUTOFIX && fs.existsSync(path.join(testDir, testFileWithExt + AUTOFIX_SKIP_EXT))) { + if (mode === Mode.AUTOFIX && fs.existsSync(path.join(testDir, testFile + AUTOFIX_SKIP_EXT))) { return; } - // Update test result when 'diff' exists or the 'force' option is enabled. - if (!fs.existsSync(path.join(testDir, RESULTS_DIR, testFileWithExt + DIFF_EXT)) && !force_update) { + // Update test result when: + // - '.diff' exists + // - expected '.json' doesn't exist + // - 'force' option is enabled + if (fs.existsSync(resultFilePath) && !fs.existsSync(path.join(testDir, RESULTS_DIR, resultFileWithExt + DIFF_EXT)) && !force_update) { return; } - let expectedResult = readTestFile(path.join(testDir, testFileWithExt)); + let expectedResult = readTestFile(resultFilePath); const copyright = expectedResult?.copyright ?? DEFAULT_COPYRIGHT; - let actualResult = readTestFile(path.join(testDir, RESULTS_DIR, testFileWithExt)); + let actualResult = readTestFile(path.join(testDir, RESULTS_DIR, resultFileWithExt)); if (!actualResult || !actualResult.nodes) { - console.log(`Failed to update ${testFileWithExt}: couldn't read ACTUAL result file.`); + console.log(`Failed to update ${resultFileWithExt}: couldn't read ACTUAL result file.`); return; } // Write file with actual test results. let newResultJSON = JSON.stringify({ copyright, nodes: actualResult.nodes }, null, 4); - fs.writeFileSync(path.join(testDir, testFileWithExt), newResultJSON); + fs.writeFileSync(resultFilePath, newResultJSON); - console.log(`Updated ${testFileWithExt}`); + console.log(`Updated ${resultFileWithExt}`); } for (let testDir of testDirs) { diff --git a/ets2panda/linter/src/CommandLineParser.ts b/ets2panda/linter/src/CommandLineParser.ts index 284ef5c8e55c77a39e397ba1127ea40e548ed199..47f7ddd9217d4f06592582ff7fd699b9f58a136e 100644 --- a/ets2panda/linter/src/CommandLineParser.ts +++ b/ets2panda/linter/src/CommandLineParser.ts @@ -13,11 +13,11 @@ * limitations under the License. */ -import Logger from '../utils/logger'; -import { logTscDiagnostic } from './utils/functions/LogTscDiagnostic'; -import { decodeAutofixInfo } from './utils/functions/LinterInfo'; -import { CommandLineOptions } from './CommandLineOptions'; -import { AUTOFIX_ALL } from './Autofixer'; +import { Logger } from '../lib/Logger'; +import { logTscDiagnostic } from '../lib/utils/functions/LogTscDiagnostic'; +import { decodeAutofixInfo } from './LinterInfo'; +import type { CommandLineOptions } from '../lib/CommandLineOptions'; +import { AUTOFIX_ALL } from '../lib/Autofixer'; import { Command, Option } from 'commander'; import * as ts from 'typescript'; import * as fs from 'node:fs'; @@ -28,15 +28,14 @@ const TSX_EXT = '.tsx'; const ETS_EXT = '.ets'; const JSON_EXT = '.json'; -const logger = Logger.getLogger(); - let inputFiles: string[]; let responseFile = ''; -function addSrcFile(value: string, dummy: string) { - if(value.startsWith('@')) +function addSrcFile(value: string): void { + if (value.startsWith('@')) { responseFile = value; - else + } else { inputFiles.push(value); + } } const getFiles = (dir: string): string[] => { @@ -44,87 +43,114 @@ const getFiles = (dir: string): string[] => { const files = fs.readdirSync(dir); for (let i = 0; i < files.length; ++i) { - let name = path.join(dir, files[i]); + const name = path.join(dir, files[i]); if (fs.statSync(name).isDirectory()) { resultFiles.push(...getFiles(name)); } else { - let extension = path.extname(name); - if (extension === TS_EXT || extension === TSX_EXT || extension === ETS_EXT) + const extension = path.extname(name); + if (extension === TS_EXT || extension === TSX_EXT || extension === ETS_EXT) { resultFiles.push(name); + } } } return resultFiles; }; -function addProjectFolder(projectFolder: string, previous: any ) { +function addProjectFolder(projectFolder: string, previous: string[]): string[] { return previous.concat([projectFolder]); } -export function parseCommandLine(commandLineArgs: string[]): CommandLineOptions { +function formCommandLineOptions(program: Command): CommandLineOptions { const opts: CommandLineOptions = { inputFiles: [], warningsAsErrors: false }; + // Default mode of the linter. + opts.strictMode = true; + opts.inputFiles = inputFiles; + const options = program.opts(); + if (options.relax) { + opts.strictMode = false; + } + if (options.TSC_Errors) { + opts.logTscErrors = true; + } + if (options.devecoPluginMode) { + opts.ideMode = true; + } + if (options.testMode) { + opts.testMode = true; + } + if (options.projectFolder) { + doProjectFolderArg(options.projectFolder, opts); + } + if (options.project) { + doProjectArg(options.project, opts); + } + if (options.autofix) { + doAutofixArg(options.autofix, opts); + } + if (options.warningsAsErrors) { + opts.warningsAsErrors = true; + } + return opts; +} + +export function parseCommandLine(commandLineArgs: string[]): CommandLineOptions { const program = new Command(); - program - .name('tslinter') - .description('Linter for TypeScript sources') - .version('0.0.1'); - program - .option('-E, --TSC_Errors', 'show error messages from Tsc') - .option('--relax', 'relax mode On') - .option('--test-mode', 'run linter as if running TS test files') - .option('--deveco-plugin-mode', 'run as IDE plugin') - .option('-p, --project ', 'path to TS project config file') - .option('--project-folder ', 'path to folder containig TS files to verify', addProjectFolder, []) - .option('--autofix [autofix.json]', 'fix errors specified by JSON file (all if file is omitted)', - (val: string, prev: string|boolean) => { return val.endsWith(JSON_EXT) ? val : true; }) - .addOption(new Option('--warnings-as-errors', 'treat warnings as errors').hideHelp(true)); - program - .argument('[srcFile...]', 'files to be verified', addSrcFile); - - opts.strictMode = true; // Default mode of the linter. + program.name('tslinter').description('Linter for TypeScript sources'). + version('0.0.1'); + program. + option('-E, --TSC_Errors', 'show error messages from Tsc'). + option('--relax', 'relax mode On'). + option('--test-mode', 'run linter as if running TS test files'). + option('--deveco-plugin-mode', 'run as IDE plugin'). + option('-p, --project ', 'path to TS project config file'). + option('--project-folder ', 'path to folder containig TS files to verify', addProjectFolder, []). + option('--autofix [autofix.json]', 'fix errors specified by JSON file (all if file is omitted)', (val: string) => { + return val.endsWith(JSON_EXT) ? val : true; + }). + addOption(new Option('--warnings-as-errors', 'treat warnings as errors').hideHelp(true)); + program.argument('[srcFile...]', 'files to be verified', addSrcFile); + inputFiles = []; - let cmdArgs: string[] = ['dummy', 'dummy']; // method parse() eats two first args, so make them dummy + // method parse() eats two first args, so make them dummy + let cmdArgs: string[] = ['dummy', 'dummy']; cmdArgs.push(...commandLineArgs); program.parse(cmdArgs); if (responseFile !== '') { try { - commandLineArgs = fs.readFileSync(responseFile.slice(1)).toString().split('\n').filter((e) => e.trimEnd()); + commandLineArgs = fs. + readFileSync(responseFile.slice(1)). + toString(). + split('\n'). + filter((e) => { + return e.trimEnd(); + }); cmdArgs = ['dummy', 'dummy']; cmdArgs.push(...commandLineArgs); - program.parse( cmdArgs); - } catch (error: any) { - logger.error('Failed to read response file: ' + (error.message ?? error)); - process.exit(-1) + program.parse(cmdArgs); + } catch (error) { + Logger.error('Failed to read response file: ' + error); + process.exit(-1); } } - opts.inputFiles = inputFiles; - const options = program.opts(); - if (options.relax) opts.strictMode = false; - if (options.TSC_Errors) opts.logTscErrors = true; - if (options.devecoPluginMode) opts.ideMode = true; - if (options.testMode) opts.testMode = true; - if (options.projectFolder) doProjectFolderArg(options.projectFolder, opts); - if (options.project) doProjectArg(options.project, opts); - if (options.autofix) doAutofixArg(options.autofix, opts); - if (options.warningsAsErrors) opts.warningsAsErrors = true; - return opts; + return formCommandLineOptions(program); } -function doProjectFolderArg(prjFolders: string[], opts: CommandLineOptions) { - for( let i = 0; i < prjFolders.length; i++ ) { - var prjFolderPath = prjFolders[ i ]; +function doProjectFolderArg(prjFolders: string[], opts: CommandLineOptions): void { + for (let i = 0; i < prjFolders.length; i++) { + const prjFolderPath = prjFolders[i]; try { opts.inputFiles.push(...getFiles(prjFolderPath)); - } catch (error: any) { - logger.error('Failed to read folder: ' + (error.message ?? error)); + } catch (error) { + Logger.error('Failed to read folder: ' + error); process.exit(-1); } } } -function doProjectArg(cfgPath: string, opts: CommandLineOptions) { +function doProjectArg(cfgPath: string, opts: CommandLineOptions): void { // Process project file (tsconfig.json) and retrieve config arguments. const configFile = cfgPath; @@ -134,30 +160,36 @@ function doProjectArg(cfgPath: string, opts: CommandLineOptions) { try { const oldUnrecoverableDiagnostic = host.onUnRecoverableConfigFileDiagnostic; - host.onUnRecoverableConfigFileDiagnostic = (diagnostic: ts.Diagnostic) => { diagnostics.push(diagnostic); }; + host.onUnRecoverableConfigFileDiagnostic = (diagnostic: ts.Diagnostic): void => { + diagnostics.push(diagnostic); + }; opts.parsedConfigFile = ts.getParsedCommandLineOfConfigFile(configFile, {}, host); host.onUnRecoverableConfigFileDiagnostic = oldUnrecoverableDiagnostic; - if (opts.parsedConfigFile) + if (opts.parsedConfigFile) { diagnostics.push(...ts.getConfigFileParsingDiagnostics(opts.parsedConfigFile)); + } if (diagnostics.length > 0) { // Log all diagnostic messages and exit program. - logger.error('Failed to read config file.'); - logTscDiagnostic(diagnostics, logger.info); + Logger.error('Failed to read config file.'); + logTscDiagnostic(diagnostics, Logger.info); process.exit(-1); } - } catch (error: any) { - logger.error('Failed to read config file: ' + (error.message ?? error)); + } catch (error) { + Logger.error('Failed to read config file: ' + error); process.exit(-1); } } -function doAutofixArg(autofixOptVal: string|boolean, opts: CommandLineOptions) { +function doAutofixArg(autofixOptVal: string | boolean, opts: CommandLineOptions): void { if (typeof autofixOptVal === 'string') { - let autofixInfoStr = fs.readFileSync(autofixOptVal).toString(); - let autofixInfos = JSON.parse(autofixInfoStr); - opts.autofixInfo = autofixInfos.autofixInfo.map((x: string) => decodeAutofixInfo(x)); + const autofixInfoStr = fs.readFileSync(autofixOptVal).toString(); + const autofixInfos = JSON.parse(autofixInfoStr); + opts.autofixInfo = autofixInfos.autofixInfo.map((x: string) => { + return decodeAutofixInfo(x); + }); + } else { + opts.autofixInfo = [AUTOFIX_ALL]; } - else opts.autofixInfo = [AUTOFIX_ALL]; } diff --git a/ets2panda/linter/src/CompilerWrapper.ts b/ets2panda/linter/src/Compiler.ts similarity index 56% rename from ets2panda/linter/src/CompilerWrapper.ts rename to ets2panda/linter/src/Compiler.ts index 50172a72c7fe584a6758eb76543bf27d62fd2f4d..68e12b111125aa34305950525a4ef55bb4b54db1 100644 --- a/ets2panda/linter/src/CompilerWrapper.ts +++ b/ets2panda/linter/src/Compiler.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023 Huawei Device Co., Ltd. + * Copyright (c) 2023-2023 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -14,16 +14,18 @@ */ import * as ts from 'typescript'; -import { logTscDiagnostic } from './utils/functions/LogTscDiagnostic'; -import { consoleLog } from './TypeScriptLinter'; +import type { CommandLineOptions } from '../lib/CommandLineOptions'; import { formTscOptions } from './ts-compiler/FormTscOptions'; -import { LintOptions } from './LintOptions'; +import { logTscDiagnostic } from '../lib/utils/functions/LogTscDiagnostic'; +import { consoleLog } from '../lib/TypeScriptLinter'; +import type { LintOptions } from '../lib/LintOptions'; +import { TSCCompiledProgramWithDiagnostics } from '../lib/ts-diagnostics/TSCCompiledProgram'; -export function compile(options: LintOptions, extraOptions?: any): ts.Program { - const createProgramOptions = formTscOptions(options.cmdOptions, extraOptions); +function compile(cmdOptions: CommandLineOptions, overrideCompilerOptions: ts.CompilerOptions): ts.Program { + const createProgramOptions = formTscOptions(cmdOptions, overrideCompilerOptions); const program = ts.createProgram(createProgramOptions); // Log Tsc errors if needed - if (options.cmdOptions.logTscErrors) { + if (cmdOptions.logTscErrors) { const diagnostics = ts.getPreEmitDiagnostics(program); logTscDiagnostic(diagnostics, consoleLog); diagnostics.forEach((diagnostic) => { @@ -38,3 +40,12 @@ export function compile(options: LintOptions, extraOptions?: any): ts.Program { } return program; } + +export function compileLintOptions(cmdOptions: CommandLineOptions): LintOptions { + const strict = compile(cmdOptions, TSCCompiledProgramWithDiagnostics.getOverrideCompilerOptions(true)); + const nonStrict = compile(cmdOptions, TSCCompiledProgramWithDiagnostics.getOverrideCompilerOptions(false)); + return { + cmdOptions: cmdOptions, + tscCompiledProgram: new TSCCompiledProgramWithDiagnostics(strict, nonStrict, cmdOptions.inputFiles) + }; +} diff --git a/ets2panda/linter/src/FaultAttrs.ts b/ets2panda/linter/src/FaultAttrs.ts deleted file mode 100644 index d4bb026cfd1778fd803000dc21e7b10ca3422bff..0000000000000000000000000000000000000000 --- a/ets2panda/linter/src/FaultAttrs.ts +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2022-2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the 'License'); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an 'AS IS' BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { FaultID } from './utils/consts/Problems'; - -export class FaultAttributs { - migratable?: boolean; - warning?: boolean; - cookBookRef = '-1'; -} - -export const faultsAttrs: FaultAttributs[] = []; - -faultsAttrs[FaultID.LiteralAsPropertyName] = { migratable: true, cookBookRef: '1' }; -faultsAttrs[FaultID.ComputedPropertyName] = { cookBookRef: '1' }; -faultsAttrs[FaultID.SymbolType] = { cookBookRef: '2' }; -faultsAttrs[FaultID.PrivateIdentifier] = { migratable: true, cookBookRef: '3' }; -faultsAttrs[FaultID.DeclWithDuplicateName] = { migratable: true, cookBookRef: '4' }; -faultsAttrs[FaultID.VarDeclaration] = { migratable: true, cookBookRef: '5' }; -faultsAttrs[FaultID.AnyType] = { cookBookRef: '8' }; -faultsAttrs[FaultID.UnknownType] = { cookBookRef: '8' }; -faultsAttrs[FaultID.CallSignature] = { cookBookRef: '14' }; -faultsAttrs[FaultID.ConstructorType] = { cookBookRef: '15' }; -faultsAttrs[FaultID.MultipleStaticBlocks] = { cookBookRef: '16' }; -faultsAttrs[FaultID.IndexMember] = { cookBookRef: '17' }; -faultsAttrs[FaultID.IntersectionType] = { cookBookRef: '19' }; -faultsAttrs[FaultID.ThisType] = { cookBookRef: '21' }; -faultsAttrs[FaultID.ConditionalType] = { cookBookRef: '22' }; -faultsAttrs[FaultID.ParameterProperties] = { migratable: true, cookBookRef: '25' }; -faultsAttrs[FaultID.ConstructorIface] = { cookBookRef: '27' }; -faultsAttrs[FaultID.IndexedAccessType] = { cookBookRef: '28' }; -faultsAttrs[FaultID.PropertyAccessByIndex] = { migratable: true, cookBookRef: '29' }; -faultsAttrs[FaultID.StructuralIdentity] = { cookBookRef: '30' }; -faultsAttrs[FaultID.GenericCallNoTypeArgs] = { cookBookRef: '34' }; -faultsAttrs[FaultID.RegexLiteral] = { cookBookRef: '37' }; -faultsAttrs[FaultID.ObjectLiteralNoContextType] = { cookBookRef: '38' }; -faultsAttrs[FaultID.ObjectTypeLiteral] = { cookBookRef: '40' }; -faultsAttrs[FaultID.ArrayLiteralNoContextType] = { cookBookRef: '43' }; -faultsAttrs[FaultID.FunctionExpression] = { migratable: true, cookBookRef: '46' }; -faultsAttrs[FaultID.LambdaWithTypeParameters] = { migratable: true, cookBookRef: '49' }; -faultsAttrs[FaultID.ClassExpression] = { migratable: true, cookBookRef: '50' }; -faultsAttrs[FaultID.ImplementsClass] = { cookBookRef: '51' }; -faultsAttrs[FaultID.MethodReassignment] = { cookBookRef: '52' }; -faultsAttrs[FaultID.TypeAssertion] = { migratable: true, cookBookRef: '53' }; -faultsAttrs[FaultID.JsxElement] = { cookBookRef: '54' }; -faultsAttrs[FaultID.UnaryArithmNotNumber] = { cookBookRef: '55' }; -faultsAttrs[FaultID.DeleteOperator] = { cookBookRef: '59' }; -faultsAttrs[FaultID.TypeQuery] = { cookBookRef: '60' }; -faultsAttrs[FaultID.InstanceofUnsupported] = { cookBookRef: '65' }; -faultsAttrs[FaultID.InOperator] = { cookBookRef: '66' }; -faultsAttrs[FaultID.DestructuringAssignment] = { migratable: true, cookBookRef: '69' }; -faultsAttrs[FaultID.CommaOperator] = { cookBookRef: '71' }; -faultsAttrs[FaultID.DestructuringDeclaration] = { migratable: true, cookBookRef: '74' }; -faultsAttrs[FaultID.CatchWithUnsupportedType] = { migratable: true, cookBookRef: '79' }; -faultsAttrs[FaultID.ForInStatement] = { cookBookRef: '80' }; -faultsAttrs[FaultID.MappedType] = { cookBookRef: '83' }; -faultsAttrs[FaultID.WithStatement] = { cookBookRef: '84' }; -faultsAttrs[FaultID.ThrowStatement] = { migratable: true, cookBookRef: '87' }; -faultsAttrs[FaultID.LimitedReturnTypeInference] = { migratable: true, cookBookRef: '90'}; -faultsAttrs[FaultID.DestructuringParameter] = { cookBookRef: '91' }; -faultsAttrs[FaultID.LocalFunction] = { migratable: true, cookBookRef: '92' }; -faultsAttrs[FaultID.FunctionContainsThis] = { cookBookRef: '93' }; -faultsAttrs[FaultID.GeneratorFunction] = { cookBookRef: '94' }; -faultsAttrs[FaultID.YieldExpression] = { cookBookRef: '94' }; -faultsAttrs[FaultID.IsOperator] = { cookBookRef: '96' }; -faultsAttrs[FaultID.SpreadOperator] = { cookBookRef: '99' }; -faultsAttrs[FaultID.IntefaceExtendDifProps] = { cookBookRef: '102' }; -faultsAttrs[FaultID.InterfaceMerging] = { cookBookRef: '103' }; -faultsAttrs[FaultID.InterfaceExtendsClass] = { cookBookRef: '104' }; -faultsAttrs[FaultID.ConstructorFuncs] = { cookBookRef: '106' }; -faultsAttrs[FaultID.EnumMemberNonConstInit] = { cookBookRef: '111' }; -faultsAttrs[FaultID.EnumMerging] = { cookBookRef: '113' }; -faultsAttrs[FaultID.NamespaceAsObject] = { cookBookRef: '114' }; -faultsAttrs[FaultID.NonDeclarationInNamespace] = { cookBookRef: '116' }; -faultsAttrs[FaultID.ImportFromPath] = { cookBookRef: '119' }; -faultsAttrs[FaultID.TypeOnlyImport] = { migratable: true, cookBookRef: '118' }; -faultsAttrs[FaultID.DefaultImport] = { migratable: true, cookBookRef: '120' }; -faultsAttrs[FaultID.ImportAssignment] = { cookBookRef: '121' }; -faultsAttrs[FaultID.ExportAssignment] = { cookBookRef: '126' }; -faultsAttrs[FaultID.TypeOnlyExport] = { migratable: true, cookBookRef: '127' }; -faultsAttrs[FaultID.ShorthandAmbientModuleDecl] = { cookBookRef: '128' }; -faultsAttrs[FaultID.WildcardsInModuleName] = { cookBookRef: '129' }; -faultsAttrs[FaultID.UMDModuleDefinition] = { cookBookRef: '130' }; -faultsAttrs[FaultID.NewTarget] = { cookBookRef: '132' }; -faultsAttrs[FaultID.DefiniteAssignment] = { warning: true, cookBookRef: '134' }; -faultsAttrs[FaultID.Prototype] = { cookBookRef: '136' }; -faultsAttrs[FaultID.GlobalThis] = { cookBookRef: '137' }; -faultsAttrs[FaultID.UtilityType] = { cookBookRef: '138' }; -faultsAttrs[FaultID.PropertyDeclOnFunction] = { cookBookRef: '139' }; -faultsAttrs[FaultID.FunctionApplyBindCall] = { cookBookRef: '140' }; -faultsAttrs[FaultID.ConstAssertion] = { cookBookRef: '142' }; -faultsAttrs[FaultID.ImportAssertion] = { cookBookRef: '143' }; -faultsAttrs[FaultID.LimitedStdLibApi] = { cookBookRef: '144' }; -faultsAttrs[FaultID.StrictDiagnostic] = { cookBookRef: '145' }; -faultsAttrs[FaultID.ErrorSuppression] = { cookBookRef: '146' }; -faultsAttrs[FaultID.UnsupportedDecorators] = { warning: true, cookBookRef: '148' }; -faultsAttrs[FaultID.ClassAsObject] = { cookBookRef: '149' }; -faultsAttrs[FaultID.ImportAfterStatement] = { cookBookRef: '150' }; -faultsAttrs[FaultID.EsObjectType] = { warning: true, cookBookRef: '151' }; diff --git a/ets2panda/linter/src/LinterCLI.ts b/ets2panda/linter/src/LinterCLI.ts index 7fd5063d9d5e4b260641c501412b12cdf34ab8b0..d6006a1f1e9b019e3d5e3d79b8440b86ff0b4deb 100644 --- a/ets2panda/linter/src/LinterCLI.ts +++ b/ets2panda/linter/src/LinterCLI.ts @@ -13,22 +13,21 @@ * limitations under the License. */ -import { TypeScriptLinter } from './TypeScriptLinter'; +import { TypeScriptLinter } from '../lib/TypeScriptLinter'; import { parseCommandLine } from './CommandLineParser'; -import Logger from '../utils/logger'; +import { Logger } from '../lib/Logger'; import * as fs from 'node:fs'; import * as os from 'node:os'; import * as readline from 'node:readline'; import * as path from 'node:path'; -import { CommandLineOptions } from './CommandLineOptions'; -import { lint } from './LinterRunner'; +import type { CommandLineOptions } from '../lib/CommandLineOptions'; +import { lint } from '../lib/LinterRunner'; +import { compileLintOptions } from './Compiler'; -const logger = Logger.getLogger(); - -export function run() { +export function run(): void { const commandLineArgs = process.argv.slice(2); if (commandLineArgs.length === 0) { - logger.info('Command line error: no arguments'); + Logger.info('Command line error: no arguments'); process.exit(-1); } @@ -41,18 +40,18 @@ export function run() { TypeScriptLinter.initGlobals(); if (!cmdOptions.ideMode) { - const result = lint({ cmdOptions: cmdOptions, realtimeLint: false }); + const result = lint(compileLintOptions(cmdOptions)); process.exit(result.errorNodes > 0 ? 1 : 0); } else { runIDEMode(cmdOptions); } } -function getTempFileName() { +function getTempFileName(): string { return path.join(os.tmpdir(), Math.floor(Math.random() * 10000000).toString() + '_linter_tmp_file.ts'); } -function runIDEMode(cmdOptions: CommandLineOptions) { +function runIDEMode(cmdOptions: CommandLineOptions): void { TypeScriptLinter.ideMode = true; const tmpFileName = getTempFileName(); // read data from stdin @@ -60,10 +59,12 @@ function runIDEMode(cmdOptions: CommandLineOptions) { const rl = readline.createInterface({ input: process.stdin, output: writeStream, - terminal: false, + terminal: false }); - rl.on('line', (line: string) => { fs.appendFileSync(tmpFileName, line + '\n'); }); + rl.on('line', (line: string) => { + fs.appendFileSync(tmpFileName, line + '\n'); + }); rl.once('close', () => { // end of input writeStream.close(); @@ -71,24 +72,26 @@ function runIDEMode(cmdOptions: CommandLineOptions) { if (cmdOptions.parsedConfigFile) { cmdOptions.parsedConfigFile.fileNames.push(tmpFileName); } - const result = lint({ cmdOptions: cmdOptions, realtimeLint: false }); + const result = lint(compileLintOptions(cmdOptions)); const problems = Array.from(result.problemsInfos.values()); if (problems.length === 1) { - const jsonMessage = problems[0].map((x) => ({ - line: x.line, - column: x.column, - start: x.start, - end: x.end, - type: x.type, - suggest: x.suggest, - rule: x.rule, - severity: x.severity, - autofixable: x.autofixable, - autofix: x.autofix - })); - logger.info(`{"linter messages":${JSON.stringify(jsonMessage)}}`); + const jsonMessage = problems[0].map((x) => { + return { + line: x.line, + column: x.column, + start: x.start, + end: x.end, + type: x.type, + suggest: x.suggest, + rule: x.rule, + severity: x.severity, + autofixable: x.autofixable, + autofix: x.autofix + }; + }); + Logger.info(`{"linter messages":${JSON.stringify(jsonMessage)}}`); } else { - logger.error('Unexpected error: could not lint file'); + Logger.error('Unexpected error: could not lint file'); } fs.unlinkSync(tmpFileName); }); diff --git a/ets2panda/linter/src/utils/functions/LinterInfo.ts b/ets2panda/linter/src/LinterInfo.ts similarity index 65% rename from ets2panda/linter/src/utils/functions/LinterInfo.ts rename to ets2panda/linter/src/LinterInfo.ts index 9699dab3c78a0b0a2bd4f7b46c5421335e25130a..84d61272458f453fbcead696da81bd4964063e93 100644 --- a/ets2panda/linter/src/utils/functions/LinterInfo.ts +++ b/ets2panda/linter/src/LinterInfo.ts @@ -13,18 +13,16 @@ * limitations under the License. */ -import { ProblemInfo } from '../../ProblemInfo'; -import { AutofixInfo } from '../../autofixes/AutofixInfo'; - -export function encodeProblemInfo(problem: ProblemInfo): string { - return `${problem.problem}%${problem.start}%${problem.end}`; -} +import type { AutofixInfo } from '../lib/autofixes/AutofixInfo'; export function decodeAutofixInfo(info: string): AutofixInfo { - let infos = info.split('%'); + const infos = info.split('%'); + const autofixProblemIdIdx = 0; + const autofixStartIdx = 1; + const autofixEndIdx = 2; return { - problemID: infos[0], - start: Number.parseInt(infos[1]), - end: Number.parseInt(infos[2]), + problemID: infos[autofixProblemIdIdx], + start: Number.parseInt(infos[autofixStartIdx]), + end: Number.parseInt(infos[autofixEndIdx]) }; } diff --git a/ets2panda/linter/src/LoggerImpl.ts b/ets2panda/linter/src/LoggerImpl.ts new file mode 100644 index 0000000000000000000000000000000000000000..51ff1857c121c3be020e0248b2206b96ef68feb9 --- /dev/null +++ b/ets2panda/linter/src/LoggerImpl.ts @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023-2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Logger as LoggerInterface } from '../lib/Logger'; +import logger from '../utils/logger'; + +export class LoggerImpl extends LoggerInterface { + doTrace(message: string): void { + void this; + logger.getLogger().trace(message); + } + + doDebug(message: string): void { + void this; + logger.getLogger().debug(message); + } + + doInfo(message: string): void { + void this; + logger.getLogger().info(message); + } + + doWarn(message: string): void { + void this; + logger.getLogger().warn(message); + } + + doError(message: string): void { + void this; + logger.getLogger().error(message); + } +} diff --git a/ets2panda/linter/src/TestRunner.ts b/ets2panda/linter/src/TestRunner.ts index 99fa9295880361299aaf627ce609c5942cb4763b..a82ad5298e09e8ae3b8c0ae6c66ce38284777140 100644 --- a/ets2panda/linter/src/TestRunner.ts +++ b/ets2panda/linter/src/TestRunner.ts @@ -13,20 +13,23 @@ * limitations under the License. */ -import { TypeScriptLinter } from './TypeScriptLinter'; -import { lint } from './LinterRunner'; +import { Logger } from '../lib/Logger'; +import { LoggerImpl } from './LoggerImpl'; +Logger.init(new LoggerImpl()); + +import { TypeScriptLinter } from '../lib/TypeScriptLinter'; +import { lint } from '../lib/LinterRunner'; import { parseCommandLine } from './CommandLineParser'; -import { Autofix } from './Autofixer'; -import Logger from '../utils/logger'; +import type { Autofix } from '../lib/Autofixer'; import * as fs from 'node:fs'; import * as path from 'node:path'; import * as ts from 'typescript'; +import type { CommandLineOptions } from '../lib/CommandLineOptions'; +import { compileLintOptions } from './Compiler'; const TEST_DIR = 'test'; const TAB = ' '; -const logger = Logger.getLogger(); - interface TestNodeInfo { line: number; column: number; @@ -49,69 +52,64 @@ RESULT_EXT[Mode.RELAX] = '.relax.json'; RESULT_EXT[Mode.AUTOFIX] = '.autofix.json'; const AUTOFIX_CONFIG_EXT = '.autofix.cfg.json'; const AUTOFIX_SKIP_EXT = '.autofix.skip'; -const ARGS_CONFIG_EXT = '.args.json' +const ARGS_CONFIG_EXT = '.args.json'; const DIFF_EXT = '.diff'; function runTests(testDirs: string[]): number { - let hasComparisonFailures = false; - // Set the IDE mode manually to enable storing information - // about found bad nodes and also disable the log output. + /* + * Set the IDE mode manually to enable storing information + * about found bad nodes and also disable the log output. + */ TypeScriptLinter.ideMode = true; TypeScriptLinter.testMode = true; - let passed = 0, failed = 0; - + let hasComparisonFailures = false; + let passed = 0; + let failed = 0; // Get tests from test directory - if (!testDirs?.length) testDirs = [ TEST_DIR ]; + if (!testDirs?.length) { + testDirs = [TEST_DIR]; + } for (const testDir of testDirs) { - let testFiles: string[] = fs.readdirSync(testDir) - .filter((x) => (x.trimEnd().endsWith(ts.Extension.Ts) && !x.trimEnd().endsWith(ts.Extension.Dts)) || x.trimEnd().endsWith(ts.Extension.Tsx)); - - logger.info(`\nProcessing "${testDir}" directory:\n`); - + const testFiles: string[] = fs.readdirSync(testDir).filter((x) => { + return ( + x.trimEnd().endsWith(ts.Extension.Ts) && !x.trimEnd().endsWith(ts.Extension.Dts) || + x.trimEnd().endsWith(ts.Extension.Tsx) + ); + }); + Logger.info(`\nProcessing "${testDir}" directory:\n`); // Run each test in Strict, Autofix, and Relax mode: for (const testFile of testFiles) { if (runTest(testDir, testFile, Mode.STRICT)) { failed++; hasComparisonFailures = true; + } else { + passed++; } - else passed++; - if (runTest(testDir, testFile, Mode.AUTOFIX)) { failed++; hasComparisonFailures = true; + } else { + passed++; } - else passed++; - if (runTest(testDir, testFile, Mode.RELAX)) { failed++; hasComparisonFailures = true; + } else { + passed++; } - else passed++; } } - - logger.info(`\nSUMMARY: ${passed + failed} total, ${passed} passed or skipped, ${failed} failed.`); - logger.info((failed > 0) ? '\nTEST FAILED' : '\nTEST SUCCESSFUL'); - + Logger.info(`\nSUMMARY: ${passed + failed} total, ${passed} passed or skipped, ${failed} failed.`); + Logger.info(failed > 0 ? '\nTEST FAILED' : '\nTEST SUCCESSFUL'); process.exit(hasComparisonFailures ? -1 : 0); } -function runTest(testDir: string, testFile: string, mode: Mode): boolean { - let testFailed = false; - if (mode === Mode.AUTOFIX && fs.existsSync(path.join(testDir, testFile + AUTOFIX_SKIP_EXT))) { - logger.info(`Skipping test ${testFile} (${Mode[mode]} mode)`); - return false; - } - logger.info(`Running test ${testFile} (${Mode[mode]} mode)`); - - TypeScriptLinter.initGlobals(); - +function parseArgs(testDir: string, testFile: string, mode: Mode): CommandLineOptions { // Configure test parameters and run linter. const args: string[] = [path.join(testDir, testFile)]; - let argsFileName = path.join(testDir, testFile + ARGS_CONFIG_EXT); - let currentTestMode = TypeScriptLinter.testMode; + const argsFileName = path.join(testDir, testFile + ARGS_CONFIG_EXT); if (fs.existsSync(argsFileName)) { const data = fs.readFileSync(argsFileName).toString(); @@ -121,72 +119,94 @@ function runTest(testDir: string, testFile: string, mode: Mode): boolean { } } - if (mode === Mode.RELAX) args.push('--relax'); - else if (mode === Mode.AUTOFIX) { + if (mode === Mode.RELAX) { + args.push('--relax'); + } else if (mode === Mode.AUTOFIX) { args.push('--autofix'); - let autofixCfg = path.join(testDir, testFile + AUTOFIX_CONFIG_EXT); - if (fs.existsSync(autofixCfg)) args.push(autofixCfg); - } - const cmdOptions = parseCommandLine(args); - const result = lint({ cmdOptions: cmdOptions, realtimeLint: false }); - const fileProblems = result.problemsInfos.get( path.normalize(cmdOptions.inputFiles[0]) ); - if (fileProblems === undefined) { - return true; + const autofixCfg = path.join(testDir, testFile + AUTOFIX_CONFIG_EXT); + if (fs.existsSync(autofixCfg)) { + args.push(autofixCfg); + } } - TypeScriptLinter.testMode = currentTestMode; - - const resultExt = RESULT_EXT[mode]; - const testResultFileName = testFile + resultExt; - - // Get list of bad nodes from the current run. - const resultNodes: TestNodeInfo[] = - fileProblems.map( - (x) => ({ - line: x.line, column: x.column, problem: x.problem, - autofixable: mode === Mode.AUTOFIX ? x.autofixable : undefined, - autofix: mode === Mode.AUTOFIX ? x.autofix : undefined, - suggest: x.suggest, - rule: x.rule - }) - ); + return parseCommandLine(args); +} +function compareExpectedAndActual(testDir: string, testFile: string, mode: Mode, resultNodes: TestNodeInfo[]): string { // Read file with expected test result. let expectedResult: { nodes: TestNodeInfo[] }; let diff: string = ''; + const resultExt = RESULT_EXT[mode]; + const testResultFileName = testFile + resultExt; try { const expectedResultFile = fs.readFileSync(path.join(testDir, testResultFileName)).toString(); expectedResult = JSON.parse(expectedResultFile); - if (!expectedResult || !expectedResult.nodes || expectedResult.nodes.length !== resultNodes.length) { - testFailed = true; - let expectedResultCount = expectedResult && expectedResult.nodes ? expectedResult.nodes.length : 0; + if (!expectedResult?.nodes || expectedResult.nodes.length !== resultNodes.length) { + const expectedResultCount = expectedResult?.nodes ? expectedResult.nodes.length : 0; diff = `Expected count: ${expectedResultCount} vs actual count: ${resultNodes.length}`; - logger.info(`${TAB}${diff}`); + Logger.info(`${TAB}${diff}`); } else { diff = expectedAndActualMatch(expectedResult.nodes, resultNodes); - testFailed = !!diff; } - if (testFailed) { - logger.info(`${TAB}Test failed. Expected and actual results differ.`); + if (diff) { + Logger.info(`${TAB}Test failed. Expected and actual results differ.`); } - } catch (error: any) { - testFailed = true; - logger.info(`${TAB}Test failed. ${error.message ?? error}`); + } catch (error) { + Logger.info(`${TAB}Test failed. ` + error); + } + + return diff; +} + +function runTest(testDir: string, testFile: string, mode: Mode): boolean { + if (mode === Mode.AUTOFIX && fs.existsSync(path.join(testDir, testFile + AUTOFIX_SKIP_EXT))) { + Logger.info(`Skipping test ${testFile} (${Mode[mode]} mode)`); + return false; + } + Logger.info(`Running test ${testFile} (${Mode[mode]} mode)`); + + TypeScriptLinter.initGlobals(); + + const currentTestMode = TypeScriptLinter.testMode; + + const cmdOptions = parseArgs(testDir, testFile, mode); + const result = lint(compileLintOptions(cmdOptions)); + const fileProblems = result.problemsInfos.get(path.normalize(cmdOptions.inputFiles[0])); + if (fileProblems === undefined) { + return true; } + TypeScriptLinter.testMode = currentTestMode; + + // Get list of bad nodes from the current run. + const resultNodes: TestNodeInfo[] = fileProblems.map((x) => { + return { + line: x.line, + column: x.column, + problem: x.problem, + autofixable: mode === Mode.AUTOFIX ? x.autofixable : undefined, + autofix: mode === Mode.AUTOFIX ? x.autofix : undefined, + suggest: x.suggest, + rule: x.rule + }; + }); + + // Read file with expected test result. + const testResult = compareExpectedAndActual(testDir, testFile, mode, resultNodes); + // Write file with actual test results. - writeActualResultFile(testDir, testFile, resultExt, resultNodes, diff); + writeActualResultFile(testDir, testFile, mode, resultNodes, testResult); - return testFailed; + return !!testResult; } function expectedAndActualMatch(expectedNodes: TestNodeInfo[], actualNodes: TestNodeInfo[]): string { // Compare expected and actual results. for (let i = 0; i < actualNodes.length; i++) { - let actual = actualNodes[i]; - let expect = expectedNodes[i]; + const actual = actualNodes[i]; + const expect = expectedNodes[i]; if (actual.line !== expect.line || actual.column !== expect.column || actual.problem !== expect.problem) { return reportDiff(expect, actual); } @@ -205,22 +225,39 @@ function expectedAndActualMatch(expectedNodes: TestNodeInfo[], actualNodes: Test } function autofixArraysMatch(expected: Autofix[] | undefined, actual: Autofix[] | undefined): boolean { - if (!expected && !actual) return true; - if (!(expected && actual) || expected.length !== actual.length) return false; + if (!expected && !actual) { + return true; + } + if (!(expected && actual) || expected.length !== actual.length) { + return false; + } for (let i = 0; i < actual.length; ++i) { if ( - actual[i].start !== expected[i].start || actual[i].end !== expected[i].end || + actual[i].start !== expected[i].start || + actual[i].end !== expected[i].end || actual[i].replacementText.replace(/\r\n/g, '\n') !== expected[i].replacementText.replace(/\r\n/g, '\n') - ) return false; + ) { + return false; + } } return true; } -function writeActualResultFile(testDir: string, testFile: string, resultExt: string, resultNodes: TestNodeInfo[], diff: string) { +function writeActualResultFile( + testDir: string, + testFile: string, + mode: Mode, + resultNodes: TestNodeInfo[], + diff: string +): void { const actualResultsDir = path.join(testDir, 'results'); - if (!fs.existsSync(actualResultsDir)) fs.mkdirSync(actualResultsDir); + const resultExt = RESULT_EXT[mode]; + if (!fs.existsSync(actualResultsDir)) { + fs.mkdirSync(actualResultsDir); + } - const actualResultJSON = JSON.stringify({ nodes: resultNodes }, null, 4); + const tabWidth = 4; + const actualResultJSON = JSON.stringify({ nodes: resultNodes }, null, tabWidth); fs.writeFileSync(path.join(actualResultsDir, testFile + resultExt), actualResultJSON); if (diff) { @@ -229,16 +266,16 @@ function writeActualResultFile(testDir: string, testFile: string, resultExt: str } function reportDiff(expected: TestNodeInfo, actual: TestNodeInfo): string { - let expectedNode = JSON.stringify({ nodes: [expected] }, null, 4); - let actualNode = JSON.stringify({ nodes: [actual] }, null, 4); + const tabWidth = 4; + const expectedNode = JSON.stringify({ nodes: [expected] }, null, tabWidth); + const actualNode = JSON.stringify({ nodes: [actual] }, null, tabWidth); - let diff = -`Expected: + const diff = `Expected: ${expectedNode} Actual: ${actualNode}`; - logger.info(diff); + Logger.info(diff); return diff; } diff --git a/ets2panda/linter/src/main.ts b/ets2panda/linter/src/main.ts index e1b7ecf91e950ff3051b59f7d413fc88c13d01af..364d866ce5d68d68ec94f772d40de9b69e3ffba1 100644 --- a/ets2panda/linter/src/main.ts +++ b/ets2panda/linter/src/main.ts @@ -13,6 +13,10 @@ * limitations under the License. */ +import { Logger } from '../lib/Logger'; +import { LoggerImpl } from './LoggerImpl'; +Logger.init(new LoggerImpl()); + import { run } from './LinterCLI'; run(); diff --git a/ets2panda/linter/src/ts-compiler/FormTscOptions.ts b/ets2panda/linter/src/ts-compiler/FormTscOptions.ts index 08a04429a759cc8a3831715a834c3cb593266b92..bc154c376f8debd84708eba2e97dfaf99d6ad348 100644 --- a/ets2panda/linter/src/ts-compiler/FormTscOptions.ts +++ b/ets2panda/linter/src/ts-compiler/FormTscOptions.ts @@ -14,32 +14,31 @@ */ import * as ts from 'typescript'; -import { CommandLineOptions } from '../CommandLineOptions'; +import type { CommandLineOptions } from '../../lib/CommandLineOptions'; -export function formTscOptions(cmdOptions: CommandLineOptions, extraOptions?: any): ts.CreateProgramOptions { +export function formTscOptions( + cmdOptions: CommandLineOptions, + overrideCompilerOptions: ts.CompilerOptions +): ts.CreateProgramOptions { if (cmdOptions.parsedConfigFile) { - let options: ts.CreateProgramOptions = { + const options: ts.CreateProgramOptions = { rootNames: cmdOptions.parsedConfigFile.fileNames, options: cmdOptions.parsedConfigFile.options, projectReferences: cmdOptions.parsedConfigFile.projectReferences, - configFileParsingDiagnostics: ts.getConfigFileParsingDiagnostics(cmdOptions.parsedConfigFile), + configFileParsingDiagnostics: ts.getConfigFileParsingDiagnostics(cmdOptions.parsedConfigFile) }; - if (extraOptions) { - options.options = Object.assign(options.options, extraOptions); - } + options.options = Object.assign(options.options, overrideCompilerOptions); return options; } - let options: ts.CreateProgramOptions = { + const options: ts.CreateProgramOptions = { rootNames: cmdOptions.inputFiles, options: { target: ts.ScriptTarget.Latest, module: ts.ModuleKind.CommonJS, allowJs: true, - checkJs: true, - }, + checkJs: true + } }; - if (extraOptions) { - options.options = Object.assign(options.options, extraOptions); - } + options.options = Object.assign(options.options, overrideCompilerOptions); return options; } diff --git a/ets2panda/linter/src/utils/consts/StandardLibraries.ts b/ets2panda/linter/src/utils/consts/StandardLibraries.ts deleted file mode 100644 index 229ffefbfdf9772d68134e7f5912780d048c19bb..0000000000000000000000000000000000000000 --- a/ets2panda/linter/src/utils/consts/StandardLibraries.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2023-2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -export const STANDARD_LIBRARIES = [ - 'lib.dom.d.ts', 'lib.dom.iterable.d.ts', 'lib.webworker.d.ts', 'lib.webworker.importscripts.d.ts', - 'lib.webworker.iterable.d.ts', 'lib.scripthost.d.ts', 'lib.decorators.d.ts', 'lib.decorators.legacy.d.ts', - 'lib.es5.d.ts', 'lib.es2015.core.d.ts', 'lib.es2015.collection.d.ts', 'lib.es2015.generator.d.ts', - 'lib.es2015.iterable.d.ts', 'lib.es2015.promise.d.ts', 'lib.es2015.proxy.d.ts', 'lib.es2015.reflect.d.ts', - 'lib.es2015.symbol.d.ts', 'lib.es2015.symbol.wellknown.d.ts', 'lib.es2016.array.include.d.ts', - 'lib.es2017.object.d.ts', 'lib.es2017.sharedmemory.d.ts', 'lib.es2017.string.d.ts', 'lib.es2017.intl.d.ts', - 'lib.es2017.typedarrays.d.ts', 'lib.es2018.asyncgenerator.d.ts', 'lib.es2018.asynciterable.d.ts', - 'lib.es2018.intl.d.ts', 'lib.es2018.promise.d.ts', 'lib.es2018.regexp.d.ts', 'lib.es2019.array.d.ts', - 'lib.es2019.object.d.ts', 'lib.es2019.string.d.ts', 'lib.es2019.symbol.d.ts', 'lib.es2019.intl.d.ts', - 'lib.es2020.bigint.d.ts', 'lib.es2020.date.d.ts', 'lib.es2020.promise.d.ts', 'lib.es2020.sharedmemory.d.ts', - 'lib.es2020.string.d.ts', 'lib.es2020.symbol.wellknown.d.ts', 'lib.es2020.intl.d.ts', 'lib.es2020.number.d.ts', - 'lib.es2021.promise.d.ts', 'lib.es2021.string.d.ts', 'lib.es2021.weakref.d.ts', 'lib.es2021.intl.d.ts', - 'lib.es2022.array.d.ts', 'lib.es2022.error.d.ts', 'lib.es2022.intl.d.ts', 'lib.es2022.object.d.ts', - 'lib.es2022.sharedmemory.d.ts', 'lib.es2022.string.d.ts', 'lib.es2022.regexp.d.ts', 'lib.es2023.array.d.ts', -]; diff --git a/ets2panda/linter/stats_calculator/src/calculate-stats.ts b/ets2panda/linter/stats_calculator/src/calculate-stats.ts index bd0f5a4bff858416ea0c5fb1e29814117b7d11b6..3f8eb2410e7e586dc36918afd67bad9233abad6b 100644 --- a/ets2panda/linter/stats_calculator/src/calculate-stats.ts +++ b/ets2panda/linter/stats_calculator/src/calculate-stats.ts @@ -44,7 +44,7 @@ interface ArkTsIssueType { description: string; type: string; count: number; -}; +} interface Statistics { totalErrors: number; @@ -52,22 +52,22 @@ interface Statistics { linesWithErrors: number; linesWithWarnings: number; issues: Map; -}; +} function isError(defectInfo: DefectInfo): boolean { return defectInfo.category === ARK_TS_ISSUES_ERROR_CATEGORY; } -function fillIssueInfo(statistics: Statistics, defectInfo: DefectInfo) { - const recipeNo = parseInt(defectInfo.ruleDocPath!!.substring( - 'docs/recipe'.length, - defectInfo.ruleDocPath!!.length - '.md'.length)); - let issueInfo = statistics.issues.get(recipeNo); +function fillIssueInfo(statistics: Statistics, defectInfo: DefectInfo): void { + const recipeNo = parseInt( + defectInfo.ruleDocPath.substring('docs/recipe'.length, defectInfo.ruleDocPath.length - '.md'.length) + ); + const issueInfo = statistics.issues.get(recipeNo); if (!issueInfo) { statistics.issues.set(recipeNo, { description: defectInfo.description, type: isError(defectInfo) ? 'error' : 'warn', - count: 1, + count: 1 }); } else { issueInfo.count += 1; @@ -75,7 +75,7 @@ function fillIssueInfo(statistics: Statistics, defectInfo: DefectInfo) { } function parse(reportJson: ReportJson): Statistics { - let statistics: Statistics = { + const statistics: Statistics = { totalErrors: 0, totalWarnings: 0, linesWithErrors: 0, @@ -93,7 +93,7 @@ function parse(reportJson: ReportJson): Statistics { continue; } - fillIssueInfo(statistics, defectInfo) + fillIssueInfo(statistics, defectInfo); if (isError(defectInfo)) { statistics.totalErrors += 1; @@ -114,7 +114,7 @@ function read(filePath: string): ReportJson { return JSON.parse(fs.readFileSync(filePath, { encoding: 'utf8', flag: 'r' })); } -function main() { +function main(): void { if (process.argv.length < 3) { console.error('Path to input json was not provided, exiting'); process.exit(1); @@ -122,6 +122,8 @@ function main() { console.log(parse(read(process.argv[2]))); } -// file is stored in project's directory under the following path: -// /.idea/code-linter/eslintAgent/output.json -main() +/* + * file is stored in project's directory under the following path: + * /.idea/code-linter/eslintAgent/output.json + */ +main(); diff --git a/ets2panda/linter/test/ambient_module.ts.relax.json b/ets2panda/linter/test/ambient_module.ts.relax.json index a02ca7fc8935464e83947500d0dfc1e6558752dd..81f9bd90b4a34906dbf4f322ce7315bf6de3de12 100755 --- a/ets2panda/linter/test/ambient_module.ts.relax.json +++ b/ets2panda/linter/test/ambient_module.ts.relax.json @@ -26,7 +26,7 @@ "column": 3, "problem": "NonDeclarationInNamespace", "suggest": "", - "rule": "Non-declaration statements in namespaces are not supported (arkts-no-ns-statements)" + "rule": "Non-declaration statements in namespaces are not supported (single semicolons are considered as empty non-declaration statements) (arkts-no-ns-statements)" }, { "line": 20, @@ -47,7 +47,7 @@ "column": 3, "problem": "NonDeclarationInNamespace", "suggest": "", - "rule": "Non-declaration statements in namespaces are not supported (arkts-no-ns-statements)" + "rule": "Non-declaration statements in namespaces are not supported (single semicolons are considered as empty non-declaration statements) (arkts-no-ns-statements)" }, { "line": 25, diff --git a/ets2panda/linter/test/ambient_module.ts.strict.json b/ets2panda/linter/test/ambient_module.ts.strict.json index a02ca7fc8935464e83947500d0dfc1e6558752dd..81f9bd90b4a34906dbf4f322ce7315bf6de3de12 100755 --- a/ets2panda/linter/test/ambient_module.ts.strict.json +++ b/ets2panda/linter/test/ambient_module.ts.strict.json @@ -26,7 +26,7 @@ "column": 3, "problem": "NonDeclarationInNamespace", "suggest": "", - "rule": "Non-declaration statements in namespaces are not supported (arkts-no-ns-statements)" + "rule": "Non-declaration statements in namespaces are not supported (single semicolons are considered as empty non-declaration statements) (arkts-no-ns-statements)" }, { "line": 20, @@ -47,7 +47,7 @@ "column": 3, "problem": "NonDeclarationInNamespace", "suggest": "", - "rule": "Non-declaration statements in namespaces are not supported (arkts-no-ns-statements)" + "rule": "Non-declaration statements in namespaces are not supported (single semicolons are considered as empty non-declaration statements) (arkts-no-ns-statements)" }, { "line": 25, diff --git a/ets2panda/linter/test/array_literals.ts b/ets2panda/linter/test/array_literals.ts index 4c5e9de197ab904fc53feb06cf4e440e81808066..5889b08baa937a07d7b5130bb742110c9f34fe22 100644 --- a/ets2panda/linter/test/array_literals.ts +++ b/ets2panda/linter/test/array_literals.ts @@ -124,4 +124,3 @@ class P { let a1 = [ { n:1, s:"1" } as P, { n:2, s:"2" } as P ]; // OK let a2: P[] = [ { n:3, s:"3" }, { n:4, s:"4" } ]; // OK let a3 = [ { n:1, s:"1" }, { n:2, s:"2" } ]; // NOT OK - \ No newline at end of file diff --git a/ets2panda/linter/test/classB.ts.relax.json b/ets2panda/linter/test/classB.ts.relax.json index e44fd80c47fb55cd43e0a82e2db3b2b8cf8c8858..dc2b9ced44f0b0fed43aafdf3a49426c859daf07 100644 --- a/ets2panda/linter/test/classB.ts.relax.json +++ b/ets2panda/linter/test/classB.ts.relax.json @@ -15,8 +15,8 @@ ], "nodes": [ { - "line": 24, - "column": 5, + "line": 25, + "column": 16, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" diff --git a/ets2panda/linter/test/classB.ts.strict.json b/ets2panda/linter/test/classB.ts.strict.json index e44fd80c47fb55cd43e0a82e2db3b2b8cf8c8858..dc2b9ced44f0b0fed43aafdf3a49426c859daf07 100644 --- a/ets2panda/linter/test/classB.ts.strict.json +++ b/ets2panda/linter/test/classB.ts.strict.json @@ -15,8 +15,8 @@ ], "nodes": [ { - "line": 24, - "column": 5, + "line": 25, + "column": 16, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" diff --git a/ets2panda/linter/test/class_as_object.ts b/ets2panda/linter/test/class_as_object.ts index e91d6c0246cd4f7ae5b14a230acadc3ae820e676..2f9511d5d083248c3cf4fd43a50c8f1a41311cf8 100644 --- a/ets2panda/linter/test/class_as_object.ts +++ b/ets2panda/linter/test/class_as_object.ts @@ -13,7 +13,7 @@ * limitations under the License. */ -import { Something, SomethingFactory, SomethingBar, Bar } from "./oh_modules/ohos_factory"; +import { Something, SomethingFactory, SomethingBar, Bar, Select } from "./oh_modules/ohos_factory"; class C { static a = 5 @@ -91,3 +91,33 @@ for (let item = 0; item < Object.keys(Color).length; item++) { foo2(() => C); export { C as H }; + +// #14228 +let data = new Select().from(C).eq('key').query(C); // Ok +invalid_func(C); // Ok +let a: any; +a.foo(C); // Ok + +let col = 'WHITE'; +console.log(Color[col]) + +// #14184 +namespace NS { + export enum E { + A = 'A', + B = 'B', + C = 'C' + } +} + +let s: string = 'B'; +let s2: string = NS.E[s]; + +for (let item = 0; item < Object.keys(NS.E).length; item++) { + console.log(item); +} + +/** + * {@link C} - should not report error + */ +class JSDocClass {} \ No newline at end of file diff --git a/ets2panda/linter/test/class_as_object.ts.relax.json b/ets2panda/linter/test/class_as_object.ts.relax.json index 0fdca907248f03f85893c5f89d279959c5953271..ebe30080e0920cd92ae2320990d9786929a655f9 100644 --- a/ets2panda/linter/test/class_as_object.ts.relax.json +++ b/ets2panda/linter/test/class_as_object.ts.relax.json @@ -15,72 +15,121 @@ { "line": 27, "column": 9, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 28, "column": 5, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 29, "column": 11, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 30, "column": 7, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 38, "column": 20, - "problem": "TypeQuery" + "problem": "TypeQuery", + "suggest": "", + "rule": "\"typeof\" operator is allowed only in expression contexts (arkts-no-type-query)" }, { "line": 39, "column": 6, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 42, "column": 12, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 45, "column": 20, - "problem": "TypeQuery" + "problem": "TypeQuery", + "suggest": "", + "rule": "\"typeof\" operator is allowed only in expression contexts (arkts-no-type-query)" }, { "line": 46, "column": 8, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 49, "column": 14, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 58, "column": 22, - "problem": "TypeQuery" + "problem": "TypeQuery", + "suggest": "", + "rule": "\"typeof\" operator is allowed only in expression contexts (arkts-no-type-query)" }, { "line": 60, "column": 7, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 87, "column": 39, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 91, "column": 12, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" + }, + { + "line": 96, + "column": 5, + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + }, + { + "line": 98, + "column": 8, + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + }, + { + "line": 116, + "column": 42, + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" } ] } \ No newline at end of file diff --git a/ets2panda/linter/test/class_as_object.ts.strict.json b/ets2panda/linter/test/class_as_object.ts.strict.json index 0fdca907248f03f85893c5f89d279959c5953271..ebe30080e0920cd92ae2320990d9786929a655f9 100644 --- a/ets2panda/linter/test/class_as_object.ts.strict.json +++ b/ets2panda/linter/test/class_as_object.ts.strict.json @@ -15,72 +15,121 @@ { "line": 27, "column": 9, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 28, "column": 5, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 29, "column": 11, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 30, "column": 7, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 38, "column": 20, - "problem": "TypeQuery" + "problem": "TypeQuery", + "suggest": "", + "rule": "\"typeof\" operator is allowed only in expression contexts (arkts-no-type-query)" }, { "line": 39, "column": 6, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 42, "column": 12, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 45, "column": 20, - "problem": "TypeQuery" + "problem": "TypeQuery", + "suggest": "", + "rule": "\"typeof\" operator is allowed only in expression contexts (arkts-no-type-query)" }, { "line": 46, "column": 8, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 49, "column": 14, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 58, "column": 22, - "problem": "TypeQuery" + "problem": "TypeQuery", + "suggest": "", + "rule": "\"typeof\" operator is allowed only in expression contexts (arkts-no-type-query)" }, { "line": 60, "column": 7, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 87, "column": 39, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" }, { "line": 91, "column": 12, - "problem": "ClassAsObject" + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" + }, + { + "line": 96, + "column": 5, + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + }, + { + "line": 98, + "column": 8, + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + }, + { + "line": 116, + "column": 42, + "problem": "ClassAsObject", + "suggest": "", + "rule": "Classes cannot be used as objects (arkts-no-classes-as-obj)" } ] } \ No newline at end of file diff --git a/ets2panda/linter/test/default_imports.ts.autofix.json b/ets2panda/linter/test/default_imports.ts.autofix.json index f666c003d98fa83eedd2bc41c6331a7158c3fff2..b083d587f19d1116c31f7ac1486b08dc89500359 100755 --- a/ets2panda/linter/test/default_imports.ts.autofix.json +++ b/ets2panda/linter/test/default_imports.ts.autofix.json @@ -40,19 +40,6 @@ } ] }, - { - "line": 17, - "column": 8, - "problem": "TypeOnlyImport", - "autofixable": true, - "autofix": [ - { - "start": 655, - "end": 675, - "replacementText": "{ default as D2 }" - } - ] - }, { "line": 18, "column": 9, @@ -92,19 +79,6 @@ } ] }, - { - "line": 20, - "column": 8, - "problem": "TypeOnlyImport", - "autofixable": true, - "autofix": [ - { - "start": 785, - "end": 811, - "replacementText": "{ C, default as D5, D }" - } - ] - }, { "line": 21, "column": 17, @@ -117,19 +91,6 @@ "replacementText": "D5, { type E, F }" } ] - }, - { - "line": 21, - "column": 9, - "problem": "TypeOnlyImport", - "autofixable": true, - "autofix": [ - { - "start": 835, - "end": 841, - "replacementText": "E" - } - ] } ] } \ No newline at end of file diff --git a/ets2panda/linter/test/default_imports.ts.strict.json b/ets2panda/linter/test/default_imports.ts.strict.json index 07471ba2bafabe3b366b8d2655a8d79ad90895e7..ffea8cd37a136b792cb2a77f05e5caa14c7634b3 100755 --- a/ets2panda/linter/test/default_imports.ts.strict.json +++ b/ets2panda/linter/test/default_imports.ts.strict.json @@ -24,21 +24,11 @@ "column": 14, "problem": "DefaultImport" }, - { - "line": 17, - "column": 8, - "problem": "TypeOnlyImport" - }, { "line": 18, "column": 9, "problem": "DefaultImport" }, - { - "line": 18, - "column": 9, - "problem": "TypeOnlyImport" - }, { "line": 19, "column": 12, @@ -49,25 +39,10 @@ "column": 17, "problem": "DefaultImport" }, - { - "line": 20, - "column": 8, - "problem": "TypeOnlyImport" - }, { "line": 21, "column": 17, "problem": "DefaultImport" - }, - { - "line": 21, - "column": 9, - "problem": "TypeOnlyImport" - }, - { - "line": 21, - "column": 17, - "problem": "TypeOnlyImport" } ] } \ No newline at end of file diff --git a/ets2panda/linter/test/dynamic_lib.d.ts b/ets2panda/linter/test/dynamic_lib.d.ts index e3da589b5418ec259aa310a41fea0f454f17ede3..85dddff2c0af534270c57807f24c3232e3691d89 100644 --- a/ets2panda/linter/test/dynamic_lib.d.ts +++ b/ets2panda/linter/test/dynamic_lib.d.ts @@ -93,3 +93,9 @@ declare class B { } export declare function bad_func(): A & B; + +export type IndexedSignatureType = { + [key: string]: string; +} + +export declare function postCardAction(comp: Object, action: Object): void; \ No newline at end of file diff --git a/ets2panda/linter/test/dynamic_object_literals.ts b/ets2panda/linter/test/dynamic_object_literals.ts index ea7f9759a2e8cf76d1eb0dd81536959bfcf1beeb..dc4de64dffcdc883ee0d3fa120cc0ab65598a816 100644 --- a/ets2panda/linter/test/dynamic_object_literals.ts +++ b/ets2panda/linter/test/dynamic_object_literals.ts @@ -24,7 +24,9 @@ import { dynamic_array, padding, margin, - position + position, + IndexedSignatureType, + postCardAction } from "./dynamic_lib" function main(): void { @@ -84,4 +86,22 @@ dynamic_array.splice(2, 0, {a: 1, b: '2'}); // #13550 - allow literals as property names in dynamic context padding({'top': '0px', 'right': '5px', 'bottom': '10px', 'left': '15px'}); margin({'top': '10px', 'right': '20px', 'bottom': '30px', 'left': '40px'}); -position({'x': '20', 'y': '40'}); \ No newline at end of file +position({'x': '20', 'y': '40'}); + +// allow literal as property name for type aliases that come from interop +function typeAliasLitAsPropName(): IndexedSignatureType { + return { + 'a': '1', + 'b': '2', + 'c': '3' + } +} + +// #14399 +postCardAction({}, { + "action": 'router', + "abilityName": 'SomeAbility', + "params": { + "message": 'add detail' + } +}); \ No newline at end of file diff --git a/ets2panda/linter/test/es_object.ts b/ets2panda/linter/test/es_object.ts index 445b5c0c07e1db8eb18c4006d860e3476659f6ae..cd1e530caa0989a80f1226c8b1a1d9a6c6a7c8f3 100644 --- a/ets2panda/linter/test/es_object.ts +++ b/ets2panda/linter/test/es_object.ts @@ -145,7 +145,7 @@ foo4([2, 3]) foo5([2, 3]) foo4(["str1", "str2"]) foo5(["str1", "str2"]) -let n = new ESObject[0] +let n: ESObject n = null foo4(n) diff --git a/ets2panda/linter/test/es_object.ts.relax.json b/ets2panda/linter/test/es_object.ts.relax.json index f3d5acdb48589622baa499e265b74a732fa003f6..5bab1a3eee7a781b35c0996be116badfe9617820 100644 --- a/ets2panda/linter/test/es_object.ts.relax.json +++ b/ets2panda/linter/test/es_object.ts.relax.json @@ -26,231 +26,231 @@ "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 21, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 22, "column": 11, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 25, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 26, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 27, "column": 11, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 29, "column": 21, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 29, "column": 35, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 29, "column": 53, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 35, "column": 14, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 35, "column": 28, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 35, "column": 46, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 35, "column": 58, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 39, "column": 14, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 39, "column": 28, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 39, "column": 46, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 39, "column": 58, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 43, "column": 14, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 43, "column": 28, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 43, "column": 46, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 43, "column": 60, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 48, "column": 19, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 48, "column": 33, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 48, "column": 51, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 48, "column": 63, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 52, "column": 19, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 52, "column": 33, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 52, "column": 51, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 52, "column": 63, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 56, "column": 19, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 56, "column": 33, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 56, "column": 51, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 56, "column": 65, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 60, @@ -271,175 +271,175 @@ "column": 18, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 64, "column": 32, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 64, "column": 50, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 66, "column": 15, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 67, "column": 17, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 70, "column": 13, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 71, "column": 15, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 77, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 78, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 79, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 80, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 82, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 83, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 85, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 86, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 87, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 88, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 90, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 91, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 93, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 94, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 95, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 96, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 98, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 99, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 103, @@ -453,28 +453,28 @@ "column": 11, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 106, "column": 11, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 108, "column": 18, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 109, "column": 18, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 114, @@ -488,105 +488,112 @@ "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 119, "column": 25, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 119, "column": 36, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 136, "column": 25, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 136, "column": 38, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 148, "column": 5, - "problem": "AnyType", + "problem": "EsObjectType", "suggest": "", - "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" + }, + { + "line": 149, + "column": 1, + "problem": "EsObjectType", + "suggest": "", + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 154, "column": 32, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 154, "column": 45, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 154, "column": 58, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 162, "column": 32, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 162, "column": 47, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 162, "column": 60, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 170, "column": 28, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 172, "column": 22, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 174, "column": 30, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 176, @@ -600,21 +607,21 @@ "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 178, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 179, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 181, @@ -628,49 +635,49 @@ "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 183, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 184, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 185, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 188, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 189, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 190, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" } ] } \ No newline at end of file diff --git a/ets2panda/linter/test/es_object.ts.strict.json b/ets2panda/linter/test/es_object.ts.strict.json index 5c869e41f36b202e2f7537f9bfa7a312c7073b8a..86c0f49acf614ff7fd4b654983ee1a6684b0d3a3 100644 --- a/ets2panda/linter/test/es_object.ts.strict.json +++ b/ets2panda/linter/test/es_object.ts.strict.json @@ -26,231 +26,231 @@ "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 21, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 22, "column": 11, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 25, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 26, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 27, "column": 11, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 29, "column": 21, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 29, "column": 35, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 29, "column": 53, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 35, "column": 14, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 35, "column": 28, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 35, "column": 46, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 35, "column": 58, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 39, "column": 14, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 39, "column": 28, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 39, "column": 46, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 39, "column": 58, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 43, "column": 14, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 43, "column": 28, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 43, "column": 46, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 43, "column": 60, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 48, "column": 19, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 48, "column": 33, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 48, "column": 51, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 48, "column": 63, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 52, "column": 19, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 52, "column": 33, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 52, "column": 51, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 52, "column": 63, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 56, "column": 19, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 56, "column": 33, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 56, "column": 51, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 56, "column": 65, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 60, @@ -271,175 +271,175 @@ "column": 18, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 64, "column": 32, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 64, "column": 50, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 66, "column": 15, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 67, "column": 17, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 70, "column": 13, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 71, "column": 15, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 77, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 78, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 79, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 80, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 82, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 83, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 85, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 86, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 87, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 88, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 90, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 91, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 93, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 94, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 95, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 96, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 98, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 99, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 103, @@ -453,28 +453,28 @@ "column": 11, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 106, "column": 11, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 108, "column": 18, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 109, "column": 18, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 114, @@ -488,105 +488,112 @@ "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 119, "column": 25, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 119, "column": 36, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 136, "column": 25, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 136, "column": 38, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 148, "column": 5, - "problem": "AnyType", + "problem": "EsObjectType", "suggest": "", - "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" + }, + { + "line": 149, + "column": 1, + "problem": "EsObjectType", + "suggest": "", + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 154, "column": 32, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 154, "column": 45, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 154, "column": 58, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 162, "column": 32, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 162, "column": 47, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 162, "column": 60, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 170, "column": 28, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 172, "column": 22, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 174, "column": 30, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 176, @@ -600,21 +607,21 @@ "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 178, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 179, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 181, @@ -628,49 +635,49 @@ "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 183, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 184, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 185, "column": 9, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 188, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 189, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" }, { "line": 190, "column": 5, "problem": "EsObjectType", "suggest": "", - "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobject)" + "rule": "Usage of \"ESObject\" type is restricted (arkts-limited-esobj)" } ] } \ No newline at end of file diff --git a/ets2panda/linter/test/function_expression.ts b/ets2panda/linter/test/function_expression.ts index 0d290ab94b2ed7e3a4846835b33c3bdf38f2ffd0..1a7f8bc07fe0d3d88a9ae6fe8cf8dde646c2225b 100644 --- a/ets2panda/linter/test/function_expression.ts +++ b/ets2panda/linter/test/function_expression.ts @@ -62,7 +62,7 @@ const asyncFun = async function() { }; const factorial = function f(n: number): number { - return n == 1 ? 1 : n * f(n - 1); + return n === 1 ? 1 : n * f(n - 1); }; class C { diff --git a/ets2panda/linter/test/function_expression.ts.autofix.json b/ets2panda/linter/test/function_expression.ts.autofix.json index 90e6f3f3b1843e1b27a91717ae8ec47ced32aeaa..645f7e48d85a7f3dd45bf9f0b69bf382559b4d37 100644 --- a/ets2panda/linter/test/function_expression.ts.autofix.json +++ b/ets2panda/linter/test/function_expression.ts.autofix.json @@ -38,7 +38,7 @@ { "start": 658, "end": 709, - "replacementText": "(x: number, y): number => {\r\n return x * y;\r\n}" + "replacementText": "(x: number, y): number => {\n return x * y;\n}" } ], "suggest": "", @@ -61,7 +61,7 @@ { "start": 759, "end": 792, - "replacementText": "() => {\r\n return 100;\r\n}" + "replacementText": "() => {\n return 100;\n}" } ], "suggest": "", @@ -76,7 +76,7 @@ { "start": 813, "end": 863, - "replacementText": "() => {\r\n return 'get result immediately';\r\n}" + "replacementText": "() => {\n return 'get result immediately';\n}" } ], "suggest": "", @@ -91,7 +91,7 @@ { "start": 870, "end": 908, - "replacementText": "() => {\r\n console.log('foo!');\r\n}" + "replacementText": "() => {\n console.log('foo!');\n}" } ], "suggest": "", @@ -106,7 +106,7 @@ { "start": 920, "end": 958, - "replacementText": "() => {\r\n console.log('bar!');\r\n}" + "replacementText": "() => {\n console.log('bar!');\n}" } ], "suggest": "", @@ -121,7 +121,7 @@ { "start": 1023, "end": 1055, - "replacementText": "(e) => {\r\n return e * 2;\r\n}" + "replacementText": "(e) => {\n return e * 2;\n}" } ], "suggest": "", @@ -136,7 +136,7 @@ { "start": 1084, "end": 1122, - "replacementText": "(x) => {\r\n return x % 2 === 0;\r\n}" + "replacementText": "(x) => {\n return x % 2 === 0;\n}" } ], "suggest": "", @@ -236,9 +236,9 @@ "autofixable": true, "autofix": [ { - "start": 1492, - "end": 1649, - "replacementText": "(p: () => number): void => {\r\n let a = factorial(3);\r\n let b = p();\r\n let c = new C();\r\n c.m();\r\n}" + "start": 1493, + "end": 1650, + "replacementText": "(p: () => number): void => {\n let a = factorial(3);\n let b = p();\n let c = new C();\n c.m();\n}" } ], "suggest": "", @@ -251,9 +251,9 @@ "autofixable": true, "autofix": [ { - "start": 1663, - "end": 1714, - "replacementText": "(() => {\r\n console.log('called immediately');\r\n})" + "start": 1664, + "end": 1715, + "replacementText": "(() => {\n console.log('called immediately');\n})" } ], "suggest": "", @@ -267,6 +267,14 @@ "suggest": "", "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" }, + { + "line": 82, + "column": 19, + "problem": "PropertyAccessByIndex", + "autofixable": true, + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, { "line": 82, "column": 19, @@ -274,9 +282,9 @@ "autofixable": true, "autofix": [ { - "start": 1737, - "end": 1782, - "replacementText": "(() => {\r\n console.log('index access');\r\n})" + "start": 1738, + "end": 1783, + "replacementText": "(() => {\n console.log('index access');\n})" } ], "suggest": "", @@ -289,9 +297,9 @@ "autofixable": true, "autofix": [ { - "start": 1793, - "end": 1830, - "replacementText": "(() => {\r\n console.log('void');\r\n})" + "start": 1794, + "end": 1831, + "replacementText": "(() => {\n console.log('void');\n})" } ], "suggest": "", @@ -304,9 +312,9 @@ "autofixable": true, "autofix": [ { - "start": 1870, - "end": 1912, - "replacementText": "(() => {\r\n console.log('async');\r\n})" + "start": 1871, + "end": 1913, + "replacementText": "(() => {\n console.log('async');\n})" } ], "suggest": "", @@ -319,9 +327,9 @@ "autofixable": true, "autofix": [ { - "start": 1941, - "end": 1980, - "replacementText": "(() => {\r\n console.log('typeof');\r\n})" + "start": 1942, + "end": 1981, + "replacementText": "(() => {\n console.log('typeof');\n})" } ], "suggest": "", @@ -342,9 +350,9 @@ "autofixable": true, "autofix": [ { - "start": 2027, - "end": 2097, - "replacementText": "((p: boolean) => {\r\n console.log('Function.bind(this)');\r\n})" + "start": 2028, + "end": 2098, + "replacementText": "((p: boolean) => {\n console.log('Function.bind(this)');\n})" } ], "suggest": "", @@ -353,7 +361,7 @@ { "line": 104, "column": 7, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "autofixable": false, "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" @@ -365,8 +373,8 @@ "autofixable": true, "autofix": [ { - "start": 2132, - "end": 2171, + "start": 2133, + "end": 2172, "replacementText": "() => { console.log('callback'); }" } ], @@ -380,8 +388,8 @@ "autofixable": true, "autofix": [ { - "start": 2195, - "end": 2246, + "start": 2196, + "end": 2247, "replacementText": "(() => { console.log('expr || function(){}'); })" } ], @@ -395,8 +403,8 @@ "autofixable": true, "autofix": [ { - "start": 2282, - "end": 2322, + "start": 2283, + "end": 2323, "replacementText": "(() => { console.log('ternary 1'); })" } ], @@ -410,8 +418,8 @@ "autofixable": true, "autofix": [ { - "start": 2337, - "end": 2377, + "start": 2338, + "end": 2378, "replacementText": "(() => { console.log('ternary 2'); })" } ], diff --git a/ets2panda/linter/test/function_expression.ts.relax.json b/ets2panda/linter/test/function_expression.ts.relax.json index 8e60772364f880a2b1734887f006e79ab0075cdd..94c08acb658484a5b7b4c3c112b7822b7babdce7 100644 --- a/ets2panda/linter/test/function_expression.ts.relax.json +++ b/ets2panda/linter/test/function_expression.ts.relax.json @@ -59,7 +59,7 @@ { "line": 104, "column": 7, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" } diff --git a/ets2panda/linter/test/function_expression.ts.strict.json b/ets2panda/linter/test/function_expression.ts.strict.json index 1233c4e7db472396252e3157aac380db56d8e583..63b4a107b9d64b55a16e0fc9b24efcff093463f3 100644 --- a/ets2panda/linter/test/function_expression.ts.strict.json +++ b/ets2panda/linter/test/function_expression.ts.strict.json @@ -168,6 +168,13 @@ "suggest": "", "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" }, + { + "line": 82, + "column": 19, + "problem": "PropertyAccessByIndex", + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, { "line": 82, "column": 19, @@ -213,7 +220,7 @@ { "line": 104, "column": 7, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, diff --git a/ets2panda/linter/test/function_object_methods.ts.relax.json b/ets2panda/linter/test/function_object_methods.ts.relax.json index 790856d4f7ac64e42723ba8be5a20b7ae18f32a2..8b7d98debbbbd3287590b514756cf045d5bd7815 100644 --- a/ets2panda/linter/test/function_object_methods.ts.relax.json +++ b/ets2panda/linter/test/function_object_methods.ts.relax.json @@ -24,7 +24,7 @@ { "line": 29, "column": 35, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, @@ -38,21 +38,21 @@ { "line": 30, "column": 45, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 37, "column": 37, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 38, "column": 40, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, @@ -66,7 +66,7 @@ { "line": 68, "column": 44, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, @@ -80,42 +80,42 @@ { "line": 70, "column": 35, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 75, "column": 48, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 78, "column": 48, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 81, "column": 31, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 82, "column": 31, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 87, "column": 32, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, @@ -129,7 +129,7 @@ { "line": 94, "column": 37, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, @@ -143,42 +143,42 @@ { "line": 96, "column": 30, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 101, "column": 42, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 104, "column": 42, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 107, "column": 20, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 108, "column": 20, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 113, "column": 21, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, @@ -190,29 +190,29 @@ "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)" }, { - "line": 118, - "column": 7, + "line": 119, + "column": 12, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" }, { - "line": 121, - "column": 7, + "line": 122, + "column": 12, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" }, { - "line": 124, - "column": 7, + "line": 125, + "column": 12, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" }, { - "line": 127, - "column": 7, + "line": 128, + "column": 12, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" @@ -227,35 +227,35 @@ { "line": 136, "column": 23, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 137, "column": 23, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 138, "column": 23, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 139, "column": 23, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 141, "column": 5, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" } diff --git a/ets2panda/linter/test/function_object_methods.ts.strict.json b/ets2panda/linter/test/function_object_methods.ts.strict.json index 2a7c8ccd943aded4391752c20d2e26ecc66b4c29..27edb865a6acb750b13a44b8284fb70765e80099 100644 --- a/ets2panda/linter/test/function_object_methods.ts.strict.json +++ b/ets2panda/linter/test/function_object_methods.ts.strict.json @@ -24,7 +24,7 @@ { "line": 29, "column": 35, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, @@ -38,21 +38,21 @@ { "line": 30, "column": 45, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 37, "column": 37, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 38, "column": 40, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, @@ -66,7 +66,7 @@ { "line": 68, "column": 44, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, @@ -80,42 +80,42 @@ { "line": 70, "column": 35, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 75, "column": 48, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 78, "column": 48, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 81, "column": 31, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 82, "column": 31, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 87, "column": 32, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, @@ -129,7 +129,7 @@ { "line": 94, "column": 37, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, @@ -143,42 +143,42 @@ { "line": 96, "column": 30, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 101, "column": 42, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 104, "column": 42, - "problem": "FunctionApplyBindCall", + "problem": "FunctionBind", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 107, "column": 20, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 108, "column": 20, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 113, "column": 21, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, @@ -197,8 +197,8 @@ "rule": "Use arrow functions instead of function expressions (arkts-no-func-expressions)" }, { - "line": 118, - "column": 7, + "line": 119, + "column": 12, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" @@ -218,15 +218,15 @@ "rule": "Use arrow functions instead of function expressions (arkts-no-func-expressions)" }, { - "line": 121, - "column": 7, + "line": 122, + "column": 12, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" }, { - "line": 124, - "column": 7, + "line": 125, + "column": 12, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" @@ -239,8 +239,8 @@ "rule": "Function return type inference is limited (arkts-no-implicit-return-types)" }, { - "line": 127, - "column": 7, + "line": 128, + "column": 12, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" @@ -255,35 +255,35 @@ { "line": 136, "column": 23, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 137, "column": 23, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 138, "column": 23, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 139, "column": 23, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" }, { "line": 141, "column": 5, - "problem": "FunctionApplyBindCall", + "problem": "FunctionApplyCall", "suggest": "", "rule": "\"Function.apply\", \"Function.bind\", \"Function.call\" are not supported (arkts-no-func-apply-bind-call)" } diff --git a/ets2panda/linter/test/functions.ts b/ets2panda/linter/test/functions.ts index 8d67f99ff3292a00005ed662c0798ca5ab08801e..57ea35bf979b30150cc3a3bc92ea4b15486d7c6f 100644 --- a/ets2panda/linter/test/functions.ts +++ b/ets2panda/linter/test/functions.ts @@ -55,7 +55,7 @@ function arrowFunctionTest() { const sqrt = (x) => Math.sqrt(x); // shortcut syntax const even = [1, 2, 3, 4, 5, 6].filter((x) => x % 2 === 0); // shortcut syntax - const foo = (x: number, y): boolean => x == y; // types are partly omitted + const foo = (x: number, y): boolean => x === y; // types are partly omitted const generic = (t: T, e: E) => t; // Generic lambda } @@ -105,4 +105,23 @@ bar(() => { bar(() => { f(null); }, null, f(null)); -}, null, foo(f(null))); \ No newline at end of file +}, null, foo(f(null))); + +type PropDecorator = () => void; +let Builder: PropDecorator; + +// this test is useless until we use custom tsc +@Builder +function buildSwiper() { + f(null) + foo(null) { + f(null) + foo(null) { + f(null) + foo(() => { + f(null) + }) + } + .foo(null) + } +} diff --git a/ets2panda/linter/test/functions.ts.autofix.json b/ets2panda/linter/test/functions.ts.autofix.json index bdec3c538a0a38afa2968f452019ea0cc98ccffe..0b6af45e6e03484b2768c01a8afaeb72405fa74d 100755 --- a/ets2panda/linter/test/functions.ts.autofix.json +++ b/ets2panda/linter/test/functions.ts.autofix.json @@ -87,8 +87,8 @@ "rule": "Use generic functions instead of generic arrow functions (arkts-no-generic-lambdas)" }, { - "line": 63, - "column": 1, + "line": 64, + "column": 3, "problem": "FunctionContainsThis", "autofixable": false, "suggest": "", @@ -157,6 +157,38 @@ "autofixable": false, "suggest": "Argument of type 'null' is not assignable to parameter of type 'string'.", "rule": "Argument of type 'null' is not assignable to parameter of type 'string'." + }, + { + "line": 116, + "column": 5, + "problem": "StrictDiagnostic", + "autofixable": false, + "suggest": "Argument of type 'null' is not assignable to parameter of type 'string'.", + "rule": "Argument of type 'null' is not assignable to parameter of type 'string'." + }, + { + "line": 118, + "column": 7, + "problem": "StrictDiagnostic", + "autofixable": false, + "suggest": "Argument of type 'null' is not assignable to parameter of type 'string'.", + "rule": "Argument of type 'null' is not assignable to parameter of type 'string'." + }, + { + "line": 120, + "column": 11, + "problem": "StrictDiagnostic", + "autofixable": false, + "suggest": "Argument of type 'null' is not assignable to parameter of type 'string'.", + "rule": "Argument of type 'null' is not assignable to parameter of type 'string'." + }, + { + "line": 122, + "column": 11, + "problem": "StrictDiagnostic", + "autofixable": false, + "suggest": "Argument of type 'null' is not assignable to parameter of type 'string'.", + "rule": "Argument of type 'null' is not assignable to parameter of type 'string'." } ] } \ No newline at end of file diff --git a/ets2panda/linter/test/functions.ts.relax.json b/ets2panda/linter/test/functions.ts.relax.json index e11f7f9c4b4ba8fd83a515d2609ee21e3b7f6c08..6729c50d23c446b1835bbe8bb90355aa46755ce6 100644 --- a/ets2panda/linter/test/functions.ts.relax.json +++ b/ets2panda/linter/test/functions.ts.relax.json @@ -64,8 +64,8 @@ "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" }, { - "line": 63, - "column": 1, + "line": 64, + "column": 3, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" @@ -125,6 +125,34 @@ "problem": "StrictDiagnostic", "suggest": "Argument of type 'null' is not assignable to parameter of type 'string'.", "rule": "Argument of type 'null' is not assignable to parameter of type 'string'." + }, + { + "line": 116, + "column": 5, + "problem": "StrictDiagnostic", + "suggest": "Argument of type 'null' is not assignable to parameter of type 'string'.", + "rule": "Argument of type 'null' is not assignable to parameter of type 'string'." + }, + { + "line": 118, + "column": 7, + "problem": "StrictDiagnostic", + "suggest": "Argument of type 'null' is not assignable to parameter of type 'string'.", + "rule": "Argument of type 'null' is not assignable to parameter of type 'string'." + }, + { + "line": 120, + "column": 11, + "problem": "StrictDiagnostic", + "suggest": "Argument of type 'null' is not assignable to parameter of type 'string'.", + "rule": "Argument of type 'null' is not assignable to parameter of type 'string'." + }, + { + "line": 122, + "column": 11, + "problem": "StrictDiagnostic", + "suggest": "Argument of type 'null' is not assignable to parameter of type 'string'.", + "rule": "Argument of type 'null' is not assignable to parameter of type 'string'." } ] } \ No newline at end of file diff --git a/ets2panda/linter/test/functions.ts.strict.json b/ets2panda/linter/test/functions.ts.strict.json index d39199ff0024aa2f8a01d89ad214a4e3c83e8212..d57b74aa35f727c11969750a8ddbd117512ecf46 100644 --- a/ets2panda/linter/test/functions.ts.strict.json +++ b/ets2panda/linter/test/functions.ts.strict.json @@ -78,8 +78,8 @@ "rule": "Use generic functions instead of generic arrow functions (arkts-no-generic-lambdas)" }, { - "line": 63, - "column": 1, + "line": 64, + "column": 3, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" @@ -139,6 +139,34 @@ "problem": "StrictDiagnostic", "suggest": "Argument of type 'null' is not assignable to parameter of type 'string'.", "rule": "Argument of type 'null' is not assignable to parameter of type 'string'." + }, + { + "line": 116, + "column": 5, + "problem": "StrictDiagnostic", + "suggest": "Argument of type 'null' is not assignable to parameter of type 'string'.", + "rule": "Argument of type 'null' is not assignable to parameter of type 'string'." + }, + { + "line": 118, + "column": 7, + "problem": "StrictDiagnostic", + "suggest": "Argument of type 'null' is not assignable to parameter of type 'string'.", + "rule": "Argument of type 'null' is not assignable to parameter of type 'string'." + }, + { + "line": 120, + "column": 11, + "problem": "StrictDiagnostic", + "suggest": "Argument of type 'null' is not assignable to parameter of type 'string'.", + "rule": "Argument of type 'null' is not assignable to parameter of type 'string'." + }, + { + "line": 122, + "column": 11, + "problem": "StrictDiagnostic", + "suggest": "Argument of type 'null' is not assignable to parameter of type 'string'.", + "rule": "Argument of type 'null' is not assignable to parameter of type 'string'." } ] } \ No newline at end of file diff --git a/ets2panda/linter/test/limited_stdlib_api.ts b/ets2panda/linter/test/limited_stdlib_api.ts index 500c10c9b1d179d12501a1cd57028ecfddda560e..f0da40f811cd9d7270aa11756fe5c49a30595dc6 100644 --- a/ets2panda/linter/test/limited_stdlib_api.ts +++ b/ets2panda/linter/test/limited_stdlib_api.ts @@ -28,7 +28,7 @@ decodeURIComponent(''); escape(''); unescape(''); -// global and window are not portable, so they are excluded from test suite. +global.eval('console.log("foo")'); globalThis.eval('console.log("foo")'); const evl = "eval('console.log(1)')"; const res: void = Function(evl)(); @@ -48,11 +48,11 @@ const object1: Obj = { }; /// Object -//Object.__proto__(), -//Object.__defineGetter__(), -//Object.__defineSetter__(); -//Object.__lookupGetter__(); -//Object.__lookupSetter__(); +// DO NOT CHECK Object.__proto__(), +// DO NOT CHECK Object.__defineGetter__(), +// DO NOT CHECK Object.__defineSetter__(); +// DO NOT CHECK Object.__lookupGetter__(); +// DO NOT CHECK Object.__lookupSetter__(); Object.assign(c, c); Object.create(c); Object.defineProperties(c, {}); @@ -121,9 +121,3 @@ ArrayBuffer.isView({}); let a: number[] = []; let b = new ArrayBuffer(1); Array.isArray(a); - -Number.NaN; -Number.isFinite(1); -Number.isNaN(2); -Number.parseFloat('3'); -Number.parseInt('4', 10); diff --git a/ets2panda/linter/test/limited_stdlib_api.ts.autofix.json b/ets2panda/linter/test/limited_stdlib_api.ts.autofix.json index ddabd1f8994bb6f656b118c4383596b762abe056..08f902699b1857c997ff3dc22d2ff0718f268750 100644 --- a/ets2panda/linter/test/limited_stdlib_api.ts.autofix.json +++ b/ets2panda/linter/test/limited_stdlib_api.ts.autofix.json @@ -22,6 +22,14 @@ "suggest": "", "rule": "Usage of standard library is restricted (arkts-limited-stdlib)" }, + { + "line": 31, + "column": 8, + "problem": "LimitedStdLibApi", + "autofixable": false, + "suggest": "", + "rule": "Usage of standard library is restricted (arkts-limited-stdlib)" + }, { "line": 32, "column": 1, @@ -517,14 +525,6 @@ "autofixable": false, "suggest": "", "rule": "Usage of standard library is restricted (arkts-limited-stdlib)" - }, - { - "line": 120, - "column": 13, - "problem": "LimitedStdLibApi", - "autofixable": false, - "suggest": "", - "rule": "Usage of standard library is restricted (arkts-limited-stdlib)" } ] } diff --git a/ets2panda/linter/test/limited_stdlib_api.ts.relax.json b/ets2panda/linter/test/limited_stdlib_api.ts.relax.json index d77e760a87296a86fded6e3d9e77ffd7fa7ffc76..55e798673cbf641d8e6d7f0979c79cd280bcb43c 100644 --- a/ets2panda/linter/test/limited_stdlib_api.ts.relax.json +++ b/ets2panda/linter/test/limited_stdlib_api.ts.relax.json @@ -21,6 +21,13 @@ "suggest": "", "rule": "Usage of standard library is restricted (arkts-limited-stdlib)" }, + { + "line": 31, + "column": 8, + "problem": "LimitedStdLibApi", + "suggest": "", + "rule": "Usage of standard library is restricted (arkts-limited-stdlib)" + }, { "line": 32, "column": 1, @@ -454,13 +461,6 @@ "problem": "LimitedStdLibApi", "suggest": "", "rule": "Usage of standard library is restricted (arkts-limited-stdlib)" - }, - { - "line": 120, - "column": 13, - "problem": "LimitedStdLibApi", - "suggest": "", - "rule": "Usage of standard library is restricted (arkts-limited-stdlib)" } ] } diff --git a/ets2panda/linter/test/limited_stdlib_api.ts.strict.json b/ets2panda/linter/test/limited_stdlib_api.ts.strict.json index d77e760a87296a86fded6e3d9e77ffd7fa7ffc76..55e798673cbf641d8e6d7f0979c79cd280bcb43c 100644 --- a/ets2panda/linter/test/limited_stdlib_api.ts.strict.json +++ b/ets2panda/linter/test/limited_stdlib_api.ts.strict.json @@ -21,6 +21,13 @@ "suggest": "", "rule": "Usage of standard library is restricted (arkts-limited-stdlib)" }, + { + "line": 31, + "column": 8, + "problem": "LimitedStdLibApi", + "suggest": "", + "rule": "Usage of standard library is restricted (arkts-limited-stdlib)" + }, { "line": 32, "column": 1, @@ -454,13 +461,6 @@ "problem": "LimitedStdLibApi", "suggest": "", "rule": "Usage of standard library is restricted (arkts-limited-stdlib)" - }, - { - "line": 120, - "column": 13, - "problem": "LimitedStdLibApi", - "suggest": "", - "rule": "Usage of standard library is restricted (arkts-limited-stdlib)" } ] } diff --git a/ets2panda/linter/test/literals_as_prop_names.ts.autofix.json b/ets2panda/linter/test/literals_as_prop_names.ts.autofix.json index 0a9a07f38cbae7aae301133153373a54f827a526..d6ae92d315c201218886ae656cfda3df31eebc51 100644 --- a/ets2panda/linter/test/literals_as_prop_names.ts.autofix.json +++ b/ets2panda/linter/test/literals_as_prop_names.ts.autofix.json @@ -30,14 +30,6 @@ "suggest": "", "rule": "Objects with property names that are not identifiers are not supported (arkts-identifiers-as-prop-names)" }, - { - "line": 22, - "column": 46, - "problem": "ObjectLiteralNoContextType", - "autofixable": false, - "suggest": "", - "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)" - }, { "line": 24, "column": 3, diff --git a/ets2panda/linter/test/literals_as_prop_names.ts.relax.json b/ets2panda/linter/test/literals_as_prop_names.ts.relax.json index ed183dfd90179d2a3df25b2ecd66720f03d919ae..d0e9e2159796ceb8a87f9a4a309557f668d7dc24 100644 --- a/ets2panda/linter/test/literals_as_prop_names.ts.relax.json +++ b/ets2panda/linter/test/literals_as_prop_names.ts.relax.json @@ -14,13 +14,6 @@ "limitations under the License." ], "nodes": [ - { - "line": 22, - "column": 46, - "problem": "ObjectLiteralNoContextType", - "suggest": "", - "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)" - }, { "line": 49, "column": 9, diff --git a/ets2panda/linter/test/literals_as_prop_names.ts.strict.json b/ets2panda/linter/test/literals_as_prop_names.ts.strict.json index 0e33009041a8eaafa051e8fba20806f94afb98b7..edae4c16a30b3741d2d551600a925f6f648881db 100644 --- a/ets2panda/linter/test/literals_as_prop_names.ts.strict.json +++ b/ets2panda/linter/test/literals_as_prop_names.ts.strict.json @@ -28,13 +28,6 @@ "suggest": "", "rule": "Objects with property names that are not identifiers are not supported (arkts-identifiers-as-prop-names)" }, - { - "line": 22, - "column": 46, - "problem": "ObjectLiteralNoContextType", - "suggest": "", - "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)" - }, { "line": 24, "column": 3, diff --git a/ets2panda/linter/test/modules.ts.autofix.json b/ets2panda/linter/test/modules.ts.autofix.json index 3c5ec13d6f685451e8553fcdd3948471d0dd5d16..c78c786038a4bbdbe0aa25169ef04201cde936c3 100755 --- a/ets2panda/linter/test/modules.ts.autofix.json +++ b/ets2panda/linter/test/modules.ts.autofix.json @@ -111,96 +111,18 @@ "problem": "ImportAfterStatement", "autofixable": false }, - { - "line": 92, - "column": 8, - "problem": "TypeOnlyImport", - "autofixable": true, - "autofix": [ - { - "start": 1923, - "end": 1947, - "replacementText": "{ APIResponseType }" - } - ] - }, { "line": 93, "column": 1, "problem": "ImportAfterStatement", "autofixable": false }, - { - "line": 93, - "column": 8, - "problem": "TypeOnlyImport", - "autofixable": true, - "autofix": [ - { - "start": 1969, - "end": 1980, - "replacementText": "* as P" - } - ] - }, { "line": 94, "column": 1, "problem": "ImportAfterStatement", "autofixable": false }, - { - "line": 94, - "column": 10, - "problem": "TypeOnlyImport", - "autofixable": true, - "autofix": [ - { - "start": 2002, - "end": 2009, - "replacementText": "T1" - } - ] - }, - { - "line": 94, - "column": 19, - "problem": "TypeOnlyImport", - "autofixable": true, - "autofix": [ - { - "start": 2011, - "end": 2024, - "replacementText": "T2 as T3" - } - ] - }, - { - "line": 97, - "column": 1, - "problem": "TypeOnlyExport", - "autofixable": true, - "autofix": [ - { - "start": 2063, - "end": 2094, - "replacementText": "export { TypeA as TypeB };" - } - ] - }, - { - "line": 98, - "column": 10, - "problem": "TypeOnlyExport", - "autofixable": true, - "autofix": [ - { - "start": 2104, - "end": 2127, - "replacementText": "TypeFoo as TypeBar" - } - ] - }, { "line": 106, "column": 1, diff --git a/ets2panda/linter/test/modules.ts.strict.json b/ets2panda/linter/test/modules.ts.strict.json index c13f8f250c733e4a83febe03b93c51b08f61dae7..273338ee9f32ab2cf5c5e1280ab9e3371f9aaa13 100644 --- a/ets2panda/linter/test/modules.ts.strict.json +++ b/ets2panda/linter/test/modules.ts.strict.json @@ -89,46 +89,16 @@ "column": 1, "problem": "ImportAfterStatement" }, - { - "line": 92, - "column": 8, - "problem": "TypeOnlyImport" - }, { "line": 93, "column": 1, "problem": "ImportAfterStatement" }, - { - "line": 93, - "column": 8, - "problem": "TypeOnlyImport" - }, { "line": 94, "column": 1, "problem": "ImportAfterStatement" }, - { - "line": 94, - "column": 10, - "problem": "TypeOnlyImport" - }, - { - "line": 94, - "column": 19, - "problem": "TypeOnlyImport" - }, - { - "line": 97, - "column": 1, - "problem": "TypeOnlyExport" - }, - { - "line": 98, - "column": 10, - "problem": "TypeOnlyExport" - }, { "line": 106, "column": 1, diff --git a/ets2panda/linter/test/null_check_calls.ts.relax.json b/ets2panda/linter/test/null_check_calls.ts.relax.json index 8d879ca4223e2bce2d0709c852246dd3ad3ab734..dda25acbfec4d84cec0fc79719420f95ed456b73 100644 --- a/ets2panda/linter/test/null_check_calls.ts.relax.json +++ b/ets2panda/linter/test/null_check_calls.ts.relax.json @@ -14,30 +14,33 @@ "limitations under the License." ], "nodes": [ - { - "line": 33, - "column": 12, - "problem": "StrictDiagnostic" - }, { "line": 37, "column": 5, - "problem": "StrictDiagnostic" + "problem": "StrictDiagnostic", + "suggest": "Argument of type 'null' is not assignable to parameter of type 'number[]'.", + "rule": "Argument of type 'null' is not assignable to parameter of type 'number[]'." }, { "line": 42, "column": 20, - "problem": "StrictDiagnostic" + "problem": "StrictDiagnostic", + "suggest": "Argument of type 'null' is not assignable to parameter of type 'number'.", + "rule": "Argument of type 'null' is not assignable to parameter of type 'number'." }, { "line": 43, "column": 42, - "problem": "StrictDiagnostic" + "problem": "StrictDiagnostic", + "suggest": "Argument of type 'number | null' is not assignable to parameter of type 'number'.\n Type 'null' is not assignable to type 'number'.", + "rule": "Argument of type 'number | null' is not assignable to parameter of type 'number'.\n Type 'null' is not assignable to type 'number'." }, { "line": 47, "column": 5, - "problem": "StrictDiagnostic" + "problem": "StrictDiagnostic", + "suggest": "Argument of type 'number | null' is not assignable to parameter of type 'number'.\n Type 'null' is not assignable to type 'number'.", + "rule": "Argument of type 'number | null' is not assignable to parameter of type 'number'.\n Type 'null' is not assignable to type 'number'." }, { "line": 52, diff --git a/ets2panda/linter/test/null_check_calls.ts.strict.json b/ets2panda/linter/test/null_check_calls.ts.strict.json index fef4e06af40b279782d379965a669bfdb2bdbf71..6ae1263ea8889c0434cf633704ca2787dddc5c9f 100644 --- a/ets2panda/linter/test/null_check_calls.ts.strict.json +++ b/ets2panda/linter/test/null_check_calls.ts.strict.json @@ -17,32 +17,37 @@ { "line": 43, "column": 1, - "problem": "LimitedReturnTypeInference" - }, - { - "line": 33, - "column": 12, - "problem": "StrictDiagnostic" + "problem": "LimitedReturnTypeInference", + "suggest": "", + "rule": "Function return type inference is limited (arkts-no-implicit-return-types)" }, { "line": 37, "column": 5, - "problem": "StrictDiagnostic" + "problem": "StrictDiagnostic", + "suggest": "Argument of type 'null' is not assignable to parameter of type 'number[]'.", + "rule": "Argument of type 'null' is not assignable to parameter of type 'number[]'." }, { "line": 42, "column": 20, - "problem": "StrictDiagnostic" + "problem": "StrictDiagnostic", + "suggest": "Argument of type 'null' is not assignable to parameter of type 'number'.", + "rule": "Argument of type 'null' is not assignable to parameter of type 'number'." }, { "line": 43, "column": 42, - "problem": "StrictDiagnostic" + "problem": "StrictDiagnostic", + "suggest": "Argument of type 'number | null' is not assignable to parameter of type 'number'.\n Type 'null' is not assignable to type 'number'.", + "rule": "Argument of type 'number | null' is not assignable to parameter of type 'number'.\n Type 'null' is not assignable to type 'number'." }, { "line": 47, "column": 5, - "problem": "StrictDiagnostic" + "problem": "StrictDiagnostic", + "suggest": "Argument of type 'number | null' is not assignable to parameter of type 'number'.\n Type 'null' is not assignable to type 'number'.", + "rule": "Argument of type 'number | null' is not assignable to parameter of type 'number'.\n Type 'null' is not assignable to type 'number'." }, { "line": 52, diff --git a/ets2panda/linter/test/object_literals_2.ts b/ets2panda/linter/test/object_literals_2.ts index 2a78eaa7fffe292882a3fefeddf5a820326d37f8..d2068109eb5de32ab25a82dcf72b50d7b266566f 100644 --- a/ets2panda/linter/test/object_literals_2.ts +++ b/ets2panda/linter/test/object_literals_2.ts @@ -77,7 +77,7 @@ class C3 { public s: string = ''; } -//let c6: C3 = {n: 42, s: 'foo'}; // CTE in TypeScript, CTE in ArkTS + class C4 { readonly n: number = 0; @@ -118,7 +118,7 @@ class C7 { n: number = 0; s: string = ''; } -//let c10: C7 = {n: 42, s: '', extra: true}; // TS: CTE, ArtTS: CTE + let o1: Object = {s: '', n: 42} // OK in TS, CTE in ArkTS: no fields 'n' and 's' in Object let o2: object = {n: 42, s: ''} // OK in TS, CTE in ArkTS: no fields 'n' and 's' in object diff --git a/ets2panda/linter/test/object_literals_prop_func_type.ts b/ets2panda/linter/test/object_literals_prop_func_type.ts index 19ce7f092a6e925ea3b6aff8ac41eafe33a53bb2..51df3217ff2425e013a839c44eb84c3a0701d0bb 100644 --- a/ets2panda/linter/test/object_literals_prop_func_type.ts +++ b/ets2panda/linter/test/object_literals_prop_func_type.ts @@ -43,4 +43,12 @@ a = { b: w, c: e, d: r, -} \ No newline at end of file +} + +// #14569 - initialize field with 'Function' object +class B { + f: Function = () => {}; +} +let b: B = { + f: Function +}; \ No newline at end of file diff --git a/ets2panda/linter/test/oh_modules/ohos_factory.ts b/ets2panda/linter/test/oh_modules/ohos_factory.ts index e26c4c911ba886e1d930a38404f9a87434d454b3..b4fc3d2d2fe025f7deb68faa0cf3374beece71ee 100644 --- a/ets2panda/linter/test/oh_modules/ohos_factory.ts +++ b/ets2panda/linter/test/oh_modules/ohos_factory.ts @@ -16,3 +16,18 @@ export declare class SomethingBar extends Something { } export declare class Bar { constructor(arg: { new(): T }); } + +export class Select { + public from(cls: any) { + return this; + } + + // we intentionally omit generic argument of 'Select', see #14228 + public eq(name: string): Select { + return this; + } + + public query(cls: any): Promise { + return cls.foo(); + } +} \ No newline at end of file diff --git a/ets2panda/linter/test/oh_modules/ohos_lib.ts b/ets2panda/linter/test/oh_modules/ohos_lib.ts index c69856185bae174591b09b69829d4ba9aa96b5bc..cf99dd919e30412f0fa0e305775f6e0a17b581a0 100644 --- a/ets2panda/linter/test/oh_modules/ohos_lib.ts +++ b/ets2panda/linter/test/oh_modules/ohos_lib.ts @@ -1,5 +1,8 @@ export class A { - constructor(public onAction?: () => void, public onH?: () => void) {} + constructor( + public onAction?: () => void, + public onH?: () => void + ) {} } export function f(a: Partial) {} @@ -15,11 +18,17 @@ export class OhosLibCC extends OhosLibC {} export class OhosLibCI implements OhosLibI {} export interface OhosI { - f: number + f: number; } -export function ohFunction1({d: OhosI}): void {} // incorrect usage, but it was an issue, so we check it too -export function ohFunction2(p: {d: OhosI}): void {} +export function ohFunction1({ d: OhosI }): void {} // incorrect usage, but it was an issue, so we check it too +export function ohFunction2(p: { d: OhosI }): void {} export function fooOh(): any {} export function barOh(a: any) {} + +export interface OhosInterface { + fld: A & { + [key: string]: number; + }; +} diff --git a/ets2panda/linter/test/property_access_by_index.ts b/ets2panda/linter/test/property_access_by_index.ts new file mode 100644 index 0000000000000000000000000000000000000000..4fb0eb92220e59470f7fd3597a2ef2e39ca9ebc0 --- /dev/null +++ b/ets2panda/linter/test/property_access_by_index.ts @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2023-2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import {OhosInterface} from './oh_modules/ohos_lib' +// #14071 +class A { + v: string = ''; +} +function SetProperty(oldObj: T, str: string, obj: Object): void { + oldObj[str] = obj; // Should report error +} +function GetProperty(oldObj: T, str: string): U { + return oldObj[str]; // Should report error +} +function test() { + let a: A = { v: 'abc' }; + SetProperty(a, 'u', 'def'); + return GetProperty(a, 'v') + GetProperty(a, 'u'); +} + +let ar1 = [1, 2, 3, 4]; +let ar2 = [1, '2', 3, 4]; +let ar3: number[] = []; + +ar1[2]; +ar2[2]; +ar3[2]; + +const r0 = [1, 2, 3][1]; +let r1 = [1, 2, 3, 4][0] +let r2 = [1, '2', 3, 4][0] + +function fobject1(o: object) { + o['j'] +} + +function fobject2(o: Object) { + o['k'] +} + +let array1 = [0,1] +let array2 = [1,2,3,4,5] +let array3: number[] = [1,2,3,4,5] +let array4: Array = [1,2,3,4,5] +let array5 = new Array(10) +let array6 = new Int8Array(10) +let array7 = new Uint8Array(10) +let array8 = new Uint8ClampedArray(10) +let array9 = new Int16Array(10) +let array10 = new Uint16Array(10) +let array11 = new Int32Array(10) +let array12 = new Uint32Array(10) +let array13 = new Float32Array(10) +let array14 = new Float64Array(10) +let array15 = new BigInt64Array(10) +let array16 = new BigUint64Array(10) + +array1[0]; +array2[0]; +array3[0]; +array4[0]; +array5[0]; +array6[0]; +array7[0]; +array8[0]; +array9[0]; +array10[0]; +array11[0]; +array12[0]; +array13[0]; +array14[0]; +array15[0]; +array16[0]; + +function fff1(r: Record) { + r['bob'] +} + +enum CCCCCCCCC { + KATE, + BOB, + ROB, +} + +CCCCCCCCC['KATE'] +CCCCCCCCC['BOB'] +CCCCCCCCC['ROB'] + +CCCCCCCCC[CCCCCCCCC.KATE] +CCCCCCCCC[CCCCCCCCC.BOB] +CCCCCCCCC[CCCCCCCCC.ROB] + +let arr32 = new Float32Array([1,2,3]) + +let iter_arr32 = arr32[Symbol.iterator]() +let tmp_arr32 = iter_arr32.next().value; +while (!!tmp_arr32) { + console.log(tmp_arr32[0]) + + tmp_arr32 = iter_arr32.next().value +} + +let arr = new Array() +arr = ['a','f','g'] +let iter_arr = arr[Symbol.iterator]() +let tmp_arr = iter_arr.next().value; +while (!!tmp_arr) { + console.log(tmp_arr[0]) + tmp_arr = iter_arr.next().value +} + +// #14415 +class ArrayContainer { + numbers: number[] = []; +} +class NullableArray { + container: ArrayContainer | null = null; + + print() { + console.log(this.container?.numbers[0]); + } +} + +let str1 = 'sssss' +let str2 = "aaaaa" +let str3 = `sssss` +let str4 = new String('sssss') +let str5 = str1 +let str6 = str2 + +str1[1] +str2[1] +str3[1] +str4[1] +str5[1] +str6[1] + +class AString extends String {} +let str7 = new AString('dwdd') +str7[1] + +type IndexableUnion = string[] | (number | string)[] | Uint8Array; +type NonIndexableUnion = string[] | number[] | Uint8Array | number; + +function indexUnion(iu: IndexableUnion, niu: NonIndexableUnion) { + iu[0]; + niu[0]; +} + +function testLibraryUnnamedType(a: OhosInterface) { + a['kek']; +} + +class MMap extends Map {} + +let mmap1 = new Map(); +let mmap2 = new Map(); +let mmap3 = new MMap(); + +mmap1[1]; +mmap2['222']; +mmap3["kkr"]; diff --git a/ets2panda/linter/test/property_access_by_index.ts.autofix.json b/ets2panda/linter/test/property_access_by_index.ts.autofix.json new file mode 100644 index 0000000000000000000000000000000000000000..8a928547655065fa02e35fd259a18de7556e7b0a --- /dev/null +++ b/ets2panda/linter/test/property_access_by_index.ts.autofix.json @@ -0,0 +1,162 @@ +{ + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + "nodes": [ + { + "line": 21, + "column": 3, + "problem": "PropertyAccessByIndex", + "autofixable": false, + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 24, + "column": 10, + "problem": "PropertyAccessByIndex", + "autofixable": false, + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 45, + "column": 3, + "problem": "PropertyAccessByIndex", + "autofixable": true, + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 49, + "column": 3, + "problem": "PropertyAccessByIndex", + "autofixable": true, + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 56, + "column": 5, + "problem": "AnyType", + "autofixable": false, + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + }, + { + "line": 107, + "column": 5, + "problem": "AnyType", + "autofixable": false, + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + }, + { + "line": 117, + "column": 5, + "problem": "AnyType", + "autofixable": false, + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + }, + { + "line": 142, + "column": 1, + "problem": "PropertyAccessByIndex", + "autofixable": true, + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 143, + "column": 1, + "problem": "PropertyAccessByIndex", + "autofixable": true, + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 144, + "column": 1, + "problem": "PropertyAccessByIndex", + "autofixable": true, + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 145, + "column": 1, + "problem": "PropertyAccessByIndex", + "autofixable": true, + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 146, + "column": 1, + "problem": "PropertyAccessByIndex", + "autofixable": true, + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 147, + "column": 1, + "problem": "PropertyAccessByIndex", + "autofixable": true, + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 151, + "column": 1, + "problem": "PropertyAccessByIndex", + "autofixable": true, + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 158, + "column": 3, + "problem": "PropertyAccessByIndex", + "autofixable": true, + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 171, + "column": 1, + "problem": "PropertyAccessByIndex", + "autofixable": true, + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 172, + "column": 1, + "problem": "PropertyAccessByIndex", + "autofixable": true, + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 173, + "column": 1, + "problem": "PropertyAccessByIndex", + "autofixable": true, + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + } + ] +} \ No newline at end of file diff --git a/ets2panda/linter/test/property_access_by_index.ts.relax.json b/ets2panda/linter/test/property_access_by_index.ts.relax.json new file mode 100644 index 0000000000000000000000000000000000000000..b492ed7380feb4b306433cd940c731223e71b0ad --- /dev/null +++ b/ets2panda/linter/test/property_access_by_index.ts.relax.json @@ -0,0 +1,39 @@ +{ + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + "nodes": [ + { + "line": 56, + "column": 5, + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + }, + { + "line": 107, + "column": 5, + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + }, + { + "line": 117, + "column": 5, + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + } + ] +} \ No newline at end of file diff --git a/ets2panda/linter/test/property_access_by_index.ts.strict.json b/ets2panda/linter/test/property_access_by_index.ts.strict.json new file mode 100644 index 0000000000000000000000000000000000000000..0afff457f48e0802a36bc06eef8e5fefde828a12 --- /dev/null +++ b/ets2panda/linter/test/property_access_by_index.ts.strict.json @@ -0,0 +1,144 @@ +{ + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + "nodes": [ + { + "line": 21, + "column": 3, + "problem": "PropertyAccessByIndex", + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 24, + "column": 10, + "problem": "PropertyAccessByIndex", + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 45, + "column": 3, + "problem": "PropertyAccessByIndex", + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 49, + "column": 3, + "problem": "PropertyAccessByIndex", + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 56, + "column": 5, + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + }, + { + "line": 107, + "column": 5, + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + }, + { + "line": 117, + "column": 5, + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + }, + { + "line": 142, + "column": 1, + "problem": "PropertyAccessByIndex", + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 143, + "column": 1, + "problem": "PropertyAccessByIndex", + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 144, + "column": 1, + "problem": "PropertyAccessByIndex", + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 145, + "column": 1, + "problem": "PropertyAccessByIndex", + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 146, + "column": 1, + "problem": "PropertyAccessByIndex", + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 147, + "column": 1, + "problem": "PropertyAccessByIndex", + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 151, + "column": 1, + "problem": "PropertyAccessByIndex", + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 158, + "column": 3, + "problem": "PropertyAccessByIndex", + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 171, + "column": 1, + "problem": "PropertyAccessByIndex", + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 172, + "column": 1, + "problem": "PropertyAccessByIndex", + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 173, + "column": 1, + "problem": "PropertyAccessByIndex", + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + } + ] +} \ No newline at end of file diff --git a/ets2panda/linter/test/prototype_assignment.ts.autofix.json b/ets2panda/linter/test/prototype_assignment.ts.autofix.json index 381cadf69d898a62ea6f5b7b50ff017f9d8c06d8..c1e3b5c631a1796b036aa3c90164158e70e21a2a 100755 --- a/ets2panda/linter/test/prototype_assignment.ts.autofix.json +++ b/ets2panda/linter/test/prototype_assignment.ts.autofix.json @@ -15,8 +15,8 @@ ], "nodes": [ { - "line": 19, - "column": 1, + "line": 26, + "column": 19, "problem": "FunctionContainsThis", "autofixable": false, "suggest": "", @@ -31,8 +31,8 @@ "rule": "Use arrow functions instead of function expressions (arkts-no-func-expressions)" }, { - "line": 20, - "column": 20, + "line": 21, + "column": 5, "problem": "FunctionContainsThis", "autofixable": false, "suggest": "", @@ -71,8 +71,8 @@ "rule": "Use arrow functions instead of function expressions (arkts-no-func-expressions)" }, { - "line": 30, - "column": 26, + "line": 31, + "column": 12, "problem": "FunctionContainsThis", "autofixable": false, "suggest": "", diff --git a/ets2panda/linter/test/prototype_assignment.ts.relax.json b/ets2panda/linter/test/prototype_assignment.ts.relax.json index dc1c5be2ce6b380ee0700c0881a6314027de7d9b..79345f433f3e59b20e1a36a003d330eb58c5f4eb 100644 --- a/ets2panda/linter/test/prototype_assignment.ts.relax.json +++ b/ets2panda/linter/test/prototype_assignment.ts.relax.json @@ -15,15 +15,15 @@ ], "nodes": [ { - "line": 19, - "column": 1, + "line": 26, + "column": 19, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" }, { - "line": 20, - "column": 20, + "line": 21, + "column": 5, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" @@ -50,8 +50,8 @@ "rule": "Prototype assignment is not supported (arkts-no-prototype-assignment)" }, { - "line": 30, - "column": 26, + "line": 31, + "column": 12, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" diff --git a/ets2panda/linter/test/prototype_assignment.ts.strict.json b/ets2panda/linter/test/prototype_assignment.ts.strict.json index ad3f542923b0e6db48ebf7877232046f1b070e7e..f1ebe772db7f34dbdfb39e3ff7cf91c80121ccde 100644 --- a/ets2panda/linter/test/prototype_assignment.ts.strict.json +++ b/ets2panda/linter/test/prototype_assignment.ts.strict.json @@ -15,8 +15,8 @@ ], "nodes": [ { - "line": 19, - "column": 1, + "line": 26, + "column": 19, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" @@ -29,8 +29,8 @@ "rule": "Use arrow functions instead of function expressions (arkts-no-func-expressions)" }, { - "line": 20, - "column": 20, + "line": 21, + "column": 5, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" @@ -64,8 +64,8 @@ "rule": "Use arrow functions instead of function expressions (arkts-no-func-expressions)" }, { - "line": 30, - "column": 26, + "line": 31, + "column": 12, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" diff --git a/ets2panda/linter/test/record_type.ts b/ets2panda/linter/test/record_type.ts index c08cdea85578c29e128554f06f7ed6add3e05e8b..8a07e7e019f77e4596295632f71c286d8903d4ce 100644 --- a/ets2panda/linter/test/record_type.ts +++ b/ets2panda/linter/test/record_type.ts @@ -98,4 +98,44 @@ rec = { // NOT OK, key must be either string or number literal address: '124213', birthday: '2020-03-10', 23:'xx' -} \ No newline at end of file +} + +// #14360 - Record in a union type +class RecUnion { + v: Record | null = null; +} +let recU: RecUnion = { + v: { + 'a': 'v' + } +}; + +interface RecUnion2 { + param: Record; + extra?: Record; + value: Record | string; +} +let recU2: RecUnion2 = { + param: { + 'aa': 111 + }, + extra: { + 'bbb': 222 + }, + value: { + 'bbb': 222 + } +}; + +class RecUnion3 { + extra: Record | string = ''; + param?: Record | string = ''; +} +let recU3: RecUnion3 = { + extra: { + 'bbb': 222 + }, + param: { + 'bbb': 222 + } +}; \ No newline at end of file diff --git a/ets2panda/linter/test/recursive_types.ts b/ets2panda/linter/test/recursive_types.ts index 52402491bcdf2a3ada94cbd7a307cd10ecd7e4e9..c2340362a82fcaa25b41873186e3d355b74a206b 100644 --- a/ets2panda/linter/test/recursive_types.ts +++ b/ets2panda/linter/test/recursive_types.ts @@ -63,7 +63,7 @@ namespace test3 { let cur = parent.get(name); if (cur instanceof Array) { (cur as Array).push(value); - } else if (cur != undefined) { + } else if (cur !== undefined) { let array = [cur, value]; parent.set(name, array); } diff --git a/ets2panda/linter/test/symbol_api.ts b/ets2panda/linter/test/symbol_api.ts index 2d33c448f2d46c9150ee43b838b41f7dc5d98a37..8d7d25d3e16a892137741335ce918d61bd964a59 100644 --- a/ets2panda/linter/test/symbol_api.ts +++ b/ets2panda/linter/test/symbol_api.ts @@ -50,3 +50,9 @@ class TestClass { return Symbol(); } } + +class BIter { + [Symbol.iterator] = () => { + return 1; + }; +} diff --git a/ets2panda/linter/test/this_type.ts.relax.json b/ets2panda/linter/test/this_type.ts.relax.json index 1508e5c96b34e3b3431b1e03e2e7fa645a1210a7..7afe8f170d0efa17b22cc362e19f4a8275a6fb4f 100644 --- a/ets2panda/linter/test/this_type.ts.relax.json +++ b/ets2panda/linter/test/this_type.ts.relax.json @@ -50,28 +50,28 @@ "problem": "ThisType" }, { - "line": 72, - "column": 3, + "line": 73, + "column": 5, "problem": "FunctionContainsThis" }, { - "line": 77, - "column": 9, + "line": 78, + "column": 3, "problem": "FunctionContainsThis" }, { - "line": 80, - "column": 10, + "line": 81, + "column": 3, "problem": "FunctionContainsThis" }, { - "line": 83, - "column": 1, + "line": 84, + "column": 3, "problem": "FunctionContainsThis" }, { - "line": 88, - "column": 3, + "line": 89, + "column": 5, "problem": "FunctionContainsThis" } ] diff --git a/ets2panda/linter/test/this_type.ts.strict.json b/ets2panda/linter/test/this_type.ts.strict.json index 1631a08e64978ecc086b96469881274e1f9c2747..1e2f8ec865ed7f6ed4f21e8993d3e71c67fdcf08 100644 --- a/ets2panda/linter/test/this_type.ts.strict.json +++ b/ets2panda/linter/test/this_type.ts.strict.json @@ -50,13 +50,13 @@ "problem": "ThisType" }, { - "line": 72, - "column": 3, + "line": 73, + "column": 5, "problem": "FunctionContainsThis" }, { - "line": 77, - "column": 9, + "line": 78, + "column": 3, "problem": "FunctionContainsThis" }, { @@ -65,18 +65,18 @@ "problem": "FunctionExpression" }, { - "line": 80, - "column": 10, + "line": 81, + "column": 3, "problem": "FunctionContainsThis" }, { - "line": 83, - "column": 1, + "line": 84, + "column": 3, "problem": "FunctionContainsThis" }, { - "line": 88, - "column": 3, + "line": 89, + "column": 5, "problem": "FunctionContainsThis" } ] diff --git a/ets2panda/linter/test/ts_ignore.ts b/ets2panda/linter/test/ts_ignore.ts index 3676a1550465e43c9205d6645b9262154c268fe3..5cee019035189a17a674aee7a28e83590af1420c 100644 --- a/ets2panda/linter/test/ts_ignore.ts +++ b/ets2panda/linter/test/ts_ignore.ts @@ -31,11 +31,11 @@ let b: number = null; console.log("Hello" * 42); /* - @ts-expect-error + @ts-expect-error (shouldn't be reported) */ console.log("Hello" * 42); -// no @ts-expect-error +// no @ts-expect-error (shouldn't be reported) console.log("Hello" * 42); const l1 = (): void => { @@ -74,3 +74,14 @@ class ChainedCallsClass { let cc = new ChainedCallsClass() // @ts-ignore .methodOne().methodTwo(); + +// #14305 +/* +@ts-ignore (shouldn't be reported) + */ +let c: number = '1'; + +/* + @ts-ignore (shouldn't be reported) + */ +let d: number = '1'; \ No newline at end of file diff --git a/ets2panda/linter/test/ts_ignore.ts.relax.json b/ets2panda/linter/test/ts_ignore.ts.relax.json index d1f5176718b5277f4744fd1d2237f1bec3ddae0e..f802c561b84b47c7e4c752428ed86cd5c76e97ae 100644 --- a/ets2panda/linter/test/ts_ignore.ts.relax.json +++ b/ets2panda/linter/test/ts_ignore.ts.relax.json @@ -14,65 +14,89 @@ "limitations under the License." ], "nodes": [ + { + "line": 53, + "column": 3, + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + }, + { + "line": 55, + "column": 22, + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + }, { "line": 16, "column": 1, - "problem": "ErrorSuppression" + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" }, { "line": 21, "column": 3, - "problem": "ErrorSuppression" + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" }, { "line": 25, "column": 19, - "problem": "ErrorSuppression" + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" }, { "line": 30, "column": 9, - "problem": "ErrorSuppression" - }, - { - "line": 33, - "column": 2, - "problem": "ErrorSuppression" + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" }, { "line": 43, "column": 9, - "problem": "ErrorSuppression" + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" }, { "line": 52, "column": 3, - "problem": "ErrorSuppression" - }, - { - "line": 53, - "column": 3, - "problem": "AnyType" - }, - { - "line": 55, - "column": 22, - "problem": "AnyType" + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" }, { "line": 56, "column": 9, - "problem": "ErrorSuppression" + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" }, { "line": 58, "column": 9, - "problem": "ErrorSuppression" + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" + }, + { + "line": 75, + "column": 1, + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" }, { "line": 18, "column": 5, - "problem": "StrictDiagnostic" + "problem": "StrictDiagnostic", + "suggest": "Type 'null' is not assignable to type 'number'.", + "rule": "Type 'null' is not assignable to type 'number'." } ] -} +} \ No newline at end of file diff --git a/ets2panda/linter/test/ts_ignore.ts.strict.json b/ets2panda/linter/test/ts_ignore.ts.strict.json index d1f5176718b5277f4744fd1d2237f1bec3ddae0e..f802c561b84b47c7e4c752428ed86cd5c76e97ae 100644 --- a/ets2panda/linter/test/ts_ignore.ts.strict.json +++ b/ets2panda/linter/test/ts_ignore.ts.strict.json @@ -14,65 +14,89 @@ "limitations under the License." ], "nodes": [ + { + "line": 53, + "column": 3, + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + }, + { + "line": 55, + "column": 22, + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + }, { "line": 16, "column": 1, - "problem": "ErrorSuppression" + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" }, { "line": 21, "column": 3, - "problem": "ErrorSuppression" + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" }, { "line": 25, "column": 19, - "problem": "ErrorSuppression" + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" }, { "line": 30, "column": 9, - "problem": "ErrorSuppression" - }, - { - "line": 33, - "column": 2, - "problem": "ErrorSuppression" + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" }, { "line": 43, "column": 9, - "problem": "ErrorSuppression" + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" }, { "line": 52, "column": 3, - "problem": "ErrorSuppression" - }, - { - "line": 53, - "column": 3, - "problem": "AnyType" - }, - { - "line": 55, - "column": 22, - "problem": "AnyType" + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" }, { "line": 56, "column": 9, - "problem": "ErrorSuppression" + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" }, { "line": 58, "column": 9, - "problem": "ErrorSuppression" + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" + }, + { + "line": 75, + "column": 1, + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" }, { "line": 18, "column": 5, - "problem": "StrictDiagnostic" + "problem": "StrictDiagnostic", + "suggest": "Type 'null' is not assignable to type 'number'.", + "rule": "Type 'null' is not assignable to type 'number'." } ] -} +} \ No newline at end of file diff --git a/ets2panda/linter/test/ts_ignore_2.ts b/ets2panda/linter/test/ts_ignore_2.ts new file mode 100644 index 0000000000000000000000000000000000000000..bd5602e33b0392ab0775c9f5008896e6a8e9d75d --- /dev/null +++ b/ets2panda/linter/test/ts_ignore_2.ts @@ -0,0 +1,17 @@ +// @ts-ignore - check for [1, 1] position +let x: number = null; // No error, type checking is suppressed + +/* + * Copyright (c) 2023-2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ \ No newline at end of file diff --git a/ets2panda/linter/test/ts_ignore_2.ts.autofix.json b/ets2panda/linter/test/ts_ignore_2.ts.autofix.json new file mode 100644 index 0000000000000000000000000000000000000000..87d88d712fc3a3270dfea505f0c516f13315746c --- /dev/null +++ b/ets2panda/linter/test/ts_ignore_2.ts.autofix.json @@ -0,0 +1,26 @@ +{ + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + "nodes": [ + { + "line": 1, + "column": 1, + "problem": "ErrorSuppression", + "autofixable": false, + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" + } + ] +} \ No newline at end of file diff --git a/ets2panda/linter/test/ts_ignore_2.ts.relax.json b/ets2panda/linter/test/ts_ignore_2.ts.relax.json new file mode 100644 index 0000000000000000000000000000000000000000..4f498993ad6702fb887f19305de80f93303dc86a --- /dev/null +++ b/ets2panda/linter/test/ts_ignore_2.ts.relax.json @@ -0,0 +1,25 @@ +{ + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + "nodes": [ + { + "line": 1, + "column": 1, + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" + } + ] +} \ No newline at end of file diff --git a/ets2panda/linter/test/ts_ignore_2.ts.strict.json b/ets2panda/linter/test/ts_ignore_2.ts.strict.json new file mode 100644 index 0000000000000000000000000000000000000000..4f498993ad6702fb887f19305de80f93303dc86a --- /dev/null +++ b/ets2panda/linter/test/ts_ignore_2.ts.strict.json @@ -0,0 +1,25 @@ +{ + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + "nodes": [ + { + "line": 1, + "column": 1, + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" + } + ] +} \ No newline at end of file diff --git a/ets2panda/linter/test/ts_nocheck_2.ts b/ets2panda/linter/test/ts_nocheck_2.ts new file mode 100644 index 0000000000000000000000000000000000000000..950ed1bf04c4f93d2aa3ed18dd3f7cf3608c0592 --- /dev/null +++ b/ets2panda/linter/test/ts_nocheck_2.ts @@ -0,0 +1,20 @@ +// @ts-nocheck - check for [1, 1] position +// @ts-nocheck - check for pragma with two entries +let x: number = null; // No error, type checking is suppressed + +/* + * Copyright (c) 2023-2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +let y: number = null; // No error, type checking is suppressed \ No newline at end of file diff --git a/ets2panda/linter/test/ts_nocheck_2.ts.autofix.json b/ets2panda/linter/test/ts_nocheck_2.ts.autofix.json new file mode 100644 index 0000000000000000000000000000000000000000..e56817ef3edce23345c358dbc361fd68ae80794e --- /dev/null +++ b/ets2panda/linter/test/ts_nocheck_2.ts.autofix.json @@ -0,0 +1,34 @@ +{ + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + "nodes": [ + { + "line": 1, + "column": 1, + "problem": "ErrorSuppression", + "autofixable": false, + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" + }, + { + "line": 2, + "column": 1, + "problem": "ErrorSuppression", + "autofixable": false, + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" + } + ] +} \ No newline at end of file diff --git a/ets2panda/linter/test/ts_nocheck_2.ts.relax.json b/ets2panda/linter/test/ts_nocheck_2.ts.relax.json new file mode 100644 index 0000000000000000000000000000000000000000..a8844f27fc64b1418c7a0a6929a5c77d38e3a4dc --- /dev/null +++ b/ets2panda/linter/test/ts_nocheck_2.ts.relax.json @@ -0,0 +1,32 @@ +{ + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + "nodes": [ + { + "line": 1, + "column": 1, + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" + }, + { + "line": 2, + "column": 1, + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" + } + ] +} \ No newline at end of file diff --git a/ets2panda/linter/test/ts_nocheck_2.ts.strict.json b/ets2panda/linter/test/ts_nocheck_2.ts.strict.json new file mode 100644 index 0000000000000000000000000000000000000000..a8844f27fc64b1418c7a0a6929a5c77d38e3a4dc --- /dev/null +++ b/ets2panda/linter/test/ts_nocheck_2.ts.strict.json @@ -0,0 +1,32 @@ +{ + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + "nodes": [ + { + "line": 1, + "column": 1, + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" + }, + { + "line": 2, + "column": 1, + "problem": "ErrorSuppression", + "suggest": "", + "rule": "Switching off type checks with in-place comments is not allowed (arkts-strict-typing-required)" + } + ] +} \ No newline at end of file diff --git a/ets2panda/linter/test/types.ts b/ets2panda/linter/test/types.ts index 28ee81f242efe5ca0e63c6e868a070db6d215251..d34b3c9167612e47974ac7fcfa2e0d8848b37a23 100644 --- a/ets2panda/linter/test/types.ts +++ b/ets2panda/linter/test/types.ts @@ -98,7 +98,7 @@ type DescribableFunction = { function callFunctionObject(fn: DescribableFunction) { console.log(fn.description + ' returned ' + fn(5)); } -const funcWithDescr: DescribableFunction = (x: number) => x % 2 == 0; +const funcWithDescr: DescribableFunction = (x: number) => x % 2 === 0; funcWithDescr.description = 'isEven'; callFunctionObject(funcWithDescr); diff --git a/ets2panda/linter/test/types.ts.autofix.json b/ets2panda/linter/test/types.ts.autofix.json index 7833b9da06c47271168dc5b463b35b669eed9910..f51682c47bc8fa4b3f7535debbbaa7ccd62c5ed7 100644 --- a/ets2panda/linter/test/types.ts.autofix.json +++ b/ets2panda/linter/test/types.ts.autofix.json @@ -102,14 +102,6 @@ "suggest": "", "rule": "Type guarding is supported with \"instanceof\" and \"as\" (arkts-no-is)" }, - { - "line": 47, - "column": 17, - "problem": "RegexLiteral", - "autofixable": false, - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, { "line": 54, "column": 26, @@ -150,14 +142,6 @@ "suggest": "", "rule": "Objects with property names that are not identifiers are not supported (arkts-identifiers-as-prop-names)" }, - { - "line": 66, - "column": 46, - "problem": "ObjectLiteralNoContextType", - "autofixable": false, - "suggest": "", - "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)" - }, { "line": 67, "column": 3, @@ -214,6 +198,14 @@ "suggest": "", "rule": "\"in\" operator is not supported (arkts-no-in)" }, + { + "line": 78, + "column": 5, + "problem": "PropertyAccessByIndex", + "autofixable": false, + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, { "line": 92, "column": 22, @@ -293,8 +285,8 @@ "autofixable": true, "autofix": [ { - "start": 2630, - "end": 2636, + "start": 2631, + "end": 2637, "replacementText": "1 as any" } ], @@ -316,8 +308,8 @@ "autofixable": true, "autofix": [ { - "start": 2657, - "end": 2714, + "start": 2658, + "end": 2715, "replacementText": "document.getElementById('main_canvas') as HTMLCanvasElement" } ], diff --git a/ets2panda/linter/test/types.ts.relax.json b/ets2panda/linter/test/types.ts.relax.json index cb4261c22858190b840627fe507e40d918ba6500..ed2fa5fb0df50e59839f6212b6ab40b228d4a7a9 100644 --- a/ets2panda/linter/test/types.ts.relax.json +++ b/ets2panda/linter/test/types.ts.relax.json @@ -84,13 +84,6 @@ "suggest": "", "rule": "Type guarding is supported with \"instanceof\" and \"as\" (arkts-no-is)" }, - { - "line": 47, - "column": 17, - "problem": "RegexLiteral", - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, { "line": 54, "column": 26, @@ -112,13 +105,6 @@ "suggest": "", "rule": "Objects with property names that are not identifiers are not supported (arkts-identifiers-as-prop-names)" }, - { - "line": 66, - "column": 46, - "problem": "ObjectLiteralNoContextType", - "suggest": "", - "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)" - }, { "line": 71, "column": 19, diff --git a/ets2panda/linter/test/types.ts.strict.json b/ets2panda/linter/test/types.ts.strict.json index 12af0241f0080993d1207515856d070dd647e821..0daaa7cf3bb185a5489a8bccc033c12226a89049 100644 --- a/ets2panda/linter/test/types.ts.strict.json +++ b/ets2panda/linter/test/types.ts.strict.json @@ -91,13 +91,6 @@ "suggest": "", "rule": "Type guarding is supported with \"instanceof\" and \"as\" (arkts-no-is)" }, - { - "line": 47, - "column": 17, - "problem": "RegexLiteral", - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, { "line": 54, "column": 26, @@ -133,13 +126,6 @@ "suggest": "", "rule": "Objects with property names that are not identifiers are not supported (arkts-identifiers-as-prop-names)" }, - { - "line": 66, - "column": 46, - "problem": "ObjectLiteralNoContextType", - "suggest": "", - "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)" - }, { "line": 67, "column": 3, @@ -189,6 +175,13 @@ "suggest": "", "rule": "\"in\" operator is not supported (arkts-no-in)" }, + { + "line": 78, + "column": 5, + "problem": "PropertyAccessByIndex", + "suggest": "", + "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, { "line": 92, "column": 22, diff --git a/ets2panda/linter/test/undefined_check_calls.ts.relax.json b/ets2panda/linter/test/undefined_check_calls.ts.relax.json index 33bf02fda0a29ea9c00dd44708ba54fe03e202a3..21f9e86912824a3b0262300c1ecf7d222a815994 100644 --- a/ets2panda/linter/test/undefined_check_calls.ts.relax.json +++ b/ets2panda/linter/test/undefined_check_calls.ts.relax.json @@ -14,13 +14,6 @@ "limitations under the License." ], "nodes": [ - { - "line": 33, - "column": 12, - "problem": "StrictDiagnostic", - "suggest": "No overload matches this call.\n Overload 1 of 2, '(func: Function, ...args: unknown[]): Promise', gave the following error.\n Argument of type 'undefined' is not assignable to parameter of type 'Function'.\n Overload 2 of 2, '(group: TaskGroup, priority?: Priority | undefined): Promise', gave the following error.\n Argument of type 'undefined' is not assignable to parameter of type 'TaskGroup'.", - "rule": "No overload matches this call.\n Overload 1 of 2, '(func: Function, ...args: unknown[]): Promise', gave the following error.\n Argument of type 'undefined' is not assignable to parameter of type 'Function'.\n Overload 2 of 2, '(group: TaskGroup, priority?: Priority | undefined): Promise', gave the following error.\n Argument of type 'undefined' is not assignable to parameter of type 'TaskGroup'." - }, { "line": 37, "column": 5, diff --git a/ets2panda/linter/test/undefined_check_calls.ts.strict.json b/ets2panda/linter/test/undefined_check_calls.ts.strict.json index 33bf02fda0a29ea9c00dd44708ba54fe03e202a3..21f9e86912824a3b0262300c1ecf7d222a815994 100644 --- a/ets2panda/linter/test/undefined_check_calls.ts.strict.json +++ b/ets2panda/linter/test/undefined_check_calls.ts.strict.json @@ -14,13 +14,6 @@ "limitations under the License." ], "nodes": [ - { - "line": 33, - "column": 12, - "problem": "StrictDiagnostic", - "suggest": "No overload matches this call.\n Overload 1 of 2, '(func: Function, ...args: unknown[]): Promise', gave the following error.\n Argument of type 'undefined' is not assignable to parameter of type 'Function'.\n Overload 2 of 2, '(group: TaskGroup, priority?: Priority | undefined): Promise', gave the following error.\n Argument of type 'undefined' is not assignable to parameter of type 'TaskGroup'.", - "rule": "No overload matches this call.\n Overload 1 of 2, '(func: Function, ...args: unknown[]): Promise', gave the following error.\n Argument of type 'undefined' is not assignable to parameter of type 'Function'.\n Overload 2 of 2, '(group: TaskGroup, priority?: Priority | undefined): Promise', gave the following error.\n Argument of type 'undefined' is not assignable to parameter of type 'TaskGroup'." - }, { "line": 37, "column": 5, diff --git a/ets2panda/linter/test/utility_types.ts.relax.json b/ets2panda/linter/test/utility_types.ts.relax.json index fe6131546f4334735729e5e3c1ab931d44572443..c4505b6554ab4236b44d4108945ac1eeddc44d00 100644 --- a/ets2panda/linter/test/utility_types.ts.relax.json +++ b/ets2panda/linter/test/utility_types.ts.relax.json @@ -250,8 +250,8 @@ "problem": "UtilityType" }, { - "line": 173, - "column": 3, + "line": 174, + "column": 12, "problem": "FunctionContainsThis" }, { @@ -267,11 +267,11 @@ { "line": 178, "column": 18, - "problem": "FunctionApplyBindCall" + "problem": "FunctionApplyCall" }, { - "line": 183, - "column": 3, + "line": 184, + "column": 12, "problem": "FunctionContainsThis" }, { @@ -287,11 +287,16 @@ { "line": 187, "column": 60, - "problem": "FunctionApplyBindCall" + "problem": "FunctionBind" + }, + { + "line": 208, + "column": 9, + "problem": "FunctionContainsThis" }, { - "line": 192, - "column": 1, + "line": 209, + "column": 9, "problem": "FunctionContainsThis" }, { diff --git a/ets2panda/linter/test/utility_types.ts.strict.json b/ets2panda/linter/test/utility_types.ts.strict.json index c5686de4b4c5b53e4bfe52a623bec80e0c788296..709b2384c44ce24b7df8f8f142887c86c5bedb0c 100644 --- a/ets2panda/linter/test/utility_types.ts.strict.json +++ b/ets2panda/linter/test/utility_types.ts.strict.json @@ -260,8 +260,8 @@ "problem": "UtilityType" }, { - "line": 173, - "column": 3, + "line": 174, + "column": 12, "problem": "FunctionContainsThis" }, { @@ -292,11 +292,11 @@ { "line": 178, "column": 18, - "problem": "FunctionApplyBindCall" + "problem": "FunctionApplyCall" }, { - "line": 183, - "column": 3, + "line": 184, + "column": 12, "problem": "FunctionContainsThis" }, { @@ -317,11 +317,16 @@ { "line": 187, "column": 60, - "problem": "FunctionApplyBindCall" + "problem": "FunctionBind" + }, + { + "line": 208, + "column": 9, + "problem": "FunctionContainsThis" }, { - "line": 192, - "column": 1, + "line": 209, + "column": 9, "problem": "FunctionContainsThis" }, { diff --git a/ets2panda/linter/test_regression/14412.ts b/ets2panda/linter/test_regression/14412.ts new file mode 100644 index 0000000000000000000000000000000000000000..1cd4fdfb298121102f2ed3524d968712ac3eff8b --- /dev/null +++ b/ets2panda/linter/test_regression/14412.ts @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2022-2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class C {} + +class A { + v: [C, number][] = []; + foo() { + Array.from(this.v).sort(); + } +} + +let c: [C, number][] = []; +Array.from(c).sort(); diff --git a/ets2panda/linter/test_regression/14412.ts.autofix.json b/ets2panda/linter/test_regression/14412.ts.autofix.json new file mode 100644 index 0000000000000000000000000000000000000000..e4efb45db7a6974ed8ca27490e5acb755dfb13ca --- /dev/null +++ b/ets2panda/linter/test_regression/14412.ts.autofix.json @@ -0,0 +1,17 @@ +{ + "copyright": [ + "Copyright (c) 2022-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + "nodes": [] +} diff --git a/ets2panda/linter/test_regression/14412.ts.relax.json b/ets2panda/linter/test_regression/14412.ts.relax.json new file mode 100755 index 0000000000000000000000000000000000000000..e4efb45db7a6974ed8ca27490e5acb755dfb13ca --- /dev/null +++ b/ets2panda/linter/test_regression/14412.ts.relax.json @@ -0,0 +1,17 @@ +{ + "copyright": [ + "Copyright (c) 2022-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + "nodes": [] +} diff --git a/ets2panda/linter/test_regression/14412.ts.strict.json b/ets2panda/linter/test_regression/14412.ts.strict.json new file mode 100755 index 0000000000000000000000000000000000000000..e4efb45db7a6974ed8ca27490e5acb755dfb13ca --- /dev/null +++ b/ets2panda/linter/test_regression/14412.ts.strict.json @@ -0,0 +1,17 @@ +{ + "copyright": [ + "Copyright (c) 2022-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + "nodes": [] +} diff --git a/ets2panda/linter/test_rules/rule1.ts b/ets2panda/linter/test_rules/rule1.ts index b0332f6435e764e006ec357ac106077999a5b8f6..4fdb6bf8c9763e56174841c614debe807bdf0cbc 100644 --- a/ets2panda/linter/test_rules/rule1.ts +++ b/ets2panda/linter/test_rules/rule1.ts @@ -1,14 +1,41 @@ -var x = {"name": 1, 2: 3} +var x = { 'name': 1, 2: 3 }; -console.log(x["name"]) -console.log(x[2]) +console.log(x['name']); +console.log(x[2]); class X { - public name: number = 0 + public name: number = 0; } -let y = {name: 1} -console.log(x.name) +let y = { name: 1 }; +console.log(x.name); -let z = [1, 2, 3] -console.log(y[2]) \ No newline at end of file +let z = [1, 2, 3]; +console.log(y[2]); + +enum S1 { + SS = 'qwqwq' +} + +enum S2 { + SS = 123 +} + +interface A1 { + [S1.SS]: string; +} + +interface A2 { + [S2.SS]: string; +} + +const a1: A1 = { + [S1.SS]: 'fld1' +}; + +const a2: A2 = { + [S2.SS]: 'fld2' +}; + +S1['SS']; +S2['SS']; diff --git a/ets2panda/linter/test_rules/rule1.ts.autofix.json b/ets2panda/linter/test_rules/rule1.ts.autofix.json index 32a60bb65fcb0cb40bb1b2c2b5f7a80779c2f0cf..5ca84dd5491ea12909f106ef51f57287d156baa5 100644 --- a/ets2panda/linter/test_rules/rule1.ts.autofix.json +++ b/ets2panda/linter/test_rules/rule1.ts.autofix.json @@ -32,7 +32,7 @@ }, { "line": 2, - "column": 10, + "column": 11, "problem": "LiteralAsPropertyName", "autofixable": true, "suggest": "", @@ -40,7 +40,7 @@ }, { "line": 2, - "column": 21, + "column": 22, "problem": "LiteralAsPropertyName", "autofixable": true, "suggest": "", @@ -77,6 +77,22 @@ "autofixable": true, "suggest": "", "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 29, + "column": 3, + "problem": "ComputedPropertyName", + "autofixable": false, + "suggest": "", + "rule": "Objects with property names that are not identifiers are not supported (arkts-identifiers-as-prop-names)" + }, + { + "line": 37, + "column": 3, + "problem": "ComputedPropertyName", + "autofixable": false, + "suggest": "", + "rule": "Objects with property names that are not identifiers are not supported (arkts-identifiers-as-prop-names)" } ] } \ No newline at end of file diff --git a/ets2panda/linter/test_rules/rule1.ts.relax.json b/ets2panda/linter/test_rules/rule1.ts.relax.json index 861e06d092491f53b401c5fa2a633ce8e932a417..0e101cb06ac20a3aa0593256cdf157084728c469 100644 --- a/ets2panda/linter/test_rules/rule1.ts.relax.json +++ b/ets2panda/linter/test_rules/rule1.ts.relax.json @@ -13,6 +13,20 @@ "problem": "ObjectLiteralNoContextType", "suggest": "", "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)" + }, + { + "line": 29, + "column": 3, + "problem": "ComputedPropertyName", + "suggest": "", + "rule": "Objects with property names that are not identifiers are not supported (arkts-identifiers-as-prop-names)" + }, + { + "line": 37, + "column": 3, + "problem": "ComputedPropertyName", + "suggest": "", + "rule": "Objects with property names that are not identifiers are not supported (arkts-identifiers-as-prop-names)" } ] } \ No newline at end of file diff --git a/ets2panda/linter/test_rules/rule1.ts.strict.json b/ets2panda/linter/test_rules/rule1.ts.strict.json index 5728ad6cdb5502758ccbdee12f51fe5023b9ef89..5bd7f047e69e6755a73856803de279a57bde56c3 100644 --- a/ets2panda/linter/test_rules/rule1.ts.strict.json +++ b/ets2panda/linter/test_rules/rule1.ts.strict.json @@ -16,14 +16,14 @@ }, { "line": 2, - "column": 10, + "column": 11, "problem": "LiteralAsPropertyName", "suggest": "", "rule": "Objects with property names that are not identifiers are not supported (arkts-identifiers-as-prop-names)" }, { "line": 2, - "column": 21, + "column": 22, "problem": "LiteralAsPropertyName", "suggest": "", "rule": "Objects with property names that are not identifiers are not supported (arkts-identifiers-as-prop-names)" @@ -55,6 +55,20 @@ "problem": "PropertyAccessByIndex", "suggest": "", "rule": "Indexed access is not supported for fields (arkts-no-props-by-index)" + }, + { + "line": 29, + "column": 3, + "problem": "ComputedPropertyName", + "suggest": "", + "rule": "Objects with property names that are not identifiers are not supported (arkts-identifiers-as-prop-names)" + }, + { + "line": 37, + "column": 3, + "problem": "ComputedPropertyName", + "suggest": "", + "rule": "Objects with property names that are not identifiers are not supported (arkts-identifiers-as-prop-names)" } ] } \ No newline at end of file diff --git a/ets2panda/linter/test_rules/rule116.ts.autofix.json b/ets2panda/linter/test_rules/rule116.ts.autofix.json index 8449964f6b0aebed7a3a412a87e959afbcb33487..d623882bd479636af0d59d8773f1f8a22b032488 100644 --- a/ets2panda/linter/test_rules/rule116.ts.autofix.json +++ b/ets2panda/linter/test_rules/rule116.ts.autofix.json @@ -1,4 +1,18 @@ { + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], "nodes": [ { "line": 3, @@ -6,7 +20,7 @@ "problem": "NonDeclarationInNamespace", "autofixable": false, "suggest": "", - "rule": "Non-declaration statements in namespaces are not supported (arkts-no-ns-statements)" + "rule": "Non-declaration statements in namespaces are not supported (single semicolons are considered as empty non-declaration statements) (arkts-no-ns-statements)" } ] } \ No newline at end of file diff --git a/ets2panda/linter/test_rules/rule116.ts.relax.json b/ets2panda/linter/test_rules/rule116.ts.relax.json index a5030d64e8a3e5c962cfc25bcfc7d49e88e73a86..a13f450c7608f607236040ef625cc1bf432c068f 100644 --- a/ets2panda/linter/test_rules/rule116.ts.relax.json +++ b/ets2panda/linter/test_rules/rule116.ts.relax.json @@ -1,11 +1,25 @@ { + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], "nodes": [ { "line": 3, "column": 5, "problem": "NonDeclarationInNamespace", "suggest": "", - "rule": "Non-declaration statements in namespaces are not supported (arkts-no-ns-statements)" + "rule": "Non-declaration statements in namespaces are not supported (single semicolons are considered as empty non-declaration statements) (arkts-no-ns-statements)" } ] } \ No newline at end of file diff --git a/ets2panda/linter/test_rules/rule116.ts.strict.json b/ets2panda/linter/test_rules/rule116.ts.strict.json index a5030d64e8a3e5c962cfc25bcfc7d49e88e73a86..a13f450c7608f607236040ef625cc1bf432c068f 100644 --- a/ets2panda/linter/test_rules/rule116.ts.strict.json +++ b/ets2panda/linter/test_rules/rule116.ts.strict.json @@ -1,11 +1,25 @@ { + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], "nodes": [ { "line": 3, "column": 5, "problem": "NonDeclarationInNamespace", "suggest": "", - "rule": "Non-declaration statements in namespaces are not supported (arkts-no-ns-statements)" + "rule": "Non-declaration statements in namespaces are not supported (single semicolons are considered as empty non-declaration statements) (arkts-no-ns-statements)" } ] } \ No newline at end of file diff --git a/ets2panda/linter/test_rules/rule118.ts.autofix.json b/ets2panda/linter/test_rules/rule118.ts.autofix.json index 3ff68f6c73f0870b0af431b06348497a9bf6a784..13f13363f579325755e8954b4011963971667481 100644 --- a/ets2panda/linter/test_rules/rule118.ts.autofix.json +++ b/ets2panda/linter/test_rules/rule118.ts.autofix.json @@ -1,19 +1,3 @@ { - "nodes": [ - { - "line": 5, - "column": 12, - "problem": "TypeOnlyImport", - "autofixable": true, - "autofix": [ - { - "start": 120, - "end": 144, - "replacementText": "{ APIResponseType }" - } - ], - "suggest": "", - "rule": "Special import type declarations are not supported (arkts-no-special-imports)" - } - ] + "nodes": [] } \ No newline at end of file diff --git a/ets2panda/linter/test_rules/rule118.ts.strict.json b/ets2panda/linter/test_rules/rule118.ts.strict.json index d30836a74acd706fd8fe37f455f2cb38b9d30f96..13f13363f579325755e8954b4011963971667481 100644 --- a/ets2panda/linter/test_rules/rule118.ts.strict.json +++ b/ets2panda/linter/test_rules/rule118.ts.strict.json @@ -1,11 +1,3 @@ { - "nodes": [ - { - "line": 5, - "column": 12, - "problem": "TypeOnlyImport", - "suggest": "", - "rule": "Special import type declarations are not supported (arkts-no-special-imports)" - } - ] + "nodes": [] } \ No newline at end of file diff --git a/ets2panda/linter/test_rules/rule127.ts.autofix.json b/ets2panda/linter/test_rules/rule127.ts.autofix.json index c32c0f461dd55626cd83972b7aa2a8d7bff66f60..13f13363f579325755e8954b4011963971667481 100644 --- a/ets2panda/linter/test_rules/rule127.ts.autofix.json +++ b/ets2panda/linter/test_rules/rule127.ts.autofix.json @@ -1,19 +1,3 @@ { - "nodes": [ - { - "line": 12, - "column": 5, - "problem": "TypeOnlyExport", - "autofixable": true, - "autofix": [ - { - "start": 218, - "end": 240, - "replacementText": "export { Class2 };" - } - ], - "suggest": "", - "rule": "Special \"export type\" declarations are not supported (arkts-no-special-exports)" - } - ] + "nodes": [] } \ No newline at end of file diff --git a/ets2panda/linter/test_rules/rule127.ts.strict.json b/ets2panda/linter/test_rules/rule127.ts.strict.json index 35f1a58896b2262887e713eb7ea091a5342d8b98..13f13363f579325755e8954b4011963971667481 100644 --- a/ets2panda/linter/test_rules/rule127.ts.strict.json +++ b/ets2panda/linter/test_rules/rule127.ts.strict.json @@ -1,11 +1,3 @@ { - "nodes": [ - { - "line": 12, - "column": 5, - "problem": "TypeOnlyExport", - "suggest": "", - "rule": "Special \"export type\" declarations are not supported (arkts-no-special-exports)" - } - ] + "nodes": [] } \ No newline at end of file diff --git a/ets2panda/linter/test_rules/rule129.ts.autofix.json b/ets2panda/linter/test_rules/rule129.ts.autofix.json index 6708658aec6699b02802c0afa208ab7c076c39a4..897f1d325796ef6f4807901695a64a2380426250 100644 --- a/ets2panda/linter/test_rules/rule129.ts.autofix.json +++ b/ets2panda/linter/test_rules/rule129.ts.autofix.json @@ -1,4 +1,18 @@ { + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], "nodes": [ { "line": 4, @@ -6,7 +20,7 @@ "problem": "NonDeclarationInNamespace", "autofixable": false, "suggest": "", - "rule": "Non-declaration statements in namespaces are not supported (arkts-no-ns-statements)" + "rule": "Non-declaration statements in namespaces are not supported (single semicolons are considered as empty non-declaration statements) (arkts-no-ns-statements)" }, { "line": 2, @@ -29,6 +43,7 @@ "column": 5, "problem": "ImportAfterStatement", "autofixable": false, + "suggest": "", "rule": "\"import\" statements after other statements are not allowed (arkts-no-misplaced-imports)" } ] diff --git a/ets2panda/linter/test_rules/rule129.ts.relax.json b/ets2panda/linter/test_rules/rule129.ts.relax.json index cc882319377db9e73f996ceb24a77cc508180fdd..0cb1a6d72636f892b2dbb36e0361ac0a8bc1088d 100644 --- a/ets2panda/linter/test_rules/rule129.ts.relax.json +++ b/ets2panda/linter/test_rules/rule129.ts.relax.json @@ -1,11 +1,25 @@ { + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], "nodes": [ { "line": 4, "column": 9, "problem": "NonDeclarationInNamespace", "suggest": "", - "rule": "Non-declaration statements in namespaces are not supported (arkts-no-ns-statements)" + "rule": "Non-declaration statements in namespaces are not supported (single semicolons are considered as empty non-declaration statements) (arkts-no-ns-statements)" }, { "line": 2, @@ -25,6 +39,7 @@ "line": 8, "column": 5, "problem": "ImportAfterStatement", + "suggest": "", "rule": "\"import\" statements after other statements are not allowed (arkts-no-misplaced-imports)" } ] diff --git a/ets2panda/linter/test_rules/rule129.ts.strict.json b/ets2panda/linter/test_rules/rule129.ts.strict.json index cc882319377db9e73f996ceb24a77cc508180fdd..0cb1a6d72636f892b2dbb36e0361ac0a8bc1088d 100644 --- a/ets2panda/linter/test_rules/rule129.ts.strict.json +++ b/ets2panda/linter/test_rules/rule129.ts.strict.json @@ -1,11 +1,25 @@ { + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], "nodes": [ { "line": 4, "column": 9, "problem": "NonDeclarationInNamespace", "suggest": "", - "rule": "Non-declaration statements in namespaces are not supported (arkts-no-ns-statements)" + "rule": "Non-declaration statements in namespaces are not supported (single semicolons are considered as empty non-declaration statements) (arkts-no-ns-statements)" }, { "line": 2, @@ -25,6 +39,7 @@ "line": 8, "column": 5, "problem": "ImportAfterStatement", + "suggest": "", "rule": "\"import\" statements after other statements are not allowed (arkts-no-misplaced-imports)" } ] diff --git a/ets2panda/linter/test_rules/rule136.ts b/ets2panda/linter/test_rules/rule136.ts index 41d0b50953924c05f2ff989b0b23d98f5c2f92b8..92706ab87a00fb37782ca552bad7d445efac9734 100644 --- a/ets2panda/linter/test_rules/rule136.ts +++ b/ets2panda/linter/test_rules/rule136.ts @@ -1,13 +1,13 @@ -var C = function(p: number) { - this.p = p // Compile-time error only with noImplicitThis -} +var C = function (p: number) { + this.p = p; // Compile-time error only with noImplicitThis +}; C.prototype = { - m() { - console.log(this.p) - } -} + m() { + console.log(this.p); + } +}; -C.prototype.q = function(r: number) { - return this.p == r -} \ No newline at end of file +C.prototype.q = function (r: number) { + return this.p === r; +}; diff --git a/ets2panda/linter/test_rules/rule136.ts.autofix.json b/ets2panda/linter/test_rules/rule136.ts.autofix.json index fb3ab2780e7b41d6ce964d9fdaae740e3df24cb2..3a63117c56d241e6360b6147e2aeac0d27f31d78 100644 --- a/ets2panda/linter/test_rules/rule136.ts.autofix.json +++ b/ets2panda/linter/test_rules/rule136.ts.autofix.json @@ -17,8 +17,8 @@ "rule": "Use arrow functions instead of function expressions (arkts-no-func-expressions)" }, { - "line": 1, - "column": 9, + "line": 2, + "column": 3, "problem": "FunctionContainsThis", "autofixable": false, "suggest": "", @@ -49,8 +49,8 @@ "rule": "Use arrow functions instead of function expressions (arkts-no-func-expressions)" }, { - "line": 11, - "column": 17, + "line": 12, + "column": 10, "problem": "FunctionContainsThis", "autofixable": false, "suggest": "", diff --git a/ets2panda/linter/test_rules/rule136.ts.relax.json b/ets2panda/linter/test_rules/rule136.ts.relax.json index d9d652719abe74c16916a18a344c7bc48bb31a1c..d118bff3e2c814b40b650b0831e4b7aee9b37f2f 100644 --- a/ets2panda/linter/test_rules/rule136.ts.relax.json +++ b/ets2panda/linter/test_rules/rule136.ts.relax.json @@ -1,8 +1,8 @@ { "nodes": [ { - "line": 1, - "column": 9, + "line": 2, + "column": 3, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" @@ -22,8 +22,8 @@ "rule": "Prototype assignment is not supported (arkts-no-prototype-assignment)" }, { - "line": 11, - "column": 17, + "line": 12, + "column": 10, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" diff --git a/ets2panda/linter/test_rules/rule136.ts.strict.json b/ets2panda/linter/test_rules/rule136.ts.strict.json index 1df9367d3575996793782d76b077665c8275a070..df0aabd38fc76570832ab8a2d0af899725a4eb3f 100644 --- a/ets2panda/linter/test_rules/rule136.ts.strict.json +++ b/ets2panda/linter/test_rules/rule136.ts.strict.json @@ -15,8 +15,8 @@ "rule": "Use arrow functions instead of function expressions (arkts-no-func-expressions)" }, { - "line": 1, - "column": 9, + "line": 2, + "column": 3, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" @@ -43,8 +43,8 @@ "rule": "Use arrow functions instead of function expressions (arkts-no-func-expressions)" }, { - "line": 11, - "column": 17, + "line": 12, + "column": 10, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" diff --git a/ets2panda/linter/test_rules/rule145.ts b/ets2panda/linter/test_rules/rule145.ts index cb34a60bcb147ecffdf618d27b7d53edb23f3a88..bcf14f0f735a1828f387a627b1a02ab29f590e31 100644 --- a/ets2panda/linter/test_rules/rule145.ts +++ b/ets2panda/linter/test_rules/rule145.ts @@ -1,51 +1,52 @@ class C { - n: number // Compile-time error only with strictPropertyInitialization - s: string // Compile-time error only with strictPropertyInitialization + n: number; // Compile-time error only with strictPropertyInitialization + s: string; // Compile-time error only with strictPropertyInitialization } // Compile-time error only with noImplicitReturns function foo(s: string): string { - if (s != "") { - console.log(s) - return s - } else { - console.log(s) - } + if (s !== '') { + console.log(s); + return s; + } else { + console.log(s); + } } -let n: number = null // Compile-time error only with strictNullChecks +let n: number = null; // Compile-time error only with strictNullChecks -function bar(): number { -} +function bar(): number {} function get1(): boolean { - return true; + return true; } function get2(): boolean { - return false; + return false; } function solve(): boolean { - if(get1() && get2()) { - } else if(!get2()) { - return true; - } else { - } + if (get1() && get2()) { + } else if (!get2()) { + return true; + } else { + } } - - -let lr = (): number => {} -let le = (): number => { if(get()) return 1; } +let lr = (): number => {}; +let le = (): number => { + if (get()) return 1; +}; class testClass { - static readonly lem = (): number => { if(get()) return 1; } - - solve(): boolean { - if(get1() && get2()) { - } else if(!get2()) { - return true; - } else { - } + static readonly lem = (): number => { + if (get()) return 1; + }; + + solve(): boolean { + if (get1() && get2()) { + } else if (!get2()) { + return true; + } else { } + } } diff --git a/ets2panda/linter/test_rules/rule145.ts.autofix.json b/ets2panda/linter/test_rules/rule145.ts.autofix.json index a9ba4df8ce6247ed97c444eb13a136ca4e1e5f03..308c5d9c2ac5e7f18ee4791d1c956af10cea2a6d 100644 --- a/ets2panda/linter/test_rules/rule145.ts.autofix.json +++ b/ets2panda/linter/test_rules/rule145.ts.autofix.json @@ -1,8 +1,22 @@ { + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], "nodes": [ { "line": 2, - "column": 5, + "column": 3, "problem": "StrictDiagnostic", "autofixable": false, "suggest": "Property 'n' has no initializer and is not definitely assigned in the constructor.", @@ -10,7 +24,7 @@ }, { "line": 3, - "column": 5, + "column": 3, "problem": "StrictDiagnostic", "autofixable": false, "suggest": "Property 's' has no initializer and is not definitely assigned in the constructor.", @@ -33,7 +47,7 @@ "rule": "Type 'null' is not assignable to type 'number'." }, { - "line": 28, + "line": 27, "column": 19, "problem": "StrictDiagnostic", "autofixable": false, @@ -41,7 +55,7 @@ "rule": "Function lacks ending return statement and return type does not include 'undefined'." }, { - "line": 39, + "line": 36, "column": 14, "problem": "StrictDiagnostic", "autofixable": false, @@ -49,20 +63,20 @@ "rule": "Function lacks ending return statement and return type does not include 'undefined'." }, { - "line": 42, - "column": 32, + "line": 41, + "column": 29, "problem": "StrictDiagnostic", "autofixable": false, "suggest": "Function lacks ending return statement and return type does not include 'undefined'.", "rule": "Function lacks ending return statement and return type does not include 'undefined'." }, { - "line": 44, - "column": 14, + "line": 45, + "column": 12, "problem": "StrictDiagnostic", "autofixable": false, "suggest": "Function lacks ending return statement and return type does not include 'undefined'.", "rule": "Function lacks ending return statement and return type does not include 'undefined'." } ] -} +} \ No newline at end of file diff --git a/ets2panda/linter/test_rules/rule145.ts.relax.json b/ets2panda/linter/test_rules/rule145.ts.relax.json index c27f5888b89d68a3128497fe8235fca0958db536..fd48119d99cfdcb806208f93d3ca8260e4008670 100644 --- a/ets2panda/linter/test_rules/rule145.ts.relax.json +++ b/ets2panda/linter/test_rules/rule145.ts.relax.json @@ -1,15 +1,29 @@ { + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], "nodes": [ { "line": 2, - "column": 5, + "column": 3, "problem": "StrictDiagnostic", "suggest": "Property 'n' has no initializer and is not definitely assigned in the constructor.", "rule": "Property 'n' has no initializer and is not definitely assigned in the constructor." }, { "line": 3, - "column": 5, + "column": 3, "problem": "StrictDiagnostic", "suggest": "Property 's' has no initializer and is not definitely assigned in the constructor.", "rule": "Property 's' has no initializer and is not definitely assigned in the constructor." @@ -29,32 +43,32 @@ "rule": "Type 'null' is not assignable to type 'number'." }, { - "line": 28, + "line": 27, "column": 19, "problem": "StrictDiagnostic", "suggest": "Function lacks ending return statement and return type does not include 'undefined'.", "rule": "Function lacks ending return statement and return type does not include 'undefined'." }, { - "line": 39, + "line": 36, "column": 14, "problem": "StrictDiagnostic", "suggest": "Function lacks ending return statement and return type does not include 'undefined'.", "rule": "Function lacks ending return statement and return type does not include 'undefined'." }, { - "line": 42, - "column": 32, + "line": 41, + "column": 29, "problem": "StrictDiagnostic", "suggest": "Function lacks ending return statement and return type does not include 'undefined'.", "rule": "Function lacks ending return statement and return type does not include 'undefined'." }, { - "line": 44, - "column": 14, + "line": 45, + "column": 12, "problem": "StrictDiagnostic", "suggest": "Function lacks ending return statement and return type does not include 'undefined'.", "rule": "Function lacks ending return statement and return type does not include 'undefined'." } ] -} +} \ No newline at end of file diff --git a/ets2panda/linter/test_rules/rule145.ts.strict.json b/ets2panda/linter/test_rules/rule145.ts.strict.json index c27f5888b89d68a3128497fe8235fca0958db536..fd48119d99cfdcb806208f93d3ca8260e4008670 100644 --- a/ets2panda/linter/test_rules/rule145.ts.strict.json +++ b/ets2panda/linter/test_rules/rule145.ts.strict.json @@ -1,15 +1,29 @@ { + "copyright": [ + "Copyright (c) 2023-2023 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], "nodes": [ { "line": 2, - "column": 5, + "column": 3, "problem": "StrictDiagnostic", "suggest": "Property 'n' has no initializer and is not definitely assigned in the constructor.", "rule": "Property 'n' has no initializer and is not definitely assigned in the constructor." }, { "line": 3, - "column": 5, + "column": 3, "problem": "StrictDiagnostic", "suggest": "Property 's' has no initializer and is not definitely assigned in the constructor.", "rule": "Property 's' has no initializer and is not definitely assigned in the constructor." @@ -29,32 +43,32 @@ "rule": "Type 'null' is not assignable to type 'number'." }, { - "line": 28, + "line": 27, "column": 19, "problem": "StrictDiagnostic", "suggest": "Function lacks ending return statement and return type does not include 'undefined'.", "rule": "Function lacks ending return statement and return type does not include 'undefined'." }, { - "line": 39, + "line": 36, "column": 14, "problem": "StrictDiagnostic", "suggest": "Function lacks ending return statement and return type does not include 'undefined'.", "rule": "Function lacks ending return statement and return type does not include 'undefined'." }, { - "line": 42, - "column": 32, + "line": 41, + "column": 29, "problem": "StrictDiagnostic", "suggest": "Function lacks ending return statement and return type does not include 'undefined'.", "rule": "Function lacks ending return statement and return type does not include 'undefined'." }, { - "line": 44, - "column": 14, + "line": 45, + "column": 12, "problem": "StrictDiagnostic", "suggest": "Function lacks ending return statement and return type does not include 'undefined'.", "rule": "Function lacks ending return statement and return type does not include 'undefined'." } ] -} +} \ No newline at end of file diff --git a/ets2panda/linter/test_rules/rule37.ts b/ets2panda/linter/test_rules/rule37.ts deleted file mode 100644 index 1ab2f2733dfdedeb85646586ddd162480f1e4fb7..0000000000000000000000000000000000000000 --- a/ets2panda/linter/test_rules/rule37.ts +++ /dev/null @@ -1,36 +0,0 @@ -let regex: RegExp = /bc*d/ - -let regex2: RegExp = new RegExp("/bc*d/") - -const regex3 = /^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*(\.[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*)*$/; -const regex4: RegExp = new RegExp('^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*(\.[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*)*$'); - -class A { - static readonly classregex0: RegExp = /bc*d/ - - static readonly classregex2 = /^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*(\.[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*)*$/; - - classregex3: RegExp = new RegExp("bc*d"); - - static staticMethodOne() { - let regex = /bc*d/ - } - - static staticMethodTwo() { - let regex: RegExp = new RegExp("/bc*d/"); - } - - methodOne() { - let regex = /bc*d/ - } - - methodTwo() { - let regex: RegExp = new RegExp("/bc*d/"); - } - - methodRet(): RegExp { - return /^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*(\.[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*)*$/; - } -} - -const regexLambda = () => /^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*(\.[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*)*$/; diff --git a/ets2panda/linter/test_rules/rule37.ts.autofix.json b/ets2panda/linter/test_rules/rule37.ts.autofix.json deleted file mode 100644 index 4f840a64a318a8705de1c692864054470cfe911f..0000000000000000000000000000000000000000 --- a/ets2panda/linter/test_rules/rule37.ts.autofix.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "nodes": [ - { - "line": 1, - "column": 21, - "problem": "RegexLiteral", - "autofixable": false, - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 5, - "column": 16, - "problem": "RegexLiteral", - "autofixable": false, - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 9, - "column": 43, - "problem": "RegexLiteral", - "autofixable": false, - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 11, - "column": 35, - "problem": "RegexLiteral", - "autofixable": false, - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 16, - "column": 21, - "problem": "RegexLiteral", - "autofixable": false, - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 24, - "column": 21, - "problem": "RegexLiteral", - "autofixable": false, - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 32, - "column": 16, - "problem": "RegexLiteral", - "autofixable": false, - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 36, - "column": 27, - "problem": "RegexLiteral", - "autofixable": false, - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - } - ] -} diff --git a/ets2panda/linter/test_rules/rule37.ts.relax.json b/ets2panda/linter/test_rules/rule37.ts.relax.json deleted file mode 100644 index a214861d2978d451e65f49ef3964077b361903d5..0000000000000000000000000000000000000000 --- a/ets2panda/linter/test_rules/rule37.ts.relax.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "nodes": [ - { - "line": 1, - "column": 21, - "problem": "RegexLiteral", - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 5, - "column": 16, - "problem": "RegexLiteral", - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 9, - "column": 43, - "problem": "RegexLiteral", - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 11, - "column": 35, - "problem": "RegexLiteral", - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 16, - "column": 21, - "problem": "RegexLiteral", - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 24, - "column": 21, - "problem": "RegexLiteral", - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 32, - "column": 16, - "problem": "RegexLiteral", - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 36, - "column": 27, - "problem": "RegexLiteral", - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - } - ] -} diff --git a/ets2panda/linter/test_rules/rule37.ts.strict.json b/ets2panda/linter/test_rules/rule37.ts.strict.json deleted file mode 100644 index a214861d2978d451e65f49ef3964077b361903d5..0000000000000000000000000000000000000000 --- a/ets2panda/linter/test_rules/rule37.ts.strict.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "nodes": [ - { - "line": 1, - "column": 21, - "problem": "RegexLiteral", - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 5, - "column": 16, - "problem": "RegexLiteral", - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 9, - "column": 43, - "problem": "RegexLiteral", - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 11, - "column": 35, - "problem": "RegexLiteral", - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 16, - "column": 21, - "problem": "RegexLiteral", - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 24, - "column": 21, - "problem": "RegexLiteral", - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 32, - "column": 16, - "problem": "RegexLiteral", - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - }, - { - "line": 36, - "column": 27, - "problem": "RegexLiteral", - "suggest": "", - "rule": "RegExp literals are not supported (arkts-no-regexp-literals)" - } - ] -} diff --git a/ets2panda/linter/test_rules/rule93.ts.autofix.json b/ets2panda/linter/test_rules/rule93.ts.autofix.json index 872acd394066cbc9e387b56a855037bb74ddc8ab..819c25194284cf48a8701462d1c1c31871221d4f 100644 --- a/ets2panda/linter/test_rules/rule93.ts.autofix.json +++ b/ets2panda/linter/test_rules/rule93.ts.autofix.json @@ -1,8 +1,8 @@ { "nodes": [ { - "line": 1, - "column": 1, + "line": 2, + "column": 5, "problem": "FunctionContainsThis", "autofixable": false, "suggest": "", diff --git a/ets2panda/linter/test_rules/rule93.ts.relax.json b/ets2panda/linter/test_rules/rule93.ts.relax.json index a16283a186b51f9545d9a78a95a673bbd7acbd15..772a1f628e47461607ed2422e22212586394fbc1 100644 --- a/ets2panda/linter/test_rules/rule93.ts.relax.json +++ b/ets2panda/linter/test_rules/rule93.ts.relax.json @@ -1,8 +1,8 @@ { "nodes": [ { - "line": 1, - "column": 1, + "line": 2, + "column": 5, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" diff --git a/ets2panda/linter/test_rules/rule93.ts.strict.json b/ets2panda/linter/test_rules/rule93.ts.strict.json index a16283a186b51f9545d9a78a95a673bbd7acbd15..772a1f628e47461607ed2422e22212586394fbc1 100644 --- a/ets2panda/linter/test_rules/rule93.ts.strict.json +++ b/ets2panda/linter/test_rules/rule93.ts.strict.json @@ -1,8 +1,8 @@ { "nodes": [ { - "line": 1, - "column": 1, + "line": 2, + "column": 5, "problem": "FunctionContainsThis", "suggest": "", "rule": "Using \"this\" inside stand-alone functions is not supported (arkts-no-standalone-this)" diff --git a/ets2panda/linter/tsconfig.json b/ets2panda/linter/tsconfig.json index 38bce99f852138baa0ded32cf404e45eeb327e4b..5e1d00ae015576cb61181716a3726ab419935bc2 100644 --- a/ets2panda/linter/tsconfig.json +++ b/ets2panda/linter/tsconfig.json @@ -12,5 +12,5 @@ "strict": true }, - "include": ["src/**/*", "utils/*"] -} \ No newline at end of file + "include": ["src/**/*", "lib/**/*", "utils/*"] +} diff --git a/ets2panda/linter/utils/logger.ts b/ets2panda/linter/utils/logger.ts index 88bf50d16882271a9373fdb8bc09270d747ac64f..ed6a6d7a7cbca18bad872a50d4e818f45564b532 100644 --- a/ets2panda/linter/utils/logger.ts +++ b/ets2panda/linter/utils/logger.ts @@ -13,35 +13,35 @@ * limitations under the License. */ -import { Logger } from 'log4js'; +import type { Logger } from 'log4js'; import { configure, getLogger } from 'log4js'; export default class ConsoleLogger { private static isConfigured = false; - public static configure(): void { + static configure(): void { configure({ appenders: { console: { type: 'console', layout: { type: 'pattern', - pattern: '%m', - }, - }, + pattern: '%m' + } + } }, categories: { default: { appenders: ['console'], - level: 'all', - }, - }, + level: 'all' + } + } }); ConsoleLogger.isConfigured = true; } - public static getLogger(): Logger { + static getLogger(): Logger { if (!ConsoleLogger.isConfigured) { ConsoleLogger.configure(); }