1/* 2 * Copyright (c) 2022-2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15import * as ts from "../_namespaces/ts"; 16import { 17 ArrayLiteralExpression, ArrowFunction, AsExpression, BinaryExpression, BindingName, CallExpression, CatchClause, 18 ClassDeclaration, ClassStaticBlockDeclaration, CommentRange, ComputedPropertyName, ConciseBody, Declaration, 19 Decorator, Diagnostic, DiagnosticMessageChain, ElementAccessExpression, EnumDeclaration, EnumMember, 20 ExportAssignment, ExpressionWithTypeArguments, forEachChild, ForInStatement, ForOfStatement, ForStatement, 21 FunctionDeclaration, FunctionExpression, FunctionLikeDeclaration, GetAccessorDeclaration, getCombinedNodeFlags, 22 getDecorators, getLeadingCommentRanges, getModifiers, getTrailingCommentRanges, HeritageClause, Identifier, 23 ImportClause, ImportDeclaration, ImportSpecifier, InterfaceDeclaration, isAccessor, isArrayBindingPattern, 24 isArrayLiteralExpression, isArrayTypeNode, isArrowFunction, isBinaryExpression, isBlock, isCallExpression, 25 isCallLikeExpression, isCatchClause, isClassDeclaration, isClassExpression, isClassLike, 26 isClassStaticBlockDeclaration, isConstructorDeclaration, isElementAccessExpression, isEnumDeclaration, 27 isExportAssignment, isExportSpecifier, isExpressionWithTypeArguments, isFunctionDeclaration, isFunctionExpression, 28 isFunctionTypeNode, isIdentifier, isImportClause, isImportDeclaration, isImportEqualsDeclaration, isImportSpecifier, 29 isInterfaceDeclaration, isMetaProperty, isMethodDeclaration, isModuleBlock, isModuleDeclaration, isNamedImports, 30 isNamespaceImport, isNewExpression, isObjectBindingPattern, isObjectLiteralExpression, isOmittedExpression, 31 isParameter, isPrivateIdentifier, isPropertyAccessExpression, isPropertyAssignment, isPropertyDeclaration, 32 isQualifiedName, isReturnStatement, isShorthandPropertyAssignment, isSourceFile, isSpreadElement, isStringLiteral, 33 isStructDeclaration, isTypeNode, isTypeOfExpression, isVariableDeclaration, Map, MetaProperty, MethodDeclaration, 34 MethodSignature, ModuleDeclaration, NamespaceImport, NewExpression, Node, NodeArray, NodeBuilderFlags, NodeFlags, 35 normalizePath, NumericLiteral, ObjectFlags, ObjectLiteralExpression, ObjectType, ParameterDeclaration, 36 PrefixUnaryExpression, Program, PropertyAccessExpression, PropertyAssignment, PropertyDeclaration, 37 PropertySignature, Set, SetAccessorDeclaration, Signature, SourceFile, Symbol, SymbolFlags, SyntaxKind, 38 ThrowStatement, Type, TypeAliasDeclaration, TypeAssertion, TypeChecker, TypeFlags, TypeNode, TypeReference, 39 TypeReferenceNode, VariableDeclaration, perfLogger as Logger 40} from "../_namespaces/ts"; 41 42//import Utils = Utils; 43import { 44 ARGUMENT_OF_TYPE_0_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_ERROR_CODE, 45 TYPE_0_IS_NOT_ASSIGNABLE_TO_TYPE_1_ERROR_CODE, 46 NO_OVERLOAD_MATCHES_THIS_CALL_ERROR_CODE, 47 LibraryTypeCallDiagnosticChecker, autofixInfo, cookBookTag, cookBookMsg, DiagnosticChecker, FaultID, faultsAttrs, LinterConfig, fixLiteralAsPropertyName, 48 shouldAutofix, fixFunctionExpression, fixReturnType, fixPropertyAccessByIndex, Autofix, getStartPos, getEndPos, ProblemSeverity, 49 symbolHasDuplicateName, trueSymbolAtLocation, isLibrarySymbol, isAnyType, isPrototypeSymbol, isTypeSymbol, isFunctionSymbol, isDestructuringAssignmentLHS, 50 isStructObjectInitializer, isDynamicLiteralInitializer, isExpressionAssignableToType, hasModifier, isDerivedFrom, CheckType, isSymbolAPI, 51 ALLOWED_STD_SYMBOL_API, symbolHasEsObjectType, isStdRecordType, NON_INITIALIZABLE_PROPERTY_DECORATORS, PROPERTY_HAS_NO_INITIALIZER_ERROR_CODE, 52 NON_INITIALIZABLE_PROPERTY_ClASS_DECORATORS, hasPredecessor, isLibraryType, isUnsupportedType, isCallToFunctionWithOmittedReturnType, unwrapParenthesized, 53 isIntegerConstantValue, isAssignmentOperator, isMethodAssignment, isEnumMemberType, isNumberType, isStringLikeType, isPrimitiveType, 54 needToDeduceStructuralIdentity, getVariableDeclarationTypeNode, isEsObjectType, isInsideBlock, isValueAssignableToESObject, isDefaultImport, 55 NON_RETURN_FUNCTION_DECORATORS, FUNCTION_HAS_NO_RETURN_ERROR_CODE, hasLibraryType, isStruct, isGenericArrayType, isThisOrSuperExpr, isTypedArray, 56 isObjectLiteralType, hasEsObjectType, isValidEnumMemberInit, isInterfaceType, LIMITED_STD_GLOBAL_FUNC, LIMITED_STD_OBJECT_API, LIMITED_STD_REFLECT_API, 57 LIMITED_STD_PROXYHANDLER_API, getParentSymbolName, isBooleanType, isEsObjectPossiblyAllowed, entityNameToString, LIMITED_STANDARD_UTILITY_TYPES, 58 isEsObjectSymbol, isSymbolIterator, isUnknownType, ARKUI_DECORATORS 59} from "../_namespaces/ts.ArkTSLinter_1_0"; 60import * as ArkTSLinter_1_0 from "../_namespaces/ts.ArkTSLinter_1_0"; 61//import cookBookMsg = cookBookMsg; 62//import cookBookTag = ts.cookBookTag; 63 64//import LinterConfig = ts.LinterConfig; 65//import Autofixer = ts.Autofixer; 66 67//const logger = Logger.getLogger(); 68 69export interface ProblemInfo { 70 line: number; 71 column: number; 72 start: number; 73 end: number; 74 type: string; 75 severity: number; 76 problem: string; 77 suggest: string; 78 rule: string; 79 ruleTag: number; 80 autofixable: boolean; 81 autofix?: Autofix[]; 82} 83 84export class TypeScriptLinter { 85 static ideMode: boolean; 86 static strictMode: boolean; 87 static logTscErrors: boolean; 88 static warningsAsErrors: boolean; 89 static lintEtsOnly: boolean; 90 static totalVisitedNodes: number; 91 static nodeCounters: number[]; 92 static lineCounters: number[]; 93 94 static totalErrorLines: number; 95 static errorLineNumbersString: string; 96 static totalWarningLines: number; 97 static warningLineNumbersString: string; 98 static reportDiagnostics = true; 99 100 // The SyntaxKind enum defines additional elements at the end of the enum 101 // that serve as markers (FirstX/LastX). Those elements are initialized 102 // with indices of the previously defined elements. As result, the enum 103 // may return incorrect name for a certain kind index (e.g. 'FirstStatement' 104 // instead of 'VariableStatement'). 105 // The following code creates a map with correct syntax kind names. 106 // It can be used when need to print name of syntax kind of certain 107 // AST node in diagnostic messages. 108 //private static tsSyntaxKindNames: string[]; 109 110 static problemsInfos: ProblemInfo[] = []; 111 112 static filteredDiagnosticMessages: DiagnosticMessageChain[] = []; 113 114 public static initGlobals(): void { 115 TypeScriptLinter.filteredDiagnosticMessages = [] 116 } 117 118 public static initStatic(): void { 119 TypeScriptLinter.strictMode = true; 120 TypeScriptLinter.logTscErrors = false; 121 TypeScriptLinter.warningsAsErrors = false; 122 TypeScriptLinter.lintEtsOnly = true; 123 TypeScriptLinter.totalVisitedNodes = 0; 124 TypeScriptLinter.nodeCounters = []; 125 TypeScriptLinter.lineCounters = []; 126 127 TypeScriptLinter.totalErrorLines = 0; 128 TypeScriptLinter.totalWarningLines = 0; 129 TypeScriptLinter.errorLineNumbersString = ""; 130 TypeScriptLinter.warningLineNumbersString = ""; 131 132 autofixInfo.length = 0; 133 134 //TypeScriptLinter.tsSyntaxKindNames = []; 135 //const keys = Object.keys(ts.SyntaxKind); 136 //const keys: string[] = []; 137 //const values = Object.values(ts.SyntaxKind); 138 //const values: string[] = []; 139 140 /* 141 for (let i = 0; i < values.length; i++) { 142 const val = values[i]; 143 const kindNum = typeof val === "string" ? parseInt(val) : val; 144 if (kindNum && !TypeScriptLinter.tsSyntaxKindNames[kindNum]) 145 TypeScriptLinter.tsSyntaxKindNames[kindNum] = keys[i]; 146 } 147 */ 148 149 for (let i = 0; i < FaultID.LAST_ID; i++) { 150 TypeScriptLinter.nodeCounters[i] = 0; 151 TypeScriptLinter.lineCounters[i] = 0; 152 } 153 154 TypeScriptLinter.problemsInfos = []; 155 } 156 157 public static tsTypeChecker: TypeChecker; 158 159 currentErrorLine: number; 160 currentWarningLine: number; 161 staticBlocks: Set<string>; 162 libraryTypeCallDiagnosticChecker: LibraryTypeCallDiagnosticChecker; 163 skipArkTSStaticBlocksCheck: boolean; 164 165 constructor(private sourceFile: SourceFile, 166 /* private */ tsProgram: Program, 167 private tscStrictDiagnostics?: Map<Diagnostic[]>) { 168 TypeScriptLinter.tsTypeChecker = tsProgram.getLinterTypeChecker(); 169 this.currentErrorLine = 0; 170 this.currentWarningLine = 0; 171 this.staticBlocks = new Set<string>(); 172 this.libraryTypeCallDiagnosticChecker = new LibraryTypeCallDiagnosticChecker(TypeScriptLinter.filteredDiagnosticMessages); 173 174 const options = tsProgram.getCompilerOptions(); 175 this.skipArkTSStaticBlocksCheck = false; 176 if (options.skipArkTSStaticBlocksCheck) { 177 this.skipArkTSStaticBlocksCheck = options.skipArkTSStaticBlocksCheck as boolean; 178 } 179 } 180 181 public static clearTsTypeChecker(): void { 182 TypeScriptLinter.tsTypeChecker = {} as TypeChecker; 183 } 184 185 public static clearQualifiedNameCache(): void { 186 if (TypeScriptLinter.tsTypeChecker) { 187 TypeScriptLinter.tsTypeChecker.clearQualifiedNameCache && TypeScriptLinter.tsTypeChecker.clearQualifiedNameCache(); 188 } 189 } 190 191 readonly handlersMap: ts.ESMap<SyntaxKind, (node: Node) => void> = new Map([ 192 [SyntaxKind.ObjectLiteralExpression, this.handleObjectLiteralExpression], 193 [SyntaxKind.ArrayLiteralExpression, this.handleArrayLiteralExpression], 194 [SyntaxKind.Parameter, this.handleParameter], 195 [SyntaxKind.EnumDeclaration, this.handleEnumDeclaration], 196 [SyntaxKind.InterfaceDeclaration, this.handleInterfaceDeclaration], 197 [SyntaxKind.ThrowStatement, this.handleThrowStatement], [SyntaxKind.ImportClause, this.handleImportClause], 198 [SyntaxKind.ForStatement, this.handleForStatement], 199 [SyntaxKind.ForInStatement, this.handleForInStatement], 200 [SyntaxKind.ForOfStatement, this.handleForOfStatement], 201 [SyntaxKind.ImportDeclaration, this.handleImportDeclaration], 202 [SyntaxKind.PropertyAccessExpression, this.handlePropertyAccessExpression], 203 [SyntaxKind.PropertyDeclaration, this.handlePropertyAssignmentOrDeclaration], 204 [SyntaxKind.PropertyAssignment, this.handlePropertyAssignmentOrDeclaration], 205 [SyntaxKind.FunctionExpression, this.handleFunctionExpression], 206 [SyntaxKind.ArrowFunction, this.handleArrowFunction], 207 [SyntaxKind.ClassExpression, this.handleClassExpression], [SyntaxKind.CatchClause, this.handleCatchClause], 208 [SyntaxKind.FunctionDeclaration, this.handleFunctionDeclaration], 209 [SyntaxKind.PrefixUnaryExpression, this.handlePrefixUnaryExpression], 210 [SyntaxKind.BinaryExpression, this.handleBinaryExpression], 211 [SyntaxKind.VariableDeclarationList, this.handleVariableDeclarationList], 212 [SyntaxKind.VariableDeclaration, this.handleVariableDeclaration], 213 [SyntaxKind.ClassDeclaration, this.handleClassDeclaration], 214 [SyntaxKind.ModuleDeclaration, this.handleModuleDeclaration], 215 [SyntaxKind.TypeAliasDeclaration, this.handleTypeAliasDeclaration], 216 [SyntaxKind.ImportSpecifier, this.handleImportSpecifier], 217 [SyntaxKind.NamespaceImport, this.handleNamespaceImport], 218 [SyntaxKind.TypeAssertionExpression, this.handleTypeAssertionExpression], 219 [SyntaxKind.MethodDeclaration, this.handleMethodDeclaration], 220 [SyntaxKind.Identifier, this.handleIdentifier], 221 [SyntaxKind.ElementAccessExpression, this.handleElementAccessExpression], 222 [SyntaxKind.EnumMember, this.handleEnumMember], [SyntaxKind.TypeReference, this.handleTypeReference], 223 [SyntaxKind.ExportAssignment, this.handleExportAssignment], 224 [SyntaxKind.CallExpression, this.handleCallExpression], [SyntaxKind.MetaProperty, this.handleMetaProperty], 225 [SyntaxKind.NewExpression, this.handleNewExpression], [SyntaxKind.AsExpression, this.handleAsExpression], 226 [SyntaxKind.SpreadElement, this.handleSpreadOp], [SyntaxKind.SpreadAssignment, this.handleSpreadOp], 227 [SyntaxKind.GetAccessor, this.handleGetAccessor], [SyntaxKind.SetAccessor, this.handleSetAccessor], 228 [SyntaxKind.ConstructSignature, this.handleConstructSignature], 229 [SyntaxKind.ExpressionWithTypeArguments, this.handleExpressionWithTypeArguments], 230 [SyntaxKind.ComputedPropertyName, this.handleComputedPropertyName], 231 [SyntaxKind.ClassStaticBlockDeclaration, this.handleClassStaticBlockDeclaration], 232 ]); 233 234 public incrementCounters(node: Node | CommentRange, faultId: number, autofixable = false, autofix?: Autofix[]): void { 235 if (!TypeScriptLinter.strictMode && faultsAttrs[faultId].migratable) { return; } // In relax mode skip migratable 236 237 const startPos = getStartPos(node); 238 const endPos = getEndPos(node); 239 240 TypeScriptLinter.nodeCounters[faultId]++; 241 // TSC counts lines and columns from zero 242 let { line, character } = this.sourceFile.getLineAndCharacterOfPosition(startPos); 243 ++line; 244 ++character; 245 246 const faultDescr = LinterConfig.nodeDesc[faultId]; 247 const faultType = "unknown"; //TypeScriptLinter.tsSyntaxKindNames[node.kind]; 248 249 const cookBookMsgNum = faultsAttrs[faultId] ? Number(faultsAttrs[faultId].cookBookRef) : 0; 250 const cookBookTg = cookBookTag[cookBookMsgNum]; 251 let severity = ProblemSeverity.ERROR; 252 if (faultsAttrs[faultId] && faultsAttrs[faultId].warning) { 253 severity = ProblemSeverity.WARNING; 254 } 255 const badNodeInfo: ProblemInfo = { 256 line: line, 257 column: character, 258 start: startPos, 259 end: endPos, 260 type: faultType, 261 severity: severity, 262 problem: FaultID[faultId], 263 suggest: cookBookMsgNum > 0 ? cookBookMsg[cookBookMsgNum] : "", 264 rule: cookBookMsgNum > 0 && cookBookTg !== "" ? cookBookTg : faultDescr ? faultDescr : faultType, 265 ruleTag: cookBookMsgNum, 266 autofixable: autofixable, 267 autofix: autofix 268 }; 269 270 TypeScriptLinter.problemsInfos.push(badNodeInfo); 271 272 if (!TypeScriptLinter.reportDiagnostics) { 273 //logger.info( 274 Logger.logEvent( 275 `Warning: ${this.sourceFile.fileName} (${line}, ${character}): ${faultDescr ? faultDescr : faultType}` 276 ); 277 } 278 279 TypeScriptLinter.lineCounters[faultId]++; 280 281 if (faultsAttrs[faultId].warning) { 282 if (line !== this.currentWarningLine) { 283 this.currentWarningLine = line; 284 ++TypeScriptLinter.totalWarningLines; 285 TypeScriptLinter.warningLineNumbersString += line + ", " ; 286 } 287 } 288 else if (line !== this.currentErrorLine) { 289 this.currentErrorLine = line; 290 ++TypeScriptLinter.totalErrorLines; 291 TypeScriptLinter.errorLineNumbersString += line + ", "; 292 } 293 } 294 295 public visitTSNode(node: Node): void { 296 const self = this; 297 visitTSNodeImpl(node); 298 function visitTSNodeImpl(node: Node): void { 299 if (node === null || node.kind === null) { 300 return; 301 } 302 TypeScriptLinter.totalVisitedNodes++; 303 304 //if (TypeScriptLinter.tsSyntaxKindNames[node.kind] === "StructDeclaration") { 305 // if (node.kind === SyntaxKind.StructDeclaration) { 306 //if ( SyntaxKind[node.kind] === 'StructDeclaration') { 307 if(isStructDeclaration(node)) { 308 self.handleStructDeclaration(node); 309 return; 310 } 311 312 self.handleComments(node); 313 314 if (LinterConfig.terminalTokens.has(node.kind)) return; 315 316 const incrementedType = LinterConfig.incrementOnlyTokens.get(node.kind); 317 if (incrementedType !== undefined) { 318 self.incrementCounters(node, incrementedType); 319 } 320 else { 321 const handler = self.handlersMap.get(node.kind); 322 if (handler !== undefined) { 323 handler.call(self, node); 324 } 325 } 326 327 forEachChild(node, visitTSNodeImpl); 328 } 329 } 330 331 private countInterfaceExtendsDifferentPropertyTypes( 332 node: Node, 333 prop2type: Map<string /*, string*/>, 334 propName: string, 335 type: TypeNode | undefined 336 ): void { 337 if (type) { 338 const methodType = type.getText(); 339 const propType = prop2type.get(propName); 340 if (!propType) { 341 prop2type.set(propName, methodType); 342 } 343 else if (propType !== methodType) { 344 this.incrementCounters(node, FaultID.IntefaceExtendDifProps); 345 } 346 } 347 } 348 349 private countDeclarationsWithDuplicateName( 350 tsNode: Node, tsDeclNode: Node, tsDeclKind?: SyntaxKind 351 ): void { 352 const symbol = TypeScriptLinter.tsTypeChecker.getSymbolAtLocation(tsNode); 353 354 // If specific declaration kind is provided, check against it. 355 // Otherwise, use syntax kind of corresponding declaration node. 356 if (!!symbol && symbolHasDuplicateName(symbol, tsDeclKind ?? tsDeclNode.kind)) { 357 this.incrementCounters(tsDeclNode, FaultID.DeclWithDuplicateName); 358 } 359 } 360 361 private countClassMembersWithDuplicateName(tsClassDecl: ClassDeclaration): void { 362 for (const tsCurrentMember of tsClassDecl.members) { 363 if ( 364 !tsCurrentMember.name || 365 !(isIdentifier(tsCurrentMember.name) || isPrivateIdentifier(tsCurrentMember.name)) 366 ) { 367 continue; 368 } 369 for (const tsClassMember of tsClassDecl.members) { 370 if (tsCurrentMember === tsClassMember) continue; 371 372 if ( 373 !tsClassMember.name || 374 !(isIdentifier(tsClassMember.name) || isPrivateIdentifier(tsClassMember.name)) 375 ) { 376 continue; 377 } 378 if ( 379 isIdentifier(tsCurrentMember.name) && 380 isPrivateIdentifier(tsClassMember.name) && 381 tsCurrentMember.name.text === tsClassMember.name.text.substring(1) 382 ) { 383 this.incrementCounters(tsCurrentMember, FaultID.DeclWithDuplicateName); 384 break; 385 } 386 387 if ( 388 isPrivateIdentifier(tsCurrentMember.name) && 389 isIdentifier(tsClassMember.name) && 390 tsCurrentMember.name.text.substring(1) === tsClassMember.name.text 391 ) { 392 this.incrementCounters(tsCurrentMember, FaultID.DeclWithDuplicateName); 393 break; 394 } 395 } 396 } 397 } 398 399 private functionContainsThis(tsNode: Node): boolean { 400 let found = false; 401 402 function visitNode(tsNode: Node): void { 403 // Stop visiting child nodes if finished searching. 404 if (found) return; 405 406 if (tsNode.kind === SyntaxKind.ThisKeyword) { 407 found = true; 408 return; 409 } 410 411 // Visit children nodes. Skip any local declaration that defines 412 // its own scope as it needs to be checked separately. 413 if ( 414 !isClassDeclaration(tsNode) && 415 !isClassExpression(tsNode) && 416 !isModuleDeclaration(tsNode) && 417 !isFunctionDeclaration(tsNode) && 418 !isFunctionExpression(tsNode) 419 ) { 420 tsNode.forEachChild(visitNode); 421 } 422 } 423 424 visitNode(tsNode); 425 426 return found; 427 } 428 429 private isPrototypePropertyAccess(tsPropertyAccess: PropertyAccessExpression, propAccessSym: Symbol | undefined, baseExprSym: Symbol | undefined, baseExprType: Type): boolean { 430 if (!(isIdentifier(tsPropertyAccess.name) && tsPropertyAccess.name.text === "prototype")) { 431 return false; 432 } 433 // #13600: Relax prototype check when expression comes from interop. 434 let curPropAccess: Node = tsPropertyAccess; 435 while (curPropAccess && isPropertyAccessExpression(curPropAccess)) { 436 const baseExprSym = trueSymbolAtLocation(curPropAccess.expression); 437 if (isLibrarySymbol(baseExprSym)) { 438 return false; 439 } 440 curPropAccess = curPropAccess.expression; 441 } 442 if (isIdentifier(curPropAccess) && curPropAccess.text !== 'prototype') { 443 const type = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(curPropAccess); 444 if (isAnyType(type)) { 445 return false; 446 } 447 } 448 // Check if property symbol is "Prototype" 449 if (isPrototypeSymbol(propAccessSym)) { 450 return true; 451 } 452 453 // Check if symbol of LHS-expression is Class or Function. 454 if (isTypeSymbol(baseExprSym) || isFunctionSymbol(baseExprSym)) { 455 return true; 456 } 457 // Check if type of LHS expression Function type or Any type. 458 // The latter check is to cover cases with multiple prototype 459 // chain (as the 'Prototype' property should be 'Any' type): 460 // X.prototype.prototype.prototype = ... 461 const baseExprTypeNode = TypeScriptLinter.tsTypeChecker.typeToTypeNode( 462 baseExprType, undefined, NodeBuilderFlags.None 463 ); 464 465 return ((baseExprTypeNode && isFunctionTypeNode(baseExprTypeNode)) || isAnyType(baseExprType)); 466 } 467 468 private interfaceInheritanceLint(node: Node, heritageClauses: NodeArray<HeritageClause>): void { 469 for (const hClause of heritageClauses) { 470 if (hClause.token !== SyntaxKind.ExtendsKeyword) continue; 471 472 const prop2type = new Map<string, string>(); 473 for (const tsTypeExpr of hClause.types) { 474 const tsExprType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(tsTypeExpr.expression); 475 if (tsExprType.isClass()) { 476 this.incrementCounters(node, FaultID.InterfaceExtendsClass); 477 } 478 else if (tsExprType.isClassOrInterface()) { 479 this.lintForInterfaceExtendsDifferentPorpertyTypes(node, tsExprType, prop2type); 480 } 481 } 482 } 483 } 484 485 private lintForInterfaceExtendsDifferentPorpertyTypes( 486 node: Node, tsExprType: Type, prop2type: Map</*string,*/ string> 487 ): void { 488 const props = tsExprType.getProperties(); 489 for (const p of props) { 490 if (!p.declarations) continue; 491 492 const decl: Declaration = p.declarations[0]; 493 if (decl.kind === SyntaxKind.MethodSignature) { 494 this.countInterfaceExtendsDifferentPropertyTypes( 495 node, prop2type, p.name, (decl as MethodSignature).type 496 ); 497 } 498 else if (decl.kind === SyntaxKind.MethodDeclaration) { 499 this.countInterfaceExtendsDifferentPropertyTypes( 500 node, prop2type, p.name, (decl as MethodDeclaration).type 501 ); 502 } 503 else if (decl.kind === SyntaxKind.PropertyDeclaration) { 504 this.countInterfaceExtendsDifferentPropertyTypes( 505 node, prop2type, p.name, (decl as PropertyDeclaration).type 506 ); 507 } 508 else if (decl.kind === SyntaxKind.PropertySignature) { 509 this.countInterfaceExtendsDifferentPropertyTypes( 510 node, prop2type, p.name, (decl as PropertySignature).type 511 ); 512 } 513 } 514 } 515 516 private handleObjectLiteralExpression(node: Node): void { 517 const objectLiteralExpr = node as ObjectLiteralExpression; 518 519 // If object literal is a part of destructuring assignment, then don't process it further. 520 if (isDestructuringAssignmentLHS(objectLiteralExpr)) { 521 return; 522 } 523 const objectLiteralType = TypeScriptLinter.tsTypeChecker.getContextualType(objectLiteralExpr); 524 if (!isStructObjectInitializer(objectLiteralExpr) && 525 !isDynamicLiteralInitializer(objectLiteralExpr) && 526 !isExpressionAssignableToType(objectLiteralType, objectLiteralExpr) 527 // !Utils.validateObjectLiteralType(objectLiteralType) || Utils.hasMemberFunction(objectLiteralExpr) || 528 // !Utils.validateFields(objectLiteralType, objectLiteralExpr) 529 ) { 530 this.incrementCounters(node, FaultID.ObjectLiteralNoContextType); 531 } 532 } 533 534 private handleArrayLiteralExpression(node: Node): void { 535 // If array literal is a part of destructuring assignment, then 536 // don't process it further. 537 if (isDestructuringAssignmentLHS(node as ArrayLiteralExpression)) { 538 return; 539 } 540 541 const arrayLitNode = node as ArrayLiteralExpression; 542 let noContextTypeForArrayLiteral = false; 543 544 // check that array literal consists of inferrable types 545 // e.g. there is no element which is untyped object literals 546 const arrayLitElements = arrayLitNode.elements; 547 for(const element of arrayLitElements) { 548 if(element.kind === SyntaxKind.ObjectLiteralExpression) { 549 const objectLiteralType = TypeScriptLinter.tsTypeChecker.getContextualType(element); 550 if (!isDynamicLiteralInitializer(arrayLitNode) && 551 !isExpressionAssignableToType(objectLiteralType, element)) { 552 noContextTypeForArrayLiteral = true; 553 break; 554 } 555 } 556 } 557 558 if (noContextTypeForArrayLiteral) { 559 this.incrementCounters(node, FaultID.ArrayLiteralNoContextType); 560 } 561 } 562 563 private handleParameter(node: Node): void { 564 const tsParam = node as ParameterDeclaration; 565 if (isArrayBindingPattern(tsParam.name) || isObjectBindingPattern(tsParam.name)) { 566 this.incrementCounters(node, FaultID.DestructuringParameter); 567 } 568 const tsParamMods = getModifiers(tsParam); //tsParam.modifiers; 569 if ( 570 tsParamMods && 571 (hasModifier(tsParamMods, SyntaxKind.PublicKeyword) || 572 hasModifier(tsParamMods, SyntaxKind.ProtectedKeyword) || 573 hasModifier(tsParamMods, SyntaxKind.ReadonlyKeyword) || 574 hasModifier(tsParamMods, SyntaxKind.PrivateKeyword)) 575 ) { 576 this.incrementCounters(node, FaultID.ParameterProperties); 577 } 578 this.handleDecorators(getDecorators(tsParam)); 579 580 this.handleDeclarationInferredType(tsParam); 581 } 582 583 private handleEnumDeclaration(node: Node): void { 584 const enumNode = node as EnumDeclaration; 585 this.countDeclarationsWithDuplicateName(enumNode.name, enumNode); 586 587 const enumSymbol = trueSymbolAtLocation(enumNode.name); 588 if (!enumSymbol) return; 589 590 const enumDecls = enumSymbol.getDeclarations(); 591 if (!enumDecls) return; 592 593 // Since type checker merges all declarations with the same name 594 // into one symbol, we need to check that there's more than one 595 // enum declaration related to that specific symbol. 596 // See 'countDeclarationsWithDuplicateName' method for details. 597 let enumDeclCount = 0; 598 for (const decl of enumDecls) { 599 if (decl.kind === SyntaxKind.EnumDeclaration) enumDeclCount++; 600 } 601 602 if (enumDeclCount > 1) this.incrementCounters(node, FaultID.EnumMerging); 603 } 604 605 private handleInterfaceDeclaration(node: Node): void { 606 const interfaceNode = node as InterfaceDeclaration; 607 const iSymbol = trueSymbolAtLocation(interfaceNode.name); 608 const iDecls = iSymbol ? iSymbol.getDeclarations() : null; 609 if (iDecls) { 610 // Since type checker merges all declarations with the same name 611 // into one symbol, we need to check that there's more than one 612 // interface declaration related to that specific symbol. 613 // See 'countDeclarationsWithDuplicateName' method for details. 614 let iDeclCount = 0; 615 for (const decl of iDecls) { 616 if (decl.kind === SyntaxKind.InterfaceDeclaration) iDeclCount++; 617 } 618 619 if (iDeclCount > 1) this.incrementCounters(node, FaultID.InterfaceMerging); 620 } 621 622 if (interfaceNode.heritageClauses) this.interfaceInheritanceLint(node, interfaceNode.heritageClauses); 623 624 this.countDeclarationsWithDuplicateName(interfaceNode.name, interfaceNode); 625 } 626 627 private handleThrowStatement(node: Node): void { 628 const throwStmt = node as ThrowStatement; 629 const throwExprType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(throwStmt.expression); 630 if (!throwExprType.isClassOrInterface() || !isDerivedFrom(throwExprType, CheckType.Error)) { 631 this.incrementCounters(node, FaultID.ThrowStatement); 632 } 633 } 634 635 private handleForStatement(node: Node): void { 636 const tsForStmt = node as ForStatement; 637 const tsForInit = tsForStmt.initializer; 638 if (tsForInit && (isArrayLiteralExpression(tsForInit) || isObjectLiteralExpression(tsForInit))) { 639 this.incrementCounters(tsForInit, FaultID.DestructuringAssignment); 640 } 641 } 642 643 private handleForInStatement(node: Node): void { 644 const tsForInStmt = node as ForInStatement; 645 const tsForInInit = tsForInStmt.initializer; 646 if (isArrayLiteralExpression(tsForInInit) || isObjectLiteralExpression(tsForInInit)) { 647 this.incrementCounters(tsForInInit, FaultID.DestructuringAssignment); 648 } 649 this.incrementCounters(node, FaultID.ForInStatement); 650 } 651 652 private handleForOfStatement(node: Node): void { 653 const tsForOfStmt = node as ForOfStatement; 654 const tsForOfInit = tsForOfStmt.initializer; 655 if (isArrayLiteralExpression(tsForOfInit) || isObjectLiteralExpression(tsForOfInit)) { 656 this.incrementCounters(tsForOfInit, FaultID.DestructuringAssignment); 657 } 658 } 659 660 private handleImportDeclaration(node: Node): void { 661 const importDeclNode = node as ImportDeclaration; 662 for (const stmt of importDeclNode.parent.statements) { 663 if (stmt === importDeclNode) { 664 break; 665 } 666 if (!isImportDeclaration(stmt)) { 667 this.incrementCounters(node, FaultID.ImportAfterStatement); 668 break; 669 } 670 } 671 const expr1 = importDeclNode.moduleSpecifier; 672 if (expr1.kind === SyntaxKind.StringLiteral) { 673 if (!importDeclNode.importClause) this.incrementCounters(node, FaultID.ImportFromPath); 674 } 675 } 676 677 private handlePropertyAccessExpression(node: Node): void { 678 if (isCallExpression(node.parent) && node === node.parent.expression) { 679 return; 680 } 681 682 const propertyAccessNode = node as PropertyAccessExpression; 683 const exprSym = trueSymbolAtLocation(propertyAccessNode); 684 const baseExprSym = trueSymbolAtLocation(propertyAccessNode.expression); 685 const baseExprType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(propertyAccessNode.expression); 686 if (this.isPrototypePropertyAccess(propertyAccessNode, exprSym, baseExprSym, baseExprType)) { 687 this.incrementCounters(propertyAccessNode.name, FaultID.Prototype); 688 } 689 if (!!exprSym && isSymbolAPI(exprSym) && !ALLOWED_STD_SYMBOL_API.includes(exprSym.getName())) { 690 this.incrementCounters(propertyAccessNode, FaultID.SymbolType); 691 } 692 if (!!baseExprSym && symbolHasEsObjectType(baseExprSym)) { 693 this.incrementCounters(propertyAccessNode, FaultID.EsObjectType); 694 } 695 } 696 697 private handlePropertyAssignmentOrDeclaration(node: Node) { 698 const propName = (node as PropertyAssignment | PropertyDeclaration).name; 699 700 if (propName && (propName.kind === SyntaxKind.NumericLiteral || propName.kind === SyntaxKind.StringLiteral)) { 701 // We can use literals as property names only when creating Record or any interop instances. 702 let isRecordObjectInitializer = false; 703 let isLibraryType = false; 704 let isDynamic = false; 705 if (isPropertyAssignment(node)) { 706 let objectLiteralType = TypeScriptLinter.tsTypeChecker.getContextualType(node.parent); 707 if (objectLiteralType) { 708 isRecordObjectInitializer = isStdRecordType(objectLiteralType); 709 isLibraryType = ArkTSLinter_1_0.isLibraryType(objectLiteralType); 710 } 711 isDynamic = isLibraryType || isDynamicLiteralInitializer(node.parent); 712 } 713 714 if (!isRecordObjectInitializer && !isDynamic) { 715 let autofix: Autofix[] | undefined = fixLiteralAsPropertyName(node); 716 const autofixable = autofix !== undefined; 717 if (!shouldAutofix(node, FaultID.LiteralAsPropertyName)) { 718 autofix = undefined; 719 } 720 this.incrementCounters(node, FaultID.LiteralAsPropertyName, autofixable, autofix); 721 } 722 } 723 724 if (isPropertyDeclaration(node)) { 725 const decorators = getDecorators(node); 726 this.handleDecorators(decorators); 727 this.filterOutDecoratorsDiagnostics( 728 decorators, 729 NON_INITIALIZABLE_PROPERTY_DECORATORS, 730 { begin: propName.getStart(), end: propName.getStart() }, 731 PROPERTY_HAS_NO_INITIALIZER_ERROR_CODE 732 ); 733 const classDecorators = getDecorators(node.parent); 734 const propType = (node as PropertyDeclaration).type?.getText(); 735 this.filterOutDecoratorsDiagnostics(classDecorators, NON_INITIALIZABLE_PROPERTY_ClASS_DECORATORS, 736 {begin: propName.getStart(), end: propName.getStart()}, PROPERTY_HAS_NO_INITIALIZER_ERROR_CODE, propType); 737 this.handleDeclarationInferredType(node); 738 this.handleDefiniteAssignmentAssertion(node); 739 } 740 } 741 742 private filterOutDecoratorsDiagnostics(decorators: readonly Decorator[] | undefined, 743 expectedDecorators: readonly string[], 744 range: { begin: number, end: number}, 745 code: number, 746 prop_type?: string) { 747 // Filter out non-initializable property decorators from strict diagnostics. 748 if (this.tscStrictDiagnostics && this.sourceFile) { 749 if (decorators?.some(x => { 750 let decoratorName = ""; 751 if (isIdentifier(x.expression)) { 752 decoratorName = x.expression.text; 753 } 754 else if (isCallExpression(x.expression) && isIdentifier(x.expression.expression)) { 755 decoratorName = x.expression.expression.text; 756 } 757 // special case for property of type CustomDialogController of the @CustomDialog-decorated class 758 if (expectedDecorators.includes(NON_INITIALIZABLE_PROPERTY_ClASS_DECORATORS[0])) { 759 return expectedDecorators.includes(decoratorName) && prop_type === "CustomDialogController" 760 } 761 //return Utils.NON_INITIALIZABLE_PROPERTY_DECORATORS.includes(decoratorName); 762 return expectedDecorators.includes(decoratorName); 763 })) { 764 const file = normalizePath(this.sourceFile.fileName); 765 const tscDiagnostics = this.tscStrictDiagnostics.get(file); 766 if (tscDiagnostics) { 767 const filteredDiagnostics = tscDiagnostics.filter( 768 (val /*, idx, array */) => { 769 if (val.code !== code) return true; 770 if (val.start === undefined) return true; 771 if (val.start < range.begin) return true; 772 if (val.start > range.end) return true; 773 return false; 774 } 775 ); 776 this.tscStrictDiagnostics.set(file, filteredDiagnostics); 777 } 778 } 779 } 780 } 781 782 private checkInRange(rangesToFilter: { begin: number, end: number }[], pos: number): boolean { 783 for (let i = 0; i < rangesToFilter.length; i++) { 784 if (pos >= rangesToFilter[i].begin && pos < rangesToFilter[i].end) { 785 return false; 786 } 787 } 788 return true; 789 } 790 791 private filterStrictDiagnostics(filters: { [code: number]: (pos: number) => boolean }, 792 diagnosticChecker: DiagnosticChecker): boolean { 793 if (!this.tscStrictDiagnostics || !this.sourceFile) { 794 return false; 795 } 796 const file = normalizePath(this.sourceFile.fileName); 797 const tscDiagnostics = this.tscStrictDiagnostics.get(file); 798 if (!tscDiagnostics) { 799 return false; 800 } 801 802 const checkDiagnostic = (val: Diagnostic) => { 803 const checkInRange = filters[val.code]; 804 if (!checkInRange) { 805 return true; 806 } 807 if (val.start === undefined || checkInRange(val.start)) { 808 return true; 809 } 810 return diagnosticChecker.checkDiagnosticMessage(val.messageText); 811 }; 812 813 if (tscDiagnostics.every(checkDiagnostic)) { 814 return false; 815 } 816 this.tscStrictDiagnostics.set(file, tscDiagnostics.filter(checkDiagnostic)); 817 return true; 818 } 819 820 private handleFunctionExpression(node: Node): void { 821 const funcExpr = node as FunctionExpression; 822 const isGenerator = funcExpr.asteriskToken !== undefined; 823 const containsThis = this.functionContainsThis(funcExpr.body); 824 const hasValidContext = hasPredecessor(funcExpr, isClassLike) || 825 hasPredecessor(funcExpr, isInterfaceDeclaration); 826 const isGeneric = funcExpr.typeParameters !== undefined && funcExpr.typeParameters.length > 0; 827 const [hasUnfixableReturnType, newRetTypeNode] = this.handleMissingReturnType(funcExpr); 828 const autofixable = !isGeneric && !isGenerator && !containsThis && !hasUnfixableReturnType; 829 830 let autofix: Autofix[] | undefined; 831 if (autofixable && shouldAutofix(node, FaultID.FunctionExpression)) { 832 autofix = [fixFunctionExpression(funcExpr, funcExpr.parameters, newRetTypeNode)]; 833 } 834 835 this.incrementCounters(node, FaultID.FunctionExpression, autofixable, autofix); 836 if (isGeneric) { 837 this.incrementCounters(funcExpr, FaultID.LambdaWithTypeParameters); 838 } 839 if (isGenerator) { 840 this.incrementCounters(funcExpr, FaultID.GeneratorFunction); 841 } 842 if (containsThis && !hasValidContext) { 843 this.incrementCounters(funcExpr, FaultID.FunctionContainsThis); 844 } 845 if (hasUnfixableReturnType) { 846 this.incrementCounters(funcExpr, FaultID.LimitedReturnTypeInference); 847 } 848 } 849 850 private handleArrowFunction(node: Node): void { 851 const arrowFunc = node as ArrowFunction; 852 const containsThis = this.functionContainsThis(arrowFunc.body); 853 const hasValidContext = hasPredecessor(arrowFunc, isClassLike) || 854 hasPredecessor(arrowFunc, isInterfaceDeclaration); 855 if (containsThis && !hasValidContext) { 856 this.incrementCounters(arrowFunc, FaultID.FunctionContainsThis); 857 } 858 859 const contextType = TypeScriptLinter.tsTypeChecker.getContextualType(arrowFunc); 860 if (!(contextType && isLibraryType(contextType))) { 861 if (!arrowFunc.type) { 862 this.handleMissingReturnType(arrowFunc); 863 } 864 865 if (arrowFunc.typeParameters && arrowFunc.typeParameters.length > 0) { 866 this.incrementCounters(node, FaultID.LambdaWithTypeParameters); 867 } 868 } 869 } 870 871 private handleClassExpression(node: Node): void { 872 //let tsClassExpr = node as ClassExpression; 873 this.incrementCounters(node, FaultID.ClassExpression); 874 //this.handleDecorators(tsClassExpr.decorators); 875 } 876 877 private handleFunctionDeclaration(node: Node) { 878 const tsFunctionDeclaration = node as FunctionDeclaration; 879 if (!tsFunctionDeclaration.type) this.handleMissingReturnType(tsFunctionDeclaration); 880 if (tsFunctionDeclaration.name) { 881 this.countDeclarationsWithDuplicateName(tsFunctionDeclaration.name, tsFunctionDeclaration); 882 } 883 if (tsFunctionDeclaration.body && this.functionContainsThis(tsFunctionDeclaration.body)) { 884 this.incrementCounters(node, FaultID.FunctionContainsThis); 885 } 886 if (!isSourceFile(tsFunctionDeclaration.parent) && !isModuleBlock(tsFunctionDeclaration.parent)) { 887 this.incrementCounters(tsFunctionDeclaration, FaultID.LocalFunction); 888 } 889 if (tsFunctionDeclaration.asteriskToken) this.incrementCounters(node, FaultID.GeneratorFunction); 890 } 891 892 private handleMissingReturnType(funcLikeDecl: FunctionLikeDeclaration): [boolean, TypeNode | undefined] { 893 // if (funcLikeDecl.type) return [false, funcLikeDecl.type]; 894 895 // Note: Return type can't be inferred for function without body. 896 if (!funcLikeDecl.body) return [false, undefined]; 897 898 let autofixable = false; 899 let autofix: Autofix[] | undefined; 900 let newRetTypeNode: TypeNode | undefined; 901 const isFuncExpr = isFunctionExpression(funcLikeDecl); 902 903 // Currently, ArkTS can't infer return type of function, when expression 904 // in the return statement is a call to a function or method whose return 905 // value type is omitted. In that case, we attempt to prepare an autofix. 906 let hasLimitedRetTypeInference = this.hasLimitedTypeInferenceFromReturnExpr(funcLikeDecl.body); 907 908 const tsSignature = TypeScriptLinter.tsTypeChecker.getSignatureFromDeclaration(funcLikeDecl); 909 if (tsSignature) { 910 const tsRetType = TypeScriptLinter.tsTypeChecker.getReturnTypeOfSignature(tsSignature); 911 912 if (!tsRetType || isUnsupportedType(tsRetType)) { 913 hasLimitedRetTypeInference = true; 914 } 915 else if (hasLimitedRetTypeInference) { 916 newRetTypeNode = TypeScriptLinter.tsTypeChecker.typeToTypeNode(tsRetType, funcLikeDecl, NodeBuilderFlags.None); 917 autofixable = !!newRetTypeNode; 918 919 if (!isFuncExpr && newRetTypeNode && shouldAutofix(funcLikeDecl, FaultID.LimitedReturnTypeInference)) { 920 autofix = [fixReturnType(funcLikeDecl, newRetTypeNode)]; 921 } 922 } 923 } 924 925 // Don't report here if in function expression context. 926 // See handleFunctionExpression for details. 927 if (hasLimitedRetTypeInference && !isFuncExpr) { 928 this.incrementCounters(funcLikeDecl, FaultID.LimitedReturnTypeInference, autofixable, autofix); 929 } 930 return [hasLimitedRetTypeInference && !newRetTypeNode, newRetTypeNode]; 931 } 932 933 private hasLimitedTypeInferenceFromReturnExpr(funBody: ConciseBody): boolean { 934 let hasLimitedTypeInference = false; 935 function visitNode(tsNode: Node): void { 936 if (hasLimitedTypeInference) { 937 return; 938 } 939 940 if ( 941 isReturnStatement(tsNode) && tsNode.expression && 942 isCallToFunctionWithOmittedReturnType(unwrapParenthesized(tsNode.expression)) 943 ) { 944 hasLimitedTypeInference = true; 945 return; 946 } 947 948 // Visit children nodes. Don't traverse other nested function-like declarations. 949 if ( 950 !isFunctionDeclaration(tsNode) && 951 !isFunctionExpression(tsNode) && 952 !isMethodDeclaration(tsNode) && 953 !isAccessor(tsNode) && 954 !isArrowFunction(tsNode) 955 ) { 956 tsNode.forEachChild(visitNode); 957 } 958 } 959 960 if (isBlock(funBody)) { 961 visitNode(funBody); 962 } 963 else { 964 const tsExpr = unwrapParenthesized(funBody); 965 hasLimitedTypeInference = isCallToFunctionWithOmittedReturnType(tsExpr); 966 } 967 968 return hasLimitedTypeInference; 969 } 970 971 private handlePrefixUnaryExpression(node: Node) { 972 const tsUnaryArithm = node as PrefixUnaryExpression; 973 const tsUnaryOp = tsUnaryArithm.operator; 974 if ( 975 tsUnaryOp === SyntaxKind.PlusToken || 976 tsUnaryOp === SyntaxKind.MinusToken || 977 tsUnaryOp === SyntaxKind.TildeToken 978 ) { 979 const tsOperatndType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(tsUnaryArithm.operand); 980 if (!(tsOperatndType.getFlags() & (TypeFlags.NumberLike | TypeFlags.BigIntLiteral)) || 981 (tsUnaryOp === SyntaxKind.TildeToken && tsUnaryArithm.operand.kind === SyntaxKind.NumericLiteral && 982 !isIntegerConstantValue(tsUnaryArithm.operand as NumericLiteral)) 983 ) { 984 this.incrementCounters(node, FaultID.UnaryArithmNotNumber); 985 } 986 } 987 } 988 989 private handleBinaryExpression(node: Node): void { 990 const tsBinaryExpr = node as BinaryExpression; 991 const tsLhsExpr = tsBinaryExpr.left; 992 const tsRhsExpr = tsBinaryExpr.right; 993 994 if (isAssignmentOperator(tsBinaryExpr.operatorToken)) { 995 if (isObjectLiteralExpression(tsLhsExpr) || isArrayLiteralExpression(tsLhsExpr)) { 996 this.incrementCounters(node, FaultID.DestructuringAssignment); 997 } 998 if (isPropertyAccessExpression(tsLhsExpr)) { 999 const tsLhsSymbol = trueSymbolAtLocation(tsLhsExpr); 1000 const tsLhsBaseSymbol = trueSymbolAtLocation(tsLhsExpr.expression); 1001 if (tsLhsSymbol && (tsLhsSymbol.flags & SymbolFlags.Method)) { 1002 this.incrementCounters(tsLhsExpr, FaultID.NoUndefinedPropAccess); 1003 } 1004 if ( 1005 isMethodAssignment(tsLhsSymbol) && tsLhsBaseSymbol && 1006 (tsLhsBaseSymbol.flags & SymbolFlags.Function) !== 0 1007 ) { 1008 this.incrementCounters(tsLhsExpr, FaultID.PropertyDeclOnFunction); 1009 } 1010 } 1011 } 1012 1013 const leftOperandType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(tsLhsExpr); 1014 const rightOperandType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(tsRhsExpr); 1015 1016 if (tsBinaryExpr.operatorToken.kind === SyntaxKind.PlusToken) { 1017 if (isEnumMemberType(leftOperandType) && isEnumMemberType(rightOperandType)) { 1018 if ( 1019 ((leftOperandType.flags & (TypeFlags.NumberLike)) && (rightOperandType.getFlags() & (TypeFlags.NumberLike))) || 1020 ((leftOperandType.flags & (TypeFlags.StringLike)) && (rightOperandType.getFlags() & (TypeFlags.StringLike))) 1021 ) { 1022 return; 1023 } 1024 } 1025 else if (isNumberType(leftOperandType) && isNumberType(rightOperandType)) { 1026 return; 1027 } 1028 else if (isStringLikeType(leftOperandType) || isStringLikeType(rightOperandType)) { 1029 return; 1030 } 1031 } 1032 else if ( 1033 tsBinaryExpr.operatorToken.kind === SyntaxKind.AmpersandToken || 1034 tsBinaryExpr.operatorToken.kind === SyntaxKind.BarToken || 1035 tsBinaryExpr.operatorToken.kind === SyntaxKind.CaretToken || 1036 tsBinaryExpr.operatorToken.kind === SyntaxKind.LessThanLessThanToken || 1037 tsBinaryExpr.operatorToken.kind === SyntaxKind.GreaterThanGreaterThanToken || 1038 tsBinaryExpr.operatorToken.kind === SyntaxKind.GreaterThanGreaterThanGreaterThanToken 1039 ) { 1040 if (!(isNumberType(leftOperandType) && isNumberType(rightOperandType)) || 1041 (tsLhsExpr.kind === SyntaxKind.NumericLiteral && !isIntegerConstantValue(tsLhsExpr as NumericLiteral)) || 1042 (tsRhsExpr.kind === SyntaxKind.NumericLiteral && !isIntegerConstantValue(tsRhsExpr as NumericLiteral)) 1043 ) { 1044 return; //this.incrementCounters(node, FaultID.BitOpWithWrongType); 1045 } 1046 } 1047 else if (tsBinaryExpr.operatorToken.kind === SyntaxKind.CommaToken) { 1048 // CommaOpertor is allowed in 'for' statement initalizer and incrementor 1049 let tsExprNode: Node = tsBinaryExpr; 1050 let tsParentNode = tsExprNode.parent; 1051 while (tsParentNode && tsParentNode.kind === SyntaxKind.BinaryExpression) { 1052 tsExprNode = tsParentNode; 1053 tsParentNode = tsExprNode.parent; 1054 } 1055 1056 if (tsParentNode && tsParentNode.kind === SyntaxKind.ForStatement) { 1057 const tsForNode = tsParentNode as ForStatement; 1058 if (tsExprNode === tsForNode.initializer || tsExprNode === tsForNode.incrementor) return; 1059 } 1060 this.incrementCounters(node, FaultID.CommaOperator); 1061 } 1062 else if (tsBinaryExpr.operatorToken.kind === SyntaxKind.InstanceOfKeyword) { 1063 const leftExpr = unwrapParenthesized(tsBinaryExpr.left); 1064 const leftSymbol = trueSymbolAtLocation(leftExpr); 1065 // In STS, the left-hand side expression may be of any reference type, otherwise 1066 // a compile-time error occurs. In addition, the left operand in STS cannot be a type. 1067 if (tsLhsExpr.kind === SyntaxKind.ThisKeyword) { 1068 return; 1069 } 1070 if (isPrimitiveType(leftOperandType) || isTypeNode(leftExpr) || isTypeSymbol(leftSymbol)) { 1071 this.incrementCounters(node, FaultID.InstanceofUnsupported); 1072 } 1073 } 1074 else if (tsBinaryExpr.operatorToken.kind === SyntaxKind.EqualsToken) { 1075 if (needToDeduceStructuralIdentity(rightOperandType, leftOperandType)) { 1076 this.incrementCounters(tsBinaryExpr, FaultID.StructuralIdentity); 1077 } 1078 const typeNode = getVariableDeclarationTypeNode(tsLhsExpr); 1079 this.handleEsObjectAssignment(tsBinaryExpr, typeNode, tsRhsExpr); 1080 } 1081 } 1082 1083 private handleVariableDeclarationList(node: Node): void { 1084 const varDeclFlags = getCombinedNodeFlags(node); 1085 if (!(varDeclFlags & (NodeFlags.Let | NodeFlags.Const))) { 1086 this.incrementCounters(node, FaultID.VarDeclaration); 1087 } 1088 } 1089 1090 private handleVariableDeclaration(node: Node): void { 1091 const tsVarDecl = node as VariableDeclaration; 1092 if (isArrayBindingPattern(tsVarDecl.name) || isObjectBindingPattern(tsVarDecl.name)) { 1093 this.incrementCounters(node, FaultID.DestructuringDeclaration); 1094 } 1095 { 1096 // Check variable declaration for duplicate name. 1097 const visitBindingPatternNames = (tsBindingName: BindingName): void => { 1098 if (isIdentifier(tsBindingName)) { 1099 // The syntax kind of the declaration is defined here by the parent of 'BindingName' node. 1100 this.countDeclarationsWithDuplicateName(tsBindingName, tsBindingName, tsBindingName.parent.kind); 1101 } 1102 else { 1103 for (const tsBindingElem of tsBindingName.elements) { 1104 if (isOmittedExpression(tsBindingElem)) continue; 1105 1106 visitBindingPatternNames(tsBindingElem.name); 1107 } 1108 } 1109 }; 1110 1111 visitBindingPatternNames(tsVarDecl.name); 1112 } 1113 1114 if (tsVarDecl.type && tsVarDecl.initializer) { 1115 const tsVarInit = tsVarDecl.initializer; 1116 const tsVarType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(tsVarDecl.type); 1117 const tsInitType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(tsVarInit); 1118 if (needToDeduceStructuralIdentity(tsInitType, tsVarType)) { 1119 this.incrementCounters(tsVarDecl, FaultID.StructuralIdentity); 1120 } 1121 } 1122 1123 this.handleEsObjectDelaration(tsVarDecl); 1124 this.handleDeclarationInferredType(tsVarDecl); 1125 this.handleDefiniteAssignmentAssertion(tsVarDecl); 1126 } 1127 1128 private handleEsObjectDelaration(node: VariableDeclaration) { 1129 const isDeclaredESObject = !!node.type && isEsObjectType(node.type); 1130 const initalizerTypeNode = node.initializer && getVariableDeclarationTypeNode(node.initializer); 1131 const isInitializedWithESObject = !!initalizerTypeNode && isEsObjectType(initalizerTypeNode); 1132 const isLocal = isInsideBlock(node); 1133 if ((isDeclaredESObject || isInitializedWithESObject) && !isLocal) { 1134 this.incrementCounters(node, FaultID.EsObjectType); 1135 return; 1136 } 1137 1138 if (node.initializer) { 1139 this.handleEsObjectAssignment(node, node.type, node.initializer); 1140 return; 1141 } 1142 } 1143 1144 private handleEsObjectAssignment(node: Node, nodeDeclType: TypeNode | undefined, initializer: Node) { 1145 const isTypeAnnotated = !!nodeDeclType; 1146 const isDeclaredESObject = !!nodeDeclType && isEsObjectType(nodeDeclType); 1147 const initalizerTypeNode = getVariableDeclarationTypeNode(initializer); 1148 const isInitializedWithESObject = !!initalizerTypeNode && isEsObjectType(initalizerTypeNode); 1149 if (isTypeAnnotated && !isDeclaredESObject && isInitializedWithESObject) { 1150 this.incrementCounters(node, FaultID.EsObjectType); 1151 return; 1152 } 1153 1154 if (isDeclaredESObject && !isValueAssignableToESObject(initializer)) { 1155 this.incrementCounters(node, FaultID.EsObjectType); 1156 } 1157 } 1158 1159 1160 private handleCatchClause(node: Node): void { 1161 const tsCatch = node as CatchClause; 1162 // In TS catch clause doesn't permit specification of the exception varible type except 'any' or 'unknown'. 1163 // It is not compatible with STS 'catch' where the exception varilab has to be of type 1164 // 'Exception' or derived from it. 1165 // So each 'catch' which has explicite type for the exception object goes to problems in strict mode. 1166 if (tsCatch.variableDeclaration && tsCatch.variableDeclaration.type) { 1167 this.incrementCounters(node, FaultID.CatchWithUnsupportedType); 1168 } 1169 } 1170 1171 private handleClassDeclaration(node: Node): void { 1172 const tsClassDecl = node as ClassDeclaration; 1173 this.staticBlocks.clear(); 1174 1175 if (tsClassDecl.name) { 1176 this.countDeclarationsWithDuplicateName(tsClassDecl.name, tsClassDecl); 1177 } 1178 this.countClassMembersWithDuplicateName(tsClassDecl); 1179 1180 const visitHClause = (hClause: HeritageClause) => { 1181 for (const tsTypeExpr of hClause.types) { 1182 const tsExprType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(tsTypeExpr.expression); 1183 if (tsExprType.isClass() && hClause.token === SyntaxKind.ImplementsKeyword) { 1184 this.incrementCounters(tsTypeExpr, FaultID.ImplementsClass); 1185 } 1186 } 1187 }; 1188 1189 if (tsClassDecl.heritageClauses) { 1190 for (const hClause of tsClassDecl.heritageClauses) { 1191 if (!hClause) { 1192 continue; 1193 } 1194 visitHClause(hClause); 1195 } 1196 } 1197 1198 this.handleDecorators(getDecorators(tsClassDecl)); 1199 1200 if (!this.skipArkTSStaticBlocksCheck) { 1201 let hasStaticBlock = false; 1202 for (const element of tsClassDecl.members) { 1203 if (isClassStaticBlockDeclaration(element)) { 1204 if (hasStaticBlock) { 1205 this.incrementCounters(element, FaultID.MultipleStaticBlocks); 1206 } else { 1207 hasStaticBlock = true; 1208 } 1209 } 1210 } 1211 } 1212 } 1213 1214 private handleModuleDeclaration(node: Node): void { 1215 const tsModuleDecl = node as ModuleDeclaration; 1216 this.countDeclarationsWithDuplicateName(tsModuleDecl.name, tsModuleDecl); 1217 1218 const tsModuleBody = tsModuleDecl.body; 1219 const tsModifiers = tsModuleDecl.modifiers; // TSC 4.2 doesn't have 'getModifiers()' method 1220 if (tsModuleBody) { 1221 if (isModuleBlock(tsModuleBody)) { 1222 for (const tsModuleStmt of tsModuleBody.statements) { 1223 switch (tsModuleStmt.kind) { 1224 case SyntaxKind.VariableStatement: 1225 case SyntaxKind.FunctionDeclaration: 1226 case SyntaxKind.ClassDeclaration: 1227 case SyntaxKind.InterfaceDeclaration: 1228 case SyntaxKind.TypeAliasDeclaration: 1229 case SyntaxKind.EnumDeclaration: 1230 case SyntaxKind.ExportDeclaration: 1231 break; 1232 // Nested namespace declarations are prohibited 1233 // but there is no cookbook recipe for it! 1234 case SyntaxKind.ModuleDeclaration: 1235 break; 1236 default: 1237 this.incrementCounters(tsModuleStmt, FaultID.NonDeclarationInNamespace); 1238 break; 1239 } 1240 } 1241 } 1242 } 1243 1244 if (!(tsModuleDecl.flags & NodeFlags.Namespace) && 1245 hasModifier(tsModifiers, SyntaxKind.DeclareKeyword)) { 1246 this.incrementCounters(tsModuleDecl, FaultID.ShorthandAmbientModuleDecl); 1247 } 1248 1249 if (isStringLiteral(tsModuleDecl.name) && tsModuleDecl.name.text.includes("*")) { 1250 this.incrementCounters(tsModuleDecl, FaultID.WildcardsInModuleName); 1251 } 1252 } 1253 1254 private handleTypeAliasDeclaration(node: Node): void { 1255 const tsTypeAlias = node as TypeAliasDeclaration; 1256 this.countDeclarationsWithDuplicateName(tsTypeAlias.name, tsTypeAlias); 1257 } 1258 1259 private handleImportClause(node: Node): void { 1260 const tsImportClause = node as ImportClause; 1261 if (tsImportClause.name) { 1262 this.countDeclarationsWithDuplicateName(tsImportClause.name, tsImportClause); 1263 } 1264 1265 if (tsImportClause.namedBindings && isNamedImports(tsImportClause.namedBindings)) { 1266 const nonDefaultSpecs: ImportSpecifier[] = []; 1267 let defaultSpec: ImportSpecifier | undefined; 1268 for (const importSpec of tsImportClause.namedBindings.elements) { 1269 if (isDefaultImport(importSpec)) { 1270 defaultSpec = importSpec; 1271 } 1272 else nonDefaultSpecs.push(importSpec); 1273 } 1274 if (defaultSpec) { 1275 let autofix: Autofix[] | undefined; 1276 // if (Autofixer.shouldAutofix(defaultSpec, FaultID.DefaultImport)) 1277 // autofix = [ Autofixer.fixDefaultImport(tsImportClause, defaultSpec, nonDefaultSpecs) ]; 1278 this.incrementCounters(defaultSpec, FaultID.DefaultImport, true, autofix); 1279 } 1280 } 1281 } 1282 1283 private handleImportSpecifier(node: Node): void { 1284 const tsImportSpecifier = node as ImportSpecifier; 1285 this.countDeclarationsWithDuplicateName(tsImportSpecifier.name, tsImportSpecifier); 1286 } 1287 1288 private handleNamespaceImport(node: Node): void { 1289 const tsNamespaceImport = node as NamespaceImport; 1290 this.countDeclarationsWithDuplicateName(tsNamespaceImport.name, tsNamespaceImport); 1291 } 1292 1293 private handleTypeAssertionExpression(node: Node): void { 1294 const tsTypeAssertion = node as TypeAssertion; 1295 if (tsTypeAssertion.type.getText() === "const") { 1296 this.incrementCounters(tsTypeAssertion, FaultID.ConstAssertion); 1297 } 1298 else { 1299 this.incrementCounters(node, FaultID.TypeAssertion); 1300 } 1301 } 1302 1303 private handleMethodDeclaration(node: Node): void { 1304 const tsMethodDecl = node as MethodDeclaration; 1305 const hasThis = this.functionContainsThis(tsMethodDecl); 1306 let isStatic = false; 1307 if (tsMethodDecl.modifiers) { 1308 for (const mod of tsMethodDecl.modifiers) { 1309 if (mod.kind === SyntaxKind.StaticKeyword) { 1310 isStatic = true; 1311 break; 1312 } 1313 } 1314 } 1315 1316 if (isStatic && hasThis) { 1317 this.incrementCounters(node, FaultID.FunctionContainsThis); 1318 } 1319 if (!tsMethodDecl.type) { 1320 this.handleMissingReturnType(tsMethodDecl); 1321 } 1322 if (tsMethodDecl.asteriskToken) { 1323 this.incrementCounters(node, FaultID.GeneratorFunction); 1324 } 1325 this.handleDecorators(getDecorators(tsMethodDecl)); 1326 this.filterOutDecoratorsDiagnostics(getDecorators(tsMethodDecl), NON_RETURN_FUNCTION_DECORATORS, 1327 { begin: tsMethodDecl.parameters.end, end: tsMethodDecl.body?.getStart() ?? tsMethodDecl.parameters.end }, 1328 FUNCTION_HAS_NO_RETURN_ERROR_CODE); 1329 } 1330 1331 private handleIdentifier(node: Node): void { 1332 const tsIdentifier = node as Identifier; 1333 const tsIdentSym = trueSymbolAtLocation(tsIdentifier); 1334 1335 if (!tsIdentSym) { 1336 return; 1337 } 1338 1339 if ( 1340 (tsIdentSym.flags & SymbolFlags.Module) !== 0 && 1341 (tsIdentSym.flags & SymbolFlags.Transient) !== 0 && 1342 tsIdentifier.text === "globalThis" 1343 ) { 1344 this.incrementCounters(node, FaultID.GlobalThis); 1345 } else { 1346 this.handleRestrictedValues(tsIdentifier, tsIdentSym); 1347 } 1348 } 1349 1350 private isAllowedClassValueContext(tsIdentifier: Identifier /* Param not used ! ??, tsIdentSym: Symbol */ /* Param not used ! ??, tsIdentSym: Symbol */): boolean { 1351 let ctx: Node = tsIdentifier; 1352 while (isPropertyAccessExpression(ctx.parent) || isQualifiedName(ctx.parent)) { 1353 ctx = ctx.parent; 1354 } 1355 if (isPropertyAssignment(ctx.parent) && isObjectLiteralExpression(ctx.parent.parent)) { 1356 ctx = ctx.parent.parent; 1357 } 1358 if (isArrowFunction(ctx.parent) && ctx.parent.body === ctx) { 1359 ctx = ctx.parent; 1360 } 1361 1362 if (isCallExpression(ctx.parent) || isNewExpression(ctx.parent)) { 1363 const callee = ctx.parent.expression; 1364 const isAny = isAnyType(TypeScriptLinter.tsTypeChecker.getTypeAtLocation(callee)); 1365 const isDynamic = isAny || hasLibraryType(callee); 1366 if (callee !== ctx && isDynamic) { 1367 return true; 1368 } 1369 } 1370 return false; 1371 } 1372 1373 private handleRestrictedValues(tsIdentifier: Identifier, tsIdentSym: Symbol) { 1374 const illegalValues = SymbolFlags.ConstEnum | SymbolFlags.RegularEnum | SymbolFlags.ValueModule | SymbolFlags.Class; 1375 // If module name is duplicated by another declaration, this increases the possibility 1376 // of finding a lot of false positives. Thus, do not check further in that case. 1377 if ((tsIdentSym.flags & SymbolFlags.ValueModule) !== 0) { 1378 if (!!tsIdentSym && symbolHasDuplicateName(tsIdentSym, SyntaxKind.ModuleDeclaration)) { 1379 return; 1380 } 1381 } 1382 if ((tsIdentSym.flags & illegalValues) === 0 || isStruct(tsIdentSym) || 1383 !this.identiferUseInValueContext(tsIdentifier, tsIdentSym)) { 1384 return; 1385 } 1386 if ((tsIdentSym.flags & SymbolFlags.Class) !== 0) { 1387 if (this.isAllowedClassValueContext(tsIdentifier /* param not used!?? , tsIdentSym */)) { 1388 return; 1389 } 1390 } 1391 1392 if (tsIdentSym.flags & SymbolFlags.ValueModule) { 1393 this.incrementCounters(tsIdentifier, FaultID.NamespaceAsObject); 1394 } 1395 else { 1396 // missing EnumAsObject 1397 this.incrementCounters(tsIdentifier, FaultID.ClassAsObject); 1398 } 1399 } 1400 1401 private identiferUseInValueContext( 1402 ident: Identifier, 1403 tsSym: Symbol 1404 ) { 1405 // If identifier is the right-most name of Property Access chain or Qualified name, 1406 // or it's a separate identifier expression, then identifier is being referenced as an value. 1407 let qualifiedStart: Node = ident; 1408 while (isPropertyAccessExpression(qualifiedStart.parent) || isQualifiedName(qualifiedStart.parent)) { 1409 qualifiedStart = qualifiedStart.parent; 1410 } 1411 const parent = qualifiedStart.parent; 1412 return !( 1413 // treat TypeQuery as valid because it's already forbidden (FaultID.TypeQuery) 1414 (isTypeNode(parent) && !isTypeOfExpression(parent)) || 1415 // ElementAccess is allowed for enum types 1416 this.isEnumPropAccess(ident, tsSym, parent) || 1417 isExpressionWithTypeArguments(parent) || 1418 isExportAssignment(parent) || 1419 isExportSpecifier(parent) || 1420 isMetaProperty(parent) || 1421 isImportClause(parent) || 1422 isClassLike(parent) || 1423 isInterfaceDeclaration(parent) || 1424 isModuleDeclaration(parent) || 1425 isEnumDeclaration(parent) || 1426 isNamespaceImport(parent) || 1427 isImportSpecifier(parent) || 1428 isImportEqualsDeclaration(parent) || 1429 (isQualifiedName(qualifiedStart) && ident !== qualifiedStart.right) || 1430 (isPropertyAccessExpression(qualifiedStart) && 1431 ident !== qualifiedStart.name) || 1432 (isNewExpression(qualifiedStart.parent) && 1433 qualifiedStart === qualifiedStart.parent.expression) || 1434 (isBinaryExpression(qualifiedStart.parent) && 1435 qualifiedStart.parent.operatorToken.kind === 1436 SyntaxKind.InstanceOfKeyword) 1437 ); 1438 } 1439 1440 private isEnumPropAccess(ident: Identifier, tsSym: Symbol, context: Node): boolean { 1441 return isElementAccessExpression(context) && !!(tsSym.flags & SymbolFlags.Enum) && 1442 (context.expression == ident || 1443 (isPropertyAccessExpression(context.expression) && context.expression.name === ident)); 1444 } 1445 1446 private handleElementAccessExpression(node: Node): void { 1447 const tsElementAccessExpr = node as ElementAccessExpression; 1448 const tsElemAccessBaseExprType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(tsElementAccessExpr.expression); 1449 const tsElemAccessBaseExprTypeNode = TypeScriptLinter.tsTypeChecker.typeToTypeNode(tsElemAccessBaseExprType, undefined, NodeBuilderFlags.None); 1450 const isDerivedFromArray = isDerivedFrom(tsElemAccessBaseExprType, CheckType.Array); 1451 const checkClassOrInterface = tsElemAccessBaseExprType.isClassOrInterface() && 1452 !isGenericArrayType(tsElemAccessBaseExprType) && 1453 !isDerivedFromArray; 1454 const checkThisOrSuper = isThisOrSuperExpr(tsElementAccessExpr.expression) && !isDerivedFromArray; 1455 1456 // if (this.tsUtils.isEnumType(tsElemAccessBaseExprType)) { 1457 // implement argument expression type check 1458 // let argType = this.tsTypeChecker.getTypeAtLocation(tsElementAccessExpr.argumentExpression); 1459 // if (argType.aliasSymbol == this.tsUtils.trueSymbolAtLocation(tsElementAccessExpr.expression)) { 1460 // return; 1461 // } 1462 // check if constant EnumMember inferred ... 1463 // this.incrementCounters(node, FaultID.PropertyAccessByIndex, autofixable, autofix); 1464 // } 1465 if ( 1466 !isLibraryType(tsElemAccessBaseExprType) && !isTypedArray(tsElemAccessBaseExprTypeNode) && 1467 (checkClassOrInterface || 1468 isObjectLiteralType(tsElemAccessBaseExprType) || checkThisOrSuper) 1469 ) { 1470 let autofix = fixPropertyAccessByIndex(node); 1471 const autofixable = autofix !== undefined; 1472 if (!shouldAutofix(node, FaultID.PropertyAccessByIndex)) { 1473 autofix = undefined; 1474 } 1475 this.incrementCounters(node, FaultID.PropertyAccessByIndex, autofixable, autofix); 1476 } 1477 if (hasEsObjectType(tsElementAccessExpr.expression)) { 1478 this.incrementCounters(node, FaultID.EsObjectType); 1479 } 1480 } 1481 1482 private handleEnumMember(node: Node): void { 1483 const tsEnumMember = node as EnumMember; 1484 const tsEnumMemberType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(tsEnumMember); 1485 const constVal = TypeScriptLinter.tsTypeChecker.getConstantValue(tsEnumMember); 1486 1487 if (tsEnumMember.initializer && !isValidEnumMemberInit(tsEnumMember.initializer)) { 1488 this.incrementCounters(node, FaultID.EnumMemberNonConstInit); 1489 } 1490 // check for type - all members should be of same type 1491 const enumDecl = tsEnumMember.parent; 1492 const firstEnumMember = enumDecl.members[0]; 1493 const firstEnumMemberType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(firstEnumMember); 1494 const firstElewmVal = TypeScriptLinter.tsTypeChecker.getConstantValue(firstEnumMember); 1495 // each string enum member has its own type 1496 // so check that value type is string 1497 if(constVal !==undefined && typeof constVal === "string" && 1498 firstElewmVal !==undefined && typeof firstElewmVal === "string") { 1499 return; 1500 } 1501 if (constVal !==undefined && typeof constVal === "number" && 1502 firstElewmVal !==undefined && typeof firstElewmVal === "number") { 1503 return; 1504 } 1505 if(firstEnumMemberType !== tsEnumMemberType) { 1506 this.incrementCounters(node, FaultID.EnumMemberNonConstInit); 1507 } 1508 } 1509 1510 private handleExportAssignment(node: Node): void { 1511 // (nsizov): check exportEquals and determine if it's an actual `export assignment` 1512 // or a `default export namespace` when this two cases will be determined in cookbook 1513 const exportAssignment = node as ExportAssignment; 1514 if (exportAssignment.isExportEquals) { 1515 this.incrementCounters(node, FaultID.ExportAssignment); 1516 } 1517 } 1518 1519 private handleCallExpression(node: Node): void { 1520 const tsCallExpr = node as CallExpression; 1521 1522 const calleeSym = trueSymbolAtLocation(tsCallExpr.expression); 1523 const calleeType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(tsCallExpr.expression); 1524 const callSignature = TypeScriptLinter.tsTypeChecker.getResolvedSignature(tsCallExpr); 1525 1526 this.handleImportCall(tsCallExpr); 1527 this.handleRequireCall(tsCallExpr); 1528 // NOTE: Keep handleFunctionApplyBindPropCall above handleGenericCallWithNoTypeArgs here!!! 1529 if (calleeSym !== undefined) { 1530 this.handleStdlibAPICall(tsCallExpr, calleeSym); 1531 this.handleFunctionApplyBindPropCall(tsCallExpr, calleeSym); 1532 if (symbolHasEsObjectType(calleeSym)) { 1533 this.incrementCounters(tsCallExpr, FaultID.EsObjectType); 1534 } 1535 } 1536 if (callSignature !== undefined) { 1537 if (!isLibrarySymbol(calleeSym)) { 1538 this.handleGenericCallWithNoTypeArgs(tsCallExpr, callSignature); 1539 } 1540 this.handleStructIdentAndUndefinedInArgs(tsCallExpr, callSignature); 1541 } 1542 this.handleLibraryTypeCall(tsCallExpr, calleeType); 1543 1544 if (isPropertyAccessExpression(tsCallExpr.expression) && hasEsObjectType(tsCallExpr.expression.expression)) { 1545 this.incrementCounters(node, FaultID.EsObjectType); 1546 } 1547 } 1548 1549 private handleImportCall(tsCallExpr: CallExpression): void { 1550 if (tsCallExpr.expression.kind === SyntaxKind.ImportKeyword) { 1551 // relax rule#133 "arkts-no-runtime-import" 1552 // this.incrementCounters(tsCallExpr, FaultID.DynamicImport); 1553 const tsArgs = tsCallExpr.arguments; 1554 if (tsArgs.length > 1 && isObjectLiteralExpression(tsArgs[1])) { 1555 const objLitExpr = tsArgs[1] as ObjectLiteralExpression; 1556 for (const tsProp of objLitExpr.properties) { 1557 if (isPropertyAssignment(tsProp) || isShorthandPropertyAssignment(tsProp)) { 1558 if (tsProp.name.getText() === "assert") { 1559 this.incrementCounters(tsProp, FaultID.ImportAssertion); 1560 break; 1561 } 1562 } 1563 } 1564 } 1565 } 1566 } 1567 1568 private handleRequireCall(tsCallExpr: CallExpression): void { 1569 if ( 1570 isIdentifier(tsCallExpr.expression) && 1571 tsCallExpr.expression.text === "require" && 1572 isVariableDeclaration(tsCallExpr.parent) 1573 ) { 1574 const tsType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(tsCallExpr.expression); 1575 if (isInterfaceType(tsType) && tsType.symbol.name === "NodeRequire") { 1576 this.incrementCounters(tsCallExpr.parent, FaultID.ImportAssignment); 1577 } 1578 } 1579 } 1580 1581 private handleGenericCallWithNoTypeArgs(callLikeExpr: CallExpression | NewExpression, callSignature: Signature) { 1582 1583 const tsSyntaxKind = isNewExpression(callLikeExpr) ? SyntaxKind.Constructor : SyntaxKind.FunctionDeclaration; 1584 const signDecl = TypeScriptLinter.tsTypeChecker.signatureToSignatureDeclaration(callSignature, tsSyntaxKind, 1585 undefined, NodeBuilderFlags.WriteTypeArgumentsOfSignature | NodeBuilderFlags.IgnoreErrors); 1586 1587 if (signDecl?.typeArguments) { 1588 const resolvedTypeArgs = signDecl.typeArguments; 1589 1590 const startTypeArg = callLikeExpr.typeArguments?.length ?? 0; 1591 for (let i = startTypeArg; i < resolvedTypeArgs.length; ++i) { 1592 const typeNode = resolvedTypeArgs[i]; 1593 // if compiler infers 'unknown' type there are 2 possible cases: 1594 // 1. Compiler unable to infer type from arguments and use 'unknown' 1595 // 2. Compiler infer 'unknown' from arguments 1596 // We report error in both cases. It is ok because we cannot use 'unknown' 1597 // in ArkTS and already have separate check for it. 1598 if (typeNode.kind === SyntaxKind.UnknownKeyword) { 1599 this.incrementCounters(callLikeExpr, FaultID.GenericCallNoTypeArgs); 1600 break; 1601 } 1602 } 1603 } 1604 } 1605 1606 private static listApplyBindCallApis = [ 1607 "Function.apply", 1608 "Function.call", 1609 "Function.bind", 1610 "CallableFunction.apply", 1611 "CallableFunction.call", 1612 "CallableFunction.bind" 1613 ]; 1614 private handleFunctionApplyBindPropCall(tsCallExpr: CallExpression, calleeSym: Symbol) { 1615 const exprName = TypeScriptLinter.tsTypeChecker.getFullyQualifiedName(calleeSym); 1616 if (TypeScriptLinter.listApplyBindCallApis.includes(exprName)) { 1617 this.incrementCounters(tsCallExpr, FaultID.FunctionApplyBindCall); 1618 } 1619 } 1620 1621 private handleStructIdentAndUndefinedInArgs(tsCallOrNewExpr: CallExpression | NewExpression, callSignature: Signature) { 1622 if (!tsCallOrNewExpr.arguments) { 1623 return; 1624 } 1625 1626 for (let argIndex = 0; argIndex < tsCallOrNewExpr.arguments.length; ++argIndex) { 1627 const tsArg = tsCallOrNewExpr.arguments[argIndex]; 1628 const tsArgType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(tsArg); 1629 if (!tsArgType) continue; 1630 1631 let paramIndex = argIndex < callSignature.parameters.length ? argIndex : callSignature.parameters.length-1; 1632 let tsParamSym = callSignature.parameters[paramIndex]; 1633 if (!tsParamSym) continue; 1634 1635 const tsParamDecl = tsParamSym.valueDeclaration; 1636 if (tsParamDecl && isParameter(tsParamDecl)) { 1637 let tsParamType = TypeScriptLinter.tsTypeChecker.getTypeOfSymbolAtLocation(tsParamSym, tsParamDecl); 1638 if (tsParamDecl.dotDotDotToken && isGenericArrayType(tsParamType) && tsParamType.typeArguments) { 1639 tsParamType = tsParamType.typeArguments[0]; 1640 } 1641 if (!tsParamType) continue; 1642 1643 if (needToDeduceStructuralIdentity(tsArgType, tsParamType)) { 1644 this.incrementCounters(tsArg, FaultID.StructuralIdentity); 1645 } 1646 } 1647 } 1648 } 1649 1650 // let re = new RegExp("^(" + arr.reduce((acc, v) => ((acc ? (acc + "|") : "") + v)) +")$") 1651 private static LimitedApis = new Map<string, {arr: Array<string> | null, fault: FaultID}> ([ 1652 ["global", {arr: LIMITED_STD_GLOBAL_FUNC, fault: FaultID.LimitedStdLibApi}], 1653 ["Object", {arr: LIMITED_STD_OBJECT_API, fault: FaultID.LimitedStdLibApi}], 1654 ["ObjectConstructor", {arr: LIMITED_STD_OBJECT_API, fault: FaultID.LimitedStdLibApi}], 1655 ["Reflect", {arr: LIMITED_STD_REFLECT_API, fault: FaultID.LimitedStdLibApi}], 1656 ["ProxyHandler", {arr: LIMITED_STD_PROXYHANDLER_API, fault: FaultID.LimitedStdLibApi}], 1657 ["Symbol", {arr: null, fault: FaultID.SymbolType}], 1658 ["SymbolConstructor", {arr: null, fault: FaultID.SymbolType}], 1659 ]) 1660 1661 private handleStdlibAPICall(callExpr: CallExpression, calleeSym: Symbol) { 1662 const name = calleeSym.getName(); 1663 const parName = getParentSymbolName(calleeSym); 1664 if (parName === undefined) { 1665 if (LIMITED_STD_GLOBAL_FUNC.includes(name)) { 1666 this.incrementCounters(callExpr, FaultID.LimitedStdLibApi); 1667 return; 1668 } 1669 let escapedName = calleeSym.escapedName; 1670 if (escapedName === 'Symbol' || escapedName === 'SymbolConstructor') { 1671 this.incrementCounters(callExpr, FaultID.SymbolType); 1672 } 1673 return; 1674 } 1675 let lookup = TypeScriptLinter.LimitedApis.get(parName); 1676 if (lookup !== undefined && (lookup.arr === null || lookup.arr.includes(name))) { 1677 this.incrementCounters(callExpr, lookup.fault); 1678 }; 1679 } 1680 1681 private findNonFilteringRangesFunctionCalls(callExpr: CallExpression): { begin: number, end: number }[] { 1682 let args = callExpr.arguments; 1683 let result: { begin: number, end: number }[] = []; 1684 for (const arg of args) { 1685 if (isArrowFunction(arg)) { 1686 let arrowFuncExpr = arg as ArrowFunction; 1687 result.push({begin: arrowFuncExpr.body.pos, end: arrowFuncExpr.body.end}); 1688 } else if (isCallExpression(arg)) { 1689 result.push({begin: arg.arguments.pos, end: arg.arguments.end}); 1690 } 1691 // there may be other cases 1692 } 1693 return result; 1694 } 1695 1696 private handleLibraryTypeCall(callExpr: CallExpression, calleeType: Type) { 1697 let inLibCall = isLibraryType(calleeType); 1698 const diagnosticMessages: Array<DiagnosticMessageChain> = []; 1699 this.libraryTypeCallDiagnosticChecker.configure(inLibCall, diagnosticMessages); 1700 1701 let nonFilteringRanges = this.findNonFilteringRangesFunctionCalls(callExpr); 1702 let rangesToFilter: { begin: number, end: number }[] = []; 1703 if (nonFilteringRanges.length !== 0) { 1704 let rangesSize = nonFilteringRanges.length; 1705 rangesToFilter.push({ begin: callExpr.arguments.pos, end: nonFilteringRanges[0].begin }) 1706 rangesToFilter.push({ begin: nonFilteringRanges[rangesSize - 1].end, end: callExpr.arguments.end }) 1707 for (let i = 0; i < rangesSize - 1; i++) { 1708 rangesToFilter.push({ begin: nonFilteringRanges[i].end, end: nonFilteringRanges[i + 1].begin }) 1709 } 1710 } else { 1711 rangesToFilter.push({ begin: callExpr.arguments.pos, end: callExpr.arguments.end }) 1712 } 1713 1714 this.filterStrictDiagnostics({ 1715 [ARGUMENT_OF_TYPE_0_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_ERROR_CODE]: (pos: number) => { 1716 return this.checkInRange([{begin: callExpr.pos, end: callExpr.end}], pos); 1717 }, 1718 [NO_OVERLOAD_MATCHES_THIS_CALL_ERROR_CODE]: (pos: number) => { 1719 return this.checkInRange([{begin: callExpr.pos, end: callExpr.end}], pos); 1720 }, 1721 [TYPE_0_IS_NOT_ASSIGNABLE_TO_TYPE_1_ERROR_CODE]: (pos: number) => { 1722 return this.checkInRange(rangesToFilter, pos); 1723 } 1724 }, 1725 this.libraryTypeCallDiagnosticChecker 1726 ); 1727 1728 for (const msgChain of diagnosticMessages) { 1729 TypeScriptLinter.filteredDiagnosticMessages.push(msgChain); 1730 } 1731 } 1732 1733 private handleNewExpression(node: Node): void { 1734 const tsNewExpr = node as NewExpression; 1735 let callSignature = TypeScriptLinter.tsTypeChecker.getResolvedSignature(tsNewExpr); 1736 if (callSignature !== undefined) { 1737 this.handleStructIdentAndUndefinedInArgs(tsNewExpr, callSignature); 1738 this.handleGenericCallWithNoTypeArgs(tsNewExpr, callSignature); 1739 } 1740 } 1741 1742 private handleAsExpression(node: Node): void { 1743 const tsAsExpr = node as AsExpression; 1744 if (tsAsExpr.type.getText() === "const") this.incrementCounters(node, FaultID.ConstAssertion); 1745 1746 const targetType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(tsAsExpr.type).getNonNullableType(); 1747 const exprType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(tsAsExpr.expression).getNonNullableType(); 1748 if (needToDeduceStructuralIdentity(exprType, targetType, true)) { 1749 this.incrementCounters(tsAsExpr, FaultID.StructuralIdentity); 1750 } 1751 // check for rule#65: "number as Number" and "boolean as Boolean" are disabled 1752 if( 1753 (isNumberType(exprType) && targetType.getSymbol()?.getName() === "Number") || 1754 (isBooleanType(exprType) && targetType.getSymbol()?.getName() === "Boolean") 1755 ) { 1756 this.incrementCounters(node, FaultID.TypeAssertion); 1757 } 1758 } 1759 1760 private handleTypeReference(node: Node): void { 1761 const typeRef = node as TypeReferenceNode; 1762 1763 const isESObject = isEsObjectType(typeRef); 1764 const isPossiblyValidContext = isEsObjectPossiblyAllowed(typeRef); 1765 if (isESObject && !isPossiblyValidContext) { 1766 this.incrementCounters(node, FaultID.EsObjectType); 1767 return; 1768 } 1769 const typeName = entityNameToString(typeRef.typeName); 1770 const isStdUtilityType = LIMITED_STANDARD_UTILITY_TYPES.includes(typeName); 1771 if (isStdUtilityType) { 1772 this.incrementCounters(node, FaultID.UtilityType); 1773 return; 1774 } 1775 1776 // Using Partial<T> type is allowed only when its argument type is either Class or Interface. 1777 const isStdPartial = entityNameToString(typeRef.typeName) === 'Partial'; 1778 const hasSingleTypeArgument = !!typeRef.typeArguments && typeRef.typeArguments.length === 1; 1779 const firstTypeArg = !!typeRef.typeArguments && hasSingleTypeArgument && typeRef.typeArguments[0]; 1780 const argType = firstTypeArg && TypeScriptLinter.tsTypeChecker.getTypeFromTypeNode(firstTypeArg); 1781 if (isStdPartial && argType && !argType.isClassOrInterface()) { 1782 this.incrementCounters(node, FaultID.UtilityType); 1783 return; 1784 } 1785 } 1786 1787 private handleMetaProperty(node: Node): void { 1788 const tsMetaProperty = node as MetaProperty; 1789 if (tsMetaProperty.name.text === "target") { 1790 this.incrementCounters(node, FaultID.NewTarget); 1791 } 1792 } 1793 1794 private handleStructDeclaration(node: Node): void { 1795 node.forEachChild(child => { 1796 // Skip synthetic constructor in Struct declaration. 1797 if (!isConstructorDeclaration(child)) this.visitTSNode(child); 1798 }); 1799 } 1800 1801 private handleSpreadOp(node: Node) { 1802 // spread assignment is disabled 1803 // spread element is allowed only for arrays as rest parameter 1804 if (isSpreadElement(node)) { 1805 const spreadElemNode = node; 1806 const spreadExprType = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(spreadElemNode.expression); 1807 if (spreadExprType) { 1808 const spreadExprTypeNode = TypeScriptLinter.tsTypeChecker.typeToTypeNode(spreadExprType, undefined, NodeBuilderFlags.None); 1809 if (spreadExprTypeNode !== undefined && 1810 (isCallLikeExpression(node.parent) || isArrayLiteralExpression(node.parent))) { 1811 if (isArrayTypeNode(spreadExprTypeNode) || 1812 isTypedArray(spreadExprTypeNode) || 1813 isDerivedFrom(spreadExprType, CheckType.Array)) { 1814 return; 1815 } 1816 } 1817 } 1818 } 1819 this.incrementCounters(node, FaultID.SpreadOperator); 1820 } 1821 1822 private handleConstructSignature(node: Node) { 1823 switch (node.parent.kind) { 1824 case SyntaxKind.TypeLiteral: 1825 this.incrementCounters(node, FaultID.ConstructorType); 1826 break; 1827 case SyntaxKind.InterfaceDeclaration: 1828 this.incrementCounters(node, FaultID.ConstructorIface); 1829 break; 1830 default: 1831 return; 1832 } 1833 } 1834 1835 private handleComments(node: Node) { 1836 // Note: Same comment may be owned by several nodes if their 1837 // start/end position matches. Thus, look for the most parental 1838 // owner of the specific comment (by the node's position). 1839 const srcText = node.getSourceFile().getFullText(); 1840 1841 const parent = node.parent; 1842 if (!parent || parent.getFullStart() !== node.getFullStart()) { 1843 const leadingComments = getLeadingCommentRanges(srcText, node.getFullStart()); 1844 if (leadingComments) { 1845 for (const comment of leadingComments) { 1846 this.checkErrorSuppressingAnnotation(comment, srcText); 1847 } 1848 } 1849 } 1850 1851 if (!parent || parent.getEnd() !== node.getEnd()) { 1852 const trailingComments = getTrailingCommentRanges(srcText, node.getEnd()); 1853 if (trailingComments) { 1854 for (const comment of trailingComments) { 1855 this.checkErrorSuppressingAnnotation(comment, srcText); 1856 } 1857 } 1858 } 1859 } 1860 1861 private handleExpressionWithTypeArguments(node: Node) { 1862 const tsTypeExpr = node as ExpressionWithTypeArguments; 1863 const symbol = trueSymbolAtLocation(tsTypeExpr.expression); 1864 if (!!symbol && isEsObjectSymbol(symbol)) { 1865 this.incrementCounters(tsTypeExpr, FaultID.EsObjectType); 1866 } 1867 } 1868 1869 private handleComputedPropertyName(node: Node) { 1870 const computedProperty = node as ComputedPropertyName; 1871 const symbol = trueSymbolAtLocation(computedProperty.expression); 1872 if (!!symbol && isSymbolIterator(symbol)) { 1873 return; 1874 } 1875 this.incrementCounters(node, FaultID.ComputedPropertyName); 1876 } 1877 1878 private checkErrorSuppressingAnnotation(comment: CommentRange, srcText: string) { 1879 const commentContent = comment.kind === SyntaxKind.MultiLineCommentTrivia 1880 ? srcText.slice(comment.pos + 2, comment.end - 2) 1881 : srcText.slice(comment.pos + 2, comment.end); 1882 // if comment is multiline end closing '*/' is not at the same line as '@ts-xxx' - do nothing (#13851) 1883 if (commentContent.endsWith('\n')) { 1884 return 1885 } 1886 const trimmedContent = commentContent.trim(); 1887 if (trimmedContent.startsWith("@ts-ignore") || 1888 trimmedContent.startsWith("@ts-nocheck") || 1889 trimmedContent.startsWith("@ts-expect-error")) { 1890 this.incrementCounters(comment, FaultID.ErrorSuppression); 1891 } 1892 } 1893 1894 private handleDecorators(decorators: readonly Decorator[] | undefined): void { 1895 if (!decorators) return; 1896 1897 for (const decorator of decorators) { 1898 let decoratorName = ""; 1899 if (isIdentifier(decorator.expression)) { 1900 decoratorName = decorator.expression.text; 1901 } 1902 else if (isCallExpression(decorator.expression) && isIdentifier(decorator.expression.expression)) { 1903 decoratorName = decorator.expression.expression.text; 1904 } 1905 if (!ARKUI_DECORATORS.includes(decoratorName)) { 1906 this.incrementCounters(decorator, FaultID.UnsupportedDecorators); 1907 } 1908 } 1909 } 1910 1911 private handleGetAccessor(node: Node) { 1912 this.handleDecorators(getDecorators(node as GetAccessorDeclaration)); 1913 } 1914 1915 private handleSetAccessor(node: Node) { 1916 this.handleDecorators(getDecorators(node as SetAccessorDeclaration)); 1917 } 1918 1919 private handleDeclarationInferredType( 1920 decl: VariableDeclaration | PropertyDeclaration | ParameterDeclaration 1921 ) { 1922 // The type is explicitly specified, no need to check inferred type. 1923 if (decl.type) return; 1924 1925 // issue 13161: 1926 // In TypeScript, the catch clause variable must be 'any' or 'unknown' type. Since 1927 // ArkTS doesn't support these types, the type for such variable is simply omitted, 1928 // and we don't report it as an error. 1929 if (isCatchClause(decl.parent)) return; 1930 1931 //Destructuring declarations are not supported, do not process them. 1932 if (isArrayBindingPattern(decl.name) || isObjectBindingPattern(decl.name)) return; 1933 1934 const type = TypeScriptLinter.tsTypeChecker.getTypeAtLocation(decl); 1935 if (type) this.validateDeclInferredType(type, decl); 1936 } 1937 1938 private handleDefiniteAssignmentAssertion(decl: VariableDeclaration | PropertyDeclaration) { 1939 if (decl.exclamationToken !== undefined) { 1940 this.incrementCounters(decl, FaultID.DefiniteAssignment); 1941 } 1942 } 1943 1944 private validatedTypesSet = new Set<Type>(); 1945 1946private checkAnyOrUnknownChildNode(node: Node): boolean { 1947 if (node.kind === SyntaxKind.AnyKeyword || 1948 node.kind === SyntaxKind.UnknownKeyword) { 1949 return true; 1950 } 1951 const isAnyOrUnknown = forEachChild(node, (child) => { 1952 if (this.checkAnyOrUnknownChildNode(child)) { 1953 return true; 1954 } 1955 return undefined; 1956 }); 1957 return !!isAnyOrUnknown; 1958} 1959 1960 private handleInferredObjectreference( 1961 type: Type, 1962 decl: VariableDeclaration | PropertyDeclaration | ParameterDeclaration 1963 ) { 1964 const typeArgs = TypeScriptLinter.tsTypeChecker.getTypeArguments(type as TypeReference); 1965 if (typeArgs) { 1966 const haveAnyOrUnknownNodes = this.checkAnyOrUnknownChildNode(decl); 1967 if (!haveAnyOrUnknownNodes) { 1968 for (const typeArg of typeArgs) { 1969 this.validateDeclInferredType(typeArg, decl); 1970 } 1971 } 1972 } 1973 } 1974 1975 private validateDeclInferredType( 1976 type: Type, 1977 decl: VariableDeclaration | PropertyDeclaration | ParameterDeclaration 1978 ): void { 1979 if (type.aliasSymbol !== undefined) { 1980 return; 1981 } 1982 const isObject = type.flags & TypeFlags.Object; 1983 const isReference = (type as ObjectType).objectFlags & ObjectFlags.Reference; 1984 if (isObject && isReference) { 1985 this.handleInferredObjectreference(type, decl); 1986 return; 1987 } 1988 if (this.validatedTypesSet.has(type)) { 1989 return; 1990 } 1991 if (type.isUnion()) { 1992 this.validatedTypesSet.add(type); 1993 for (let unionElem of type.types) { 1994 this.validateDeclInferredType(unionElem, decl); 1995 } 1996 } 1997 1998 if (isAnyType(type)) { 1999 this.incrementCounters(decl, FaultID.AnyType); 2000 } 2001 else if (isUnknownType(type)) { 2002 this.incrementCounters(decl, FaultID.UnknownType); 2003 } 2004 } 2005 2006 private handleClassStaticBlockDeclaration(node: Node): void { 2007 if (this.skipArkTSStaticBlocksCheck) { 2008 return; 2009 } 2010 const classStaticBlockDecl = node as ClassStaticBlockDeclaration; 2011 if (!isClassDeclaration(classStaticBlockDecl.parent)) { 2012 return; 2013 } 2014 if (this.functionContainsThis(node)) { 2015 this.incrementCounters(node, FaultID.FunctionContainsThis); 2016 } 2017 } 2018 2019 public lint(): void { 2020 this.visitTSNode(this.sourceFile); 2021 } 2022 2023}