From 664e641f9a7585f1557566f60919c5f02b6a0eb2 Mon Sep 17 00:00:00 2001 From: Vsevolod Pukhov Date: Fri, 15 Sep 2023 13:03:54 +0300 Subject: [PATCH] [ArkTS Linter] Fix *AsObject checks, handle aliased identifier symbols Signed-off-by: Vsevolod Pukhov --- linter-4.2/src/TypeScriptLinter.ts | 215 +++++------------- linter-4.2/src/Utils.ts | 12 + linter-4.2/test/for_stmts.ts.relax.json | 33 +-- linter-4.2/test/for_stmts.ts.strict.json | 37 ++- .../test/imported_use_as_object.ts | 57 +++-- .../imported_use_as_object.ts.autofix.skip | 0 .../test/imported_use_as_object.ts.relax.json | 108 +++++++++ .../imported_use_as_object.ts.strict.json | 108 +++++++++ .../test/imported_use_as_object_module.ts | 3 + ...orted_use_as_object_module.ts.autofix.skip | 0 ...mported_use_as_object_module.ts.relax.json | 18 ++ ...ported_use_as_object_module.ts.strict.json | 18 ++ linter-4.2/test/modules.ts.autofix.json | 12 - linter-4.2/test/modules.ts.relax.json | 10 - linter-4.2/test/modules.ts.strict.json | 10 - .../test/type_declarations.ts.relax.json | 5 + .../test/type_declarations.ts.strict.json | 5 + linter/src/TypeScriptLinter.ts | 135 ++++------- linter/src/utils/TsUtils.ts | 30 ++- ...ssUse.ts => identiferUseInValueContext.ts} | 16 +- linter/test/for_stmts.ts.relax.json | 33 +-- linter/test/for_stmts.ts.strict.json | 37 ++- linter/test/imported_use_as_object.ts | 47 ++++ .../imported_use_as_object.ts.autofix.skip | 0 .../test/imported_use_as_object.ts.relax.json | 108 +++++++++ .../imported_use_as_object.ts.strict.json | 108 +++++++++ linter/test/imported_use_as_object_module.ts | 3 + ...orted_use_as_object_module.ts.autofix.skip | 0 ...mported_use_as_object_module.ts.relax.json | 18 ++ ...ported_use_as_object_module.ts.strict.json | 18 ++ linter/test/modules.ts.autofix.json | 12 - linter/test/modules.ts.relax.json | 10 - linter/test/modules.ts.strict.json | 10 - linter/test/type_declarations.ts.relax.json | 5 + linter/test/type_declarations.ts.strict.json | 5 + 35 files changed, 808 insertions(+), 438 deletions(-) rename linter/src/utils/functions/IsNamespaceUsedAsObject.ts => linter-4.2/test/imported_use_as_object.ts (40%) create mode 100644 linter-4.2/test/imported_use_as_object.ts.autofix.skip create mode 100644 linter-4.2/test/imported_use_as_object.ts.relax.json create mode 100644 linter-4.2/test/imported_use_as_object.ts.strict.json create mode 100644 linter-4.2/test/imported_use_as_object_module.ts create mode 100644 linter-4.2/test/imported_use_as_object_module.ts.autofix.skip create mode 100644 linter-4.2/test/imported_use_as_object_module.ts.relax.json create mode 100644 linter-4.2/test/imported_use_as_object_module.ts.strict.json rename linter/src/utils/functions/{IsValidClassUse.ts => identiferUseInValueContext.ts} (69%) create mode 100644 linter/test/imported_use_as_object.ts create mode 100644 linter/test/imported_use_as_object.ts.autofix.skip create mode 100644 linter/test/imported_use_as_object.ts.relax.json create mode 100644 linter/test/imported_use_as_object.ts.strict.json create mode 100644 linter/test/imported_use_as_object_module.ts create mode 100644 linter/test/imported_use_as_object_module.ts.autofix.skip create mode 100644 linter/test/imported_use_as_object_module.ts.relax.json create mode 100644 linter/test/imported_use_as_object_module.ts.strict.json diff --git a/linter-4.2/src/TypeScriptLinter.ts b/linter-4.2/src/TypeScriptLinter.ts index d2852f46a..1fa0752f7 100644 --- a/linter-4.2/src/TypeScriptLinter.ts +++ b/linter-4.2/src/TypeScriptLinter.ts @@ -276,20 +276,14 @@ export class TypeScriptLinter { return false; } - private countDeclarationsWithDuplicateName( - symbol: ts.Symbol | undefined, - tsDeclNode: ts.Node, - tsDeclKind?: ts.SyntaxKind + private countDeclarationsWithDuplicateName(tsNode: ts.Node, tsDeclNode: ts.Node, tsDeclKind?: ts.SyntaxKind ): void { - // Sanity check. - if (!symbol) return; - + let symbol = this.tsTypeChecker.getSymbolAtLocation(tsNode); // If specific declaration kind is provided, check against it. // Otherwise, use syntax kind of corresponding declaration node. - if ( - this.tsUtils.symbolHasDuplicateName(symbol, tsDeclKind ?? tsDeclNode.kind) - ) + if (!!symbol && this.tsUtils.symbolHasDuplicateName(symbol, tsDeclKind ?? tsDeclNode.kind)) { this.incrementCounters(tsDeclNode, FaultID.DeclWithDuplicateName); + } } private countClassMembersWithDuplicateName( @@ -409,11 +403,9 @@ export class TypeScriptLinter { } private isIIFEasNamespace(tsExpr: ts.PropertyAccessExpression): boolean { - const nameSymbol = this.tsTypeChecker.getSymbolAtLocation(tsExpr.name); + const nameSymbol = this.tsUtils.trueSymbolAtLocation(tsExpr.name); if (!nameSymbol) { - const leftHandSymbol = this.tsTypeChecker.getSymbolAtLocation( - tsExpr.expression - ); + const leftHandSymbol = this.tsUtils.trueSymbolAtLocation(tsExpr.expression); if (leftHandSymbol) { const decls = leftHandSymbol.getDeclarations(); if (!decls || decls.length !== 1) return false; @@ -445,13 +437,12 @@ export class TypeScriptLinter { return false; // Check if property symbol is 'Prototype' - const propAccessSym = - this.tsTypeChecker.getSymbolAtLocation(tsPropertyAccess); + const propAccessSym = this.tsUtils.trueSymbolAtLocation(tsPropertyAccess); if (this.tsUtils.isPrototypeSymbol(propAccessSym)) return true; // Check if symbol of LHS-expression is Class or Function. const tsBaseExpr = tsPropertyAccess.expression; - const baseExprSym = this.tsTypeChecker.getSymbolAtLocation(tsBaseExpr); + const baseExprSym = this.tsUtils.trueSymbolAtLocation(tsBaseExpr); if ( this.tsUtils.isTypeSymbol(baseExprSym) || this.tsUtils.isFunctionSymbol(baseExprSym) @@ -618,12 +609,9 @@ export class TypeScriptLinter { private handleEnumDeclaration(node: ts.Node) { let enumNode = node as ts.EnumDeclaration; - this.countDeclarationsWithDuplicateName( - this.tsTypeChecker.getSymbolAtLocation(enumNode.name), - enumNode - ); + this.countDeclarationsWithDuplicateName(enumNode.name, enumNode); - let enumSymbol = this.tsTypeChecker.getSymbolAtLocation(enumNode.name); + let enumSymbol = this.tsUtils.trueSymbolAtLocation(enumNode.name); if (!enumSymbol) return; let enumDecls = enumSymbol.getDeclarations(); @@ -644,7 +632,7 @@ export class TypeScriptLinter { private handleInterfaceDeclaration(node: ts.Node) { let interfaceNode = node as ts.InterfaceDeclaration; - let iSymbol = this.tsTypeChecker.getSymbolAtLocation(interfaceNode.name); + let iSymbol = this.tsUtils.trueSymbolAtLocation(interfaceNode.name); let iDecls = iSymbol ? iSymbol.getDeclarations() : null; if (iDecls) { // Since type checker merges all declarations with the same name @@ -663,10 +651,7 @@ export class TypeScriptLinter { if (interfaceNode.heritageClauses) this.interfaceInharitanceLint(node, interfaceNode.heritageClauses); - this.countDeclarationsWithDuplicateName( - this.tsTypeChecker.getSymbolAtLocation(interfaceNode.name), - interfaceNode - ); + this.countDeclarationsWithDuplicateName(interfaceNode.name, interfaceNode); } private handleThrowStatement(node: ts.Node) { @@ -780,7 +765,7 @@ export class TypeScriptLinter { this.incrementCounters(propertyAccessNode.name, FaultID.Prototype); } - let symbol = this.tsTypeChecker.getSymbolAtLocation(propertyAccessNode); + let symbol = this.tsUtils.trueSymbolAtLocation(propertyAccessNode); if (!!symbol && this.tsUtils.isSymbolAPI(symbol)) { this.incrementCounters(node, FaultID.SymbolType); } @@ -1002,10 +987,7 @@ export class TypeScriptLinter { if (!tsFunctionDeclaration.type) this.handleMissingReturnType(tsFunctionDeclaration); if (tsFunctionDeclaration.name) - this.countDeclarationsWithDuplicateName( - this.tsTypeChecker.getSymbolAtLocation(tsFunctionDeclaration.name), - tsFunctionDeclaration - ); + this.countDeclarationsWithDuplicateName(tsFunctionDeclaration.name, tsFunctionDeclaration); if ( tsFunctionDeclaration.body && @@ -1161,8 +1143,8 @@ export class TypeScriptLinter { this.incrementCounters(node, FaultID.DestructuringAssignment); if (ts.isPropertyAccessExpression(tsLhsExpr)) { - const tsLhsSymbol = this.tsTypeChecker.getSymbolAtLocation(tsLhsExpr); - const tsLhsBaseSymbol = this.tsTypeChecker.getSymbolAtLocation( + const tsLhsSymbol = this.tsUtils.trueSymbolAtLocation(tsLhsExpr); + const tsLhsBaseSymbol = this.tsUtils.trueSymbolAtLocation( tsLhsExpr.expression ); if (tsLhsSymbol && (tsLhsSymbol.flags & ts.SymbolFlags.Method)) { @@ -1250,7 +1232,7 @@ export class TypeScriptLinter { tsBinaryExpr.operatorToken.kind === ts.SyntaxKind.InstanceOfKeyword ) { const leftExpr = this.tsUtils.unwrapParenthesized(tsBinaryExpr.left); - const leftSymbol = this.tsTypeChecker.getSymbolAtLocation(leftExpr); + 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) { @@ -1295,11 +1277,7 @@ export class TypeScriptLinter { const visitBindingPatternNames = (tsBindingName: ts.BindingName) => { if (ts.isIdentifier(tsBindingName)) // The syntax kind of the declaration is defined here by the parent of 'BindingName' node. - this.countDeclarationsWithDuplicateName( - this.tsTypeChecker.getSymbolAtLocation(tsBindingName), - tsBindingName, - tsBindingName.parent.kind - ); + this.countDeclarationsWithDuplicateName(tsBindingName, tsBindingName, tsBindingName.parent.kind); else { for (const tsBindingElem of tsBindingName.elements) { if (ts.isOmittedExpression(tsBindingElem)) continue; @@ -1384,10 +1362,7 @@ export class TypeScriptLinter { this.staticBlocks.clear(); if (tsClassDecl.name) - this.countDeclarationsWithDuplicateName( - this.tsTypeChecker.getSymbolAtLocation(tsClassDecl.name), - tsClassDecl - ); + this.countDeclarationsWithDuplicateName(tsClassDecl.name, tsClassDecl); this.countClassMembersWithDuplicateName(tsClassDecl); @@ -1419,10 +1394,7 @@ export class TypeScriptLinter { private handleModuleDeclaration(node: ts.Node) { let tsModuleDecl = node as ts.ModuleDeclaration; - this.countDeclarationsWithDuplicateName( - this.tsTypeChecker.getSymbolAtLocation(tsModuleDecl.name), - tsModuleDecl - ); + this.countDeclarationsWithDuplicateName(tsModuleDecl.name, tsModuleDecl); let tsModuleBody = tsModuleDecl.body; let tsModifiers = tsModuleDecl.modifiers; // TSC 4.2 doesn't have 'ts.getModifiers()' method @@ -1468,10 +1440,7 @@ export class TypeScriptLinter { private handleTypeAliasDeclaration(node: ts.Node) { let tsTypeAlias = node as ts.TypeAliasDeclaration; - this.countDeclarationsWithDuplicateName( - this.tsTypeChecker.getSymbolAtLocation(tsTypeAlias.name), - tsTypeAlias - ); + this.countDeclarationsWithDuplicateName(tsTypeAlias.name, tsTypeAlias); if (this.tsUtils.typeIsRecursive(this.tsTypeChecker.getTypeAtLocation(node))) { this.incrementCounters(tsTypeAlias, FaultID.ClassAsObject); } @@ -1480,10 +1449,7 @@ export class TypeScriptLinter { private handleImportClause(node: ts.Node) { let tsImportClause = node as ts.ImportClause; if (tsImportClause.name) { - this.countDeclarationsWithDuplicateName( - this.tsTypeChecker.getSymbolAtLocation(tsImportClause.name), - tsImportClause - ); + this.countDeclarationsWithDuplicateName(tsImportClause.name, tsImportClause); } if ( @@ -1527,18 +1493,12 @@ export class TypeScriptLinter { private handleImportSpecifier(node: ts.Node) { let importSpec = node as ts.ImportSpecifier; - this.countDeclarationsWithDuplicateName( - this.tsTypeChecker.getSymbolAtLocation(importSpec.name), - importSpec - ); + this.countDeclarationsWithDuplicateName(importSpec.name, importSpec); } private handleNamespaceImport(node: ts.Node) { let tsNamespaceImport = node as ts.NamespaceImport; - this.countDeclarationsWithDuplicateName( - this.tsTypeChecker.getSymbolAtLocation(tsNamespaceImport.name), - tsNamespaceImport - ); + this.countDeclarationsWithDuplicateName(tsNamespaceImport.name, tsNamespaceImport); } private handleTypeAssertionExpression(node: ts.Node) { @@ -1582,12 +1542,10 @@ export class TypeScriptLinter { private handleIdentifier(node: ts.Node) { let tsIdentifier = node as ts.Identifier; - let tsIdentSym = this.tsTypeChecker.getSymbolAtLocation(tsIdentifier); + let tsIdentSym = this.tsUtils.trueSymbolAtLocation(tsIdentifier); if (tsIdentSym) { - this.handleNamespaceAsObject(tsIdentifier, tsIdentSym); - this.handleClassAsObject(tsIdentifier, tsIdentSym); - + this.handleRestrictedValues(tsIdentifier, tsIdentSym); if ( (tsIdentSym.flags & ts.SymbolFlags.Module) !== 0 && (tsIdentSym.flags & ts.SymbolFlags.Transient) !== 0 && @@ -1603,93 +1561,45 @@ export class TypeScriptLinter { } } - private handleNamespaceAsObject( - tsIdentifier: ts.Identifier, - tsIdentSym: ts.Symbol - ) { - if ( - tsIdentSym && - (tsIdentSym.getFlags() & ts.SymbolFlags.Module) !== 0 && - (tsIdentSym.getFlags() & ts.SymbolFlags.Variable) === 0 && - !ts.isModuleDeclaration(tsIdentifier.parent) - ) { - // 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 ( - !this.tsUtils.symbolHasDuplicateName( - tsIdentSym, - ts.SyntaxKind.ModuleDeclaration - ) - ) { - // If module name is the right-most name of Property Access chain or Qualified name, - // or it's a separate identifier expression, then module is being referenced as an object. - let tsIdentParent: ts.Node = tsIdentifier; - - while ( - ts.isPropertyAccessExpression(tsIdentParent.parent) || - ts.isQualifiedName(tsIdentParent.parent) - ) - tsIdentParent = tsIdentParent.parent; + private handleRestrictedValues(tsIdentifier: ts.Identifier, tsIdentSym: ts.Symbol) { + const illegalValues = ts.SymbolFlags.Class | ts.SymbolFlags.ConstEnum | ts.SymbolFlags.RegularEnum | + ts.SymbolFlags.ValueModule; - let isNamespace: boolean = - (tsIdentSym.getFlags() & ts.SymbolFlags.Namespace) != 0; - let isEmptyModuleBlock = false; - if ( - tsIdentSym.declarations && - tsIdentSym.declarations.length > 0 && - ts.isModuleDeclaration(tsIdentSym.declarations[0]) - ) { - let moduleDecl = tsIdentSym.declarations[0] as ts.ModuleDeclaration; - if (moduleDecl.body && ts.isModuleBlock(moduleDecl.body)) { - let moduleBlock = moduleDecl.body as ts.ModuleBlock; - if (moduleBlock.statements && moduleBlock.statements.length == 0) { - isEmptyModuleBlock = true; - } - } - } - - if ( - (!ts.isPropertyAccessExpression(tsIdentParent) && - !ts.isQualifiedName(tsIdentParent) && - !(isNamespace && isEmptyModuleBlock)) || - (ts.isPropertyAccessExpression(tsIdentParent) && - tsIdentifier === tsIdentParent.name) || - (ts.isQualifiedName(tsIdentParent) && - tsIdentifier === tsIdentParent.right) - ) - this.incrementCounters(tsIdentifier, FaultID.NamespaceAsObject); + // 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)) { + return; } } - } - - private handleClassAsObject( - tsIdentifier: ts.Identifier, - tsIdentSym: ts.Symbol - ) { - // Only process class references. - if ((tsIdentSym.getFlags() & ts.SymbolFlags.Class) === 0) { + // No check for ArkUI struct. + if (this.tsUtils.isStruct(tsIdentSym)) { return; } - // No check for ArkUI struct. - if (this.tsUtils.isStruct(tsIdentSym)) { + if ((tsIdentSym.flags & illegalValues) == 0 || !this.identiferUseInValueContext(tsIdentifier)) { return; } + if (tsIdentSym.flags & ts.SymbolFlags.ValueModule) { + this.incrementCounters(tsIdentifier, FaultID.NamespaceAsObject); + } else { + // missing EnumAsObject + this.incrementCounters(tsIdentifier, FaultID.ClassAsObject); + } + } - // If class name is the right-most name of Property Access chain or Qualified name, - // or it's a separate identifier expression, then class is being referenced as an object. + private identiferUseInValueContext( + tsIdentifier: ts.Identifier + ) { + // 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 tsIdentStart: ts.Node = tsIdentifier; - - while ( - ts.isPropertyAccessExpression(tsIdentStart.parent) || - ts.isQualifiedName(tsIdentStart.parent) - ) { + while (ts.isPropertyAccessExpression(tsIdentStart.parent) || ts.isQualifiedName(tsIdentStart.parent)) { tsIdentStart = tsIdentStart.parent; } - - // contexts where type is used as value, but it's intended - if ( - ts.isTypeNode(tsIdentStart.parent) || + return !( + // treat TypeQuery as valid because it's already forbidden (FaultID.TypeQuery) + (ts.isTypeNode(tsIdentStart.parent) && !ts.isTypeOfExpression(tsIdentStart.parent)) || ts.isExpressionWithTypeArguments(tsIdentStart.parent) || ts.isExportAssignment(tsIdentStart.parent) || ts.isExportSpecifier(tsIdentStart.parent) || @@ -1698,22 +1608,19 @@ export class TypeScriptLinter { ts.isClassLike(tsIdentStart.parent) || ts.isInterfaceDeclaration(tsIdentStart.parent) || ts.isModuleDeclaration(tsIdentStart.parent) || + ts.isEnumDeclaration(tsIdentStart.parent) || ts.isNamespaceImport(tsIdentStart.parent) || ts.isImportSpecifier(tsIdentStart.parent) || - (ts.isQualifiedName(tsIdentStart) && - tsIdentifier !== tsIdentStart.right) || + // rightmost in AST is rightmost in qualified name chain + (ts.isQualifiedName(tsIdentStart) && tsIdentifier !== tsIdentStart.right) || (ts.isPropertyAccessExpression(tsIdentStart) && tsIdentifier !== tsIdentStart.name) || (ts.isNewExpression(tsIdentStart.parent) && tsIdentStart === tsIdentStart.parent.expression) || (ts.isBinaryExpression(tsIdentStart.parent) && tsIdentStart.parent.operatorToken.kind === - ts.SyntaxKind.InstanceOfKeyword) - ) { - return; - } - - this.incrementCounters(tsIdentifier, FaultID.ClassAsObject); + ts.SyntaxKind.InstanceOfKeyword) + ); } private handleElementAccessExpression(node: ts.Node) { @@ -1912,7 +1819,7 @@ export class TypeScriptLinter { `${callableFunction}.bind`, ]; - const exprSymbol = this.tsTypeChecker.getSymbolAtLocation( + const exprSymbol = this.tsUtils.trueSymbolAtLocation( tsCallExpr.expression ); if (exprSymbol === undefined) { @@ -1973,7 +1880,7 @@ export class TypeScriptLinter { let callSignature = this.tsTypeChecker.getResolvedSignature(callExpr); if (!callSignature) return; - let sym = this.tsTypeChecker.getSymbolAtLocation(callExpr.expression); + let sym = this.tsUtils.trueSymbolAtLocation(callExpr.expression); if (sym) { let name = sym.getName(); if ( @@ -2176,7 +2083,7 @@ export class TypeScriptLinter { private handleExpressionWithTypeArguments(node: ts.Node) { let tsTypeExpr = node as ts.ExpressionWithTypeArguments; - let symbol = this.tsTypeChecker.getSymbolAtLocation(tsTypeExpr.expression); + let symbol = this.tsUtils.trueSymbolAtLocation(tsTypeExpr.expression); if (!!symbol && this.tsUtils.isEsObjectSymbol(symbol)) { this.incrementCounters(tsTypeExpr, FaultID.EsObjectType); } diff --git a/linter-4.2/src/Utils.ts b/linter-4.2/src/Utils.ts index 1fc3ed1ab..8a4381f3c 100644 --- a/linter-4.2/src/Utils.ts +++ b/linter-4.2/src/Utils.ts @@ -346,6 +346,18 @@ export class TsUtils { return unwrappedExpr; } + public followIfAliased(sym: ts.Symbol): ts.Symbol { + if ((sym.getFlags() & ts.SymbolFlags.Alias) !== 0) { + return this.tsTypeChecker.getAliasedSymbol(sym); + } + return sym; + } + + public trueSymbolAtLocation(node: ts.Node): ts.Symbol | undefined { + let sym = this.tsTypeChecker.getSymbolAtLocation(node); + return sym == undefined ? undefined : this.followIfAliased(sym); + } + 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 diff --git a/linter-4.2/test/for_stmts.ts.relax.json b/linter-4.2/test/for_stmts.ts.relax.json index f8dbfe66f..290b62d75 100644 --- a/linter-4.2/test/for_stmts.ts.relax.json +++ b/linter-4.2/test/for_stmts.ts.relax.json @@ -19,54 +19,45 @@ "column": 1, "problem": "ForInStatement" }, + { + "line": 22, + "column": 17, + "problem": "ClassAsObject" + }, { "line": 43, "column": 18, - "problem": "TupleType", - "suggest": "", - "rule": "Use \"Object[]\" instead of tuples (arkts-no-tuples)" + "problem": "TupleType" }, { "line": 49, "column": 1, - "problem": "ForInStatement", - "suggest": "", - "rule": "\"for .. in\" is not supported (arkts-no-for-in)" + "problem": "ForInStatement" }, { "line": 75, "column": 1, - "problem": "ForInStatement", - "suggest": "", - "rule": "\"for .. in\" is not supported (arkts-no-for-in)" + "problem": "ForInStatement" }, { "line": 91, "column": 14, - "problem": "CommaOperator", - "suggest": "", - "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" + "problem": "CommaOperator" }, { "line": 91, "column": 54, - "problem": "CommaOperator", - "suggest": "", - "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" + "problem": "CommaOperator" }, { "line": 99, "column": 7, - "problem": "AnyType", - "suggest": "", - "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + "problem": "AnyType" }, { "line": 124, "column": 12, - "problem": "AnyType", - "suggest": "", - "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + "problem": "AnyType" } ] } \ No newline at end of file diff --git a/linter-4.2/test/for_stmts.ts.strict.json b/linter-4.2/test/for_stmts.ts.strict.json index 43b49748f..a9d22cfd5 100644 --- a/linter-4.2/test/for_stmts.ts.strict.json +++ b/linter-4.2/test/for_stmts.ts.strict.json @@ -19,61 +19,50 @@ "column": 1, "problem": "ForInStatement" }, + { + "line": 22, + "column": 17, + "problem": "ClassAsObject" + }, { "line": 43, "column": 18, - "problem": "TupleType", - "suggest": "", - "rule": "Use \"Object[]\" instead of tuples (arkts-no-tuples)" + "problem": "TupleType" }, { "line": 44, "column": 1, - "problem": "ForOfNonArray", - "suggest": "", - "rule": "\"for-of\" is supported only for arrays and strings (arkts-for-of-str-arr)" + "problem": "ForOfNonArray" }, { "line": 49, "column": 1, - "problem": "ForInStatement", - "suggest": "", - "rule": "\"for .. in\" is not supported (arkts-no-for-in)" + "problem": "ForInStatement" }, { "line": 75, "column": 1, - "problem": "ForInStatement", - "suggest": "", - "rule": "\"for .. in\" is not supported (arkts-no-for-in)" + "problem": "ForInStatement" }, { "line": 91, "column": 14, - "problem": "CommaOperator", - "suggest": "", - "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" + "problem": "CommaOperator" }, { "line": 91, "column": 54, - "problem": "CommaOperator", - "suggest": "", - "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" + "problem": "CommaOperator" }, { "line": 99, "column": 7, - "problem": "AnyType", - "suggest": "", - "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + "problem": "AnyType" }, { "line": 124, "column": 12, - "problem": "AnyType", - "suggest": "", - "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + "problem": "AnyType" } ] } \ No newline at end of file diff --git a/linter/src/utils/functions/IsNamespaceUsedAsObject.ts b/linter-4.2/test/imported_use_as_object.ts similarity index 40% rename from linter/src/utils/functions/IsNamespaceUsedAsObject.ts rename to linter-4.2/test/imported_use_as_object.ts index 9f1594568..da742fba2 100644 --- a/linter/src/utils/functions/IsNamespaceUsedAsObject.ts +++ b/linter-4.2/test/imported_use_as_object.ts @@ -13,28 +13,35 @@ * limitations under the License. */ -import * as ts from 'typescript'; - -export function isNamespaceUsedAsObject( - tsIdentifier: ts.Identifier, - tsIdentSym: ts.Symbol, - tsIdentParent: ts.Node -) { - const isNamespace: boolean = - (tsIdentSym.getFlags() & ts.SymbolFlags.Namespace) != 0; - const isEmptyModuleBlock = - tsIdentSym.declarations && - tsIdentSym.declarations.length > 0 && - ts.isModuleDeclaration(tsIdentSym.declarations[0]) && - tsIdentSym.declarations[0].body && - ts.isModuleBlock(tsIdentSym.declarations[0].body) && - tsIdentSym.declarations[0].body.statements.length == 0; - return ( - (!ts.isPropertyAccessExpression(tsIdentParent) && - !ts.isQualifiedName(tsIdentParent) && - !(isNamespace && isEmptyModuleBlock)) || - (ts.isPropertyAccessExpression(tsIdentParent) && - tsIdentifier === tsIdentParent.name) || - (ts.isQualifiedName(tsIdentParent) && tsIdentifier === tsIdentParent.right) - ); -} +import { OuterC, OuterN, OuterE } from './imported_use_as_object_module' + +class InnerC { } +namespace InnerN { let x = 1; } +enum InnerE { e = 1 } + +function foo1(f: Function) { } +function foo2(o: Object) { } + +foo1(() => OuterC); +foo2(OuterC); +typeof OuterC; + +foo1(() => OuterN); +foo2(OuterN); +typeof OuterN; + +foo1(() => OuterE); +foo2(OuterE); +typeof OuterE; + +foo1(() => InnerC); +foo2(InnerC); +typeof InnerC; + +foo1(() => InnerN); +foo2(InnerN); +typeof InnerN; + +foo1(() => InnerE); +foo2(InnerE); +typeof InnerE; diff --git a/linter-4.2/test/imported_use_as_object.ts.autofix.skip b/linter-4.2/test/imported_use_as_object.ts.autofix.skip new file mode 100644 index 000000000..e69de29bb diff --git a/linter-4.2/test/imported_use_as_object.ts.relax.json b/linter-4.2/test/imported_use_as_object.ts.relax.json new file mode 100644 index 000000000..8120a622b --- /dev/null +++ b/linter-4.2/test/imported_use_as_object.ts.relax.json @@ -0,0 +1,108 @@ +{ + "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": [ + { + "line": 25, + "column": 12, + "problem": "ClassAsObject" + }, + { + "line": 26, + "column": 6, + "problem": "ClassAsObject" + }, + { + "line": 27, + "column": 8, + "problem": "ClassAsObject" + }, + { + "line": 29, + "column": 12, + "problem": "NamespaceAsObject" + }, + { + "line": 30, + "column": 6, + "problem": "NamespaceAsObject" + }, + { + "line": 31, + "column": 8, + "problem": "NamespaceAsObject" + }, + { + "line": 33, + "column": 12, + "problem": "ClassAsObject" + }, + { + "line": 34, + "column": 6, + "problem": "ClassAsObject" + }, + { + "line": 35, + "column": 8, + "problem": "ClassAsObject" + }, + { + "line": 37, + "column": 12, + "problem": "ClassAsObject" + }, + { + "line": 38, + "column": 6, + "problem": "ClassAsObject" + }, + { + "line": 39, + "column": 8, + "problem": "ClassAsObject" + }, + { + "line": 41, + "column": 12, + "problem": "NamespaceAsObject" + }, + { + "line": 42, + "column": 6, + "problem": "NamespaceAsObject" + }, + { + "line": 43, + "column": 8, + "problem": "NamespaceAsObject" + }, + { + "line": 45, + "column": 12, + "problem": "ClassAsObject" + }, + { + "line": 46, + "column": 6, + "problem": "ClassAsObject" + }, + { + "line": 47, + "column": 8, + "problem": "ClassAsObject" + } + ] +} \ No newline at end of file diff --git a/linter-4.2/test/imported_use_as_object.ts.strict.json b/linter-4.2/test/imported_use_as_object.ts.strict.json new file mode 100644 index 000000000..8120a622b --- /dev/null +++ b/linter-4.2/test/imported_use_as_object.ts.strict.json @@ -0,0 +1,108 @@ +{ + "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": [ + { + "line": 25, + "column": 12, + "problem": "ClassAsObject" + }, + { + "line": 26, + "column": 6, + "problem": "ClassAsObject" + }, + { + "line": 27, + "column": 8, + "problem": "ClassAsObject" + }, + { + "line": 29, + "column": 12, + "problem": "NamespaceAsObject" + }, + { + "line": 30, + "column": 6, + "problem": "NamespaceAsObject" + }, + { + "line": 31, + "column": 8, + "problem": "NamespaceAsObject" + }, + { + "line": 33, + "column": 12, + "problem": "ClassAsObject" + }, + { + "line": 34, + "column": 6, + "problem": "ClassAsObject" + }, + { + "line": 35, + "column": 8, + "problem": "ClassAsObject" + }, + { + "line": 37, + "column": 12, + "problem": "ClassAsObject" + }, + { + "line": 38, + "column": 6, + "problem": "ClassAsObject" + }, + { + "line": 39, + "column": 8, + "problem": "ClassAsObject" + }, + { + "line": 41, + "column": 12, + "problem": "NamespaceAsObject" + }, + { + "line": 42, + "column": 6, + "problem": "NamespaceAsObject" + }, + { + "line": 43, + "column": 8, + "problem": "NamespaceAsObject" + }, + { + "line": 45, + "column": 12, + "problem": "ClassAsObject" + }, + { + "line": 46, + "column": 6, + "problem": "ClassAsObject" + }, + { + "line": 47, + "column": 8, + "problem": "ClassAsObject" + } + ] +} \ No newline at end of file diff --git a/linter-4.2/test/imported_use_as_object_module.ts b/linter-4.2/test/imported_use_as_object_module.ts new file mode 100644 index 000000000..52fcec20b --- /dev/null +++ b/linter-4.2/test/imported_use_as_object_module.ts @@ -0,0 +1,3 @@ +export class OuterC {} +export namespace OuterN { let x = 1; } +export enum OuterE { e = 1 } diff --git a/linter-4.2/test/imported_use_as_object_module.ts.autofix.skip b/linter-4.2/test/imported_use_as_object_module.ts.autofix.skip new file mode 100644 index 000000000..e69de29bb diff --git a/linter-4.2/test/imported_use_as_object_module.ts.relax.json b/linter-4.2/test/imported_use_as_object_module.ts.relax.json new file mode 100644 index 000000000..9ef989840 --- /dev/null +++ b/linter-4.2/test/imported_use_as_object_module.ts.relax.json @@ -0,0 +1,18 @@ +{ + "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": [ + ] +} \ No newline at end of file diff --git a/linter-4.2/test/imported_use_as_object_module.ts.strict.json b/linter-4.2/test/imported_use_as_object_module.ts.strict.json new file mode 100644 index 000000000..9ef989840 --- /dev/null +++ b/linter-4.2/test/imported_use_as_object_module.ts.strict.json @@ -0,0 +1,18 @@ +{ + "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": [ + ] +} \ No newline at end of file diff --git a/linter-4.2/test/modules.ts.autofix.json b/linter-4.2/test/modules.ts.autofix.json index 6efcd4568..850a7acfd 100644 --- a/linter-4.2/test/modules.ts.autofix.json +++ b/linter-4.2/test/modules.ts.autofix.json @@ -62,12 +62,6 @@ "problem": "TypeQuery", "autofixable": false }, - { - "line": 70, - "column": 25, - "problem": "NamespaceAsObject", - "autofixable": false - }, { "line": 73, "column": 6, @@ -80,12 +74,6 @@ "problem": "TypeQuery", "autofixable": false }, - { - "line": 75, - "column": 29, - "problem": "NamespaceAsObject", - "autofixable": false - }, { "line": 78, "column": 9, diff --git a/linter-4.2/test/modules.ts.relax.json b/linter-4.2/test/modules.ts.relax.json index 7dec750ab..d8993a897 100644 --- a/linter-4.2/test/modules.ts.relax.json +++ b/linter-4.2/test/modules.ts.relax.json @@ -54,11 +54,6 @@ "column": 18, "problem": "TypeQuery" }, - { - "line": 70, - "column": 25, - "problem": "NamespaceAsObject" - }, { "line": 73, "column": 6, @@ -69,11 +64,6 @@ "column": 20, "problem": "TypeQuery" }, - { - "line": 75, - "column": 29, - "problem": "NamespaceAsObject" - }, { "line": 78, "column": 9, diff --git a/linter-4.2/test/modules.ts.strict.json b/linter-4.2/test/modules.ts.strict.json index 4e31337aa..1ba70a04a 100644 --- a/linter-4.2/test/modules.ts.strict.json +++ b/linter-4.2/test/modules.ts.strict.json @@ -54,11 +54,6 @@ "column": 18, "problem": "TypeQuery" }, - { - "line": 70, - "column": 25, - "problem": "NamespaceAsObject" - }, { "line": 73, "column": 6, @@ -69,11 +64,6 @@ "column": 20, "problem": "TypeQuery" }, - { - "line": 75, - "column": 29, - "problem": "NamespaceAsObject" - }, { "line": 78, "column": 9, diff --git a/linter-4.2/test/type_declarations.ts.relax.json b/linter-4.2/test/type_declarations.ts.relax.json index 9c847299b..aec9b187e 100644 --- a/linter-4.2/test/type_declarations.ts.relax.json +++ b/linter-4.2/test/type_declarations.ts.relax.json @@ -24,6 +24,11 @@ "column": 14, "problem": "InOperator" }, + { + "line": 36, + "column": 17, + "problem": "ClassAsObject" + }, { "line": 41, "column": 18, diff --git a/linter-4.2/test/type_declarations.ts.strict.json b/linter-4.2/test/type_declarations.ts.strict.json index e8a5f34dc..a9097524e 100644 --- a/linter-4.2/test/type_declarations.ts.strict.json +++ b/linter-4.2/test/type_declarations.ts.strict.json @@ -24,6 +24,11 @@ "column": 14, "problem": "InOperator" }, + { + "line": 36, + "column": 17, + "problem": "ClassAsObject" + }, { "line": 41, "column": 18, diff --git a/linter/src/TypeScriptLinter.ts b/linter/src/TypeScriptLinter.ts index dc31177e0..f340c3e00 100644 --- a/linter/src/TypeScriptLinter.ts +++ b/linter/src/TypeScriptLinter.ts @@ -40,8 +40,7 @@ import { NON_RETURN_FUNCTION_DECORATORS } from './utils/consts/NonReturnFunction 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 { isNamespaceUsedAsObject } from './utils/functions/IsNamespaceUsedAsObject' -import { isValidClassUse } from './utils/functions/IsValidClassUse' +import { identiferUseInValueContext } from './utils/functions/identiferUseInValueContext' import { typeHierarchyHasTypeError } from './utils/functions/TypeHelpers' import { hasPredecessor } from './utils/functions/HasPredecessor' import { scopeContainsThis } from './utils/functions/ContainsThis'; @@ -265,9 +264,9 @@ export class TypeScriptLinter { } } - private countDeclarationsWithDuplicateName( - symbol: ts.Symbol | undefined, 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)) { @@ -341,9 +340,9 @@ export class TypeScriptLinter { } private isIIFEasNamespace(tsExpr: ts.PropertyAccessExpression): boolean { - const nameSymbol = this.tsTypeChecker.getSymbolAtLocation(tsExpr.name); + const nameSymbol = this.tsUtils.trueSymbolAtLocation(tsExpr.name); if (!nameSymbol) { - const leftHandSymbol = this.tsTypeChecker.getSymbolAtLocation(tsExpr.expression); + const leftHandSymbol = this.tsUtils.trueSymbolAtLocation(tsExpr.expression); if (leftHandSymbol) { const decls = leftHandSymbol.getDeclarations(); if (!decls || decls.length !== 1) { @@ -371,13 +370,13 @@ export class TypeScriptLinter { return false; } // Check if property symbol is 'Prototype' - const propAccessSym = this.tsTypeChecker.getSymbolAtLocation(tsPropertyAccess); + const propAccessSym = this.tsUtils.trueSymbolAtLocation(tsPropertyAccess); if (this.tsUtils.isPrototypeSymbol(propAccessSym)) { return true; } // Check if symbol of LHS-expression is Class or Function. const tsBaseExpr = tsPropertyAccess.expression; - const baseExprSym = this.tsTypeChecker.getSymbolAtLocation(tsBaseExpr); + const baseExprSym = this.tsUtils.trueSymbolAtLocation(tsBaseExpr); if (this.tsUtils.isTypeSymbol(baseExprSym) || this.tsUtils.isFunctionSymbol(baseExprSym)) { return true; } @@ -504,10 +503,8 @@ export class TypeScriptLinter { private handleEnumDeclaration(node: ts.Node) { let enumNode = node as ts.EnumDeclaration; - this.countDeclarationsWithDuplicateName( - this.tsTypeChecker.getSymbolAtLocation(enumNode.name), enumNode - ); - let enumSymbol = this.tsTypeChecker.getSymbolAtLocation(enumNode.name); + this.countDeclarationsWithDuplicateName(enumNode.name, enumNode); + let enumSymbol = this.tsUtils.trueSymbolAtLocation(enumNode.name); if (!enumSymbol) { return; } @@ -533,7 +530,7 @@ export class TypeScriptLinter { this.cancellationToken?.throwIfCancellationRequested(); let interfaceNode = node as ts.InterfaceDeclaration; - let iSymbol = this.tsTypeChecker.getSymbolAtLocation(interfaceNode.name); + let iSymbol = this.tsUtils.trueSymbolAtLocation(interfaceNode.name); let iDecls = iSymbol ? iSymbol.getDeclarations() : null; if (iDecls) { // Since type checker merges all declarations with the same name @@ -551,9 +548,7 @@ export class TypeScriptLinter { if (interfaceNode.heritageClauses) { this.interfaceInharitanceLint(node, interfaceNode.heritageClauses); } - this.countDeclarationsWithDuplicateName( - this.tsTypeChecker.getSymbolAtLocation(interfaceNode.name), interfaceNode - ); + this.countDeclarationsWithDuplicateName(interfaceNode.name, interfaceNode); } private handleThrowStatement(node: ts.Node) { @@ -650,7 +645,7 @@ export class TypeScriptLinter { if (this.isPrototypePropertyAccess(propertyAccessNode)) { this.incrementCounters(propertyAccessNode.name, FaultID.Prototype); } - let symbol = this.tsTypeChecker.getSymbolAtLocation(propertyAccessNode); + let symbol = this.tsUtils.trueSymbolAtLocation(propertyAccessNode); if(!!symbol && this.tsUtils.isSymbolAPI(symbol)) { this.incrementCounters(node, FaultID.SymbolType); } @@ -826,9 +821,7 @@ export class TypeScriptLinter { let tsFunctionDeclaration = node as ts.FunctionDeclaration; if (!tsFunctionDeclaration.type) this.handleMissingReturnType(tsFunctionDeclaration); if (tsFunctionDeclaration.name) - this.countDeclarationsWithDuplicateName( - this.tsTypeChecker.getSymbolAtLocation(tsFunctionDeclaration.name), tsFunctionDeclaration - ); + this.countDeclarationsWithDuplicateName(tsFunctionDeclaration.name, tsFunctionDeclaration); if (tsFunctionDeclaration.body && scopeContainsThis(tsFunctionDeclaration.body)) { this.incrementCounters(node, FaultID.FunctionContainsThis); } @@ -962,8 +955,8 @@ export class TypeScriptLinter { this.incrementCounters(node, FaultID.DestructuringAssignment); } if (ts.isPropertyAccessExpression(tsLhsExpr)) { - const tsLhsSymbol = this.tsTypeChecker.getSymbolAtLocation(tsLhsExpr); - const tsLhsBaseSymbol = this.tsTypeChecker.getSymbolAtLocation(tsLhsExpr.expression); + 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); } @@ -992,7 +985,7 @@ export class TypeScriptLinter { private processBinaryInstanceOf(node: ts.Node, tsLhsExpr: ts.Expression, leftOperandType: ts.Type) { const leftExpr = this.tsUtils.unwrapParenthesized(tsLhsExpr); - const leftSymbol = this.tsTypeChecker.getSymbolAtLocation(leftExpr); + 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) { @@ -1019,10 +1012,7 @@ export class TypeScriptLinter { const visitBindingPatternNames = (tsBindingName: ts.BindingName) => { if (ts.isIdentifier(tsBindingName)) { // The syntax kind of the declaration is defined here by the parent of 'BindingName' node. - this.countDeclarationsWithDuplicateName( - this.tsTypeChecker.getSymbolAtLocation(tsBindingName), tsBindingName, - tsBindingName.parent.kind - ); + this.countDeclarationsWithDuplicateName(tsBindingName, tsBindingName, tsBindingName.parent.kind); return; } for (const tsBindingElem of tsBindingName.elements) { @@ -1095,10 +1085,7 @@ export class TypeScriptLinter { let tsClassDecl = node as ts.ClassDeclaration; this.staticBlocks.clear(); if (tsClassDecl.name) { - this.countDeclarationsWithDuplicateName( - this.tsTypeChecker.getSymbolAtLocation(tsClassDecl.name), - tsClassDecl - ); + this.countDeclarationsWithDuplicateName(tsClassDecl.name, tsClassDecl); } this.countClassMembersWithDuplicateName(tsClassDecl); @@ -1132,10 +1119,7 @@ export class TypeScriptLinter { let tsModuleDecl = node as ts.ModuleDeclaration; - this.countDeclarationsWithDuplicateName( - this.tsTypeChecker.getSymbolAtLocation(tsModuleDecl.name), - tsModuleDecl - ); + this.countDeclarationsWithDuplicateName(tsModuleDecl.name, tsModuleDecl); let tsModuleBody = tsModuleDecl.body; let tsModifiers = ts.getModifiers(tsModuleDecl); @@ -1173,9 +1157,7 @@ export class TypeScriptLinter { private handleTypeAliasDeclaration(node: ts.Node) { let tsTypeAlias = node as ts.TypeAliasDeclaration; - this.countDeclarationsWithDuplicateName( - this.tsTypeChecker.getSymbolAtLocation(tsTypeAlias.name), tsTypeAlias - ); + this.countDeclarationsWithDuplicateName(tsTypeAlias.name, tsTypeAlias); if (this.tsUtils.typeIsRecursive(this.tsTypeChecker.getTypeAtLocation(node))) { this.incrementCounters(tsTypeAlias, FaultID.ClassAsObject); } @@ -1184,9 +1166,7 @@ export class TypeScriptLinter { private handleImportClause(node: ts.Node) { let tsImportClause = node as ts.ImportClause; if (tsImportClause.name) { - this.countDeclarationsWithDuplicateName( - this.tsTypeChecker.getSymbolAtLocation(tsImportClause.name), tsImportClause - ); + this.countDeclarationsWithDuplicateName(tsImportClause.name, tsImportClause); } if (tsImportClause.namedBindings && ts.isNamedImports(tsImportClause.namedBindings)) { let nonDefaultSpecs: ts.ImportSpecifier[] = []; @@ -1212,9 +1192,7 @@ export class TypeScriptLinter { private handleImportSpecifier(node: ts.Node) { let importSpec = node as ts.ImportSpecifier; - this.countDeclarationsWithDuplicateName( - this.tsTypeChecker.getSymbolAtLocation(importSpec.name), importSpec - ); + 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 && @@ -1229,9 +1207,7 @@ export class TypeScriptLinter { private handleNamespaceImport(node: ts.Node) { let tsNamespaceImport = node as ts.NamespaceImport; - this.countDeclarationsWithDuplicateName( - this.tsTypeChecker.getSymbolAtLocation(tsNamespaceImport.name), tsNamespaceImport - ); + this.countDeclarationsWithDuplicateName(tsNamespaceImport.name, tsNamespaceImport); } private handleTypeAssertionExpression(node: ts.Node) { @@ -1291,10 +1267,9 @@ export class TypeScriptLinter { private handleIdentifier(node: ts.Node) { let tsIdentifier = node as ts.Identifier; - let tsIdentSym = this.tsTypeChecker.getSymbolAtLocation(tsIdentifier); + let tsIdentSym = this.tsUtils.trueSymbolAtLocation(tsIdentifier); if (tsIdentSym) { - this.handleNamespaceAsObject(tsIdentifier, tsIdentSym); - this.handleClassAsObject(tsIdentifier, tsIdentSym); + this.handleRestrictedValues(tsIdentifier, tsIdentSym); if ( (tsIdentSym.flags & ts.SymbolFlags.Module) !== 0 && (tsIdentSym.flags & ts.SymbolFlags.Transient) !== 0 && @@ -1306,49 +1281,31 @@ export class TypeScriptLinter { } } - private handleNamespaceAsObject(tsIdentifier: ts.Identifier, tsIdentSym: ts.Symbol) { - if ( - tsIdentSym && - (tsIdentSym.getFlags() & ts.SymbolFlags.Module) !== 0 && - (tsIdentSym.getFlags() & ts.SymbolFlags.Variable) === 0 && - !ts.isModuleDeclaration(tsIdentifier.parent) - ) { - // 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 (!this.tsUtils.symbolHasDuplicateName(tsIdentSym, ts.SyntaxKind.ModuleDeclaration)) { - // If module name is the right-most name of Property Access chain or Qualified name, - // or it's a separate identifier expression, then module is being referenced as an object. - let tsIdentParent: ts.Node = tsIdentifier; - - while (ts.isPropertyAccessExpression(tsIdentParent.parent) || ts.isQualifiedName(tsIdentParent.parent)) - tsIdentParent = tsIdentParent.parent; - - if (isNamespaceUsedAsObject(tsIdentifier, tsIdentSym, tsIdentParent)) - this.incrementCounters(tsIdentifier, FaultID.NamespaceAsObject); + private handleRestrictedValues(tsIdentifier: ts.Identifier, tsIdentSym: ts.Symbol) { + const illegalValues = ts.SymbolFlags.Class | ts.SymbolFlags.ConstEnum | ts.SymbolFlags.RegularEnum | + ts.SymbolFlags.ValueModule; + + // 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)) { + return; } } - } - - private handleClassAsObject(tsIdentifier: ts.Identifier, tsIdentSym: ts.Symbol) { - // Only process class references. - if ((tsIdentSym.getFlags() & ts.SymbolFlags.Class) === 0) { - return; - } // No check for ArkUI struct. if (isStruct(tsIdentSym)) { return; } - // If class name is the right-most name of Property Access chain or Qualified name, - // or it's a separate identifier expression, then class is being referenced as an object. - let tsIdentStart: ts.Node = tsIdentifier; - while (ts.isPropertyAccessExpression(tsIdentStart.parent) || ts.isQualifiedName(tsIdentStart.parent)) { - tsIdentStart = tsIdentStart.parent; - } - // contexts where type is used as value, but it's intended - if (isValidClassUse(tsIdentStart, tsIdentifier)) { + + if ((tsIdentSym.flags & illegalValues) == 0 || !identiferUseInValueContext(tsIdentifier)) { return; } - this.incrementCounters(tsIdentifier, FaultID.ClassAsObject); + if (tsIdentSym.flags & ts.SymbolFlags.ValueModule) { + this.incrementCounters(tsIdentifier, FaultID.NamespaceAsObject); + } else { + // missing EnumAsObject + this.incrementCounters(tsIdentifier, FaultID.ClassAsObject); + } } private handleElementAccessExpression(node: ts.Node) { @@ -1511,9 +1468,7 @@ export class TypeScriptLinter { `${callableFunction}.call`, `${callableFunction}.bind`, ]; - const exprSymbol = this.tsTypeChecker.getSymbolAtLocation( - tsCallExpr.expression - ); + const exprSymbol = this.tsUtils.trueSymbolAtLocation(tsCallExpr.expression); if (exprSymbol === undefined) { return; } @@ -1559,7 +1514,7 @@ export class TypeScriptLinter { if (!this.tsTypeChecker.getResolvedSignature(callExpr)) { return; } - const sym = this.tsTypeChecker.getSymbolAtLocation(callExpr.expression); + const sym = this.tsUtils.trueSymbolAtLocation(callExpr.expression); if (!sym) { return; } @@ -1730,7 +1685,7 @@ export class TypeScriptLinter { private handleExpressionWithTypeArguments(node: ts.Node) { let tsTypeExpr = node as ts.ExpressionWithTypeArguments; - let symbol = this.tsTypeChecker.getSymbolAtLocation(tsTypeExpr.expression); + let symbol = this.tsUtils.trueSymbolAtLocation(tsTypeExpr.expression); if (!!symbol && this.tsUtils.isEsObjectSymbol(symbol)) { this.incrementCounters(tsTypeExpr, FaultID.EsObjectType); } diff --git a/linter/src/utils/TsUtils.ts b/linter/src/utils/TsUtils.ts index 6aa0e7814..ce1be4f9b 100644 --- a/linter/src/utils/TsUtils.ts +++ b/linter/src/utils/TsUtils.ts @@ -181,6 +181,18 @@ export class TsUtils { return unwrappedExpr; } + public followIfAliased(sym: ts.Symbol): ts.Symbol { + if ((sym.getFlags() & ts.SymbolFlags.Alias) !== 0) { + return this.tsTypeChecker.getAliasedSymbol(sym); + } + return sym; + } + + public trueSymbolAtLocation(node: ts.Node): ts.Symbol | undefined { + let sym = this.tsTypeChecker.getSymbolAtLocation(node); + return sym == undefined ? undefined : this.followIfAliased(sym); + } + 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 @@ -369,7 +381,7 @@ export class TsUtils { const propertyAccess = tsExpr as ts.PropertyAccessExpression; if (this.isNumberConstantValue(propertyAccess)) return true; - const leftHandSymbol = this.tsTypeChecker.getSymbolAtLocation(propertyAccess.expression); + const leftHandSymbol = this.trueSymbolAtLocation(propertyAccess.expression); if (!leftHandSymbol) return false; const decls = leftHandSymbol.getDeclarations(); @@ -398,7 +410,7 @@ export class TsUtils { } private isIdentifierValidEnumMemberInit(tsExpr: ts.Identifier): boolean { - let tsSymbol = this.tsTypeChecker.getSymbolAtLocation(tsExpr); + let tsSymbol = this.trueSymbolAtLocation(tsExpr); let tsDecl = this.getDeclaration(tsSymbol); return (!!tsDecl && ((this.isVarDeclaration(tsDecl) && this.isConst(tsDecl.parent)) || @@ -722,7 +734,7 @@ export class TsUtils { if (isStdLibraryType(lhsType) || this.isPrimitiveType(lhsType)) { let rhsSym = ts.isCallExpression(rhsExpr) ? this.getSymbolOfCallExpression(rhsExpr) - : this.tsTypeChecker.getSymbolAtLocation(rhsExpr); + : this.trueSymbolAtLocation(rhsExpr); if (rhsSym && this.isLibrarySymbol(rhsSym)) return true; @@ -1006,12 +1018,9 @@ export class TsUtils { // #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)) { + sym = this.trueSymbolAtLocation(callExpr.expression.expression); + if (sym && this.isLibrarySymbol(sym)) { return true; - } } } } @@ -1052,8 +1061,7 @@ export class TsUtils { } public getVariableDeclarationTypeNode(node: ts.Node): ts.TypeNode | undefined { - const symbol = this.tsTypeChecker.getSymbolAtLocation(node); - const decl = this.getDeclaration(symbol); + const decl = this.getDeclaration(this.trueSymbolAtLocation(node)); if (!!decl && ts.isVariableDeclaration(decl)) { return decl.type; } @@ -1089,7 +1097,7 @@ export class TsUtils { const signature = this.tsTypeChecker.getResolvedSignature(callExpr); const signDecl = signature?.getDeclaration(); if (signDecl && signDecl.name) { - return this.tsTypeChecker.getSymbolAtLocation(signDecl.name); + return this.trueSymbolAtLocation(signDecl.name); } return undefined; } diff --git a/linter/src/utils/functions/IsValidClassUse.ts b/linter/src/utils/functions/identiferUseInValueContext.ts similarity index 69% rename from linter/src/utils/functions/IsValidClassUse.ts rename to linter/src/utils/functions/identiferUseInValueContext.ts index ccdbb7b9b..3b2066087 100644 --- a/linter/src/utils/functions/IsValidClassUse.ts +++ b/linter/src/utils/functions/identiferUseInValueContext.ts @@ -15,12 +15,18 @@ import * as ts from 'typescript'; -export function isValidClassUse( - tsIdentStart: ts.Node, +export function identiferUseInValueContext( tsIdentifier: ts.Identifier ) { - return ( - ts.isTypeNode(tsIdentStart.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. + let tsIdentStart: ts.Node = tsIdentifier; + while (ts.isPropertyAccessExpression(tsIdentStart.parent) || ts.isQualifiedName(tsIdentStart.parent)) { + tsIdentStart = tsIdentStart.parent; + } + return !( + // treat TypeQuery as valid because it's already forbidden (FaultID.TypeQuery) + (ts.isTypeNode(tsIdentStart.parent) && !ts.isTypeOfExpression(tsIdentStart.parent)) || ts.isExpressionWithTypeArguments(tsIdentStart.parent) || ts.isExportAssignment(tsIdentStart.parent) || ts.isExportSpecifier(tsIdentStart.parent) || @@ -29,8 +35,10 @@ export function isValidClassUse( ts.isClassLike(tsIdentStart.parent) || ts.isInterfaceDeclaration(tsIdentStart.parent) || ts.isModuleDeclaration(tsIdentStart.parent) || + ts.isEnumDeclaration(tsIdentStart.parent) || ts.isNamespaceImport(tsIdentStart.parent) || ts.isImportSpecifier(tsIdentStart.parent) || + // rightmost in AST is rightmost in qualified name chain (ts.isQualifiedName(tsIdentStart) && tsIdentifier !== tsIdentStart.right) || (ts.isPropertyAccessExpression(tsIdentStart) && tsIdentifier !== tsIdentStart.name) || diff --git a/linter/test/for_stmts.ts.relax.json b/linter/test/for_stmts.ts.relax.json index f8dbfe66f..290b62d75 100644 --- a/linter/test/for_stmts.ts.relax.json +++ b/linter/test/for_stmts.ts.relax.json @@ -19,54 +19,45 @@ "column": 1, "problem": "ForInStatement" }, + { + "line": 22, + "column": 17, + "problem": "ClassAsObject" + }, { "line": 43, "column": 18, - "problem": "TupleType", - "suggest": "", - "rule": "Use \"Object[]\" instead of tuples (arkts-no-tuples)" + "problem": "TupleType" }, { "line": 49, "column": 1, - "problem": "ForInStatement", - "suggest": "", - "rule": "\"for .. in\" is not supported (arkts-no-for-in)" + "problem": "ForInStatement" }, { "line": 75, "column": 1, - "problem": "ForInStatement", - "suggest": "", - "rule": "\"for .. in\" is not supported (arkts-no-for-in)" + "problem": "ForInStatement" }, { "line": 91, "column": 14, - "problem": "CommaOperator", - "suggest": "", - "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" + "problem": "CommaOperator" }, { "line": 91, "column": 54, - "problem": "CommaOperator", - "suggest": "", - "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" + "problem": "CommaOperator" }, { "line": 99, "column": 7, - "problem": "AnyType", - "suggest": "", - "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + "problem": "AnyType" }, { "line": 124, "column": 12, - "problem": "AnyType", - "suggest": "", - "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + "problem": "AnyType" } ] } \ No newline at end of file diff --git a/linter/test/for_stmts.ts.strict.json b/linter/test/for_stmts.ts.strict.json index 43b49748f..a9d22cfd5 100644 --- a/linter/test/for_stmts.ts.strict.json +++ b/linter/test/for_stmts.ts.strict.json @@ -19,61 +19,50 @@ "column": 1, "problem": "ForInStatement" }, + { + "line": 22, + "column": 17, + "problem": "ClassAsObject" + }, { "line": 43, "column": 18, - "problem": "TupleType", - "suggest": "", - "rule": "Use \"Object[]\" instead of tuples (arkts-no-tuples)" + "problem": "TupleType" }, { "line": 44, "column": 1, - "problem": "ForOfNonArray", - "suggest": "", - "rule": "\"for-of\" is supported only for arrays and strings (arkts-for-of-str-arr)" + "problem": "ForOfNonArray" }, { "line": 49, "column": 1, - "problem": "ForInStatement", - "suggest": "", - "rule": "\"for .. in\" is not supported (arkts-no-for-in)" + "problem": "ForInStatement" }, { "line": 75, "column": 1, - "problem": "ForInStatement", - "suggest": "", - "rule": "\"for .. in\" is not supported (arkts-no-for-in)" + "problem": "ForInStatement" }, { "line": 91, "column": 14, - "problem": "CommaOperator", - "suggest": "", - "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" + "problem": "CommaOperator" }, { "line": 91, "column": 54, - "problem": "CommaOperator", - "suggest": "", - "rule": "The comma operator \",\" is supported only in \"for\" loops (arkts-no-comma-outside-loops)" + "problem": "CommaOperator" }, { "line": 99, "column": 7, - "problem": "AnyType", - "suggest": "", - "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + "problem": "AnyType" }, { "line": 124, "column": 12, - "problem": "AnyType", - "suggest": "", - "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + "problem": "AnyType" } ] } \ No newline at end of file diff --git a/linter/test/imported_use_as_object.ts b/linter/test/imported_use_as_object.ts new file mode 100644 index 000000000..da742fba2 --- /dev/null +++ b/linter/test/imported_use_as_object.ts @@ -0,0 +1,47 @@ +/* + * 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 { OuterC, OuterN, OuterE } from './imported_use_as_object_module' + +class InnerC { } +namespace InnerN { let x = 1; } +enum InnerE { e = 1 } + +function foo1(f: Function) { } +function foo2(o: Object) { } + +foo1(() => OuterC); +foo2(OuterC); +typeof OuterC; + +foo1(() => OuterN); +foo2(OuterN); +typeof OuterN; + +foo1(() => OuterE); +foo2(OuterE); +typeof OuterE; + +foo1(() => InnerC); +foo2(InnerC); +typeof InnerC; + +foo1(() => InnerN); +foo2(InnerN); +typeof InnerN; + +foo1(() => InnerE); +foo2(InnerE); +typeof InnerE; diff --git a/linter/test/imported_use_as_object.ts.autofix.skip b/linter/test/imported_use_as_object.ts.autofix.skip new file mode 100644 index 000000000..e69de29bb diff --git a/linter/test/imported_use_as_object.ts.relax.json b/linter/test/imported_use_as_object.ts.relax.json new file mode 100644 index 000000000..8120a622b --- /dev/null +++ b/linter/test/imported_use_as_object.ts.relax.json @@ -0,0 +1,108 @@ +{ + "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": [ + { + "line": 25, + "column": 12, + "problem": "ClassAsObject" + }, + { + "line": 26, + "column": 6, + "problem": "ClassAsObject" + }, + { + "line": 27, + "column": 8, + "problem": "ClassAsObject" + }, + { + "line": 29, + "column": 12, + "problem": "NamespaceAsObject" + }, + { + "line": 30, + "column": 6, + "problem": "NamespaceAsObject" + }, + { + "line": 31, + "column": 8, + "problem": "NamespaceAsObject" + }, + { + "line": 33, + "column": 12, + "problem": "ClassAsObject" + }, + { + "line": 34, + "column": 6, + "problem": "ClassAsObject" + }, + { + "line": 35, + "column": 8, + "problem": "ClassAsObject" + }, + { + "line": 37, + "column": 12, + "problem": "ClassAsObject" + }, + { + "line": 38, + "column": 6, + "problem": "ClassAsObject" + }, + { + "line": 39, + "column": 8, + "problem": "ClassAsObject" + }, + { + "line": 41, + "column": 12, + "problem": "NamespaceAsObject" + }, + { + "line": 42, + "column": 6, + "problem": "NamespaceAsObject" + }, + { + "line": 43, + "column": 8, + "problem": "NamespaceAsObject" + }, + { + "line": 45, + "column": 12, + "problem": "ClassAsObject" + }, + { + "line": 46, + "column": 6, + "problem": "ClassAsObject" + }, + { + "line": 47, + "column": 8, + "problem": "ClassAsObject" + } + ] +} \ No newline at end of file diff --git a/linter/test/imported_use_as_object.ts.strict.json b/linter/test/imported_use_as_object.ts.strict.json new file mode 100644 index 000000000..8120a622b --- /dev/null +++ b/linter/test/imported_use_as_object.ts.strict.json @@ -0,0 +1,108 @@ +{ + "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": [ + { + "line": 25, + "column": 12, + "problem": "ClassAsObject" + }, + { + "line": 26, + "column": 6, + "problem": "ClassAsObject" + }, + { + "line": 27, + "column": 8, + "problem": "ClassAsObject" + }, + { + "line": 29, + "column": 12, + "problem": "NamespaceAsObject" + }, + { + "line": 30, + "column": 6, + "problem": "NamespaceAsObject" + }, + { + "line": 31, + "column": 8, + "problem": "NamespaceAsObject" + }, + { + "line": 33, + "column": 12, + "problem": "ClassAsObject" + }, + { + "line": 34, + "column": 6, + "problem": "ClassAsObject" + }, + { + "line": 35, + "column": 8, + "problem": "ClassAsObject" + }, + { + "line": 37, + "column": 12, + "problem": "ClassAsObject" + }, + { + "line": 38, + "column": 6, + "problem": "ClassAsObject" + }, + { + "line": 39, + "column": 8, + "problem": "ClassAsObject" + }, + { + "line": 41, + "column": 12, + "problem": "NamespaceAsObject" + }, + { + "line": 42, + "column": 6, + "problem": "NamespaceAsObject" + }, + { + "line": 43, + "column": 8, + "problem": "NamespaceAsObject" + }, + { + "line": 45, + "column": 12, + "problem": "ClassAsObject" + }, + { + "line": 46, + "column": 6, + "problem": "ClassAsObject" + }, + { + "line": 47, + "column": 8, + "problem": "ClassAsObject" + } + ] +} \ No newline at end of file diff --git a/linter/test/imported_use_as_object_module.ts b/linter/test/imported_use_as_object_module.ts new file mode 100644 index 000000000..52fcec20b --- /dev/null +++ b/linter/test/imported_use_as_object_module.ts @@ -0,0 +1,3 @@ +export class OuterC {} +export namespace OuterN { let x = 1; } +export enum OuterE { e = 1 } diff --git a/linter/test/imported_use_as_object_module.ts.autofix.skip b/linter/test/imported_use_as_object_module.ts.autofix.skip new file mode 100644 index 000000000..e69de29bb diff --git a/linter/test/imported_use_as_object_module.ts.relax.json b/linter/test/imported_use_as_object_module.ts.relax.json new file mode 100644 index 000000000..9ef989840 --- /dev/null +++ b/linter/test/imported_use_as_object_module.ts.relax.json @@ -0,0 +1,18 @@ +{ + "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": [ + ] +} \ No newline at end of file diff --git a/linter/test/imported_use_as_object_module.ts.strict.json b/linter/test/imported_use_as_object_module.ts.strict.json new file mode 100644 index 000000000..9ef989840 --- /dev/null +++ b/linter/test/imported_use_as_object_module.ts.strict.json @@ -0,0 +1,18 @@ +{ + "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": [ + ] +} \ No newline at end of file diff --git a/linter/test/modules.ts.autofix.json b/linter/test/modules.ts.autofix.json index ee501d6b6..e8743d7b8 100755 --- a/linter/test/modules.ts.autofix.json +++ b/linter/test/modules.ts.autofix.json @@ -62,12 +62,6 @@ "problem": "TypeQuery", "autofixable": false }, - { - "line": 70, - "column": 25, - "problem": "NamespaceAsObject", - "autofixable": false - }, { "line": 73, "column": 6, @@ -80,12 +74,6 @@ "problem": "TypeQuery", "autofixable": false }, - { - "line": 75, - "column": 29, - "problem": "NamespaceAsObject", - "autofixable": false - }, { "line": 78, "column": 9, diff --git a/linter/test/modules.ts.relax.json b/linter/test/modules.ts.relax.json index e024a9be5..5d04f8dfe 100644 --- a/linter/test/modules.ts.relax.json +++ b/linter/test/modules.ts.relax.json @@ -54,11 +54,6 @@ "column": 18, "problem": "TypeQuery" }, - { - "line": 70, - "column": 25, - "problem": "NamespaceAsObject" - }, { "line": 73, "column": 6, @@ -69,11 +64,6 @@ "column": 20, "problem": "TypeQuery" }, - { - "line": 75, - "column": 29, - "problem": "NamespaceAsObject" - }, { "line": 78, "column": 9, diff --git a/linter/test/modules.ts.strict.json b/linter/test/modules.ts.strict.json index 550974d82..ae55e25bf 100644 --- a/linter/test/modules.ts.strict.json +++ b/linter/test/modules.ts.strict.json @@ -54,11 +54,6 @@ "column": 18, "problem": "TypeQuery" }, - { - "line": 70, - "column": 25, - "problem": "NamespaceAsObject" - }, { "line": 73, "column": 6, @@ -69,11 +64,6 @@ "column": 20, "problem": "TypeQuery" }, - { - "line": 75, - "column": 29, - "problem": "NamespaceAsObject" - }, { "line": 78, "column": 9, diff --git a/linter/test/type_declarations.ts.relax.json b/linter/test/type_declarations.ts.relax.json index 9c847299b..aec9b187e 100644 --- a/linter/test/type_declarations.ts.relax.json +++ b/linter/test/type_declarations.ts.relax.json @@ -24,6 +24,11 @@ "column": 14, "problem": "InOperator" }, + { + "line": 36, + "column": 17, + "problem": "ClassAsObject" + }, { "line": 41, "column": 18, diff --git a/linter/test/type_declarations.ts.strict.json b/linter/test/type_declarations.ts.strict.json index e8a5f34dc..a9097524e 100644 --- a/linter/test/type_declarations.ts.strict.json +++ b/linter/test/type_declarations.ts.strict.json @@ -24,6 +24,11 @@ "column": 14, "problem": "InOperator" }, + { + "line": 36, + "column": 17, + "problem": "ClassAsObject" + }, { "line": 41, "column": 18, -- Gitee