diff --git a/.npmignore b/.npmignore index ba11de6daa..e19fb437cd 100644 --- a/.npmignore +++ b/.npmignore @@ -40,4 +40,5 @@ tmp app_define.json VersionSet.xml build_package/ -OAT.xml \ No newline at end of file +OAT.xml +bundle.json \ No newline at end of file diff --git a/build_package/ohos-typescript-4.2.3-r2.tgz b/build_package/ohos-typescript-4.2.3-r2.tgz index 1aed2b1413..93b9cde57b 100644 Binary files a/build_package/ohos-typescript-4.2.3-r2.tgz and b/build_package/ohos-typescript-4.2.3-r2.tgz differ diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5bb82c58a0..797f7d4cbe 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3341,8 +3341,8 @@ namespace ts { // It should in standard mode, or will return before. if (isSoFile && moduleNotFoundError !== undefined && - ((currentSourceFile && currentSourceFile.fileName.indexOf("/oh_modules/") !== -1) || - (resolvedModule && resolvedModule.resolvedFileName.indexOf("/oh_modules/") !== -1))) { + ((currentSourceFile && normalizePath(currentSourceFile.fileName).indexOf("/oh_modules/") !== -1) || + (resolvedModule && normalizePath(resolvedModule.resolvedFileName).indexOf("/oh_modules/") !== -1))) { const diagnostic = createDiagnosticForNode(errorNode, Diagnostics.Currently_module_for_0_is_not_verified_If_you_re_importing_napi_its_verification_will_be_enabled_in_later_SDK_version_Please_make_sure_the_corresponding_d_ts_file_is_provided_and_the_napis_are_correctly_declared, moduleReference); diagnostics.add(diagnostic); moduleNotFoundError = undefined; diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 593413e905..0361a49960 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1935,7 +1935,7 @@ namespace ts { const messageFlag = item.messageText !== (options.isCompatibleVersion ? Diagnostics.Importing_ArkTS_files_in_JS_and_TS_files_is_about_to_be_forbidden.message : Diagnostics.Importing_ArkTS_files_in_JS_and_TS_files_is_forbidden.message); - const isOhModule = item.file?.fileName.indexOf("/oh_modules/") !== -1; + const isOhModule = (item.file !== undefined) && (normalizePath(item.file.fileName).indexOf("/oh_modules/") !== -1); const isApplicationsStandard = (item.file !== undefined) && (item.file.fileName.indexOf("/applications/standard/") !== -1); return !((item.file?.scriptKind === ScriptKind.TS && item.file?.isDeclarationFile && messageFlag) || isOhModule || isApplicationsStandard); }); diff --git a/src/linter/CookBookMsg.ts b/src/linter/CookBookMsg.ts index 624f9740a9..2fc1e649b1 100644 --- a/src/linter/CookBookMsg.ts +++ b/src/linter/CookBookMsg.ts @@ -144,7 +144,7 @@ cookBookTag[121] = "\"require\" and \"import\" assignment are not supported (ark cookBookTag[122] = ""; cookBookTag[123] = ""; cookBookTag[124] = ""; -cookBookTag[125] = "Re-exporting is supported with restrictions (arkts-limited-reexport)"; +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[128] = "Ambient module declaration is not supported (arkts-no-ambient-decls)"; diff --git a/src/linter/LinterRunner.ts b/src/linter/LinterRunner.ts index 7d846a8654..7603fca6d5 100644 --- a/src/linter/LinterRunner.ts +++ b/src/linter/LinterRunner.ts @@ -21,7 +21,8 @@ function makeDiag(category: DiagnosticCategory, code: number, file: SourceFile, export function translateDiag(srcFile: SourceFile, problemInfo: ProblemInfo): Diagnostic { const LINTER_MSG_CODE_START = -1; - return makeDiag(DiagnosticCategory.Error, LINTER_MSG_CODE_START /*+ problemInfo.ruleTag */, srcFile , problemInfo.start, (problemInfo.end - problemInfo.start + 1), problemInfo.rule); + const severity = (problemInfo.severity === Utils.ProblemSeverity.ERROR ? DiagnosticCategory.Error : DiagnosticCategory.Warning); + return makeDiag(severity, LINTER_MSG_CODE_START /*+ problemInfo.ruleTag */, srcFile , problemInfo.start, (problemInfo.end - problemInfo.start + 1), problemInfo.rule); } export function runArkTSLinter(tsProgram: Program, host: CompilerHost, srcFile?: SourceFile): Diagnostic[] { diff --git a/src/linter/Problems.ts b/src/linter/Problems.ts index 742f613ba2..bf1ec00b92 100644 --- a/src/linter/Problems.ts +++ b/src/linter/Problems.ts @@ -30,7 +30,7 @@ export enum FaultID { NonDeclarationInNamespace, GeneratorFunction, FunctionContainsThis, PropertyAccessByIndex, JsxElement, EnumMemberNonConstInit, ImplementsClass, NoUndefinedPropAccess, MultipleStaticBlocks, ThisType, IntefaceExtendDifProps, StructuralIdentity, TypeOnlyImport, TypeOnlyExport, DefaultImport, - LimitedReExporting, ExportAssignment, ImportAssignment, PropertyRuntimeCheck, + ExportAssignment, ImportAssignment, PropertyRuntimeCheck, GenericCallNoTypeArgs, ParameterProperties, InstanceofUnsupported, ShorthandAmbientModuleDecl, WildcardsInModuleName, UMDModuleDefinition, NewTarget, DefiniteAssignment, IifeAsNamespace, Prototype, GlobalThis, @@ -85,7 +85,6 @@ faultsAttrs[FaultID.JsxElement] = { cookBookRef: "54", }; faultsAttrs[FaultID.UnaryArithmNotNumber] = { cookBookRef: "55", }; faultsAttrs[FaultID.DeleteOperator] = { cookBookRef: "59", }; faultsAttrs[FaultID.TypeQuery] = { cookBookRef: "60", }; -// remove as rule#61: FaultID.BitOpWithWrongType => { cookBookRef: "61", }; faultsAttrs[FaultID.InstanceofUnsupported] = { cookBookRef: "65", }; faultsAttrs[FaultID.InOperator] = { cookBookRef: "66", }; faultsAttrs[FaultID.DestructuringAssignment] = { migratable: true, cookBookRef: "69", }; @@ -119,7 +118,6 @@ 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.LimitedReExporting] = { cookBookRef: "125", }; faultsAttrs[FaultID.ExportAssignment] = { cookBookRef: "126", }; faultsAttrs[FaultID.TypeOnlyExport] = { migratable: true, cookBookRef: "127", }; faultsAttrs[FaultID.ShorthandAmbientModuleDecl] = { cookBookRef: "128", }; @@ -142,8 +140,8 @@ faultsAttrs[FaultID.ErrorSuppression] = { cookBookRef: "146", }; faultsAttrs[FaultID.UnsupportedDecorators] = { cookBookRef: "148", }; faultsAttrs[FaultID.ClassAsObject] = { cookBookRef: "149", }; faultsAttrs[FaultID.ImportAfterStatement] = { cookBookRef: "150", }; -faultsAttrs[FaultID.EsObjectType] = { cookBookRef: "8" }; -faultsAttrs[FaultID.EsObjectAssignment] = { cookBookRef: "8" }; -faultsAttrs[FaultID.EsObjectAccess] = { cookBookRef: "8" }; +faultsAttrs[FaultID.EsObjectType] = { warning: true, cookBookRef: "8" }; +faultsAttrs[FaultID.EsObjectAssignment] = { warning: true, cookBookRef: "8" }; +faultsAttrs[FaultID.EsObjectAccess] = { warning: true, cookBookRef: "8" }; } } \ No newline at end of file diff --git a/src/linter/TypeScriptLinter.ts b/src/linter/TypeScriptLinter.ts index ab8d53b91e..f3ef292cc3 100644 --- a/src/linter/TypeScriptLinter.ts +++ b/src/linter/TypeScriptLinter.ts @@ -174,11 +174,6 @@ export class TypeScriptLinter { public incrementCounters(node: Node | CommentRange, faultId: number, autofixable = false, autofix?: Autofix[]): void { if (!TypeScriptLinter.strictMode && faultsAttrs[faultId].migratable) { return; } // In relax mode skip migratable - // Relax EsObject - if (faultId === FaultID.EsObjectType || faultId === FaultID.EsObjectAssignment || faultId === FaultID.EsObjectAccess) { - return; - } - const startPos = Utils.getStartPos(node); const endPos = Utils.getEndPos(node); @@ -305,14 +300,13 @@ export class TypeScriptLinter { } private countDeclarationsWithDuplicateName( - symbol: Symbol | undefined, tsDeclNode: Node, tsDeclKind?: SyntaxKind + tsNode: Node, tsDeclNode: Node, tsDeclKind?: ts.SyntaxKind ): void { - // Sanity check. - if (!symbol) return; + const symbol = TypeScriptLinter.tsTypeChecker.getSymbolAtLocation(tsNode); // If specific declaration kind is provided, check against it. // Otherwise, use syntax kind of corresponding declaration node. - if (Utils.symbolHasDuplicateName(symbol, tsDeclKind ?? tsDeclNode.kind)) { + if (!!symbol && Utils.symbolHasDuplicateName(symbol, tsDeclKind ?? tsDeclNode.kind)) { this.incrementCounters(tsDeclNode, FaultID.DeclWithDuplicateName); } } @@ -414,9 +408,9 @@ export class TypeScriptLinter { } private isIIFEasNamespace(tsExpr: PropertyAccessExpression): boolean { - const nameSymbol = TypeScriptLinter.tsTypeChecker.getSymbolAtLocation(tsExpr.name); + const nameSymbol = Utils.trueSymbolAtLocation(tsExpr.name); if (!nameSymbol) { - const leftHandSymbol = TypeScriptLinter.tsTypeChecker.getSymbolAtLocation(tsExpr.expression); + const leftHandSymbol = Utils.trueSymbolAtLocation(tsExpr.expression); if (leftHandSymbol) { const decls = leftHandSymbol.getDeclarations(); if (!decls || decls.length !== 1) return false; @@ -441,12 +435,12 @@ export class TypeScriptLinter { return false; } // Check if property symbol is "Prototype" - const propAccessSym = TypeScriptLinter.tsTypeChecker.getSymbolAtLocation(tsPropertyAccess); + const propAccessSym = Utils.trueSymbolAtLocation(tsPropertyAccess); if (Utils.isPrototypeSymbol(propAccessSym)) return true; // Check if symbol of LHS-expression is Class or Function. const tsBaseExpr = tsPropertyAccess.expression; - const baseExprSym = TypeScriptLinter.tsTypeChecker.getSymbolAtLocation(tsBaseExpr); + const baseExprSym = Utils.trueSymbolAtLocation(tsBaseExpr); if (Utils.isTypeSymbol(baseExprSym) || Utils.isFunctionSymbol(baseExprSym)) { return true; } @@ -580,11 +574,9 @@ export class TypeScriptLinter { private handleEnumDeclaration(node: Node): void { const enumNode = node as EnumDeclaration; - this.countDeclarationsWithDuplicateName( - TypeScriptLinter.tsTypeChecker.getSymbolAtLocation(enumNode.name), enumNode - ); + this.countDeclarationsWithDuplicateName(enumNode.name, enumNode); - const enumSymbol = TypeScriptLinter.tsTypeChecker.getSymbolAtLocation(enumNode.name); + const enumSymbol = Utils.trueSymbolAtLocation(enumNode.name); if (!enumSymbol) return; const enumDecls = enumSymbol.getDeclarations(); @@ -604,7 +596,7 @@ export class TypeScriptLinter { private handleInterfaceDeclaration(node: Node): void { const interfaceNode = node as InterfaceDeclaration; - const iSymbol = TypeScriptLinter.tsTypeChecker.getSymbolAtLocation(interfaceNode.name); + const iSymbol = Utils.trueSymbolAtLocation(interfaceNode.name); const iDecls = iSymbol ? iSymbol.getDeclarations() : null; if (iDecls) { // Since type checker merges all declarations with the same name @@ -621,9 +613,7 @@ export class TypeScriptLinter { if (interfaceNode.heritageClauses) this.interfaceInharitanceLint(node, interfaceNode.heritageClauses); - this.countDeclarationsWithDuplicateName( - TypeScriptLinter.tsTypeChecker.getSymbolAtLocation(interfaceNode.name), interfaceNode - ); + this.countDeclarationsWithDuplicateName(interfaceNode.name, interfaceNode); } private handleThrowStatement(node: Node): void { @@ -706,7 +696,7 @@ export class TypeScriptLinter { if (this.isPrototypePropertyAccess(propertyAccessNode)) { this.incrementCounters(propertyAccessNode.name, FaultID.Prototype); } - const symbol = TypeScriptLinter.tsTypeChecker.getSymbolAtLocation(propertyAccessNode); + const symbol = Utils.trueSymbolAtLocation(propertyAccessNode); if(!!symbol && Utils.isSymbolAPI(symbol)) { this.incrementCounters(node, FaultID.SymbolType); } @@ -876,9 +866,7 @@ export class TypeScriptLinter { const tsFunctionDeclaration = node as FunctionDeclaration; if (!tsFunctionDeclaration.type) this.handleMissingReturnType(tsFunctionDeclaration); if (tsFunctionDeclaration.name) { - this.countDeclarationsWithDuplicateName( - TypeScriptLinter.tsTypeChecker.getSymbolAtLocation(tsFunctionDeclaration.name), tsFunctionDeclaration - ); + this.countDeclarationsWithDuplicateName(tsFunctionDeclaration.name, tsFunctionDeclaration); } if (tsFunctionDeclaration.body && this.functionContainsThis(tsFunctionDeclaration.body)) { this.incrementCounters(node, FaultID.FunctionContainsThis); @@ -994,8 +982,8 @@ export class TypeScriptLinter { this.incrementCounters(node, FaultID.DestructuringAssignment); } if (isPropertyAccessExpression(tsLhsExpr)) { - const tsLhsSymbol = TypeScriptLinter.tsTypeChecker.getSymbolAtLocation(tsLhsExpr); - const tsLhsBaseSymbol = TypeScriptLinter.tsTypeChecker.getSymbolAtLocation(tsLhsExpr.expression); + const tsLhsSymbol = Utils.trueSymbolAtLocation(tsLhsExpr); + const tsLhsBaseSymbol = Utils.trueSymbolAtLocation(tsLhsExpr.expression); if (tsLhsSymbol && (tsLhsSymbol.flags & SymbolFlags.Method)) { this.incrementCounters(tsLhsExpr, FaultID.NoUndefinedPropAccess); } @@ -1059,7 +1047,7 @@ export class TypeScriptLinter { } else if (tsBinaryExpr.operatorToken.kind === SyntaxKind.InstanceOfKeyword) { const leftExpr = Utils.unwrapParenthesized(tsBinaryExpr.left); - const leftSymbol = TypeScriptLinter.tsTypeChecker.getSymbolAtLocation(leftExpr); + const leftSymbol = Utils.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 === SyntaxKind.ThisKeyword) { @@ -1097,10 +1085,7 @@ export class TypeScriptLinter { const visitBindingPatternNames = (tsBindingName: BindingName): void => { if (isIdentifier(tsBindingName)) { // The syntax kind of the declaration is defined here by the parent of 'BindingName' node. - this.countDeclarationsWithDuplicateName( - TypeScriptLinter.tsTypeChecker.getSymbolAtLocation(tsBindingName), tsBindingName, - tsBindingName.parent.kind - ); + this.countDeclarationsWithDuplicateName(tsBindingName, tsBindingName, tsBindingName.parent.kind); } else { for (const tsBindingElem of tsBindingName.elements) { @@ -1171,24 +1156,16 @@ export class TypeScriptLinter { this.staticBlocks.clear(); if (tsClassDecl.name) { - this.countDeclarationsWithDuplicateName( - TypeScriptLinter.tsTypeChecker.getSymbolAtLocation(tsClassDecl.name), - tsClassDecl - ); + this.countDeclarationsWithDuplicateName(tsClassDecl.name, tsClassDecl); } this.countClassMembersWithDuplicateName(tsClassDecl); - const tsClassDeclType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(tsClassDecl); - const visitHClause = (hClause: HeritageClause) => { for (const tsTypeExpr of hClause.types) { const tsExprType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(tsTypeExpr.expression); if (tsExprType.isClass() && hClause.token === SyntaxKind.ImplementsKeyword) { this.incrementCounters(tsTypeExpr, FaultID.ImplementsClass); } - else if (Utils.typeIsRecursive(tsClassDeclType, TypeScriptLinter.tsTypeChecker.getTypeAtLocation(tsTypeExpr))) { - this.incrementCounters(tsTypeExpr, FaultID.ClassAsObject); - } } }; @@ -1206,10 +1183,7 @@ export class TypeScriptLinter { private handleModuleDeclaration(node: Node): void { const tsModuleDecl = node as ModuleDeclaration; - this.countDeclarationsWithDuplicateName( - TypeScriptLinter.tsTypeChecker.getSymbolAtLocation(tsModuleDecl.name), - tsModuleDecl - ); + this.countDeclarationsWithDuplicateName(tsModuleDecl.name, tsModuleDecl); const tsModuleBody = tsModuleDecl.body; const tsModifiers = tsModuleDecl.modifiers; // TSC 4.2 doesn't have 'getModifiers()' method @@ -1248,20 +1222,13 @@ export class TypeScriptLinter { private handleTypeAliasDeclaration(node: Node): void { const tsTypeAlias = node as TypeAliasDeclaration; - this.countDeclarationsWithDuplicateName( - TypeScriptLinter.tsTypeChecker.getSymbolAtLocation(tsTypeAlias.name), tsTypeAlias - ); - if (Utils.typeIsRecursive(TypeScriptLinter.tsTypeChecker.getTypeAtLocation(node))) { - this.incrementCounters(tsTypeAlias, FaultID.ClassAsObject); - } + this.countDeclarationsWithDuplicateName(tsTypeAlias.name, tsTypeAlias); } private handleImportClause(node: Node): void { const tsImportClause = node as ImportClause; if (tsImportClause.name) { - this.countDeclarationsWithDuplicateName( - TypeScriptLinter.tsTypeChecker.getSymbolAtLocation(tsImportClause.name), tsImportClause - ); + this.countDeclarationsWithDuplicateName(tsImportClause.name, tsImportClause); } if (tsImportClause.namedBindings && isNamedImports(tsImportClause.namedBindings)) { @@ -1289,16 +1256,12 @@ export class TypeScriptLinter { private handleImportSpecifier(node: Node): void { const tsImportSpecifier = node as ImportSpecifier; - this.countDeclarationsWithDuplicateName( - TypeScriptLinter.tsTypeChecker.getSymbolAtLocation(tsImportSpecifier.name), tsImportSpecifier - ); + this.countDeclarationsWithDuplicateName(tsImportSpecifier.name, tsImportSpecifier); } private handleNamespaceImport(node: Node): void { const tsNamespaceImport = node as NamespaceImport; - this.countDeclarationsWithDuplicateName( - TypeScriptLinter.tsTypeChecker.getSymbolAtLocation(tsNamespaceImport.name), tsNamespaceImport - ); + this.countDeclarationsWithDuplicateName(tsNamespaceImport.name, tsNamespaceImport); } private handleTypeAssertionExpression(node: Node): void { @@ -1341,11 +1304,10 @@ export class TypeScriptLinter { private handleIdentifier(node: Node): void { const tsIdentifier = node as Identifier; - const tsIdentSym = TypeScriptLinter.tsTypeChecker.getSymbolAtLocation(tsIdentifier); + const tsIdentSym = Utils.trueSymbolAtLocation(tsIdentifier); if (tsIdentSym) { - this.handleNamespaceAsObject(tsIdentifier, tsIdentSym); - this.handleClassAsObject(tsIdentifier, tsIdentSym); + this.handleRestrictedValues(tsIdentifier, tsIdentSym); if ( (tsIdentSym.flags & SymbolFlags.Module) !== 0 && @@ -1360,85 +1322,62 @@ export class TypeScriptLinter { } } - private handleNamespaceAsObject(tsIdentifier: Identifier, tsIdentSym: Symbol): void { - if ( - tsIdentSym && - (tsIdentSym.getFlags() & SymbolFlags.Module) !== 0 && - (tsIdentSym.getFlags() & SymbolFlags.Variable) === 0 && - !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 (!Utils.symbolHasDuplicateName(tsIdentSym, 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: Node = tsIdentifier; + private handleRestrictedValues(tsIdentifier: Identifier, tsIdentSym: Symbol) { + const illegalValues = SymbolFlags.Class | SymbolFlags.ConstEnum | SymbolFlags.RegularEnum | SymbolFlags.ValueModule; - while (isPropertyAccessExpression(tsIdentParent.parent) || isQualifiedName(tsIdentParent.parent)) { - tsIdentParent = tsIdentParent.parent; - } - const isNamespace: boolean = (tsIdentSym.getFlags() & SymbolFlags.Namespace) !== 0; - let isEmptyModuleBlock = false; - if (tsIdentSym.declarations && tsIdentSym.declarations.length > 0 && isModuleDeclaration(tsIdentSym.declarations[0])) { - const moduleDecl = tsIdentSym.declarations[0] as ModuleDeclaration; - if (moduleDecl.body && isModuleBlock(moduleDecl.body)) { - const moduleBlock = moduleDecl.body; - if (moduleBlock.statements && moduleBlock.statements.length === 0) { - isEmptyModuleBlock = true; - } - } - } - - if ( - (!isPropertyAccessExpression(tsIdentParent) && !isQualifiedName(tsIdentParent) && !(isNamespace && isEmptyModuleBlock)) || - (isPropertyAccessExpression(tsIdentParent) && tsIdentifier === tsIdentParent.name) || - (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 & SymbolFlags.ValueModule) != 0) { + if (!!tsIdentSym && Utils.symbolHasDuplicateName(tsIdentSym, SyntaxKind.ModuleDeclaration)) { + return; } } - } - - private handleClassAsObject(tsIdentifier: Identifier, tsIdentSym: Symbol) { - // Only process class references. - if ((tsIdentSym.getFlags() & SymbolFlags.Class) === 0) { - return; - } - // No check for ArkUI struct. if (Utils.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: Node = tsIdentifier; + if ((tsIdentSym.flags & illegalValues) == 0 || !this.identiferUseInValueContext(tsIdentifier)) { + return; + } + if (tsIdentSym.flags & SymbolFlags.ValueModule) { + this.incrementCounters(tsIdentifier, FaultID.NamespaceAsObject); + } + else { + // missing EnumAsObject + this.incrementCounters(tsIdentifier, FaultID.ClassAsObject); + } + } + private identiferUseInValueContext( + tsIdentifier: 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: Node = tsIdentifier; while (isPropertyAccessExpression(tsIdentStart.parent) || isQualifiedName(tsIdentStart.parent)) { tsIdentStart = tsIdentStart.parent; } - - // contexts where type is used as value, but it's intended - if (isTypeNode(tsIdentStart.parent) || - isExpressionWithTypeArguments(tsIdentStart.parent) || - isExportAssignment(tsIdentStart.parent) || - isExportSpecifier(tsIdentStart.parent) || - isMetaProperty(tsIdentStart.parent) || - isImportClause(tsIdentStart.parent) || - isClassLike(tsIdentStart.parent) || - isInterfaceDeclaration(tsIdentStart.parent) || - isModuleDeclaration(tsIdentStart.parent) || - isNamespaceImport(tsIdentStart.parent) || - isImportSpecifier(tsIdentStart.parent) || - (isQualifiedName(tsIdentStart) && tsIdentifier !== tsIdentStart.right) || - (isPropertyAccessExpression(tsIdentStart) && tsIdentifier !== tsIdentStart.name) || - (isNewExpression(tsIdentStart.parent) && tsIdentStart === tsIdentStart.parent.expression) || - (isBinaryExpression(tsIdentStart.parent) && tsIdentStart.parent.operatorToken.kind === SyntaxKind.InstanceOfKeyword)) { - return; - } - - this.incrementCounters(tsIdentifier, FaultID.ClassAsObject); + return !( + // treat TypeQuery as valid because it's already forbidden (FaultID.TypeQuery) + (isTypeNode(tsIdentStart.parent) && !isTypeOfExpression(tsIdentStart.parent)) || + isExpressionWithTypeArguments(tsIdentStart.parent) || + isExportAssignment(tsIdentStart.parent) || + isExportSpecifier(tsIdentStart.parent) || + isMetaProperty(tsIdentStart.parent) || + isImportClause(tsIdentStart.parent) || + isClassLike(tsIdentStart.parent) || + isInterfaceDeclaration(tsIdentStart.parent) || + isModuleDeclaration(tsIdentStart.parent) || + isEnumDeclaration(tsIdentStart.parent) || + isNamespaceImport(tsIdentStart.parent) || + isImportSpecifier(tsIdentStart.parent) || + // rightmost in AST is rightmost in qualified name chain + (isQualifiedName(tsIdentStart) && tsIdentifier !== tsIdentStart.right) || + (isPropertyAccessExpression(tsIdentStart) && tsIdentifier !== tsIdentStart.name) || + (isNewExpression(tsIdentStart.parent) && tsIdentStart === tsIdentStart.parent.expression) || + (isBinaryExpression(tsIdentStart.parent) && tsIdentStart.parent.operatorToken.kind === SyntaxKind.InstanceOfKeyword) + ); } private handleElementAccessExpression(node: Node): void { @@ -1504,11 +1443,6 @@ export class TypeScriptLinter { // autofix = [ Autofixer.dropTypeOnlyFlag(tsExportDecl) ]; this.incrementCounters(node, FaultID.TypeOnlyExport, true, autofix); } - - const exportClause = tsExportDecl.exportClause; - if(exportClause && isNamespaceExport(exportClause)) { - this.incrementCounters(node, FaultID.LimitedReExporting); - } } private handleExportAssignment(node: Node): void { @@ -1574,8 +1508,12 @@ export class TypeScriptLinter { if (!callSignature) return; const tsSyntaxKind = isNewExpression(callLikeExpr) ? SyntaxKind.Constructor : SyntaxKind.FunctionDeclaration; - const signDecl = TypeScriptLinter.tsTypeChecker.signatureToSignatureDeclaration(callSignature, tsSyntaxKind, - undefined, NodeBuilderFlags.WriteTypeArgumentsOfSignature | NodeBuilderFlags.IgnoreErrors); + const sym = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(callLikeExpr.expression).symbol; + const signDecl = TypeScriptLinter.tsTypeChecker.signatureToSignatureDeclaration( + callSignature, + tsSyntaxKind, + (!!sym && !!sym.declarations) ? sym.declarations[0] : undefined, + NodeBuilderFlags.WriteTypeArgumentsOfSignature | NodeBuilderFlags.IgnoreErrors); if (signDecl?.typeArguments) { const resolvedTypeArgs = signDecl.typeArguments; @@ -1602,7 +1540,7 @@ export class TypeScriptLinter { `${callableFunction}.bind`, ]; - const exprSymbol = TypeScriptLinter.tsTypeChecker.getSymbolAtLocation(tsCallExpr.expression); + const exprSymbol = Utils.trueSymbolAtLocation(tsCallExpr.expression); if (exprSymbol === undefined) { return; } @@ -1645,7 +1583,7 @@ export class TypeScriptLinter { const callSignature = TypeScriptLinter.tsTypeChecker.getResolvedSignature(callExpr); if (!callSignature) return; - const sym = TypeScriptLinter.tsTypeChecker.getSymbolAtLocation(callExpr.expression); + const sym = Utils.trueSymbolAtLocation(callExpr.expression); if (sym) { const name = sym.getName(); if ( @@ -1819,7 +1757,7 @@ export class TypeScriptLinter { private handleExpressionWithTypeArguments(node: Node) { const tsTypeExpr = node as ExpressionWithTypeArguments; - const symbol = TypeScriptLinter.tsTypeChecker.getSymbolAtLocation(tsTypeExpr.expression); + const symbol = Utils.trueSymbolAtLocation(tsTypeExpr.expression); if (!!symbol && Utils.isEsObjectSymbol(symbol)) { this.incrementCounters(tsTypeExpr, FaultID.EsObjectType); } @@ -1888,6 +1826,8 @@ export class TypeScriptLinter { } } + private validatedTypesSet = new Set(); + private validateDeclInferredType( type: Type, decl: VariableDeclaration | PropertyDeclaration | ParameterDeclaration @@ -1895,12 +1835,6 @@ export class TypeScriptLinter { if (type.aliasSymbol !== undefined) { return; } - if (type.isUnion()) { - for (const unionElem of type.types) { - this.validateDeclInferredType(unionElem, decl); - } - } - if (type.flags & TypeFlags.Object && (type as ObjectType).objectFlags & ObjectFlags.Reference) { const typeArgs = TypeScriptLinter.tsTypeChecker.getTypeArguments(type as TypeReference); if (typeArgs) { @@ -1908,6 +1842,16 @@ export class TypeScriptLinter { this.validateDeclInferredType(typeArg, decl); } } + return; + } + if (this.validatedTypesSet.has(type)) { + return; + } + if (type.isUnion()) { + this.validatedTypesSet.add(type); + for (let unionElem of type.types) { + this.validateDeclInferredType(unionElem, decl); + } } if (Utils.isAnyType(type)) { diff --git a/src/linter/TypeScriptLinterConfig.ts b/src/linter/TypeScriptLinterConfig.ts index fb90f800be..efa16f2ae5 100644 --- a/src/linter/TypeScriptLinterConfig.ts +++ b/src/linter/TypeScriptLinterConfig.ts @@ -100,7 +100,6 @@ export class LinterConfig { LinterConfig.nodeDesc[FaultID.TypeOnlyImport] = "Type-only imports"; LinterConfig.nodeDesc[FaultID.TypeOnlyExport] = "Type-only exports"; LinterConfig.nodeDesc[FaultID.DefaultImport] = "Default import declarations"; - LinterConfig.nodeDesc[FaultID.LimitedReExporting] = "Limited re-exporting declarations"; LinterConfig.nodeDesc[FaultID.ExportAssignment] = "Export assignments (export = ..)"; LinterConfig.nodeDesc[FaultID.ImportAssignment] = "Import assignments (import = ..)"; LinterConfig.nodeDesc[FaultID.PropertyRuntimeCheck] = "Property-based runtime checks"; diff --git a/src/linter/Utils.ts b/src/linter/Utils.ts index 1357cc129a..082fd360eb 100644 --- a/src/linter/Utils.ts +++ b/src/linter/Utils.ts @@ -209,6 +209,18 @@ export function unwrapParenthesized(tsExpr: Expression): Expression { return unwrappedExpr; } +export function followIfAliased(sym: Symbol): Symbol { + if ((sym.getFlags() & SymbolFlags.Alias) !== 0) { + return typeChecker.getAliasedSymbol(sym); + } + return sym; +} + +export function trueSymbolAtLocation(node: Node): Symbol | undefined { + const sym = typeChecker.getSymbolAtLocation(node); + return sym === undefined ? undefined : followIfAliased(sym); +} + export function symbolHasDuplicateName(symbol: Symbol, tsDeclKind: 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 @@ -955,13 +967,13 @@ export const LIMITED_STD_GLOBAL_VAR = ["Infinity", "NaN"]; export const LIMITED_STD_OBJECT_API = [ "__proto__", "__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__", "assign", "create", "defineProperties", "defineProperty", "entries", "freeze", "fromEntries", "getOwnPropertyDescriptor", - "getOwnPropertyDescriptors", "getOwnPropertyNames", "getOwnPropertySymbols", "getPrototypeOf", "hasOwn", - "hasOwnProperty", "is", "isExtensible", "isFrozen", "isPrototypeOf", "isSealed", "keys", "preventExtensions", - "propertyIsEnumerable", "seal", "setPrototypeOf", "values" + "getOwnPropertyDescriptors", "getOwnPropertySymbols", "getPrototypeOf", "hasOwnProperty", "is", + "isExtensible", "isFrozen", "isPrototypeOf", "isSealed", "preventExtensions", "propertyIsEnumerable", + "seal", "setPrototypeOf" ]; export const LIMITED_STD_REFLECT_API = [ - "apply", "construct", "defineProperty", "deleteProperty", "get", "getOwnPropertyDescriptor", "getPrototypeOf", - "has", "isExtensible", "ownKeys", "preventExtensions", "set", "setPrototypeOf" + "apply", "construct", "defineProperty", "deleteProperty", "getOwnPropertyDescriptor", "getPrototypeOf", + "isExtensible", "preventExtensions", "setPrototypeOf" ]; export const LIMITED_STD_PROXYHANDLER_API = [ "apply", "construct", "defineProperty", "deleteProperty", "get", "getOwnPropertyDescriptor", "getPrototypeOf", @@ -1203,22 +1215,19 @@ export function isDynamicType(type: Type | undefined): boolean | undefined { // 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 + // Check the non-nullable version of type to eliminate 'undefined' type // from the union type elements. type = type.getNonNullableType(); if (type.isUnion()) { for (const compType of type.types) { - if (isLibraryType(compType)) { - return true; - } - - if (!isStdLibraryType(compType) && !isIntrinsicObjectType(compType) && !isAnyType(compType)) { - return false; + const isDynamic = isDynamicType(compType); + if (isDynamic || isDynamic === undefined) { + return isDynamic; } } - return undefined; + return false; } if (isLibraryType(type)) { @@ -1259,13 +1268,18 @@ export function isDynamicLiteralInitializer(expr: Expression): boolean { // foo({ ... }) if (isCallExpression(curNode)) { const callExpr = curNode; - let sym: Symbol | undefined = typeChecker.getTypeAtLocation(callExpr.expression).symbol; + const type = typeChecker.getTypeAtLocation(callExpr.expression); + + // this check is a hack to fix #13474, only for tac 4.2 + if (isAnyType(type)) return true; + + let sym: Symbol | undefined = type.symbol; if(isLibrarySymbol(sym)) { return true; } // #13483: - // x.foo({ ... }), where 'x' is exported from some library: + // x.foo({ ... }), where 'x' is a variable exported from some library: if (isPropertyAccessExpression(callExpr.expression)) { sym = typeChecker.getSymbolAtLocation(callExpr.expression.expression); if (sym && sym.getFlags() & SymbolFlags.Alias) { diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 22924d806e..5ecb2174f9 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -11388,37 +11388,36 @@ declare namespace ts { TypeOnlyImport = 63, TypeOnlyExport = 64, DefaultImport = 65, - LimitedReExporting = 66, - ExportAssignment = 67, - ImportAssignment = 68, - PropertyRuntimeCheck = 69, - GenericCallNoTypeArgs = 70, - ParameterProperties = 71, - InstanceofUnsupported = 72, - ShorthandAmbientModuleDecl = 73, - WildcardsInModuleName = 74, - UMDModuleDefinition = 75, - NewTarget = 76, - DefiniteAssignment = 77, - IifeAsNamespace = 78, - Prototype = 79, - GlobalThis = 80, - UtilityType = 81, - PropertyDeclOnFunction = 82, - FunctionApplyBindCall = 83, - ReadonlyArr = 84, - ConstAssertion = 85, - ImportAssertion = 86, - SpreadOperator = 87, - LimitedStdLibApi = 88, - ErrorSuppression = 89, - StrictDiagnostic = 90, - UnsupportedDecorators = 91, - ImportAfterStatement = 92, - EsObjectType = 93, - EsObjectAssignment = 94, - EsObjectAccess = 95, - LAST_ID = 96 + ExportAssignment = 66, + ImportAssignment = 67, + PropertyRuntimeCheck = 68, + GenericCallNoTypeArgs = 69, + ParameterProperties = 70, + InstanceofUnsupported = 71, + ShorthandAmbientModuleDecl = 72, + WildcardsInModuleName = 73, + UMDModuleDefinition = 74, + NewTarget = 75, + DefiniteAssignment = 76, + IifeAsNamespace = 77, + Prototype = 78, + GlobalThis = 79, + UtilityType = 80, + PropertyDeclOnFunction = 81, + FunctionApplyBindCall = 82, + ReadonlyArr = 83, + ConstAssertion = 84, + ImportAssertion = 85, + SpreadOperator = 86, + LimitedStdLibApi = 87, + ErrorSuppression = 88, + StrictDiagnostic = 89, + UnsupportedDecorators = 90, + ImportAfterStatement = 91, + EsObjectType = 92, + EsObjectAssignment = 93, + EsObjectAccess = 94, + LAST_ID = 95 } class FaultAttributs { migratable?: boolean; @@ -11460,6 +11459,8 @@ declare namespace ts { function isNumberLikeType(tsType: Type): boolean; function hasModifier(tsModifiers: readonly Modifier[] | undefined, tsModifierKind: number): boolean; function unwrapParenthesized(tsExpr: Expression): Expression; + function followIfAliased(sym: Symbol): Symbol; + function trueSymbolAtLocation(node: Node): Symbol | undefined; function symbolHasDuplicateName(symbol: Symbol, tsDeclKind: SyntaxKind): boolean; function isReferenceType(tsType: Type): boolean; function isPrimitiveType(type: Type): boolean; @@ -11664,8 +11665,8 @@ declare namespace ts { private handleTypeAssertionExpression; private handleMethodDeclaration; private handleIdentifier; - private handleNamespaceAsObject; - private handleClassAsObject; + private handleRestrictedValues; + private identiferUseInValueContext; private handleElementAccessExpression; private handleEnumMember; private handleExportDeclaration; @@ -11693,6 +11694,7 @@ declare namespace ts { private handleSetAccessor; private handleDeclarationInferredType; private handleDefiniteAssignmentAssertion; + private validatedTypesSet; private validateDeclInferredType; lint(): void; } diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 3130d0ba29..6aa13424aa 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -7605,37 +7605,36 @@ declare namespace ts { TypeOnlyImport = 63, TypeOnlyExport = 64, DefaultImport = 65, - LimitedReExporting = 66, - ExportAssignment = 67, - ImportAssignment = 68, - PropertyRuntimeCheck = 69, - GenericCallNoTypeArgs = 70, - ParameterProperties = 71, - InstanceofUnsupported = 72, - ShorthandAmbientModuleDecl = 73, - WildcardsInModuleName = 74, - UMDModuleDefinition = 75, - NewTarget = 76, - DefiniteAssignment = 77, - IifeAsNamespace = 78, - Prototype = 79, - GlobalThis = 80, - UtilityType = 81, - PropertyDeclOnFunction = 82, - FunctionApplyBindCall = 83, - ReadonlyArr = 84, - ConstAssertion = 85, - ImportAssertion = 86, - SpreadOperator = 87, - LimitedStdLibApi = 88, - ErrorSuppression = 89, - StrictDiagnostic = 90, - UnsupportedDecorators = 91, - ImportAfterStatement = 92, - EsObjectType = 93, - EsObjectAssignment = 94, - EsObjectAccess = 95, - LAST_ID = 96 + ExportAssignment = 66, + ImportAssignment = 67, + PropertyRuntimeCheck = 68, + GenericCallNoTypeArgs = 69, + ParameterProperties = 70, + InstanceofUnsupported = 71, + ShorthandAmbientModuleDecl = 72, + WildcardsInModuleName = 73, + UMDModuleDefinition = 74, + NewTarget = 75, + DefiniteAssignment = 76, + IifeAsNamespace = 77, + Prototype = 78, + GlobalThis = 79, + UtilityType = 80, + PropertyDeclOnFunction = 81, + FunctionApplyBindCall = 82, + ReadonlyArr = 83, + ConstAssertion = 84, + ImportAssertion = 85, + SpreadOperator = 86, + LimitedStdLibApi = 87, + ErrorSuppression = 88, + StrictDiagnostic = 89, + UnsupportedDecorators = 90, + ImportAfterStatement = 91, + EsObjectType = 92, + EsObjectAssignment = 93, + EsObjectAccess = 94, + LAST_ID = 95 } class FaultAttributs { migratable?: boolean; @@ -7677,6 +7676,8 @@ declare namespace ts { function isNumberLikeType(tsType: Type): boolean; function hasModifier(tsModifiers: readonly Modifier[] | undefined, tsModifierKind: number): boolean; function unwrapParenthesized(tsExpr: Expression): Expression; + function followIfAliased(sym: Symbol): Symbol; + function trueSymbolAtLocation(node: Node): Symbol | undefined; function symbolHasDuplicateName(symbol: Symbol, tsDeclKind: SyntaxKind): boolean; function isReferenceType(tsType: Type): boolean; function isPrimitiveType(type: Type): boolean; @@ -7881,8 +7882,8 @@ declare namespace ts { private handleTypeAssertionExpression; private handleMethodDeclaration; private handleIdentifier; - private handleNamespaceAsObject; - private handleClassAsObject; + private handleRestrictedValues; + private identiferUseInValueContext; private handleElementAccessExpression; private handleEnumMember; private handleExportDeclaration; @@ -7910,6 +7911,7 @@ declare namespace ts { private handleSetAccessor; private handleDeclarationInferredType; private handleDefiniteAssignmentAssertion; + private validatedTypesSet; private validateDeclInferredType; lint(): void; }