diff --git a/ets2panda/linter/lib/LinterRunner.ts b/ets2panda/linter/lib/LinterRunner.ts index 3820a942451d173cf94fc6cc7700167fe66a325d..d9eedbe1165cf937222278fa2ba216f33975d234 100644 --- a/ets2panda/linter/lib/LinterRunner.ts +++ b/ets2panda/linter/lib/LinterRunner.ts @@ -13,18 +13,18 @@ * limitations under the License. */ +import * as path from 'node:path'; import type * as ts from 'typescript'; -import type { ProblemInfo } from './ProblemInfo'; -import { TypeScriptLinter, consoleLog } from './TypeScriptLinter'; -import { InteropTypescriptLinter } from './InteropTypescriptLinter'; -import { FaultID } from './Problems'; -import { faultDesc } from './FaultDesc'; +import type { CommandLineOptions } from './CommandLineOptions'; import { faultsAttrs } from './FaultAttrs'; -import type { LintRunResult } from './LintRunResult'; -import * as path from 'node:path'; +import { faultDesc } from './FaultDesc'; +import { InteropTypescriptLinter } from './InteropTypescriptLinter'; import type { LintOptions } from './LintOptions'; -import type { CommandLineOptions } from './CommandLineOptions'; -import { mergeArrayMaps } from './utils/functions/MergeArrayMaps'; +import type { LintRunResult } from './LintRunResult'; +import type { ProblemInfo } from './ProblemInfo'; +import { ProblemSeverity } from './ProblemSeverity'; +import { FaultID } from './Problems'; +import { TypeScriptLinter, consoleLog } from './TypeScriptLinter'; import { getTscDiagnostics } from './ts-diagnostics/GetTscDiagnostics'; import { transformTscDiagnostics } from './ts-diagnostics/TransformTscDiagnostics'; import { @@ -32,33 +32,36 @@ import { ARKTS_IGNORE_DIRS_OH_MODULES, ARKTS_IGNORE_FILES } from './utils/consts/ArktsIgnorePaths'; +import { mergeArrayMaps } from './utils/functions/MergeArrayMaps'; import { pathContainsDirectory } from './utils/functions/PathHelper'; -import { ProblemSeverity } from './ProblemSeverity'; function prepareInputFilesList(cmdOptions: CommandLineOptions): string[] { let inputFiles = cmdOptions.inputFiles; - if (cmdOptions.parsedConfigFile) { - inputFiles = cmdOptions.parsedConfigFile.fileNames; - if (cmdOptions.inputFiles.length > 0) { + if (!cmdOptions.parsedConfigFile) { + return inputFiles; + } - /* - * 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; - }); - }); - } + inputFiles = cmdOptions.parsedConfigFile.fileNames; + if (cmdOptions.inputFiles.length <= 0) { + return inputFiles; } + /* + * 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; + }); + }); + return inputFiles; } diff --git a/ets2panda/linter/lib/TypeScriptLinter.ts b/ets2panda/linter/lib/TypeScriptLinter.ts index fb33ff4d75bcf0947cd4095b3625c07a72b71d31..7a2948568bab9243ace87f15a5e2ebe1a616d07e 100644 --- a/ets2panda/linter/lib/TypeScriptLinter.ts +++ b/ets2panda/linter/lib/TypeScriptLinter.ts @@ -44,6 +44,7 @@ import { } from './utils/consts/NonInitializablePropertyDecorators'; import { NON_RETURN_FUNCTION_DECORATORS } from './utils/consts/NonReturnFunctionDecorators'; import { PROPERTY_HAS_NO_INITIALIZER_ERROR_CODE } from './utils/consts/PropertyHasNoInitializerErrorCode'; +import { SENDABLE_DECORATOR, SENDABLE_DECORATOR_NODES } from './utils/consts/SendableAPI'; import type { DiagnosticChecker } from './utils/functions/DiagnosticChecker'; import { forEachNodeInSubtree } from './utils/functions/ForEachNodeInSubtree'; import { hasPredecessor } from './utils/functions/HasPredecessor'; @@ -59,7 +60,6 @@ import { import { SupportedStdCallApiChecker } from './utils/functions/SupportedStdCallAPI'; import { identiferUseInValueContext } from './utils/functions/identiferUseInValueContext'; import { isAssignmentOperator } from './utils/functions/isAssignmentOperator'; -import { SENDABLE_DECORATOR, SENDABLE_DECORATOR_NODES } from './utils/consts/SendableAPI'; export function consoleLog(...args: unknown[]): void { if (TypeScriptLinter.ideMode) { @@ -1829,19 +1829,23 @@ export class TypeScriptLinter { } private handleImportCall(tsCallExpr: ts.CallExpression): void { - if (tsCallExpr.expression.kind === ts.SyntaxKind.ImportKeyword) { - // relax rule#133 "arkts-no-runtime-import" - const tsArgs = tsCallExpr.arguments; - if (tsArgs.length > 1 && ts.isObjectLiteralExpression(tsArgs[1])) { - for (const tsProp of tsArgs[1].properties) { - if ( - (ts.isPropertyAssignment(tsProp) || ts.isShorthandPropertyAssignment(tsProp)) && - tsProp.name.getText() === 'assert' - ) { - this.incrementCounters(tsProp, FaultID.ImportAssertion); - break; - } - } + if (tsCallExpr.expression.kind !== ts.SyntaxKind.ImportKeyword) { + return; + } + + // relax rule#133 "arkts-no-runtime-import" + const tsArgs = tsCallExpr.arguments; + if (tsArgs.length <= 1 || !ts.isObjectLiteralExpression(tsArgs[1])) { + return; + } + + for (const tsProp of tsArgs[1].properties) { + if ( + (ts.isPropertyAssignment(tsProp) || ts.isShorthandPropertyAssignment(tsProp)) && + tsProp.name.getText() === 'assert' + ) { + this.incrementCounters(tsProp, FaultID.ImportAssertion); + break; } } } diff --git a/ets2panda/linter/lib/autofixes/Autofixer.ts b/ets2panda/linter/lib/autofixes/Autofixer.ts index eb926134ac49a33181ce7445a7a5f5485a31034a..37e6285c6970b9052baf8fada3ab439632c8570e 100644 --- a/ets2panda/linter/lib/autofixes/Autofixer.ts +++ b/ets2panda/linter/lib/autofixes/Autofixer.ts @@ -483,6 +483,36 @@ export class Autofixer { return text; } + private getEnumMembers(node: ts.Node, enumDeclsInFile: ts.Declaration[], result: Autofix[] | undefined): void { + if (result === undefined || !ts.isEnumDeclaration(node)) { + return; + } + + if (result.length) { + result.push({ start: node.getStart(), end: node.getEnd(), replacementText: '' }); + return; + } + + const members: ts.EnumMember[] = []; + for (const decl of enumDeclsInFile) { + for (const member of (decl as ts.EnumDeclaration).members) { + if ( + member.initializer && + member.initializer.kind !== ts.SyntaxKind.NumericLiteral && + member.initializer.kind !== ts.SyntaxKind.StringLiteral + ) { + result = undefined; + return; + } + } + members.push(...(decl as ts.EnumDeclaration).members); + } + + const fullEnum = ts.factory.createEnumDeclaration(node.modifiers, node.name, members); + const fullText = this.printer.printNode(ts.EmitHint.Unspecified, fullEnum, node.getSourceFile()); + result.push({ start: node.getStart(), end: node.getEnd(), replacementText: fullText }); + } + fixEnumMerging(enumSymbol: ts.Symbol, enumDeclsInFile: ts.Declaration[]): Autofix[] | undefined { if (this.enumMergingCache.has(enumSymbol)) { return this.enumMergingCache.get(enumSymbol); @@ -495,33 +525,7 @@ export class Autofixer { let result: Autofix[] | undefined = []; this.symbolCache.getReferences(enumSymbol).forEach((node) => { - if (result === undefined || !ts.isEnumDeclaration(node)) { - return; - } - - if (result.length) { - result.push({ start: node.getStart(), end: node.getEnd(), replacementText: '' }); - return; - } - - const members: ts.EnumMember[] = []; - for (const decl of enumDeclsInFile) { - for (const member of (decl as ts.EnumDeclaration).members) { - if ( - member.initializer && - member.initializer.kind !== ts.SyntaxKind.NumericLiteral && - member.initializer.kind !== ts.SyntaxKind.StringLiteral - ) { - result = undefined; - return; - } - } - members.push(...(decl as ts.EnumDeclaration).members); - } - - const fullEnum = ts.factory.createEnumDeclaration(node.modifiers, node.name, members); - const fullText = this.printer.printNode(ts.EmitHint.Unspecified, fullEnum, node.getSourceFile()); - result.push({ start: node.getStart(), end: node.getEnd(), replacementText: fullText }); + this.getEnumMembers(node, enumDeclsInFile, result); }); if (!result?.length) { result = undefined; diff --git a/ets2panda/linter/lib/utils/TsUtils.ts b/ets2panda/linter/lib/utils/TsUtils.ts index 77eb76f7ec6616dbbca679575106b041b7b22345..fa9a546620cfbec022d8bdc7d9eca5e1d2918987 100644 --- a/ets2panda/linter/lib/utils/TsUtils.ts +++ b/ets2panda/linter/lib/utils/TsUtils.ts @@ -756,15 +756,18 @@ export class TsUtils { let hasDefaultCtor: boolean = false; type.symbol.members.forEach((value) => { - if ((value.flags & ts.SymbolFlags.Constructor) !== 0) { - hasCtor = true; - - if (value.declarations !== undefined && value.declarations.length > 0) { - const declCtor = value.declarations[0] as ts.ConstructorDeclaration; - if (declCtor.parameters.length === 0) { - hasDefaultCtor = true; - } - } + if ((value.flags & ts.SymbolFlags.Constructor) === 0) { + return; + } + hasCtor = true; + + if (value.declarations === undefined || value.declarations.length <= 0) { + return; + } + + const declCtor = value.declarations[0] as ts.ConstructorDeclaration; + if (declCtor.parameters.length === 0) { + hasDefaultCtor = true; } }); @@ -2383,27 +2386,29 @@ export class TsUtils { static declarationNameExists(srcFile: ts.SourceFile, name: string): boolean { return srcFile.statements.some((stmt) => { - if (ts.isImportDeclaration(stmt)) { - if (!stmt.importClause) { - return false; - } - if (stmt.importClause.namedBindings) { - if (ts.isNamespaceImport(stmt.importClause.namedBindings)) { - return stmt.importClause.namedBindings.name.text === name; - } - return stmt.importClause.namedBindings.elements.some((x) => { - return x.name.text === name; - }); - } + if (!ts.isImportDeclaration(stmt)) { + return ( + TsUtils.isDeclarationStatement(stmt) && + stmt.name !== undefined && + ts.isIdentifier(stmt.name) && + stmt.name.text === name + ); + } + + if (!stmt.importClause) { + return false; + } + + if (!stmt.importClause.namedBindings) { return stmt.importClause.name?.text === name; } - return ( - TsUtils.isDeclarationStatement(stmt) && - stmt.name !== undefined && - ts.isIdentifier(stmt.name) && - stmt.name.text === name - ); + if (ts.isNamespaceImport(stmt.importClause.namedBindings)) { + return stmt.importClause.namedBindings.name.text === name; + } + return stmt.importClause.namedBindings.elements.some((x) => { + return x.name.text === name; + }); }); } diff --git a/ets2panda/linter/src/LinterCLI.ts b/ets2panda/linter/src/LinterCLI.ts index b6b18c81e70bee84e1c2651e2433ebe73180cef2..1f8a238d98dd4724d310ff1c5ea8caeacb2b0048 100644 --- a/ets2panda/linter/src/LinterCLI.ts +++ b/ets2panda/linter/src/LinterCLI.ts @@ -13,15 +13,16 @@ * limitations under the License. */ -import { TypeScriptLinter } from '../lib/TypeScriptLinter'; -import { parseCommandLine } from './CommandLineParser'; -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 * as readline from 'node:readline'; import type { CommandLineOptions } from '../lib/CommandLineOptions'; import { lint } from '../lib/LinterRunner'; +import { Logger } from '../lib/Logger'; +import type { ProblemInfo } from '../lib/ProblemInfo'; +import { TypeScriptLinter } from '../lib/TypeScriptLinter'; +import { parseCommandLine } from './CommandLineParser'; import { compileLintOptions } from './Compiler'; export function run(): void { @@ -51,6 +52,23 @@ function getTempFileName(): string { return path.join(os.tmpdir(), Math.floor(Math.random() * 10000000).toString() + '_linter_tmp_file.ts'); } +function showJSONMessage(problems: ProblemInfo[][]): void { + 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, + autofix: x.autofix + }; + }); + Logger.info(`{"linter messages":${JSON.stringify(jsonMessage)}}`); +} + function runIDEMode(cmdOptions: CommandLineOptions): void { TypeScriptLinter.ideMode = true; const tmpFileName = getTempFileName(); @@ -75,20 +93,7 @@ function runIDEMode(cmdOptions: CommandLineOptions): void { const result = lint(compileLintOptions(cmdOptions)); const problems = Array.from(result.problemsInfos.values()); if (problems.length === 1) { - 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, - autofix: x.autofix - }; - }); - Logger.info(`{"linter messages":${JSON.stringify(jsonMessage)}}`); + showJSONMessage(problems); } else { Logger.error('Unexpected error: could not lint file'); }