1namespace ts { 2 const enum SignatureFlags { 3 None = 0, 4 Yield = 1 << 0, 5 Await = 1 << 1, 6 Type = 1 << 2, 7 IgnoreMissingOpenBrace = 1 << 4, 8 JSDoc = 1 << 5, 9 } 10 11 const enum SpeculationKind { 12 TryParse, 13 Lookahead, 14 Reparse 15 } 16 17 let NodeConstructor: new (kind: SyntaxKind, pos?: number, end?: number) => Node; 18 let TokenConstructor: new (kind: SyntaxKind, pos?: number, end?: number) => Node; 19 let IdentifierConstructor: new (kind: SyntaxKind, pos?: number, end?: number) => Node; 20 let PrivateIdentifierConstructor: new (kind: SyntaxKind, pos?: number, end?: number) => Node; 21 let SourceFileConstructor: new (kind: SyntaxKind, pos?: number, end?: number) => Node; 22 23 /** 24 * NOTE: You should not use this, it is only exported to support `createNode` in `~/src/deprecatedCompat/deprecations.ts`. 25 */ 26 /* @internal */ 27 export const parseBaseNodeFactory: BaseNodeFactory = { 28 createBaseSourceFileNode: kind => new (SourceFileConstructor || (SourceFileConstructor = objectAllocator.getSourceFileConstructor()))(kind, -1, -1), 29 createBaseIdentifierNode: kind => new (IdentifierConstructor || (IdentifierConstructor = objectAllocator.getIdentifierConstructor()))(kind, -1, -1), 30 createBasePrivateIdentifierNode: kind => new (PrivateIdentifierConstructor || (PrivateIdentifierConstructor = objectAllocator.getPrivateIdentifierConstructor()))(kind, -1, -1), 31 createBaseTokenNode: kind => new (TokenConstructor || (TokenConstructor = objectAllocator.getTokenConstructor()))(kind, -1, -1), 32 createBaseNode: kind => new (NodeConstructor || (NodeConstructor = objectAllocator.getNodeConstructor()))(kind, -1, -1), 33 }; 34 35 /* @internal */ 36 export const parseNodeFactory = createNodeFactory(NodeFactoryFlags.NoParenthesizerRules, parseBaseNodeFactory); 37 38 function visitNode<T>(cbNode: (node: Node) => T, node: Node | undefined): T | undefined { 39 return node && cbNode(node); 40 } 41 42 function visitNodes<T>(cbNode: (node: Node) => T, cbNodes: ((node: NodeArray<Node>) => T | undefined) | undefined, nodes: NodeArray<Node> | undefined): T | undefined { 43 if (nodes) { 44 if (cbNodes) { 45 return cbNodes(nodes); 46 } 47 for (const node of nodes) { 48 const result = cbNode(node); 49 if (result) { 50 return result; 51 } 52 } 53 } 54 } 55 56 /*@internal*/ 57 export function isJSDocLikeText(text: string, start: number) { 58 return text.charCodeAt(start + 1) === CharacterCodes.asterisk && 59 text.charCodeAt(start + 2) === CharacterCodes.asterisk && 60 text.charCodeAt(start + 3) !== CharacterCodes.slash; 61 } 62 63 /** 64 * Invokes a callback for each child of the given node. The 'cbNode' callback is invoked for all child nodes 65 * stored in properties. If a 'cbNodes' callback is specified, it is invoked for embedded arrays; otherwise, 66 * embedded arrays are flattened and the 'cbNode' callback is invoked for each element. If a callback returns 67 * a truthy value, iteration stops and that value is returned. Otherwise, undefined is returned. 68 * 69 * @param node a given node to visit its children 70 * @param cbNode a callback to be invoked for all child nodes 71 * @param cbNodes a callback to be invoked for embedded array 72 * 73 * @remarks `forEachChild` must visit the children of a node in the order 74 * that they appear in the source code. The language service depends on this property to locate nodes by position. 75 */ 76 export function forEachChild<T>(node: Node, cbNode: (node: Node) => T | undefined, cbNodes?: (nodes: NodeArray<Node>) => T | undefined): T | undefined { 77 if (!node || node.kind <= SyntaxKind.LastToken) { 78 return; 79 } 80 switch (node.kind) { 81 case SyntaxKind.QualifiedName: 82 return visitNode(cbNode, (<QualifiedName>node).left) || 83 visitNode(cbNode, (<QualifiedName>node).right); 84 case SyntaxKind.TypeParameter: 85 return visitNode(cbNode, (<TypeParameterDeclaration>node).name) || 86 visitNode(cbNode, (<TypeParameterDeclaration>node).constraint) || 87 visitNode(cbNode, (<TypeParameterDeclaration>node).default) || 88 visitNode(cbNode, (<TypeParameterDeclaration>node).expression); 89 case SyntaxKind.ShorthandPropertyAssignment: 90 return visitNodes(cbNode, cbNodes, node.decorators) || 91 visitNodes(cbNode, cbNodes, node.modifiers) || 92 visitNode(cbNode, (<ShorthandPropertyAssignment>node).name) || 93 visitNode(cbNode, (<ShorthandPropertyAssignment>node).questionToken) || 94 visitNode(cbNode, (<ShorthandPropertyAssignment>node).exclamationToken) || 95 visitNode(cbNode, (<ShorthandPropertyAssignment>node).equalsToken) || 96 visitNode(cbNode, (<ShorthandPropertyAssignment>node).objectAssignmentInitializer); 97 case SyntaxKind.SpreadAssignment: 98 return visitNode(cbNode, (<SpreadAssignment>node).expression); 99 case SyntaxKind.Parameter: 100 return visitNodes(cbNode, cbNodes, node.decorators) || 101 visitNodes(cbNode, cbNodes, node.modifiers) || 102 visitNode(cbNode, (<ParameterDeclaration>node).dotDotDotToken) || 103 visitNode(cbNode, (<ParameterDeclaration>node).name) || 104 visitNode(cbNode, (<ParameterDeclaration>node).questionToken) || 105 visitNode(cbNode, (<ParameterDeclaration>node).type) || 106 visitNode(cbNode, (<ParameterDeclaration>node).initializer); 107 case SyntaxKind.PropertyDeclaration: 108 return visitNodes(cbNode, cbNodes, node.decorators) || 109 visitNodes(cbNode, cbNodes, node.modifiers) || 110 visitNode(cbNode, (<PropertyDeclaration>node).name) || 111 visitNode(cbNode, (<PropertyDeclaration>node).questionToken) || 112 visitNode(cbNode, (<PropertyDeclaration>node).exclamationToken) || 113 visitNode(cbNode, (<PropertyDeclaration>node).type) || 114 visitNode(cbNode, (<PropertyDeclaration>node).initializer); 115 case SyntaxKind.PropertySignature: 116 return visitNodes(cbNode, cbNodes, node.decorators) || 117 visitNodes(cbNode, cbNodes, node.modifiers) || 118 visitNode(cbNode, (<PropertySignature>node).name) || 119 visitNode(cbNode, (<PropertySignature>node).questionToken) || 120 visitNode(cbNode, (<PropertySignature>node).type) || 121 visitNode(cbNode, (<PropertySignature>node).initializer); 122 case SyntaxKind.PropertyAssignment: 123 return visitNodes(cbNode, cbNodes, node.decorators) || 124 visitNodes(cbNode, cbNodes, node.modifiers) || 125 visitNode(cbNode, (<PropertyAssignment>node).name) || 126 visitNode(cbNode, (<PropertyAssignment>node).questionToken) || 127 visitNode(cbNode, (<PropertyAssignment>node).initializer); 128 case SyntaxKind.VariableDeclaration: 129 return visitNodes(cbNode, cbNodes, node.decorators) || 130 visitNodes(cbNode, cbNodes, node.modifiers) || 131 visitNode(cbNode, (<VariableDeclaration>node).name) || 132 visitNode(cbNode, (<VariableDeclaration>node).exclamationToken) || 133 visitNode(cbNode, (<VariableDeclaration>node).type) || 134 visitNode(cbNode, (<VariableDeclaration>node).initializer); 135 case SyntaxKind.BindingElement: 136 return visitNodes(cbNode, cbNodes, node.decorators) || 137 visitNodes(cbNode, cbNodes, node.modifiers) || 138 visitNode(cbNode, (<BindingElement>node).dotDotDotToken) || 139 visitNode(cbNode, (<BindingElement>node).propertyName) || 140 visitNode(cbNode, (<BindingElement>node).name) || 141 visitNode(cbNode, (<BindingElement>node).initializer); 142 case SyntaxKind.FunctionType: 143 case SyntaxKind.ConstructorType: 144 case SyntaxKind.CallSignature: 145 case SyntaxKind.ConstructSignature: 146 case SyntaxKind.IndexSignature: 147 return visitNodes(cbNode, cbNodes, node.decorators) || 148 visitNodes(cbNode, cbNodes, node.modifiers) || 149 visitNodes(cbNode, cbNodes, (<SignatureDeclaration>node).typeParameters) || 150 visitNodes(cbNode, cbNodes, (<SignatureDeclaration>node).parameters) || 151 visitNode(cbNode, (<SignatureDeclaration>node).type); 152 case SyntaxKind.EtsComponentExpression: 153 return visitNode(cbNode, (<EtsComponentExpression>node).expression) || 154 visitNodes(cbNode, cbNodes, (<EtsComponentExpression>node).arguments) || 155 visitNode(cbNode, (<EtsComponentExpression>node).body); 156 case SyntaxKind.MethodDeclaration: 157 case SyntaxKind.MethodSignature: 158 case SyntaxKind.Constructor: 159 case SyntaxKind.GetAccessor: 160 case SyntaxKind.SetAccessor: 161 case SyntaxKind.FunctionExpression: 162 case SyntaxKind.FunctionDeclaration: 163 case SyntaxKind.ArrowFunction: 164 return visitNodes(cbNode, cbNodes, node.decorators) || 165 visitNodes(cbNode, cbNodes, node.modifiers) || 166 visitNode(cbNode, (<FunctionLikeDeclaration>node).asteriskToken) || 167 visitNode(cbNode, (<FunctionLikeDeclaration>node).name) || 168 visitNode(cbNode, (<FunctionLikeDeclaration>node).questionToken) || 169 visitNode(cbNode, (<FunctionLikeDeclaration>node).exclamationToken) || 170 visitNodes(cbNode, cbNodes, (<FunctionLikeDeclaration>node).typeParameters) || 171 visitNodes(cbNode, cbNodes, (<FunctionLikeDeclaration>node).parameters) || 172 visitNode(cbNode, (<FunctionLikeDeclaration>node).type) || 173 visitNode(cbNode, (<ArrowFunction>node).equalsGreaterThanToken) || 174 visitNode(cbNode, (<FunctionLikeDeclaration>node).body); 175 case SyntaxKind.TypeReference: 176 return visitNode(cbNode, (<TypeReferenceNode>node).typeName) || 177 visitNodes(cbNode, cbNodes, (<TypeReferenceNode>node).typeArguments); 178 case SyntaxKind.TypePredicate: 179 return visitNode(cbNode, (<TypePredicateNode>node).assertsModifier) || 180 visitNode(cbNode, (<TypePredicateNode>node).parameterName) || 181 visitNode(cbNode, (<TypePredicateNode>node).type); 182 case SyntaxKind.TypeQuery: 183 return visitNode(cbNode, (<TypeQueryNode>node).exprName); 184 case SyntaxKind.TypeLiteral: 185 return visitNodes(cbNode, cbNodes, (<TypeLiteralNode>node).members); 186 case SyntaxKind.ArrayType: 187 return visitNode(cbNode, (<ArrayTypeNode>node).elementType); 188 case SyntaxKind.TupleType: 189 return visitNodes(cbNode, cbNodes, (<TupleTypeNode>node).elements); 190 case SyntaxKind.UnionType: 191 case SyntaxKind.IntersectionType: 192 return visitNodes(cbNode, cbNodes, (<UnionOrIntersectionTypeNode>node).types); 193 case SyntaxKind.ConditionalType: 194 return visitNode(cbNode, (<ConditionalTypeNode>node).checkType) || 195 visitNode(cbNode, (<ConditionalTypeNode>node).extendsType) || 196 visitNode(cbNode, (<ConditionalTypeNode>node).trueType) || 197 visitNode(cbNode, (<ConditionalTypeNode>node).falseType); 198 case SyntaxKind.InferType: 199 return visitNode(cbNode, (<InferTypeNode>node).typeParameter); 200 case SyntaxKind.ImportType: 201 return visitNode(cbNode, (<ImportTypeNode>node).argument) || 202 visitNode(cbNode, (<ImportTypeNode>node).qualifier) || 203 visitNodes(cbNode, cbNodes, (<ImportTypeNode>node).typeArguments); 204 case SyntaxKind.ParenthesizedType: 205 case SyntaxKind.TypeOperator: 206 return visitNode(cbNode, (<ParenthesizedTypeNode | TypeOperatorNode>node).type); 207 case SyntaxKind.IndexedAccessType: 208 return visitNode(cbNode, (<IndexedAccessTypeNode>node).objectType) || 209 visitNode(cbNode, (<IndexedAccessTypeNode>node).indexType); 210 case SyntaxKind.MappedType: 211 return visitNode(cbNode, (<MappedTypeNode>node).readonlyToken) || 212 visitNode(cbNode, (<MappedTypeNode>node).typeParameter) || 213 visitNode(cbNode, (<MappedTypeNode>node).nameType) || 214 visitNode(cbNode, (<MappedTypeNode>node).questionToken) || 215 visitNode(cbNode, (<MappedTypeNode>node).type); 216 case SyntaxKind.LiteralType: 217 return visitNode(cbNode, (<LiteralTypeNode>node).literal); 218 case SyntaxKind.NamedTupleMember: 219 return visitNode(cbNode, (<NamedTupleMember>node).dotDotDotToken) || 220 visitNode(cbNode, (<NamedTupleMember>node).name) || 221 visitNode(cbNode, (<NamedTupleMember>node).questionToken) || 222 visitNode(cbNode, (<NamedTupleMember>node).type); 223 case SyntaxKind.ObjectBindingPattern: 224 case SyntaxKind.ArrayBindingPattern: 225 return visitNodes(cbNode, cbNodes, (<BindingPattern>node).elements); 226 case SyntaxKind.ArrayLiteralExpression: 227 return visitNodes(cbNode, cbNodes, (<ArrayLiteralExpression>node).elements); 228 case SyntaxKind.ObjectLiteralExpression: 229 return visitNodes(cbNode, cbNodes, (<ObjectLiteralExpression>node).properties); 230 case SyntaxKind.PropertyAccessExpression: 231 return visitNode(cbNode, (<PropertyAccessExpression>node).expression) || 232 visitNode(cbNode, (<PropertyAccessExpression>node).questionDotToken) || 233 visitNode(cbNode, (<PropertyAccessExpression>node).name); 234 case SyntaxKind.ElementAccessExpression: 235 return visitNode(cbNode, (<ElementAccessExpression>node).expression) || 236 visitNode(cbNode, (<ElementAccessExpression>node).questionDotToken) || 237 visitNode(cbNode, (<ElementAccessExpression>node).argumentExpression); 238 case SyntaxKind.CallExpression: 239 case SyntaxKind.NewExpression: 240 return visitNode(cbNode, (<CallExpression>node).expression) || 241 visitNode(cbNode, (<CallExpression>node).questionDotToken) || 242 visitNodes(cbNode, cbNodes, (<CallExpression>node).typeArguments) || 243 visitNodes(cbNode, cbNodes, (<CallExpression>node).arguments); 244 case SyntaxKind.TaggedTemplateExpression: 245 return visitNode(cbNode, (<TaggedTemplateExpression>node).tag) || 246 visitNode(cbNode, (<TaggedTemplateExpression>node).questionDotToken) || 247 visitNodes(cbNode, cbNodes, (<TaggedTemplateExpression>node).typeArguments) || 248 visitNode(cbNode, (<TaggedTemplateExpression>node).template); 249 case SyntaxKind.TypeAssertionExpression: 250 return visitNode(cbNode, (<TypeAssertion>node).type) || 251 visitNode(cbNode, (<TypeAssertion>node).expression); 252 case SyntaxKind.ParenthesizedExpression: 253 return visitNode(cbNode, (<ParenthesizedExpression>node).expression); 254 case SyntaxKind.DeleteExpression: 255 return visitNode(cbNode, (<DeleteExpression>node).expression); 256 case SyntaxKind.TypeOfExpression: 257 return visitNode(cbNode, (<TypeOfExpression>node).expression); 258 case SyntaxKind.VoidExpression: 259 return visitNode(cbNode, (<VoidExpression>node).expression); 260 case SyntaxKind.PrefixUnaryExpression: 261 return visitNode(cbNode, (<PrefixUnaryExpression>node).operand); 262 case SyntaxKind.YieldExpression: 263 return visitNode(cbNode, (<YieldExpression>node).asteriskToken) || 264 visitNode(cbNode, (<YieldExpression>node).expression); 265 case SyntaxKind.AwaitExpression: 266 return visitNode(cbNode, (<AwaitExpression>node).expression); 267 case SyntaxKind.PostfixUnaryExpression: 268 return visitNode(cbNode, (<PostfixUnaryExpression>node).operand); 269 case SyntaxKind.BinaryExpression: 270 return visitNode(cbNode, (<BinaryExpression>node).left) || 271 visitNode(cbNode, (<BinaryExpression>node).operatorToken) || 272 visitNode(cbNode, (<BinaryExpression>node).right); 273 case SyntaxKind.AsExpression: 274 return visitNode(cbNode, (<AsExpression>node).expression) || 275 visitNode(cbNode, (<AsExpression>node).type); 276 case SyntaxKind.NonNullExpression: 277 return visitNode(cbNode, (<NonNullExpression>node).expression); 278 case SyntaxKind.MetaProperty: 279 return visitNode(cbNode, (<MetaProperty>node).name); 280 case SyntaxKind.ConditionalExpression: 281 return visitNode(cbNode, (<ConditionalExpression>node).condition) || 282 visitNode(cbNode, (<ConditionalExpression>node).questionToken) || 283 visitNode(cbNode, (<ConditionalExpression>node).whenTrue) || 284 visitNode(cbNode, (<ConditionalExpression>node).colonToken) || 285 visitNode(cbNode, (<ConditionalExpression>node).whenFalse); 286 case SyntaxKind.SpreadElement: 287 return visitNode(cbNode, (<SpreadElement>node).expression); 288 case SyntaxKind.Block: 289 case SyntaxKind.ModuleBlock: 290 return visitNodes(cbNode, cbNodes, (<Block>node).statements); 291 case SyntaxKind.SourceFile: 292 return visitNodes(cbNode, cbNodes, (<SourceFile>node).statements) || 293 visitNode(cbNode, (<SourceFile>node).endOfFileToken); 294 case SyntaxKind.VariableStatement: 295 return visitNodes(cbNode, cbNodes, node.decorators) || 296 visitNodes(cbNode, cbNodes, node.modifiers) || 297 visitNode(cbNode, (<VariableStatement>node).declarationList); 298 case SyntaxKind.VariableDeclarationList: 299 return visitNodes(cbNode, cbNodes, (<VariableDeclarationList>node).declarations); 300 case SyntaxKind.ExpressionStatement: 301 return visitNode(cbNode, (<ExpressionStatement>node).expression); 302 case SyntaxKind.IfStatement: 303 return visitNode(cbNode, (<IfStatement>node).expression) || 304 visitNode(cbNode, (<IfStatement>node).thenStatement) || 305 visitNode(cbNode, (<IfStatement>node).elseStatement); 306 case SyntaxKind.DoStatement: 307 return visitNode(cbNode, (<DoStatement>node).statement) || 308 visitNode(cbNode, (<DoStatement>node).expression); 309 case SyntaxKind.WhileStatement: 310 return visitNode(cbNode, (<WhileStatement>node).expression) || 311 visitNode(cbNode, (<WhileStatement>node).statement); 312 case SyntaxKind.ForStatement: 313 return visitNode(cbNode, (<ForStatement>node).initializer) || 314 visitNode(cbNode, (<ForStatement>node).condition) || 315 visitNode(cbNode, (<ForStatement>node).incrementor) || 316 visitNode(cbNode, (<ForStatement>node).statement); 317 case SyntaxKind.ForInStatement: 318 return visitNode(cbNode, (<ForInStatement>node).initializer) || 319 visitNode(cbNode, (<ForInStatement>node).expression) || 320 visitNode(cbNode, (<ForInStatement>node).statement); 321 case SyntaxKind.ForOfStatement: 322 return visitNode(cbNode, (<ForOfStatement>node).awaitModifier) || 323 visitNode(cbNode, (<ForOfStatement>node).initializer) || 324 visitNode(cbNode, (<ForOfStatement>node).expression) || 325 visitNode(cbNode, (<ForOfStatement>node).statement); 326 case SyntaxKind.ContinueStatement: 327 case SyntaxKind.BreakStatement: 328 return visitNode(cbNode, (<BreakOrContinueStatement>node).label); 329 case SyntaxKind.ReturnStatement: 330 return visitNode(cbNode, (<ReturnStatement>node).expression); 331 case SyntaxKind.WithStatement: 332 return visitNode(cbNode, (<WithStatement>node).expression) || 333 visitNode(cbNode, (<WithStatement>node).statement); 334 case SyntaxKind.SwitchStatement: 335 return visitNode(cbNode, (<SwitchStatement>node).expression) || 336 visitNode(cbNode, (<SwitchStatement>node).caseBlock); 337 case SyntaxKind.CaseBlock: 338 return visitNodes(cbNode, cbNodes, (<CaseBlock>node).clauses); 339 case SyntaxKind.CaseClause: 340 return visitNode(cbNode, (<CaseClause>node).expression) || 341 visitNodes(cbNode, cbNodes, (<CaseClause>node).statements); 342 case SyntaxKind.DefaultClause: 343 return visitNodes(cbNode, cbNodes, (<DefaultClause>node).statements); 344 case SyntaxKind.LabeledStatement: 345 return visitNode(cbNode, (<LabeledStatement>node).label) || 346 visitNode(cbNode, (<LabeledStatement>node).statement); 347 case SyntaxKind.ThrowStatement: 348 return visitNode(cbNode, (<ThrowStatement>node).expression); 349 case SyntaxKind.TryStatement: 350 return visitNode(cbNode, (<TryStatement>node).tryBlock) || 351 visitNode(cbNode, (<TryStatement>node).catchClause) || 352 visitNode(cbNode, (<TryStatement>node).finallyBlock); 353 case SyntaxKind.CatchClause: 354 return visitNode(cbNode, (<CatchClause>node).variableDeclaration) || 355 visitNode(cbNode, (<CatchClause>node).block); 356 case SyntaxKind.Decorator: 357 return visitNode(cbNode, (<Decorator>node).expression); 358 case SyntaxKind.StructDeclaration: 359 case SyntaxKind.ClassDeclaration: 360 case SyntaxKind.ClassExpression: 361 return visitNodes(cbNode, cbNodes, node.decorators) || 362 visitNodes(cbNode, cbNodes, node.modifiers) || 363 visitNode(cbNode, (<ClassLikeDeclaration>node).name) || 364 visitNodes(cbNode, cbNodes, (<ClassLikeDeclaration>node).typeParameters) || 365 visitNodes(cbNode, cbNodes, (<ClassLikeDeclaration>node).heritageClauses) || 366 visitNodes(cbNode, cbNodes, (<ClassLikeDeclaration>node).members); 367 case SyntaxKind.InterfaceDeclaration: 368 return visitNodes(cbNode, cbNodes, node.decorators) || 369 visitNodes(cbNode, cbNodes, node.modifiers) || 370 visitNode(cbNode, (<InterfaceDeclaration>node).name) || 371 visitNodes(cbNode, cbNodes, (<InterfaceDeclaration>node).typeParameters) || 372 visitNodes(cbNode, cbNodes, (<ClassDeclaration>node).heritageClauses) || 373 visitNodes(cbNode, cbNodes, (<InterfaceDeclaration>node).members); 374 case SyntaxKind.TypeAliasDeclaration: 375 return visitNodes(cbNode, cbNodes, node.decorators) || 376 visitNodes(cbNode, cbNodes, node.modifiers) || 377 visitNode(cbNode, (<TypeAliasDeclaration>node).name) || 378 visitNodes(cbNode, cbNodes, (<TypeAliasDeclaration>node).typeParameters) || 379 visitNode(cbNode, (<TypeAliasDeclaration>node).type); 380 case SyntaxKind.EnumDeclaration: 381 return visitNodes(cbNode, cbNodes, node.decorators) || 382 visitNodes(cbNode, cbNodes, node.modifiers) || 383 visitNode(cbNode, (<EnumDeclaration>node).name) || 384 visitNodes(cbNode, cbNodes, (<EnumDeclaration>node).members); 385 case SyntaxKind.EnumMember: 386 return visitNode(cbNode, (<EnumMember>node).name) || 387 visitNode(cbNode, (<EnumMember>node).initializer); 388 case SyntaxKind.ModuleDeclaration: 389 return visitNodes(cbNode, cbNodes, node.decorators) || 390 visitNodes(cbNode, cbNodes, node.modifiers) || 391 visitNode(cbNode, (<ModuleDeclaration>node).name) || 392 visitNode(cbNode, (<ModuleDeclaration>node).body); 393 case SyntaxKind.ImportEqualsDeclaration: 394 return visitNodes(cbNode, cbNodes, node.decorators) || 395 visitNodes(cbNode, cbNodes, node.modifiers) || 396 visitNode(cbNode, (<ImportEqualsDeclaration>node).name) || 397 visitNode(cbNode, (<ImportEqualsDeclaration>node).moduleReference); 398 case SyntaxKind.ImportDeclaration: 399 return visitNodes(cbNode, cbNodes, node.decorators) || 400 visitNodes(cbNode, cbNodes, node.modifiers) || 401 visitNode(cbNode, (<ImportDeclaration>node).importClause) || 402 visitNode(cbNode, (<ImportDeclaration>node).moduleSpecifier); 403 case SyntaxKind.ImportClause: 404 return visitNode(cbNode, (<ImportClause>node).name) || 405 visitNode(cbNode, (<ImportClause>node).namedBindings); 406 case SyntaxKind.NamespaceExportDeclaration: 407 return visitNode(cbNode, (<NamespaceExportDeclaration>node).name); 408 409 case SyntaxKind.NamespaceImport: 410 return visitNode(cbNode, (<NamespaceImport>node).name); 411 case SyntaxKind.NamespaceExport: 412 return visitNode(cbNode, (<NamespaceExport>node).name); 413 case SyntaxKind.NamedImports: 414 case SyntaxKind.NamedExports: 415 return visitNodes(cbNode, cbNodes, (<NamedImportsOrExports>node).elements); 416 case SyntaxKind.ExportDeclaration: 417 return visitNodes(cbNode, cbNodes, node.decorators) || 418 visitNodes(cbNode, cbNodes, node.modifiers) || 419 visitNode(cbNode, (<ExportDeclaration>node).exportClause) || 420 visitNode(cbNode, (<ExportDeclaration>node).moduleSpecifier); 421 case SyntaxKind.ImportSpecifier: 422 case SyntaxKind.ExportSpecifier: 423 return visitNode(cbNode, (<ImportOrExportSpecifier>node).propertyName) || 424 visitNode(cbNode, (<ImportOrExportSpecifier>node).name); 425 case SyntaxKind.ExportAssignment: 426 return visitNodes(cbNode, cbNodes, node.decorators) || 427 visitNodes(cbNode, cbNodes, node.modifiers) || 428 visitNode(cbNode, (<ExportAssignment>node).expression); 429 case SyntaxKind.TemplateExpression: 430 return visitNode(cbNode, (<TemplateExpression>node).head) || visitNodes(cbNode, cbNodes, (<TemplateExpression>node).templateSpans); 431 case SyntaxKind.TemplateSpan: 432 return visitNode(cbNode, (<TemplateSpan>node).expression) || visitNode(cbNode, (<TemplateSpan>node).literal); 433 case SyntaxKind.TemplateLiteralType: 434 return visitNode(cbNode, (<TemplateLiteralTypeNode>node).head) || visitNodes(cbNode, cbNodes, (<TemplateLiteralTypeNode>node).templateSpans); 435 case SyntaxKind.TemplateLiteralTypeSpan: 436 return visitNode(cbNode, (<TemplateLiteralTypeSpan>node).type) || visitNode(cbNode, (<TemplateLiteralTypeSpan>node).literal); 437 case SyntaxKind.ComputedPropertyName: 438 return visitNode(cbNode, (<ComputedPropertyName>node).expression); 439 case SyntaxKind.HeritageClause: 440 return visitNodes(cbNode, cbNodes, (<HeritageClause>node).types); 441 case SyntaxKind.ExpressionWithTypeArguments: 442 return visitNode(cbNode, (<ExpressionWithTypeArguments>node).expression) || 443 visitNodes(cbNode, cbNodes, (<ExpressionWithTypeArguments>node).typeArguments); 444 case SyntaxKind.ExternalModuleReference: 445 return visitNode(cbNode, (<ExternalModuleReference>node).expression); 446 case SyntaxKind.MissingDeclaration: 447 return visitNodes(cbNode, cbNodes, node.decorators); 448 case SyntaxKind.CommaListExpression: 449 return visitNodes(cbNode, cbNodes, (<CommaListExpression>node).elements); 450 451 case SyntaxKind.JsxElement: 452 return visitNode(cbNode, (<JsxElement>node).openingElement) || 453 visitNodes(cbNode, cbNodes, (<JsxElement>node).children) || 454 visitNode(cbNode, (<JsxElement>node).closingElement); 455 case SyntaxKind.JsxFragment: 456 return visitNode(cbNode, (<JsxFragment>node).openingFragment) || 457 visitNodes(cbNode, cbNodes, (<JsxFragment>node).children) || 458 visitNode(cbNode, (<JsxFragment>node).closingFragment); 459 case SyntaxKind.JsxSelfClosingElement: 460 case SyntaxKind.JsxOpeningElement: 461 return visitNode(cbNode, (<JsxOpeningLikeElement>node).tagName) || 462 visitNodes(cbNode, cbNodes, (<JsxOpeningLikeElement>node).typeArguments) || 463 visitNode(cbNode, (<JsxOpeningLikeElement>node).attributes); 464 case SyntaxKind.JsxAttributes: 465 return visitNodes(cbNode, cbNodes, (<JsxAttributes>node).properties); 466 case SyntaxKind.JsxAttribute: 467 return visitNode(cbNode, (<JsxAttribute>node).name) || 468 visitNode(cbNode, (<JsxAttribute>node).initializer); 469 case SyntaxKind.JsxSpreadAttribute: 470 return visitNode(cbNode, (<JsxSpreadAttribute>node).expression); 471 case SyntaxKind.JsxExpression: 472 return visitNode(cbNode, (node as JsxExpression).dotDotDotToken) || 473 visitNode(cbNode, (node as JsxExpression).expression); 474 case SyntaxKind.JsxClosingElement: 475 return visitNode(cbNode, (<JsxClosingElement>node).tagName); 476 477 case SyntaxKind.OptionalType: 478 case SyntaxKind.RestType: 479 case SyntaxKind.JSDocTypeExpression: 480 case SyntaxKind.JSDocNonNullableType: 481 case SyntaxKind.JSDocNullableType: 482 case SyntaxKind.JSDocOptionalType: 483 case SyntaxKind.JSDocVariadicType: 484 return visitNode(cbNode, (<OptionalTypeNode | RestTypeNode | JSDocTypeExpression | JSDocTypeReferencingNode>node).type); 485 case SyntaxKind.JSDocFunctionType: 486 return visitNodes(cbNode, cbNodes, (<JSDocFunctionType>node).parameters) || 487 visitNode(cbNode, (<JSDocFunctionType>node).type); 488 case SyntaxKind.JSDocComment: 489 return visitNodes(cbNode, cbNodes, (<JSDoc>node).tags); 490 case SyntaxKind.JSDocSeeTag: 491 return visitNode(cbNode, (node as JSDocSeeTag).tagName) || 492 visitNode(cbNode, (node as JSDocSeeTag).name); 493 case SyntaxKind.JSDocNameReference: 494 return visitNode(cbNode, (node as JSDocNameReference).name); 495 case SyntaxKind.JSDocParameterTag: 496 case SyntaxKind.JSDocPropertyTag: 497 return visitNode(cbNode, (node as JSDocTag).tagName) || 498 ((node as JSDocPropertyLikeTag).isNameFirst 499 ? visitNode(cbNode, (<JSDocPropertyLikeTag>node).name) || 500 visitNode(cbNode, (<JSDocPropertyLikeTag>node).typeExpression) 501 : visitNode(cbNode, (<JSDocPropertyLikeTag>node).typeExpression) || 502 visitNode(cbNode, (<JSDocPropertyLikeTag>node).name)); 503 case SyntaxKind.JSDocAuthorTag: 504 return visitNode(cbNode, (node as JSDocTag).tagName); 505 case SyntaxKind.JSDocImplementsTag: 506 return visitNode(cbNode, (node as JSDocTag).tagName) || 507 visitNode(cbNode, (<JSDocImplementsTag>node).class); 508 case SyntaxKind.JSDocAugmentsTag: 509 return visitNode(cbNode, (node as JSDocTag).tagName) || 510 visitNode(cbNode, (<JSDocAugmentsTag>node).class); 511 case SyntaxKind.JSDocTemplateTag: 512 return visitNode(cbNode, (node as JSDocTag).tagName) || 513 visitNode(cbNode, (<JSDocTemplateTag>node).constraint) || 514 visitNodes(cbNode, cbNodes, (<JSDocTemplateTag>node).typeParameters); 515 case SyntaxKind.JSDocTypedefTag: 516 return visitNode(cbNode, (node as JSDocTag).tagName) || 517 ((node as JSDocTypedefTag).typeExpression && 518 (node as JSDocTypedefTag).typeExpression!.kind === SyntaxKind.JSDocTypeExpression 519 ? visitNode(cbNode, (<JSDocTypedefTag>node).typeExpression) || 520 visitNode(cbNode, (<JSDocTypedefTag>node).fullName) 521 : visitNode(cbNode, (<JSDocTypedefTag>node).fullName) || 522 visitNode(cbNode, (<JSDocTypedefTag>node).typeExpression)); 523 case SyntaxKind.JSDocCallbackTag: 524 return visitNode(cbNode, (node as JSDocTag).tagName) || 525 visitNode(cbNode, (node as JSDocCallbackTag).fullName) || 526 visitNode(cbNode, (node as JSDocCallbackTag).typeExpression); 527 case SyntaxKind.JSDocReturnTag: 528 case SyntaxKind.JSDocTypeTag: 529 case SyntaxKind.JSDocThisTag: 530 case SyntaxKind.JSDocEnumTag: 531 return visitNode(cbNode, (node as JSDocTag).tagName) || 532 visitNode(cbNode, (node as JSDocReturnTag | JSDocTypeTag | JSDocThisTag | JSDocEnumTag).typeExpression); 533 case SyntaxKind.JSDocSignature: 534 return forEach((<JSDocSignature>node).typeParameters, cbNode) || 535 forEach((<JSDocSignature>node).parameters, cbNode) || 536 visitNode(cbNode, (<JSDocSignature>node).type); 537 case SyntaxKind.JSDocTypeLiteral: 538 return forEach((node as JSDocTypeLiteral).jsDocPropertyTags, cbNode); 539 case SyntaxKind.JSDocTag: 540 case SyntaxKind.JSDocClassTag: 541 case SyntaxKind.JSDocPublicTag: 542 case SyntaxKind.JSDocPrivateTag: 543 case SyntaxKind.JSDocProtectedTag: 544 case SyntaxKind.JSDocReadonlyTag: 545 return visitNode(cbNode, (node as JSDocTag).tagName); 546 case SyntaxKind.PartiallyEmittedExpression: 547 return visitNode(cbNode, (<PartiallyEmittedExpression>node).expression); 548 } 549 } 550 551 /** @internal */ 552 /** 553 * Invokes a callback for each child of the given node. The 'cbNode' callback is invoked for all child nodes 554 * stored in properties. If a 'cbNodes' callback is specified, it is invoked for embedded arrays; additionally, 555 * unlike `forEachChild`, embedded arrays are flattened and the 'cbNode' callback is invoked for each element. 556 * If a callback returns a truthy value, iteration stops and that value is returned. Otherwise, undefined is returned. 557 * 558 * @param node a given node to visit its children 559 * @param cbNode a callback to be invoked for all child nodes 560 * @param cbNodes a callback to be invoked for embedded array 561 * 562 * @remarks Unlike `forEachChild`, `forEachChildRecursively` handles recursively invoking the traversal on each child node found, 563 * and while doing so, handles traversing the structure without relying on the callstack to encode the tree structure. 564 */ 565 export function forEachChildRecursively<T>(rootNode: Node, cbNode: (node: Node, parent: Node) => T | "skip" | undefined, cbNodes?: (nodes: NodeArray<Node>, parent: Node) => T | "skip" | undefined): T | undefined { 566 const queue: (Node | NodeArray<Node>)[] = gatherPossibleChildren(rootNode); 567 const parents: Node[] = []; // tracks parent references for elements in queue 568 while (parents.length < queue.length) { 569 parents.push(rootNode); 570 } 571 while (queue.length !== 0) { 572 const current = queue.pop()!; 573 const parent = parents.pop()!; 574 if (isArray(current)) { 575 if (cbNodes) { 576 const res = cbNodes(current, parent); 577 if (res) { 578 if (res === "skip") continue; 579 return res; 580 } 581 } 582 for (let i = current.length - 1; i >= 0; --i) { 583 queue.push(current[i]); 584 parents.push(parent); 585 } 586 } 587 else { 588 const res = cbNode(current, parent); 589 if (res) { 590 if (res === "skip") continue; 591 return res; 592 } 593 if (current.kind >= SyntaxKind.FirstNode) { 594 // add children in reverse order to the queue, so popping gives the first child 595 for (const child of gatherPossibleChildren(current)) { 596 queue.push(child); 597 parents.push(current); 598 } 599 } 600 } 601 } 602 } 603 604 function gatherPossibleChildren(node: Node) { 605 const children: (Node | NodeArray<Node>)[] = []; 606 forEachChild(node, addWorkItem, addWorkItem); // By using a stack above and `unshift` here, we emulate a depth-first preorder traversal 607 return children; 608 609 function addWorkItem(n: Node | NodeArray<Node>) { 610 children.unshift(n); 611 } 612 } 613 614 let sourceFileCompilerOptions: CompilerOptions; 615 616 export function createSourceFile(fileName: string, sourceText: string, languageVersion: ScriptTarget, setParentNodes = false, scriptKind?: ScriptKind, option?: CompilerOptions): SourceFile { 617 tracing?.push(tracing.Phase.Parse, "createSourceFile", { path: fileName }, /*separateBeginAndEnd*/ true); 618 performance.mark("beforeParse"); 619 let result: SourceFile; 620 sourceFileCompilerOptions = option ?? defaultInitCompilerOptions; 621 perfLogger.logStartParseSourceFile(fileName); 622 if (languageVersion === ScriptTarget.JSON) { 623 result = Parser.parseSourceFile(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined, setParentNodes, ScriptKind.JSON); 624 } 625 else { 626 result = Parser.parseSourceFile(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined, setParentNodes, scriptKind); 627 } 628 perfLogger.logStopParseSourceFile(); 629 630 performance.mark("afterParse"); 631 performance.measure("Parse", "beforeParse", "afterParse"); 632 tracing?.pop(); 633 return result; 634 } 635 636 export function parseIsolatedEntityName(text: string, languageVersion: ScriptTarget): EntityName | undefined { 637 return Parser.parseIsolatedEntityName(text, languageVersion); 638 } 639 640 /** 641 * Parse json text into SyntaxTree and return node and parse errors if any 642 * @param fileName 643 * @param sourceText 644 */ 645 export function parseJsonText(fileName: string, sourceText: string): JsonSourceFile { 646 return Parser.parseJsonText(fileName, sourceText); 647 } 648 649 // See also `isExternalOrCommonJsModule` in utilities.ts 650 export function isExternalModule(file: SourceFile): boolean { 651 return file.externalModuleIndicator !== undefined; 652 } 653 654 // Produces a new SourceFile for the 'newText' provided. The 'textChangeRange' parameter 655 // indicates what changed between the 'text' that this SourceFile has and the 'newText'. 656 // The SourceFile will be created with the compiler attempting to reuse as many nodes from 657 // this file as possible. 658 // 659 // Note: this function mutates nodes from this SourceFile. That means any existing nodes 660 // from this SourceFile that are being held onto may change as a result (including 661 // becoming detached from any SourceFile). It is recommended that this SourceFile not 662 // be used once 'update' is called on it. 663 export function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks = false, option?: CompilerOptions): SourceFile { 664 sourceFileCompilerOptions = option ?? defaultInitCompilerOptions; 665 const newSourceFile = IncrementalParser.updateSourceFile(sourceFile, newText, textChangeRange, aggressiveChecks); 666 // Because new source file node is created, it may not have the flag PossiblyContainDynamicImport. This is the case if there is no new edit to add dynamic import. 667 // We will manually port the flag to the new source file. 668 (newSourceFile as Mutable<SourceFile>).flags |= (sourceFile.flags & NodeFlags.PermanentlySetIncrementalFlags); 669 return newSourceFile; 670 } 671 672 /* @internal */ 673 export function parseIsolatedJSDocComment(content: string, start?: number, length?: number) { 674 const result = Parser.JSDocParser.parseIsolatedJSDocComment(content, start, length); 675 if (result && result.jsDoc) { 676 // because the jsDocComment was parsed out of the source file, it might 677 // not be covered by the fixupParentReferences. 678 Parser.fixupParentReferences(result.jsDoc); 679 } 680 681 return result; 682 } 683 684 /* @internal */ 685 // Exposed only for testing. 686 export function parseJSDocTypeExpressionForTests(content: string, start?: number, length?: number) { 687 return Parser.JSDocParser.parseJSDocTypeExpressionForTests(content, start, length); 688 } 689 690 // Implement the parser as a singleton module. We do this for perf reasons because creating 691 // parser instances can actually be expensive enough to impact us on projects with many source 692 // files. 693 namespace Parser { 694 // Share a single scanner across all calls to parse a source file. This helps speed things 695 // up by avoiding the cost of creating/compiling scanners over and over again. 696 const scanner = createScanner(ScriptTarget.Latest, /*skipTrivia*/ true); 697 698 const disallowInAndDecoratorContext = NodeFlags.DisallowInContext | NodeFlags.DecoratorContext; 699 700 // capture constructors in 'initializeState' to avoid null checks 701 // tslint:disable variable-name 702 let NodeConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node; 703 let TokenConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node; 704 let IdentifierConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node; 705 let PrivateIdentifierConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node; 706 let SourceFileConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node; 707 // tslint:enable variable-name 708 709 function countNode(node: Node) { 710 nodeCount++; 711 return node; 712 } 713 714 // Rather than using `createBaseNodeFactory` here, we establish a `BaseNodeFactory` that closes over the 715 // constructors above, which are reset each time `initializeState` is called. 716 const baseNodeFactory: BaseNodeFactory = { 717 createBaseSourceFileNode: kind => countNode(new SourceFileConstructor(kind, /*pos*/ 0, /*end*/ 0)), 718 createBaseIdentifierNode: kind => countNode(new IdentifierConstructor(kind, /*pos*/ 0, /*end*/ 0)), 719 createBasePrivateIdentifierNode: kind => countNode(new PrivateIdentifierConstructor(kind, /*pos*/ 0, /*end*/ 0)), 720 createBaseTokenNode: kind => countNode(new TokenConstructor(kind, /*pos*/ 0, /*end*/ 0)), 721 createBaseNode: kind => countNode(new NodeConstructor(kind, /*pos*/ 0, /*end*/ 0)) 722 }; 723 724 const factory = createNodeFactory(NodeFactoryFlags.NoParenthesizerRules | NodeFactoryFlags.NoNodeConverters | NodeFactoryFlags.NoOriginalNode, baseNodeFactory); 725 726 let fileName: string; 727 let sourceFlags: NodeFlags; 728 let sourceText: string; 729 let languageVersion: ScriptTarget; 730 let scriptKind: ScriptKind; 731 let languageVariant: LanguageVariant; 732 let parseDiagnostics: DiagnosticWithDetachedLocation[]; 733 let jsDocDiagnostics: DiagnosticWithDetachedLocation[]; 734 let syntaxCursor: IncrementalParser.SyntaxCursor | undefined; 735 736 let currentToken: SyntaxKind; 737 let nodeCount: number; 738 let identifiers: ESMap<string, string>; 739 let privateIdentifiers: ESMap<string, string>; 740 let identifierCount: number; 741 742 let parsingContext: ParsingContext; 743 744 let notParenthesizedArrow: Set<number> | undefined; 745 746 // Flags that dictate what parsing context we're in. For example: 747 // Whether or not we are in strict parsing mode. All that changes in strict parsing mode is 748 // that some tokens that would be considered identifiers may be considered keywords. 749 // 750 // When adding more parser context flags, consider which is the more common case that the 751 // flag will be in. This should be the 'false' state for that flag. The reason for this is 752 // that we don't store data in our nodes unless the value is in the *non-default* state. So, 753 // for example, more often than code 'allows-in' (or doesn't 'disallow-in'). We opt for 754 // 'disallow-in' set to 'false'. Otherwise, if we had 'allowsIn' set to 'true', then almost 755 // all nodes would need extra state on them to store this info. 756 // 757 // Note: 'allowIn' and 'allowYield' track 1:1 with the [in] and [yield] concepts in the ES6 758 // grammar specification. 759 // 760 // An important thing about these context concepts. By default they are effectively inherited 761 // while parsing through every grammar production. i.e. if you don't change them, then when 762 // you parse a sub-production, it will have the same context values as the parent production. 763 // This is great most of the time. After all, consider all the 'expression' grammar productions 764 // and how nearly all of them pass along the 'in' and 'yield' context values: 765 // 766 // EqualityExpression[In, Yield] : 767 // RelationalExpression[?In, ?Yield] 768 // EqualityExpression[?In, ?Yield] == RelationalExpression[?In, ?Yield] 769 // EqualityExpression[?In, ?Yield] != RelationalExpression[?In, ?Yield] 770 // EqualityExpression[?In, ?Yield] === RelationalExpression[?In, ?Yield] 771 // EqualityExpression[?In, ?Yield] !== RelationalExpression[?In, ?Yield] 772 // 773 // Where you have to be careful is then understanding what the points are in the grammar 774 // where the values are *not* passed along. For example: 775 // 776 // SingleNameBinding[Yield,GeneratorParameter] 777 // [+GeneratorParameter]BindingIdentifier[Yield] Initializer[In]opt 778 // [~GeneratorParameter]BindingIdentifier[?Yield]Initializer[In, ?Yield]opt 779 // 780 // Here this is saying that if the GeneratorParameter context flag is set, that we should 781 // explicitly set the 'yield' context flag to false before calling into the BindingIdentifier 782 // and we should explicitly unset the 'yield' context flag before calling into the Initializer. 783 // production. Conversely, if the GeneratorParameter context flag is not set, then we 784 // should leave the 'yield' context flag alone. 785 // 786 // Getting this all correct is tricky and requires careful reading of the grammar to 787 // understand when these values should be changed versus when they should be inherited. 788 // 789 // Note: it should not be necessary to save/restore these flags during speculative/lookahead 790 // parsing. These context flags are naturally stored and restored through normal recursive 791 // descent parsing and unwinding. 792 let contextFlags: NodeFlags; 793 794 let etsFlags: EtsFlags; 795 796 // Indicates whether we are currently parsing top-level statements. 797 let topLevel = true; 798 799 // Whether or not we've had a parse error since creating the last AST node. If we have 800 // encountered an error, it will be stored on the next AST node we create. Parse errors 801 // can be broken down into three categories: 802 // 803 // 1) An error that occurred during scanning. For example, an unterminated literal, or a 804 // character that was completely not understood. 805 // 806 // 2) A token was expected, but was not present. This type of error is commonly produced 807 // by the 'parseExpected' function. 808 // 809 // 3) A token was present that no parsing function was able to consume. This type of error 810 // only occurs in the 'abortParsingListOrMoveToNextToken' function when the parser 811 // decides to skip the token. 812 // 813 // In all of these cases, we want to mark the next node as having had an error before it. 814 // With this mark, we can know in incremental settings if this node can be reused, or if 815 // we have to reparse it. If we don't keep this information around, we may just reuse the 816 // node. in that event we would then not produce the same errors as we did before, causing 817 // significant confusion problems. 818 // 819 // Note: it is necessary that this value be saved/restored during speculative/lookahead 820 // parsing. During lookahead parsing, we will often create a node. That node will have 821 // this value attached, and then this value will be set back to 'false'. If we decide to 822 // rewind, we must get back to the same value we had prior to the lookahead. 823 // 824 // Note: any errors at the end of the file that do not precede a regular node, should get 825 // attached to the EOF token. 826 let parseErrorBeforeNextFinishedNode = false; 827 828 let extendEtsComponentDeclaration: { name: string, type: string, instance: string } | undefined; 829 830 let stylesEtsComponentDeclaration: { name: string, type: string, instance: string } | undefined; 831 832 // A map to record file scope '@styles' function name 833 const fileStylesComponents = new Map<string, SyntaxKind>(); 834 835 let currentStructName: string | undefined; 836 837 let stateStylesRootNode: string | undefined; 838 839 // A map to record struct scope '@styles' method name 840 const structStylesComponents = new Map<string, { structName: string, kind: SyntaxKind }>(); 841 842 export function parseSourceFile(fileName: string, sourceText: string, languageVersion: ScriptTarget, syntaxCursor: IncrementalParser.SyntaxCursor | undefined, setParentNodes = false, scriptKind?: ScriptKind): SourceFile { 843 scriptKind = ensureScriptKind(fileName, scriptKind); 844 if (scriptKind === ScriptKind.JSON) { 845 const result = parseJsonText(fileName, sourceText, languageVersion, syntaxCursor, setParentNodes); 846 convertToObjectWorker(result, result.parseDiagnostics, /*returnValue*/ false, /*knownRootOptions*/ undefined, /*jsonConversionNotifier*/ undefined); 847 result.referencedFiles = emptyArray; 848 result.typeReferenceDirectives = emptyArray; 849 result.libReferenceDirectives = emptyArray; 850 result.amdDependencies = emptyArray; 851 result.hasNoDefaultLib = false; 852 result.pragmas = emptyMap as ReadonlyPragmaMap; 853 return result; 854 } 855 856 initializeState(fileName, sourceText, languageVersion, syntaxCursor, scriptKind); 857 858 const result = parseSourceFileWorker(languageVersion, setParentNodes, scriptKind); 859 860 clearState(); 861 862 return result; 863 } 864 865 export function parseIsolatedEntityName(content: string, languageVersion: ScriptTarget): EntityName | undefined { 866 // Choice of `isDeclarationFile` should be arbitrary 867 initializeState("", content, languageVersion, /*syntaxCursor*/ undefined, ScriptKind.JS); 868 // Prime the scanner. 869 nextToken(); 870 const entityName = parseEntityName(/*allowReservedWords*/ true); 871 const isInvalid = token() === SyntaxKind.EndOfFileToken && !parseDiagnostics.length; 872 clearState(); 873 return isInvalid ? entityName : undefined; 874 } 875 876 export function parseJsonText(fileName: string, sourceText: string, languageVersion: ScriptTarget = ScriptTarget.ES2015, syntaxCursor?: IncrementalParser.SyntaxCursor, setParentNodes = false): JsonSourceFile { 877 initializeState(fileName, sourceText, languageVersion, syntaxCursor, ScriptKind.JSON); 878 sourceFlags = contextFlags; 879 880 // Prime the scanner. 881 nextToken(); 882 const pos = getNodePos(); 883 let statements, endOfFileToken; 884 if (token() === SyntaxKind.EndOfFileToken) { 885 statements = createNodeArray([], pos, pos); 886 endOfFileToken = parseTokenNode<EndOfFileToken>(); 887 } 888 else { 889 let expression; 890 switch (token()) { 891 case SyntaxKind.OpenBracketToken: 892 expression = parseArrayLiteralExpression(); 893 break; 894 case SyntaxKind.TrueKeyword: 895 case SyntaxKind.FalseKeyword: 896 case SyntaxKind.NullKeyword: 897 expression = parseTokenNode<BooleanLiteral | NullLiteral>(); 898 break; 899 case SyntaxKind.MinusToken: 900 if (lookAhead(() => nextToken() === SyntaxKind.NumericLiteral && nextToken() !== SyntaxKind.ColonToken)) { 901 expression = parsePrefixUnaryExpression() as JsonMinusNumericLiteral; 902 } 903 else { 904 expression = parseObjectLiteralExpression(); 905 } 906 break; 907 case SyntaxKind.NumericLiteral: 908 case SyntaxKind.StringLiteral: 909 if (lookAhead(() => nextToken() !== SyntaxKind.ColonToken)) { 910 expression = parseLiteralNode() as StringLiteral | NumericLiteral; 911 break; 912 } 913 // falls through 914 default: 915 expression = parseObjectLiteralExpression(); 916 break; 917 } 918 919 const statement = factory.createExpressionStatement(expression) as JsonObjectExpressionStatement; 920 finishNode(statement, pos); 921 statements = createNodeArray([statement], pos); 922 endOfFileToken = parseExpectedToken(SyntaxKind.EndOfFileToken, Diagnostics.Unexpected_token); 923 } 924 925 // Set source file so that errors will be reported with this file name 926 const sourceFile = createSourceFile(fileName, ScriptTarget.ES2015, ScriptKind.JSON, /*isDeclaration*/ false, statements, endOfFileToken, sourceFlags); 927 928 if (setParentNodes) { 929 fixupParentReferences(sourceFile); 930 } 931 932 sourceFile.nodeCount = nodeCount; 933 sourceFile.identifierCount = identifierCount; 934 sourceFile.identifiers = identifiers; 935 sourceFile.parseDiagnostics = attachFileToDiagnostics(parseDiagnostics, sourceFile); 936 if (jsDocDiagnostics) { 937 sourceFile.jsDocDiagnostics = attachFileToDiagnostics(jsDocDiagnostics, sourceFile); 938 } 939 940 const result = sourceFile as JsonSourceFile; 941 clearState(); 942 return result; 943 } 944 945 function initializeState(_fileName: string, _sourceText: string, _languageVersion: ScriptTarget, _syntaxCursor: IncrementalParser.SyntaxCursor | undefined, _scriptKind: ScriptKind) { 946 NodeConstructor = objectAllocator.getNodeConstructor(); 947 TokenConstructor = objectAllocator.getTokenConstructor(); 948 IdentifierConstructor = objectAllocator.getIdentifierConstructor(); 949 PrivateIdentifierConstructor = objectAllocator.getPrivateIdentifierConstructor(); 950 SourceFileConstructor = objectAllocator.getSourceFileConstructor(); 951 952 fileName = normalizePath(_fileName); 953 sourceText = _sourceText; 954 languageVersion = _languageVersion; 955 syntaxCursor = _syntaxCursor; 956 scriptKind = _scriptKind; 957 languageVariant = getLanguageVariant(_scriptKind); 958 959 parseDiagnostics = []; 960 parsingContext = 0; 961 identifiers = new Map<string, string>(); 962 privateIdentifiers = new Map<string, string>(); 963 identifierCount = 0; 964 nodeCount = 0; 965 sourceFlags = 0; 966 topLevel = true; 967 968 switch (scriptKind) { 969 case ScriptKind.JS: 970 case ScriptKind.JSX: 971 contextFlags = NodeFlags.JavaScriptFile; 972 break; 973 case ScriptKind.JSON: 974 contextFlags = NodeFlags.JavaScriptFile | NodeFlags.JsonFile; 975 break; 976 case ScriptKind.ETS: 977 contextFlags = NodeFlags.EtsContext; 978 break; 979 default: 980 contextFlags = NodeFlags.None; 981 break; 982 } 983 if (fileName.endsWith(Extension.Ets)) { 984 contextFlags = NodeFlags.EtsContext; 985 } 986 parseErrorBeforeNextFinishedNode = false; 987 988 // Initialize and prime the scanner before parsing the source elements. 989 scanner.setText(sourceText); 990 scanner.setOnError(scanError); 991 scanner.setScriptTarget(languageVersion); 992 scanner.setLanguageVariant(languageVariant); 993 scanner.setEtsContext(inEtsContext()); 994 } 995 996 function clearState() { 997 // Clear out the text the scanner is pointing at, so it doesn't keep anything alive unnecessarily. 998 scanner.clearCommentDirectives(); 999 scanner.setText(""); 1000 scanner.setOnError(undefined); 1001 scanner.setEtsContext(false); 1002 // Clear any data. We don't want to accidentally hold onto it for too long. 1003 sourceText = undefined!; 1004 languageVersion = undefined!; 1005 syntaxCursor = undefined; 1006 scriptKind = undefined!; 1007 languageVariant = undefined!; 1008 sourceFlags = 0; 1009 parseDiagnostics = undefined!; 1010 jsDocDiagnostics = undefined!; 1011 parsingContext = 0; 1012 identifiers = undefined!; 1013 notParenthesizedArrow = undefined!; 1014 topLevel = true; 1015 extendEtsComponentDeclaration = undefined; 1016 stylesEtsComponentDeclaration = undefined; 1017 stateStylesRootNode = undefined; 1018 fileStylesComponents.clear(); 1019 structStylesComponents.clear(); 1020 } 1021 1022 function parseSourceFileWorker(languageVersion: ScriptTarget, setParentNodes: boolean, scriptKind: ScriptKind): SourceFile { 1023 const isDeclarationFile = isDeclarationFileName(fileName); 1024 if (isDeclarationFile) { 1025 contextFlags |= NodeFlags.Ambient; 1026 } 1027 1028 sourceFlags = contextFlags; 1029 1030 // Prime the scanner. 1031 nextToken(); 1032 1033 const statements = parseList(ParsingContext.SourceElements, parseStatement); 1034 Debug.assert(token() === SyntaxKind.EndOfFileToken); 1035 const endOfFileToken = addJSDocComment(parseTokenNode<EndOfFileToken>()); 1036 1037 const sourceFile = createSourceFile(fileName, languageVersion, scriptKind, isDeclarationFile, statements, endOfFileToken, sourceFlags); 1038 1039 // A member of ReadonlyArray<T> isn't assignable to a member of T[] (and prevents a direct cast) - but this is where we set up those members so they can be readonly in the future 1040 processCommentPragmas(sourceFile as {} as PragmaContext, sourceText); 1041 processPragmasIntoFields(sourceFile as {} as PragmaContext, reportPragmaDiagnostic); 1042 1043 sourceFile.commentDirectives = scanner.getCommentDirectives(); 1044 sourceFile.nodeCount = nodeCount; 1045 sourceFile.identifierCount = identifierCount; 1046 sourceFile.identifiers = identifiers; 1047 sourceFile.parseDiagnostics = attachFileToDiagnostics(parseDiagnostics, sourceFile); 1048 if (jsDocDiagnostics) { 1049 sourceFile.jsDocDiagnostics = attachFileToDiagnostics(jsDocDiagnostics, sourceFile); 1050 } 1051 1052 if (setParentNodes) { 1053 fixupParentReferences(sourceFile); 1054 } 1055 1056 return sourceFile; 1057 1058 function reportPragmaDiagnostic(pos: number, end: number, diagnostic: DiagnosticMessage) { 1059 parseDiagnostics.push(createDetachedDiagnostic(fileName, pos, end, diagnostic)); 1060 } 1061 } 1062 1063 function withJSDoc<T extends HasJSDoc>(node: T, hasJSDoc: boolean): T { 1064 return hasJSDoc ? addJSDocComment(node) : node; 1065 } 1066 1067 let hasDeprecatedTag = false; 1068 function addJSDocComment<T extends HasJSDoc>(node: T): T { 1069 Debug.assert(!node.jsDoc); // Should only be called once per node 1070 const jsDoc = mapDefined(getJSDocCommentRanges(node, sourceText), comment => JSDocParser.parseJSDocComment(node, comment.pos, comment.end - comment.pos)); 1071 if (jsDoc.length) node.jsDoc = jsDoc; 1072 if (hasDeprecatedTag) { 1073 hasDeprecatedTag = false; 1074 (node as Mutable<T>).flags |= NodeFlags.Deprecated; 1075 } 1076 return node; 1077 } 1078 1079 function reparseTopLevelAwait(sourceFile: SourceFile) { 1080 const savedSyntaxCursor = syntaxCursor; 1081 const baseSyntaxCursor = IncrementalParser.createSyntaxCursor(sourceFile); 1082 syntaxCursor = { currentNode }; 1083 1084 const statements: Statement[] = []; 1085 const savedParseDiagnostics = parseDiagnostics; 1086 1087 parseDiagnostics = []; 1088 1089 let pos = 0; 1090 let start = findNextStatementWithAwait(sourceFile.statements, 0); 1091 while (start !== -1) { 1092 // append all statements between pos and start 1093 const prevStatement = sourceFile.statements[pos]; 1094 const nextStatement = sourceFile.statements[start]; 1095 addRange(statements, sourceFile.statements, pos, start); 1096 pos = findNextStatementWithoutAwait(sourceFile.statements, start); 1097 1098 // append all diagnostics associated with the copied range 1099 const diagnosticStart = findIndex(savedParseDiagnostics, diagnostic => diagnostic.start >= prevStatement.pos); 1100 const diagnosticEnd = diagnosticStart >= 0 ? findIndex(savedParseDiagnostics, diagnostic => diagnostic.start >= nextStatement.pos, diagnosticStart) : -1; 1101 if (diagnosticStart >= 0) { 1102 addRange(parseDiagnostics, savedParseDiagnostics, diagnosticStart, diagnosticEnd >= 0 ? diagnosticEnd : undefined); 1103 } 1104 1105 // reparse all statements between start and pos. We skip existing diagnostics for the same range and allow the parser to generate new ones. 1106 speculationHelper(() => { 1107 const savedContextFlags = contextFlags; 1108 contextFlags |= NodeFlags.AwaitContext; 1109 scanner.setTextPos(nextStatement.pos); 1110 nextToken(); 1111 1112 while (token() !== SyntaxKind.EndOfFileToken) { 1113 const startPos = scanner.getStartPos(); 1114 const statement = parseListElement(ParsingContext.SourceElements, parseStatement); 1115 statements.push(statement); 1116 if (startPos === scanner.getStartPos()) { 1117 nextToken(); 1118 } 1119 1120 if (pos >= 0) { 1121 const nonAwaitStatement = sourceFile.statements[pos]; 1122 if (statement.end === nonAwaitStatement.pos) { 1123 // done reparsing this section 1124 break; 1125 } 1126 if (statement.end > nonAwaitStatement.pos) { 1127 // we ate into the next statement, so we must reparse it. 1128 pos = findNextStatementWithoutAwait(sourceFile.statements, pos + 1); 1129 } 1130 } 1131 } 1132 1133 contextFlags = savedContextFlags; 1134 }, SpeculationKind.Reparse); 1135 1136 // find the next statement containing an `await` 1137 start = pos >= 0 ? findNextStatementWithAwait(sourceFile.statements, pos) : -1; 1138 } 1139 1140 // append all statements between pos and the end of the list 1141 if (pos >= 0) { 1142 const prevStatement = sourceFile.statements[pos]; 1143 addRange(statements, sourceFile.statements, pos); 1144 1145 // append all diagnostics associated with the copied range 1146 const diagnosticStart = findIndex(savedParseDiagnostics, diagnostic => diagnostic.start >= prevStatement.pos); 1147 if (diagnosticStart >= 0) { 1148 addRange(parseDiagnostics, savedParseDiagnostics, diagnosticStart); 1149 } 1150 } 1151 1152 syntaxCursor = savedSyntaxCursor; 1153 return factory.updateSourceFile(sourceFile, setTextRange(factory.createNodeArray(statements), sourceFile.statements)); 1154 1155 function containsPossibleTopLevelAwait(node: Node) { 1156 return !(node.flags & NodeFlags.AwaitContext) 1157 && !!(node.transformFlags & TransformFlags.ContainsPossibleTopLevelAwait); 1158 } 1159 1160 function findNextStatementWithAwait(statements: NodeArray<Statement>, start: number) { 1161 for (let i = start; i < statements.length; i++) { 1162 if (containsPossibleTopLevelAwait(statements[i])) { 1163 return i; 1164 } 1165 } 1166 return -1; 1167 } 1168 1169 function findNextStatementWithoutAwait(statements: NodeArray<Statement>, start: number) { 1170 for (let i = start; i < statements.length; i++) { 1171 if (!containsPossibleTopLevelAwait(statements[i])) { 1172 return i; 1173 } 1174 } 1175 return -1; 1176 } 1177 1178 function currentNode(position: number) { 1179 const node = baseSyntaxCursor.currentNode(position); 1180 if (topLevel && node && containsPossibleTopLevelAwait(node)) { 1181 node.intersectsChange = true; 1182 } 1183 return node; 1184 } 1185 1186 } 1187 1188 export function fixupParentReferences(rootNode: Node) { 1189 // normally parent references are set during binding. However, for clients that only need 1190 // a syntax tree, and no semantic features, then the binding process is an unnecessary 1191 // overhead. This functions allows us to set all the parents, without all the expense of 1192 // binding. 1193 setParentRecursive(rootNode, /*incremental*/ true); 1194 } 1195 1196 function createSourceFile(fileName: string, languageVersion: ScriptTarget, scriptKind: ScriptKind, isDeclarationFile: boolean, statements: readonly Statement[], endOfFileToken: EndOfFileToken, flags: NodeFlags): SourceFile { 1197 // code from createNode is inlined here so createNode won't have to deal with special case of creating source files 1198 // this is quite rare comparing to other nodes and createNode should be as fast as possible 1199 let sourceFile = factory.createSourceFile(statements, endOfFileToken, flags); 1200 setTextRangePosWidth(sourceFile, 0, sourceText.length); 1201 setExternalModuleIndicator(sourceFile); 1202 1203 // If we parsed this as an external module, it may contain top-level await 1204 if (!isDeclarationFile && isExternalModule(sourceFile) && sourceFile.transformFlags & TransformFlags.ContainsPossibleTopLevelAwait) { 1205 sourceFile = reparseTopLevelAwait(sourceFile); 1206 } 1207 1208 sourceFile.text = sourceText; 1209 sourceFile.bindDiagnostics = []; 1210 sourceFile.bindSuggestionDiagnostics = undefined; 1211 sourceFile.languageVersion = languageVersion; 1212 sourceFile.fileName = fileName; 1213 sourceFile.languageVariant = getLanguageVariant(scriptKind); 1214 sourceFile.isDeclarationFile = isDeclarationFile; 1215 sourceFile.scriptKind = scriptKind; 1216 1217 return sourceFile; 1218 } 1219 1220 function setContextFlag(val: boolean, flag: NodeFlags) { 1221 if (val) { 1222 contextFlags |= flag; 1223 } 1224 else { 1225 contextFlags &= ~flag; 1226 } 1227 } 1228 1229 function setEtsFlag(val: boolean, flag: EtsFlags) { 1230 if (val) { 1231 etsFlags |= flag; 1232 } 1233 else { 1234 etsFlags &= ~flag; 1235 } 1236 } 1237 1238 function setDisallowInContext(val: boolean) { 1239 setContextFlag(val, NodeFlags.DisallowInContext); 1240 } 1241 1242 function setYieldContext(val: boolean) { 1243 setContextFlag(val, NodeFlags.YieldContext); 1244 } 1245 1246 function setDecoratorContext(val: boolean) { 1247 setContextFlag(val, NodeFlags.DecoratorContext); 1248 } 1249 1250 function setAwaitContext(val: boolean) { 1251 setContextFlag(val, NodeFlags.AwaitContext); 1252 } 1253 1254 function setStructContext(val: boolean) { 1255 setEtsFlag(val, EtsFlags.StructContext); 1256 } 1257 1258 function setEtsComponentsContext(val: boolean) { 1259 setEtsFlag(val, EtsFlags.EtsComponentsContext); 1260 } 1261 1262 function setEtsNewExpressionContext(val: boolean) { 1263 setEtsFlag(val, EtsFlags.EtsNewExpressionContext); 1264 } 1265 1266 function setEtsExtendComponentsContext(val: boolean) { 1267 setEtsFlag(val, EtsFlags.EtsExtendComponentsContext); 1268 } 1269 1270 function setEtsStylesComponentsContext(val: boolean) { 1271 setEtsFlag(val, EtsFlags.EtsStylesComponentsContext); 1272 } 1273 1274 function setEtsBuildContext(val: boolean) { 1275 setEtsFlag(val, EtsFlags.EtsBuildContext); 1276 } 1277 1278 function setEtsBuilderContext(val: boolean) { 1279 setEtsFlag(val, EtsFlags.EtsBuilderContext); 1280 } 1281 1282 function setEtsStateStylesContext(val: boolean) { 1283 setEtsFlag(val, EtsFlags.EtsStateStylesContext); 1284 } 1285 1286 function doOutsideOfContext<T>(context: NodeFlags, func: () => T): T { 1287 // contextFlagsToClear will contain only the context flags that are 1288 // currently set that we need to temporarily clear 1289 // We don't just blindly reset to the previous flags to ensure 1290 // that we do not mutate cached flags for the incremental 1291 // parser (ThisNodeHasError, ThisNodeOrAnySubNodesHasError, and 1292 // HasAggregatedChildData). 1293 const contextFlagsToClear = context & contextFlags; 1294 if (contextFlagsToClear) { 1295 // clear the requested context flags 1296 setContextFlag(/*val*/ false, contextFlagsToClear); 1297 const result = func(); 1298 // restore the context flags we just cleared 1299 setContextFlag(/*val*/ true, contextFlagsToClear); 1300 return result; 1301 } 1302 1303 // no need to do anything special as we are not in any of the requested contexts 1304 return func(); 1305 } 1306 1307 function doInsideOfContext<T>(context: NodeFlags, func: () => T): T { 1308 // contextFlagsToSet will contain only the context flags that 1309 // are not currently set that we need to temporarily enable. 1310 // We don't just blindly reset to the previous flags to ensure 1311 // that we do not mutate cached flags for the incremental 1312 // parser (ThisNodeHasError, ThisNodeOrAnySubNodesHasError, and 1313 // HasAggregatedChildData). 1314 const contextFlagsToSet = context & ~contextFlags; 1315 if (contextFlagsToSet) { 1316 // set the requested context flags 1317 setContextFlag(/*val*/ true, contextFlagsToSet); 1318 const result = func(); 1319 // reset the context flags we just set 1320 setContextFlag(/*val*/ false, contextFlagsToSet); 1321 return result; 1322 } 1323 1324 // no need to do anything special as we are already in all of the requested contexts 1325 return func(); 1326 } 1327 1328 function allowInAnd<T>(func: () => T): T { 1329 return doOutsideOfContext(NodeFlags.DisallowInContext, func); 1330 } 1331 1332 function disallowInAnd<T>(func: () => T): T { 1333 return doInsideOfContext(NodeFlags.DisallowInContext, func); 1334 } 1335 1336 function doInYieldContext<T>(func: () => T): T { 1337 return doInsideOfContext(NodeFlags.YieldContext, func); 1338 } 1339 1340 function doInDecoratorContext<T>(func: () => T): T { 1341 // setting Ets Extend Components 1342 const extendDecorator = sourceFileCompilerOptions.ets?.extend?.decorator ?? "Extend"; 1343 if (token() === SyntaxKind.Identifier && scanner.getTokenText() === extendDecorator) { 1344 setEtsFlag(true, EtsFlags.EtsExtendComponentsContext); 1345 } 1346 // setting Ets Styles Components 1347 const stylesDecorator = sourceFileCompilerOptions.ets?.styles?.decorator ?? "Styles"; 1348 if (token() === SyntaxKind.Identifier && scanner.getTokenText() === stylesDecorator) { 1349 setEtsFlag(true, EtsFlags.EtsStylesComponentsContext); 1350 } 1351 return doInsideOfContext(NodeFlags.DecoratorContext, func); 1352 } 1353 1354 function doInAwaitContext<T>(func: () => T): T { 1355 return doInsideOfContext(NodeFlags.AwaitContext, func); 1356 } 1357 1358 function doOutsideOfAwaitContext<T>(func: () => T): T { 1359 return doOutsideOfContext(NodeFlags.AwaitContext, func); 1360 } 1361 1362 function doInYieldAndAwaitContext<T>(func: () => T): T { 1363 return doInsideOfContext(NodeFlags.YieldContext | NodeFlags.AwaitContext, func); 1364 } 1365 1366 function doOutsideOfYieldAndAwaitContext<T>(func: () => T): T { 1367 return doOutsideOfContext(NodeFlags.YieldContext | NodeFlags.AwaitContext, func); 1368 } 1369 1370 function inContext(flags: NodeFlags) { 1371 return (contextFlags & flags) !== 0; 1372 } 1373 1374 function inEtsFlagsContext(flags: EtsFlags) { 1375 return (etsFlags & flags) !== 0; 1376 } 1377 1378 function inYieldContext() { 1379 return inContext(NodeFlags.YieldContext); 1380 } 1381 1382 function inDisallowInContext() { 1383 return inContext(NodeFlags.DisallowInContext); 1384 } 1385 1386 function inDecoratorContext() { 1387 return inContext(NodeFlags.DecoratorContext); 1388 } 1389 1390 function inAwaitContext() { 1391 return inContext(NodeFlags.AwaitContext); 1392 } 1393 1394 function inEtsContext() { 1395 return inContext(NodeFlags.EtsContext); 1396 } 1397 1398 function inStructContext() { 1399 return inEtsContext() && inEtsFlagsContext(EtsFlags.StructContext); 1400 } 1401 1402 function inEtsComponentsContext() { 1403 return inEtsContext() && inEtsFlagsContext(EtsFlags.EtsComponentsContext); 1404 } 1405 1406 function inEtsNewExpressionContext() { 1407 return inEtsContext() && inEtsFlagsContext(EtsFlags.EtsNewExpressionContext); 1408 } 1409 1410 function inEtsExtendComponentsContext() { 1411 return inEtsContext() && inEtsFlagsContext(EtsFlags.EtsExtendComponentsContext); 1412 } 1413 1414 function inEtsStylesComponentsContext() { 1415 return inEtsContext() && inEtsFlagsContext(EtsFlags.EtsStylesComponentsContext); 1416 } 1417 1418 function inBuildContext() { 1419 return inEtsContext() && inStructContext() && inEtsFlagsContext(EtsFlags.EtsBuildContext); 1420 } 1421 1422 function inBuilderContext() { 1423 return inEtsContext() && inEtsFlagsContext(EtsFlags.EtsBuilderContext); 1424 } 1425 1426 function inEtsStateStylesContext() { 1427 return inEtsContext() && inStructContext() && inEtsFlagsContext(EtsFlags.EtsStateStylesContext); 1428 } 1429 1430 function parseErrorAtCurrentToken(message: DiagnosticMessage, arg0?: any): void { 1431 parseErrorAt(scanner.getTokenPos(), scanner.getTextPos(), message, arg0); 1432 } 1433 1434 function parseErrorAtPosition(start: number, length: number, message: DiagnosticMessage, arg0?: any): void { 1435 // Don't report another error if it would just be at the same position as the last error. 1436 const lastError = lastOrUndefined(parseDiagnostics); 1437 if (!lastError || start !== lastError.start) { 1438 parseDiagnostics.push(createDetachedDiagnostic(fileName, start, length, message, arg0)); 1439 } 1440 1441 // Mark that we've encountered an error. We'll set an appropriate bit on the next 1442 // node we finish so that it can't be reused incrementally. 1443 parseErrorBeforeNextFinishedNode = true; 1444 } 1445 1446 function parseErrorAt(start: number, end: number, message: DiagnosticMessage, arg0?: any): void { 1447 parseErrorAtPosition(start, end - start, message, arg0); 1448 } 1449 1450 function parseErrorAtRange(range: TextRange, message: DiagnosticMessage, arg0?: any): void { 1451 parseErrorAt(range.pos, range.end, message, arg0); 1452 } 1453 1454 function scanError(message: DiagnosticMessage, length: number): void { 1455 parseErrorAtPosition(scanner.getTextPos(), length, message); 1456 } 1457 1458 function getNodePos(): number { 1459 return scanner.getStartPos(); 1460 } 1461 1462 function hasPrecedingJSDocComment() { 1463 return scanner.hasPrecedingJSDocComment(); 1464 } 1465 1466 // Use this function to access the current token instead of reading the currentToken 1467 // variable. Since function results aren't narrowed in control flow analysis, this ensures 1468 // that the type checker doesn't make wrong assumptions about the type of the current 1469 // token (e.g. a call to nextToken() changes the current token but the checker doesn't 1470 // reason about this side effect). Mainstream VMs inline simple functions like this, so 1471 // there is no performance penalty. 1472 function token(): SyntaxKind { 1473 return currentToken; 1474 } 1475 1476 function nextTokenWithoutCheck() { 1477 return currentToken = scanner.scan(); 1478 } 1479 1480 function nextTokenAnd<T>(func: () => T): T { 1481 nextToken(); 1482 return func(); 1483 } 1484 1485 function nextToken(): SyntaxKind { 1486 // if the keyword had an escape 1487 if (isKeyword(currentToken) && (scanner.hasUnicodeEscape() || scanner.hasExtendedUnicodeEscape())) { 1488 // issue a parse error for the escape 1489 parseErrorAt(scanner.getTokenPos(), scanner.getTextPos(), Diagnostics.Keywords_cannot_contain_escape_characters); 1490 } 1491 return nextTokenWithoutCheck(); 1492 } 1493 1494 function nextTokenJSDoc(): JSDocSyntaxKind { 1495 return currentToken = scanner.scanJsDocToken(); 1496 } 1497 1498 function reScanGreaterToken(): SyntaxKind { 1499 return currentToken = scanner.reScanGreaterToken(); 1500 } 1501 1502 function reScanSlashToken(): SyntaxKind { 1503 return currentToken = scanner.reScanSlashToken(); 1504 } 1505 1506 function reScanTemplateToken(isTaggedTemplate: boolean): SyntaxKind { 1507 return currentToken = scanner.reScanTemplateToken(isTaggedTemplate); 1508 } 1509 1510 function reScanTemplateHeadOrNoSubstitutionTemplate(): SyntaxKind { 1511 return currentToken = scanner.reScanTemplateHeadOrNoSubstitutionTemplate(); 1512 } 1513 1514 function reScanLessThanToken(): SyntaxKind { 1515 return currentToken = scanner.reScanLessThanToken(); 1516 } 1517 1518 function scanJsxIdentifier(): SyntaxKind { 1519 return currentToken = scanner.scanJsxIdentifier(); 1520 } 1521 1522 function scanJsxText(): SyntaxKind { 1523 return currentToken = scanner.scanJsxToken(); 1524 } 1525 1526 function scanJsxAttributeValue(): SyntaxKind { 1527 return currentToken = scanner.scanJsxAttributeValue(); 1528 } 1529 1530 function speculationHelper<T>(callback: () => T, speculationKind: SpeculationKind): T { 1531 // Keep track of the state we'll need to rollback to if lookahead fails (or if the 1532 // caller asked us to always reset our state). 1533 const saveToken = currentToken; 1534 const saveParseDiagnosticsLength = parseDiagnostics.length; 1535 const saveParseErrorBeforeNextFinishedNode = parseErrorBeforeNextFinishedNode; 1536 1537 // Note: it is not actually necessary to save/restore the context flags here. That's 1538 // because the saving/restoring of these flags happens naturally through the recursive 1539 // descent nature of our parser. However, we still store this here just so we can 1540 // assert that invariant holds. 1541 const saveContextFlags = contextFlags; 1542 1543 // If we're only looking ahead, then tell the scanner to only lookahead as well. 1544 // Otherwise, if we're actually speculatively parsing, then tell the scanner to do the 1545 // same. 1546 const result = speculationKind !== SpeculationKind.TryParse 1547 ? scanner.lookAhead(callback) 1548 : scanner.tryScan(callback); 1549 1550 Debug.assert(saveContextFlags === contextFlags); 1551 1552 // If our callback returned something 'falsy' or we're just looking ahead, 1553 // then unconditionally restore us to where we were. 1554 if (!result || speculationKind !== SpeculationKind.TryParse) { 1555 currentToken = saveToken; 1556 if (speculationKind !== SpeculationKind.Reparse) { 1557 parseDiagnostics.length = saveParseDiagnosticsLength; 1558 } 1559 parseErrorBeforeNextFinishedNode = saveParseErrorBeforeNextFinishedNode; 1560 } 1561 1562 return result; 1563 } 1564 1565 /** Invokes the provided callback then unconditionally restores the parser to the state it 1566 * was in immediately prior to invoking the callback. The result of invoking the callback 1567 * is returned from this function. 1568 */ 1569 function lookAhead<T>(callback: () => T): T { 1570 return speculationHelper(callback, SpeculationKind.Lookahead); 1571 } 1572 1573 /** Invokes the provided callback. If the callback returns something falsy, then it restores 1574 * the parser to the state it was in immediately prior to invoking the callback. If the 1575 * callback returns something truthy, then the parser state is not rolled back. The result 1576 * of invoking the callback is returned from this function. 1577 */ 1578 function tryParse<T>(callback: () => T): T { 1579 return speculationHelper(callback, SpeculationKind.TryParse); 1580 } 1581 1582 function isBindingIdentifier(): boolean { 1583 if (token() === SyntaxKind.Identifier) { 1584 return true; 1585 } 1586 return token() > SyntaxKind.LastReservedWord; 1587 } 1588 1589 // Ignore strict mode flag because we will report an error in type checker instead. 1590 function isIdentifier(): boolean { 1591 if (token() === SyntaxKind.Identifier) { 1592 return true; 1593 } 1594 1595 // If we have a 'yield' keyword, and we're in the [yield] context, then 'yield' is 1596 // considered a keyword and is not an identifier. 1597 if (token() === SyntaxKind.YieldKeyword && inYieldContext()) { 1598 return false; 1599 } 1600 1601 // If we have a 'await' keyword, and we're in the [Await] context, then 'await' is 1602 // considered a keyword and is not an identifier. 1603 if (token() === SyntaxKind.AwaitKeyword && inAwaitContext()) { 1604 return false; 1605 } 1606 1607 return token() > SyntaxKind.LastReservedWord; 1608 } 1609 1610 function parseExpected(kind: SyntaxKind, diagnosticMessage?: DiagnosticMessage, shouldAdvance = true): boolean { 1611 if (token() === kind) { 1612 if (shouldAdvance) { 1613 nextToken(); 1614 } 1615 return true; 1616 } 1617 1618 if (token() !== kind && stateStylesRootNode && inEtsStateStylesContext()) { 1619 return true; 1620 } 1621 1622 // Report specific message if provided with one. Otherwise, report generic fallback message. 1623 if (diagnosticMessage) { 1624 parseErrorAtCurrentToken(diagnosticMessage); 1625 } 1626 else { 1627 parseErrorAtCurrentToken(Diagnostics._0_expected, tokenToString(kind)); 1628 } 1629 return false; 1630 } 1631 1632 function parseExpectedJSDoc(kind: JSDocSyntaxKind) { 1633 if (token() === kind) { 1634 nextTokenJSDoc(); 1635 return true; 1636 } 1637 parseErrorAtCurrentToken(Diagnostics._0_expected, tokenToString(kind)); 1638 return false; 1639 } 1640 1641 function parseOptional(t: SyntaxKind): boolean { 1642 if (token() === t) { 1643 nextToken(); 1644 return true; 1645 } 1646 return false; 1647 } 1648 1649 function parseOptionalToken<TKind extends SyntaxKind>(t: TKind): Token<TKind>; 1650 function parseOptionalToken(t: SyntaxKind): Node | undefined { 1651 if (token() === t) { 1652 return parseTokenNode(); 1653 } 1654 return undefined; 1655 } 1656 1657 function parseOptionalTokenJSDoc<TKind extends JSDocSyntaxKind>(t: TKind): Token<TKind>; 1658 function parseOptionalTokenJSDoc(t: JSDocSyntaxKind): Node | undefined { 1659 if (token() === t) { 1660 return parseTokenNodeJSDoc(); 1661 } 1662 return undefined; 1663 } 1664 1665 function parseExpectedToken<TKind extends SyntaxKind>(t: TKind, diagnosticMessage?: DiagnosticMessage, arg0?: any): Token<TKind>; 1666 function parseExpectedToken(t: SyntaxKind, diagnosticMessage?: DiagnosticMessage, arg0?: any): Node { 1667 return parseOptionalToken(t) || 1668 createMissingNode(t, /*reportAtCurrentPosition*/ false, diagnosticMessage || Diagnostics._0_expected, arg0 || tokenToString(t)); 1669 } 1670 1671 function parseExpectedTokenJSDoc<TKind extends JSDocSyntaxKind>(t: TKind): Token<TKind>; 1672 function parseExpectedTokenJSDoc(t: JSDocSyntaxKind): Node { 1673 return parseOptionalTokenJSDoc(t) || 1674 createMissingNode(t, /*reportAtCurrentPosition*/ false, Diagnostics._0_expected, tokenToString(t)); 1675 } 1676 1677 function parseTokenNode<T extends Node>(): T { 1678 const pos = getNodePos(); 1679 const kind = token(); 1680 nextToken(); 1681 return <T>finishNode(factory.createToken(kind), pos); 1682 } 1683 1684 function parseTokenNodeJSDoc<T extends Node>(): T { 1685 const pos = getNodePos(); 1686 const kind = token(); 1687 nextTokenJSDoc(); 1688 return <T>finishNode(factory.createToken(kind), pos); 1689 } 1690 1691 function canParseSemicolon() { 1692 // If there's a real semicolon, then we can always parse it out. 1693 if (token() === SyntaxKind.SemicolonToken) { 1694 return true; 1695 } 1696 1697 // We can parse out an optional semicolon in ASI cases in the following cases. 1698 return token() === SyntaxKind.CloseBraceToken || token() === SyntaxKind.EndOfFileToken || scanner.hasPrecedingLineBreak(); 1699 } 1700 1701 function parseSemicolon(): boolean { 1702 if (canParseSemicolon()) { 1703 if (token() === SyntaxKind.SemicolonToken) { 1704 // consume the semicolon if it was explicitly provided. 1705 nextToken(); 1706 } 1707 1708 return true; 1709 } 1710 else { 1711 return parseExpected(SyntaxKind.SemicolonToken); 1712 } 1713 } 1714 1715 function createNodeArray<T extends Node>(elements: T[], pos: number, end?: number, hasTrailingComma?: boolean): NodeArray<T> { 1716 const array = factory.createNodeArray(elements, hasTrailingComma); 1717 setTextRangePosEnd(array, pos, end ?? scanner.getStartPos()); 1718 return array; 1719 } 1720 1721 function finishNode<T extends Node>(node: T, pos: number, end?: number, virtual?: boolean): T { 1722 setTextRangePosEnd(node, pos, end ?? scanner.getStartPos()); 1723 if (contextFlags) { 1724 (node as Mutable<T>).flags |= contextFlags; 1725 } 1726 1727 // Keep track on the node if we encountered an error while parsing it. If we did, then 1728 // we cannot reuse the node incrementally. Once we've marked this node, clear out the 1729 // flag so that we don't mark any subsequent nodes. 1730 if (parseErrorBeforeNextFinishedNode) { 1731 parseErrorBeforeNextFinishedNode = false; 1732 (node as Mutable<T>).flags |= NodeFlags.ThisNodeHasError; 1733 } 1734 1735 if (virtual) { 1736 node.virtual = true; 1737 } 1738 1739 return node; 1740 } 1741 1742 function createMissingNode<T extends Node>(kind: T["kind"], reportAtCurrentPosition: false, diagnosticMessage?: DiagnosticMessage, arg0?: any): T; 1743 function createMissingNode<T extends Node>(kind: T["kind"], reportAtCurrentPosition: boolean, diagnosticMessage: DiagnosticMessage, arg0?: any): T; 1744 function createMissingNode<T extends Node>(kind: T["kind"], reportAtCurrentPosition: boolean, diagnosticMessage: DiagnosticMessage, arg0?: any): T { 1745 if (reportAtCurrentPosition) { 1746 parseErrorAtPosition(scanner.getStartPos(), 0, diagnosticMessage, arg0); 1747 } 1748 else if (diagnosticMessage) { 1749 parseErrorAtCurrentToken(diagnosticMessage, arg0); 1750 } 1751 1752 const pos = getNodePos(); 1753 const result = 1754 kind === SyntaxKind.Identifier ? factory.createIdentifier("", /*typeArguments*/ undefined, /*originalKeywordKind*/ undefined) : 1755 isTemplateLiteralKind(kind) ? factory.createTemplateLiteralLikeNode(kind, "", "", /*templateFlags*/ undefined) : 1756 kind === SyntaxKind.NumericLiteral ? factory.createNumericLiteral("", /*numericLiteralFlags*/ undefined) : 1757 kind === SyntaxKind.StringLiteral ? factory.createStringLiteral("", /*isSingleQuote*/ undefined) : 1758 kind === SyntaxKind.MissingDeclaration ? factory.createMissingDeclaration() : 1759 factory.createToken(kind); 1760 return finishNode(result, pos) as T; 1761 } 1762 1763 function internIdentifier(text: string): string { 1764 let identifier = identifiers.get(text); 1765 if (identifier === undefined) { 1766 identifiers.set(text, identifier = text); 1767 } 1768 return identifier; 1769 } 1770 1771 // An identifier that starts with two underscores has an extra underscore character prepended to it to avoid issues 1772 // with magic property names like '__proto__'. The 'identifiers' object is used to share a single string instance for 1773 // each identifier in order to reduce memory consumption. 1774 function createIdentifier(isIdentifier: boolean, diagnosticMessage?: DiagnosticMessage, privateIdentifierDiagnosticMessage?: DiagnosticMessage): Identifier { 1775 if (isIdentifier) { 1776 identifierCount++; 1777 const pos = getNodePos(); 1778 // Store original token kind if it is not just an Identifier so we can report appropriate error later in type checker 1779 const originalKeywordKind = token(); 1780 const text = internIdentifier(scanner.getTokenValue()); 1781 nextTokenWithoutCheck(); 1782 return finishNode(factory.createIdentifier(text, /*typeArguments*/ undefined, originalKeywordKind), pos); 1783 } 1784 1785 if (token() === SyntaxKind.PrivateIdentifier) { 1786 parseErrorAtCurrentToken(privateIdentifierDiagnosticMessage || Diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies); 1787 return createIdentifier(/*isIdentifier*/ true); 1788 } 1789 1790 if (token() === SyntaxKind.Unknown && scanner.tryScan(() => scanner.reScanInvalidIdentifier() === SyntaxKind.Identifier)) { 1791 // Scanner has already recorded an 'Invalid character' error, so no need to add another from the parser. 1792 return createIdentifier(/*isIdentifier*/ true); 1793 } 1794 1795 if (stateStylesRootNode && inEtsStateStylesContext() && token() === SyntaxKind.DotToken) { 1796 identifierCount++; 1797 const pos = getNodePos(); 1798 return finishVirtualNode( 1799 factory.createIdentifier(`${stateStylesRootNode}Instance`, /*typeArguments*/ undefined, SyntaxKind.Identifier), 1800 pos, 1801 pos 1802 ); 1803 } 1804 1805 identifierCount++; 1806 // Only for end of file because the error gets reported incorrectly on embedded script tags. 1807 const reportAtCurrentPosition = token() === SyntaxKind.EndOfFileToken; 1808 1809 const isReservedWord = scanner.isReservedWord(); 1810 const msgArg = scanner.getTokenText(); 1811 1812 const defaultMessage = isReservedWord ? 1813 Diagnostics.Identifier_expected_0_is_a_reserved_word_that_cannot_be_used_here : 1814 Diagnostics.Identifier_expected; 1815 1816 return createMissingNode<Identifier>(SyntaxKind.Identifier, reportAtCurrentPosition, diagnosticMessage || defaultMessage, msgArg); 1817 } 1818 1819 function parseBindingIdentifier(privateIdentifierDiagnosticMessage?: DiagnosticMessage) { 1820 return createIdentifier(isBindingIdentifier(), /*diagnosticMessage*/ undefined, privateIdentifierDiagnosticMessage); 1821 } 1822 1823 function parseIdentifier(diagnosticMessage?: DiagnosticMessage, privateIdentifierDiagnosticMessage?: DiagnosticMessage): Identifier { 1824 return createIdentifier(isIdentifier(), diagnosticMessage, privateIdentifierDiagnosticMessage); 1825 } 1826 1827 function parseEtsIdentifier(pos: number): Identifier { 1828 identifierCount++; 1829 const text = internIdentifier(stylesEtsComponentDeclaration!.type); 1830 return finishVirtualNode(factory.createIdentifier(text), pos, pos); 1831 } 1832 1833 function parseIdentifierName(diagnosticMessage?: DiagnosticMessage): Identifier { 1834 return createIdentifier(tokenIsIdentifierOrKeyword(token()), diagnosticMessage); 1835 } 1836 1837 function isLiteralPropertyName(): boolean { 1838 return tokenIsIdentifierOrKeyword(token()) || 1839 token() === SyntaxKind.StringLiteral || 1840 token() === SyntaxKind.NumericLiteral; 1841 } 1842 1843 function parsePropertyNameWorker(allowComputedPropertyNames: boolean): PropertyName { 1844 if (token() === SyntaxKind.StringLiteral || token() === SyntaxKind.NumericLiteral) { 1845 const node = <StringLiteral | NumericLiteral>parseLiteralNode(); 1846 node.text = internIdentifier(node.text); 1847 return node; 1848 } 1849 if (allowComputedPropertyNames && token() === SyntaxKind.OpenBracketToken) { 1850 return parseComputedPropertyName(); 1851 } 1852 if (token() === SyntaxKind.PrivateIdentifier) { 1853 return parsePrivateIdentifier(); 1854 } 1855 return parseIdentifierName(); 1856 } 1857 1858 function parsePropertyName(): PropertyName { 1859 return parsePropertyNameWorker(/*allowComputedPropertyNames*/ true); 1860 } 1861 1862 function parseComputedPropertyName(): ComputedPropertyName { 1863 // PropertyName [Yield]: 1864 // LiteralPropertyName 1865 // ComputedPropertyName[?Yield] 1866 const pos = getNodePos(); 1867 parseExpected(SyntaxKind.OpenBracketToken); 1868 // We parse any expression (including a comma expression). But the grammar 1869 // says that only an assignment expression is allowed, so the grammar checker 1870 // will error if it sees a comma expression. 1871 const expression = allowInAnd(parseExpression); 1872 parseExpected(SyntaxKind.CloseBracketToken); 1873 return finishNode(factory.createComputedPropertyName(expression), pos); 1874 } 1875 1876 function internPrivateIdentifier(text: string): string { 1877 let privateIdentifier = privateIdentifiers.get(text); 1878 if (privateIdentifier === undefined) { 1879 privateIdentifiers.set(text, privateIdentifier = text); 1880 } 1881 return privateIdentifier; 1882 } 1883 1884 function parsePrivateIdentifier(): PrivateIdentifier { 1885 const pos = getNodePos(); 1886 const node = factory.createPrivateIdentifier(internPrivateIdentifier(scanner.getTokenText())); 1887 nextToken(); 1888 return finishNode(node, pos); 1889 } 1890 1891 function parseContextualModifier(t: SyntaxKind): boolean { 1892 return token() === t && tryParse(nextTokenCanFollowModifier); 1893 } 1894 1895 function nextTokenIsOnSameLineAndCanFollowModifier() { 1896 nextToken(); 1897 if (scanner.hasPrecedingLineBreak()) { 1898 return false; 1899 } 1900 return canFollowModifier(); 1901 } 1902 1903 function nextTokenCanFollowModifier() { 1904 switch (token()) { 1905 case SyntaxKind.ConstKeyword: 1906 // 'const' is only a modifier if followed by 'enum'. 1907 return nextToken() === SyntaxKind.EnumKeyword; 1908 case SyntaxKind.ExportKeyword: 1909 nextToken(); 1910 if (token() === SyntaxKind.DefaultKeyword) { 1911 return lookAhead(nextTokenCanFollowDefaultKeyword); 1912 } 1913 if (token() === SyntaxKind.TypeKeyword) { 1914 return lookAhead(nextTokenCanFollowExportModifier); 1915 } 1916 return canFollowExportModifier(); 1917 case SyntaxKind.DefaultKeyword: 1918 return nextTokenCanFollowDefaultKeyword(); 1919 case SyntaxKind.StaticKeyword: 1920 return nextTokenIsOnSameLineAndCanFollowModifier(); 1921 case SyntaxKind.GetKeyword: 1922 case SyntaxKind.SetKeyword: 1923 nextToken(); 1924 return canFollowModifier(); 1925 default: 1926 return nextTokenIsOnSameLineAndCanFollowModifier(); 1927 } 1928 } 1929 1930 function canFollowExportModifier(): boolean { 1931 return token() !== SyntaxKind.AsteriskToken 1932 && token() !== SyntaxKind.AsKeyword 1933 && token() !== SyntaxKind.OpenBraceToken 1934 && canFollowModifier(); 1935 } 1936 1937 function nextTokenCanFollowExportModifier(): boolean { 1938 nextToken(); 1939 return canFollowExportModifier(); 1940 } 1941 1942 function parseAnyContextualModifier(): boolean { 1943 return isModifierKind(token()) && tryParse(nextTokenCanFollowModifier); 1944 } 1945 1946 function canFollowModifier(): boolean { 1947 return token() === SyntaxKind.OpenBracketToken 1948 || token() === SyntaxKind.OpenBraceToken 1949 || token() === SyntaxKind.AsteriskToken 1950 || token() === SyntaxKind.DotDotDotToken 1951 || isLiteralPropertyName(); 1952 } 1953 1954 function nextTokenCanFollowDefaultKeyword(): boolean { 1955 nextToken(); 1956 return token() === SyntaxKind.ClassKeyword || (inEtsContext() && token() === SyntaxKind.StructKeyword) || 1957 token() === SyntaxKind.FunctionKeyword || token() === SyntaxKind.InterfaceKeyword || 1958 (token() === SyntaxKind.AbstractKeyword && lookAhead(nextTokenIsClassKeywordOnSameLine)) || 1959 (token() === SyntaxKind.AsyncKeyword && lookAhead(nextTokenIsFunctionKeywordOnSameLine)); 1960 } 1961 1962 // True if positioned at the start of a list element 1963 function isListElement(parsingContext: ParsingContext, inErrorRecovery: boolean): boolean { 1964 const node = currentNode(parsingContext); 1965 if (node) { 1966 return true; 1967 } 1968 1969 switch (parsingContext) { 1970 case ParsingContext.SourceElements: 1971 case ParsingContext.BlockStatements: 1972 case ParsingContext.SwitchClauseStatements: 1973 // If we're in error recovery, then we don't want to treat ';' as an empty statement. 1974 // The problem is that ';' can show up in far too many contexts, and if we see one 1975 // and assume it's a statement, then we may bail out inappropriately from whatever 1976 // we're parsing. For example, if we have a semicolon in the middle of a class, then 1977 // we really don't want to assume the class is over and we're on a statement in the 1978 // outer module. We just want to consume and move on. 1979 return !(token() === SyntaxKind.SemicolonToken && inErrorRecovery) && isStartOfStatement(); 1980 case ParsingContext.SwitchClauses: 1981 return token() === SyntaxKind.CaseKeyword || token() === SyntaxKind.DefaultKeyword; 1982 case ParsingContext.TypeMembers: 1983 return lookAhead(isTypeMemberStart); 1984 case ParsingContext.ClassMembers: 1985 // We allow semicolons as class elements (as specified by ES6) as long as we're 1986 // not in error recovery. If we're in error recovery, we don't want an errant 1987 // semicolon to be treated as a class member (since they're almost always used 1988 // for statements. 1989 return lookAhead(isClassMemberStart) || (token() === SyntaxKind.SemicolonToken && !inErrorRecovery); 1990 case ParsingContext.EnumMembers: 1991 // Include open bracket computed properties. This technically also lets in indexers, 1992 // which would be a candidate for improved error reporting. 1993 return token() === SyntaxKind.OpenBracketToken || isLiteralPropertyName(); 1994 case ParsingContext.ObjectLiteralMembers: 1995 switch (token()) { 1996 case SyntaxKind.OpenBracketToken: 1997 case SyntaxKind.AsteriskToken: 1998 case SyntaxKind.DotDotDotToken: 1999 case SyntaxKind.DotToken: // Not an object literal member, but don't want to close the object (see `tests/cases/fourslash/completionsDotInObjectLiteral.ts`) 2000 return true; 2001 default: 2002 return isLiteralPropertyName(); 2003 } 2004 case ParsingContext.RestProperties: 2005 return isLiteralPropertyName(); 2006 case ParsingContext.ObjectBindingElements: 2007 return token() === SyntaxKind.OpenBracketToken || token() === SyntaxKind.DotDotDotToken || isLiteralPropertyName(); 2008 case ParsingContext.HeritageClauseElement: 2009 // If we see `{ ... }` then only consume it as an expression if it is followed by `,` or `{` 2010 // That way we won't consume the body of a class in its heritage clause. 2011 if (token() === SyntaxKind.OpenBraceToken) { 2012 return lookAhead(isValidHeritageClauseObjectLiteral); 2013 } 2014 2015 if (!inErrorRecovery) { 2016 return isStartOfLeftHandSideExpression() && !isHeritageClauseExtendsOrImplementsKeyword(); 2017 } 2018 else { 2019 // If we're in error recovery we tighten up what we're willing to match. 2020 // That way we don't treat something like "this" as a valid heritage clause 2021 // element during recovery. 2022 return isIdentifier() && !isHeritageClauseExtendsOrImplementsKeyword(); 2023 } 2024 case ParsingContext.VariableDeclarations: 2025 return isBindingIdentifierOrPrivateIdentifierOrPattern(); 2026 case ParsingContext.ArrayBindingElements: 2027 return token() === SyntaxKind.CommaToken || token() === SyntaxKind.DotDotDotToken || isBindingIdentifierOrPrivateIdentifierOrPattern(); 2028 case ParsingContext.TypeParameters: 2029 return isIdentifier() || isValidStylesContext(); 2030 case ParsingContext.ArrayLiteralMembers: 2031 switch (token()) { 2032 case SyntaxKind.CommaToken: 2033 case SyntaxKind.DotToken: // Not an array literal member, but don't want to close the array (see `tests/cases/fourslash/completionsDotInArrayLiteralInObjectLiteral.ts`) 2034 return true; 2035 } 2036 // falls through 2037 case ParsingContext.ArgumentExpressions: 2038 return token() === SyntaxKind.DotDotDotToken || isStartOfExpression(); 2039 case ParsingContext.Parameters: 2040 return isStartOfParameter(/*isJSDocParameter*/ false); 2041 case ParsingContext.JSDocParameters: 2042 return isStartOfParameter(/*isJSDocParameter*/ true); 2043 case ParsingContext.TypeArguments: 2044 case ParsingContext.TupleElementTypes: 2045 return token() === SyntaxKind.CommaToken || isStartOfType(); 2046 case ParsingContext.HeritageClauses: 2047 return isHeritageClause(); 2048 case ParsingContext.ImportOrExportSpecifiers: 2049 return tokenIsIdentifierOrKeyword(token()); 2050 case ParsingContext.JsxAttributes: 2051 return tokenIsIdentifierOrKeyword(token()) || token() === SyntaxKind.OpenBraceToken; 2052 case ParsingContext.JsxChildren: 2053 return true; 2054 } 2055 2056 return Debug.fail("Non-exhaustive case in 'isListElement'."); 2057 } 2058 2059 function isValidStylesContext(): boolean { 2060 return (inEtsStylesComponentsContext() && stylesEtsComponentDeclaration !== undefined); 2061 } 2062 2063 function isValidHeritageClauseObjectLiteral() { 2064 Debug.assert(token() === SyntaxKind.OpenBraceToken); 2065 if (nextToken() === SyntaxKind.CloseBraceToken) { 2066 // if we see "extends {}" then only treat the {} as what we're extending (and not 2067 // the class body) if we have: 2068 // 2069 // extends {} { 2070 // extends {}, 2071 // extends {} extends 2072 // extends {} implements 2073 2074 const next = nextToken(); 2075 return next === SyntaxKind.CommaToken || next === SyntaxKind.OpenBraceToken || next === SyntaxKind.ExtendsKeyword || next === SyntaxKind.ImplementsKeyword; 2076 } 2077 2078 return true; 2079 } 2080 2081 function nextTokenIsIdentifier() { 2082 nextToken(); 2083 return isIdentifier(); 2084 } 2085 2086 function nextTokenIsIdentifierOrKeyword() { 2087 nextToken(); 2088 return tokenIsIdentifierOrKeyword(token()); 2089 } 2090 2091 function nextTokenIsIdentifierOrKeywordOrGreaterThan() { 2092 nextToken(); 2093 return tokenIsIdentifierOrKeywordOrGreaterThan(token()); 2094 } 2095 2096 function isHeritageClauseExtendsOrImplementsKeyword(): boolean { 2097 if (token() === SyntaxKind.ImplementsKeyword || 2098 token() === SyntaxKind.ExtendsKeyword) { 2099 2100 return lookAhead(nextTokenIsStartOfExpression); 2101 } 2102 2103 return false; 2104 } 2105 2106 function nextTokenIsStartOfExpression() { 2107 nextToken(); 2108 return isStartOfExpression(); 2109 } 2110 2111 function nextTokenIsStartOfType() { 2112 nextToken(); 2113 return isStartOfType(); 2114 } 2115 2116 // True if positioned at a list terminator 2117 function isListTerminator(kind: ParsingContext): boolean { 2118 if (token() === SyntaxKind.EndOfFileToken) { 2119 // Being at the end of the file ends all lists. 2120 return true; 2121 } 2122 2123 switch (kind) { 2124 case ParsingContext.BlockStatements: 2125 case ParsingContext.SwitchClauses: 2126 case ParsingContext.TypeMembers: 2127 case ParsingContext.ClassMembers: 2128 case ParsingContext.EnumMembers: 2129 case ParsingContext.ObjectLiteralMembers: 2130 case ParsingContext.ObjectBindingElements: 2131 case ParsingContext.ImportOrExportSpecifiers: 2132 return token() === SyntaxKind.CloseBraceToken; 2133 case ParsingContext.SwitchClauseStatements: 2134 return token() === SyntaxKind.CloseBraceToken || token() === SyntaxKind.CaseKeyword || token() === SyntaxKind.DefaultKeyword; 2135 case ParsingContext.HeritageClauseElement: 2136 return token() === SyntaxKind.OpenBraceToken || token() === SyntaxKind.ExtendsKeyword || token() === SyntaxKind.ImplementsKeyword; 2137 case ParsingContext.VariableDeclarations: 2138 return isVariableDeclaratorListTerminator(); 2139 case ParsingContext.TypeParameters: 2140 // Tokens other than '>' are here for better error recovery 2141 return token() === SyntaxKind.GreaterThanToken || token() === SyntaxKind.OpenParenToken || token() === SyntaxKind.OpenBraceToken || token() === SyntaxKind.ExtendsKeyword || token() === SyntaxKind.ImplementsKeyword; 2142 case ParsingContext.ArgumentExpressions: 2143 // Tokens other than ')' are here for better error recovery 2144 return token() === SyntaxKind.CloseParenToken || token() === SyntaxKind.SemicolonToken; 2145 case ParsingContext.ArrayLiteralMembers: 2146 case ParsingContext.TupleElementTypes: 2147 case ParsingContext.ArrayBindingElements: 2148 return token() === SyntaxKind.CloseBracketToken; 2149 case ParsingContext.JSDocParameters: 2150 case ParsingContext.Parameters: 2151 case ParsingContext.RestProperties: 2152 // Tokens other than ')' and ']' (the latter for index signatures) are here for better error recovery 2153 return token() === SyntaxKind.CloseParenToken || token() === SyntaxKind.CloseBracketToken /*|| token === SyntaxKind.OpenBraceToken*/; 2154 case ParsingContext.TypeArguments: 2155 // All other tokens should cause the type-argument to terminate except comma token 2156 return token() !== SyntaxKind.CommaToken; 2157 case ParsingContext.HeritageClauses: 2158 return token() === SyntaxKind.OpenBraceToken || token() === SyntaxKind.CloseBraceToken; 2159 case ParsingContext.JsxAttributes: 2160 return token() === SyntaxKind.GreaterThanToken || token() === SyntaxKind.SlashToken; 2161 case ParsingContext.JsxChildren: 2162 return token() === SyntaxKind.LessThanToken && lookAhead(nextTokenIsSlash); 2163 default: 2164 return false; 2165 } 2166 } 2167 2168 function isVariableDeclaratorListTerminator(): boolean { 2169 // If we can consume a semicolon (either explicitly, or with ASI), then consider us done 2170 // with parsing the list of variable declarators. 2171 if (canParseSemicolon()) { 2172 return true; 2173 } 2174 2175 // in the case where we're parsing the variable declarator of a 'for-in' statement, we 2176 // are done if we see an 'in' keyword in front of us. Same with for-of 2177 if (isInOrOfKeyword(token())) { 2178 return true; 2179 } 2180 2181 // ERROR RECOVERY TWEAK: 2182 // For better error recovery, if we see an '=>' then we just stop immediately. We've got an 2183 // arrow function here and it's going to be very unlikely that we'll resynchronize and get 2184 // another variable declaration. 2185 if (token() === SyntaxKind.EqualsGreaterThanToken) { 2186 return true; 2187 } 2188 2189 // Keep trying to parse out variable declarators. 2190 return false; 2191 } 2192 2193 // True if positioned at element or terminator of the current list or any enclosing list 2194 function isInSomeParsingContext(): boolean { 2195 for (let kind = 0; kind < ParsingContext.Count; kind++) { 2196 if (parsingContext & (1 << kind)) { 2197 if (isListElement(kind, /*inErrorRecovery*/ true) || isListTerminator(kind)) { 2198 return true; 2199 } 2200 } 2201 } 2202 2203 return false; 2204 } 2205 2206 // Parses a list of elements 2207 function parseList<T extends Node>(kind: ParsingContext, parseElement: () => T): NodeArray<T> { 2208 const saveParsingContext = parsingContext; 2209 parsingContext |= 1 << kind; 2210 const list = []; 2211 const listPos = getNodePos(); 2212 2213 while (!isListTerminator(kind)) { 2214 if (isListElement(kind, /*inErrorRecovery*/ false)) { 2215 const element = parseListElement(kind, parseElement); 2216 list.push(element); 2217 2218 continue; 2219 } 2220 2221 if (abortParsingListOrMoveToNextToken(kind)) { 2222 break; 2223 } 2224 } 2225 2226 parsingContext = saveParsingContext; 2227 return createNodeArray(list, listPos); 2228 } 2229 2230 function parseListElement<T extends Node>(parsingContext: ParsingContext, parseElement: () => T): T { 2231 const node = currentNode(parsingContext); 2232 if (node) { 2233 return <T>consumeNode(node); 2234 } 2235 2236 return parseElement(); 2237 } 2238 2239 function currentNode(parsingContext: ParsingContext): Node | undefined { 2240 // If we don't have a cursor or the parsing context isn't reusable, there's nothing to reuse. 2241 // 2242 // If there is an outstanding parse error that we've encountered, but not attached to 2243 // some node, then we cannot get a node from the old source tree. This is because we 2244 // want to mark the next node we encounter as being unusable. 2245 // 2246 // Note: This may be too conservative. Perhaps we could reuse the node and set the bit 2247 // on it (or its leftmost child) as having the error. For now though, being conservative 2248 // is nice and likely won't ever affect perf. 2249 if (!syntaxCursor || !isReusableParsingContext(parsingContext) || parseErrorBeforeNextFinishedNode) { 2250 return undefined; 2251 } 2252 2253 const node = syntaxCursor.currentNode(scanner.getStartPos()); 2254 2255 // Can't reuse a missing node. 2256 // Can't reuse a node that intersected the change range. 2257 // Can't reuse a node that contains a parse error. This is necessary so that we 2258 // produce the same set of errors again. 2259 if (nodeIsMissing(node) || node.intersectsChange || containsParseError(node)) { 2260 return undefined; 2261 } 2262 2263 // We can only reuse a node if it was parsed under the same strict mode that we're 2264 // currently in. i.e. if we originally parsed a node in non-strict mode, but then 2265 // the user added 'using strict' at the top of the file, then we can't use that node 2266 // again as the presence of strict mode may cause us to parse the tokens in the file 2267 // differently. 2268 // 2269 // Note: we *can* reuse tokens when the strict mode changes. That's because tokens 2270 // are unaffected by strict mode. It's just the parser will decide what to do with it 2271 // differently depending on what mode it is in. 2272 // 2273 // This also applies to all our other context flags as well. 2274 const nodeContextFlags = node.flags & NodeFlags.ContextFlags; 2275 if (nodeContextFlags !== contextFlags) { 2276 return undefined; 2277 } 2278 2279 // Ok, we have a node that looks like it could be reused. Now verify that it is valid 2280 // in the current list parsing context that we're currently at. 2281 if (!canReuseNode(node, parsingContext)) { 2282 return undefined; 2283 } 2284 2285 if ((node as JSDocContainer).jsDocCache) { 2286 // jsDocCache may include tags from parent nodes, which might have been modified. 2287 (node as JSDocContainer).jsDocCache = undefined; 2288 } 2289 2290 return node; 2291 } 2292 2293 function consumeNode(node: Node) { 2294 // Move the scanner so it is after the node we just consumed. 2295 scanner.setTextPos(node.end); 2296 nextToken(); 2297 return node; 2298 } 2299 2300 function isReusableParsingContext(parsingContext: ParsingContext): boolean { 2301 switch (parsingContext) { 2302 case ParsingContext.ClassMembers: 2303 case ParsingContext.SwitchClauses: 2304 case ParsingContext.SourceElements: 2305 case ParsingContext.BlockStatements: 2306 case ParsingContext.SwitchClauseStatements: 2307 case ParsingContext.EnumMembers: 2308 case ParsingContext.TypeMembers: 2309 case ParsingContext.VariableDeclarations: 2310 case ParsingContext.JSDocParameters: 2311 case ParsingContext.Parameters: 2312 return true; 2313 } 2314 return false; 2315 } 2316 2317 function canReuseNode(node: Node, parsingContext: ParsingContext): boolean { 2318 switch (parsingContext) { 2319 case ParsingContext.ClassMembers: 2320 return isReusableClassMember(node); 2321 2322 case ParsingContext.SwitchClauses: 2323 return isReusableSwitchClause(node); 2324 2325 case ParsingContext.SourceElements: 2326 case ParsingContext.BlockStatements: 2327 case ParsingContext.SwitchClauseStatements: 2328 return isReusableStatement(node); 2329 2330 case ParsingContext.EnumMembers: 2331 return isReusableEnumMember(node); 2332 2333 case ParsingContext.TypeMembers: 2334 return isReusableTypeMember(node); 2335 2336 case ParsingContext.VariableDeclarations: 2337 return isReusableVariableDeclaration(node); 2338 2339 case ParsingContext.JSDocParameters: 2340 case ParsingContext.Parameters: 2341 return isReusableParameter(node); 2342 2343 // Any other lists we do not care about reusing nodes in. But feel free to add if 2344 // you can do so safely. Danger areas involve nodes that may involve speculative 2345 // parsing. If speculative parsing is involved with the node, then the range the 2346 // parser reached while looking ahead might be in the edited range (see the example 2347 // in canReuseVariableDeclaratorNode for a good case of this). 2348 2349 // case ParsingContext.HeritageClauses: 2350 // This would probably be safe to reuse. There is no speculative parsing with 2351 // heritage clauses. 2352 2353 // case ParsingContext.TypeParameters: 2354 // This would probably be safe to reuse. There is no speculative parsing with 2355 // type parameters. Note that that's because type *parameters* only occur in 2356 // unambiguous *type* contexts. While type *arguments* occur in very ambiguous 2357 // *expression* contexts. 2358 2359 // case ParsingContext.TupleElementTypes: 2360 // This would probably be safe to reuse. There is no speculative parsing with 2361 // tuple types. 2362 2363 // Technically, type argument list types are probably safe to reuse. While 2364 // speculative parsing is involved with them (since type argument lists are only 2365 // produced from speculative parsing a < as a type argument list), we only have 2366 // the types because speculative parsing succeeded. Thus, the lookahead never 2367 // went past the end of the list and rewound. 2368 // case ParsingContext.TypeArguments: 2369 2370 // Note: these are almost certainly not safe to ever reuse. Expressions commonly 2371 // need a large amount of lookahead, and we should not reuse them as they may 2372 // have actually intersected the edit. 2373 // case ParsingContext.ArgumentExpressions: 2374 2375 // This is not safe to reuse for the same reason as the 'AssignmentExpression' 2376 // cases. i.e. a property assignment may end with an expression, and thus might 2377 // have lookahead far beyond it's old node. 2378 // case ParsingContext.ObjectLiteralMembers: 2379 2380 // This is probably not safe to reuse. There can be speculative parsing with 2381 // type names in a heritage clause. There can be generic names in the type 2382 // name list, and there can be left hand side expressions (which can have type 2383 // arguments.) 2384 // case ParsingContext.HeritageClauseElement: 2385 2386 // Perhaps safe to reuse, but it's unlikely we'd see more than a dozen attributes 2387 // on any given element. Same for children. 2388 // case ParsingContext.JsxAttributes: 2389 // case ParsingContext.JsxChildren: 2390 2391 } 2392 2393 return false; 2394 } 2395 2396 function isReusableClassMember(node: Node) { 2397 if (node) { 2398 switch (node.kind) { 2399 case SyntaxKind.Constructor: 2400 case SyntaxKind.IndexSignature: 2401 case SyntaxKind.GetAccessor: 2402 case SyntaxKind.SetAccessor: 2403 case SyntaxKind.SemicolonClassElement: 2404 return true; 2405 case SyntaxKind.PropertyDeclaration: 2406 return !inStructContext(); 2407 case SyntaxKind.MethodDeclaration: 2408 // Method declarations are not necessarily reusable. An object-literal 2409 // may have a method calls "constructor(...)" and we must reparse that 2410 // into an actual .ConstructorDeclaration. 2411 const methodDeclaration = <MethodDeclaration>node; 2412 const nameIsConstructor = methodDeclaration.name.kind === SyntaxKind.Identifier && 2413 methodDeclaration.name.originalKeywordKind === SyntaxKind.ConstructorKeyword; 2414 2415 return !nameIsConstructor; 2416 } 2417 } 2418 2419 return false; 2420 } 2421 2422 function isReusableSwitchClause(node: Node) { 2423 if (node) { 2424 switch (node.kind) { 2425 case SyntaxKind.CaseClause: 2426 case SyntaxKind.DefaultClause: 2427 return true; 2428 } 2429 } 2430 2431 return false; 2432 } 2433 2434 function isReusableStatement(node: Node) { 2435 if (node) { 2436 switch (node.kind) { 2437 case SyntaxKind.FunctionDeclaration: 2438 case SyntaxKind.VariableStatement: 2439 case SyntaxKind.Block: 2440 case SyntaxKind.IfStatement: 2441 case SyntaxKind.ExpressionStatement: 2442 case SyntaxKind.ThrowStatement: 2443 case SyntaxKind.ReturnStatement: 2444 case SyntaxKind.SwitchStatement: 2445 case SyntaxKind.BreakStatement: 2446 case SyntaxKind.ContinueStatement: 2447 case SyntaxKind.ForInStatement: 2448 case SyntaxKind.ForOfStatement: 2449 case SyntaxKind.ForStatement: 2450 case SyntaxKind.WhileStatement: 2451 case SyntaxKind.WithStatement: 2452 case SyntaxKind.EmptyStatement: 2453 case SyntaxKind.TryStatement: 2454 case SyntaxKind.LabeledStatement: 2455 case SyntaxKind.DoStatement: 2456 case SyntaxKind.DebuggerStatement: 2457 case SyntaxKind.ImportDeclaration: 2458 case SyntaxKind.ImportEqualsDeclaration: 2459 case SyntaxKind.ExportDeclaration: 2460 case SyntaxKind.ExportAssignment: 2461 case SyntaxKind.ModuleDeclaration: 2462 case SyntaxKind.ClassDeclaration: 2463 case SyntaxKind.StructDeclaration: 2464 case SyntaxKind.InterfaceDeclaration: 2465 case SyntaxKind.EnumDeclaration: 2466 case SyntaxKind.TypeAliasDeclaration: 2467 return true; 2468 } 2469 } 2470 2471 return false; 2472 } 2473 2474 function isReusableEnumMember(node: Node) { 2475 return node.kind === SyntaxKind.EnumMember; 2476 } 2477 2478 function isReusableTypeMember(node: Node) { 2479 if (node) { 2480 switch (node.kind) { 2481 case SyntaxKind.ConstructSignature: 2482 case SyntaxKind.MethodSignature: 2483 case SyntaxKind.IndexSignature: 2484 case SyntaxKind.PropertySignature: 2485 case SyntaxKind.CallSignature: 2486 return true; 2487 } 2488 } 2489 2490 return false; 2491 } 2492 2493 function isReusableVariableDeclaration(node: Node) { 2494 if (node.kind !== SyntaxKind.VariableDeclaration) { 2495 return false; 2496 } 2497 2498 // Very subtle incremental parsing bug. Consider the following code: 2499 // 2500 // let v = new List < A, B 2501 // 2502 // This is actually legal code. It's a list of variable declarators "v = new List<A" 2503 // on one side and "B" on the other. If you then change that to: 2504 // 2505 // let v = new List < A, B >() 2506 // 2507 // then we have a problem. "v = new List<A" doesn't intersect the change range, so we 2508 // start reparsing at "B" and we completely fail to handle this properly. 2509 // 2510 // In order to prevent this, we do not allow a variable declarator to be reused if it 2511 // has an initializer. 2512 const variableDeclarator = <VariableDeclaration>node; 2513 return variableDeclarator.initializer === undefined; 2514 } 2515 2516 function isReusableParameter(node: Node) { 2517 if (node.kind !== SyntaxKind.Parameter) { 2518 return false; 2519 } 2520 2521 // See the comment in isReusableVariableDeclaration for why we do this. 2522 const parameter = <ParameterDeclaration>node; 2523 return parameter.initializer === undefined; 2524 } 2525 2526 // Returns true if we should abort parsing. 2527 function abortParsingListOrMoveToNextToken(kind: ParsingContext) { 2528 parsingContextErrors(kind); 2529 if (isInSomeParsingContext()) { 2530 return true; 2531 } 2532 2533 nextToken(); 2534 return false; 2535 } 2536 2537 function parsingContextErrors(context: ParsingContext) { 2538 switch (context) { 2539 case ParsingContext.SourceElements: return parseErrorAtCurrentToken(Diagnostics.Declaration_or_statement_expected); 2540 case ParsingContext.BlockStatements: return parseErrorAtCurrentToken(Diagnostics.Declaration_or_statement_expected); 2541 case ParsingContext.SwitchClauses: return parseErrorAtCurrentToken(Diagnostics.case_or_default_expected); 2542 case ParsingContext.SwitchClauseStatements: return parseErrorAtCurrentToken(Diagnostics.Statement_expected); 2543 case ParsingContext.RestProperties: // fallthrough 2544 case ParsingContext.TypeMembers: return parseErrorAtCurrentToken(Diagnostics.Property_or_signature_expected); 2545 case ParsingContext.ClassMembers: return parseErrorAtCurrentToken(Diagnostics.Unexpected_token_A_constructor_method_accessor_or_property_was_expected); 2546 case ParsingContext.EnumMembers: return parseErrorAtCurrentToken(Diagnostics.Enum_member_expected); 2547 case ParsingContext.HeritageClauseElement: return parseErrorAtCurrentToken(Diagnostics.Expression_expected); 2548 case ParsingContext.VariableDeclarations: 2549 return isKeyword(token()) 2550 ? parseErrorAtCurrentToken(Diagnostics._0_is_not_allowed_as_a_variable_declaration_name, tokenToString(token())) 2551 : parseErrorAtCurrentToken(Diagnostics.Variable_declaration_expected); 2552 case ParsingContext.ObjectBindingElements: return parseErrorAtCurrentToken(Diagnostics.Property_destructuring_pattern_expected); 2553 case ParsingContext.ArrayBindingElements: return parseErrorAtCurrentToken(Diagnostics.Array_element_destructuring_pattern_expected); 2554 case ParsingContext.ArgumentExpressions: return parseErrorAtCurrentToken(Diagnostics.Argument_expression_expected); 2555 case ParsingContext.ObjectLiteralMembers: return parseErrorAtCurrentToken(Diagnostics.Property_assignment_expected); 2556 case ParsingContext.ArrayLiteralMembers: return parseErrorAtCurrentToken(Diagnostics.Expression_or_comma_expected); 2557 case ParsingContext.JSDocParameters: return parseErrorAtCurrentToken(Diagnostics.Parameter_declaration_expected); 2558 case ParsingContext.Parameters: return parseErrorAtCurrentToken(Diagnostics.Parameter_declaration_expected); 2559 case ParsingContext.TypeParameters: return parseErrorAtCurrentToken(Diagnostics.Type_parameter_declaration_expected); 2560 case ParsingContext.TypeArguments: return parseErrorAtCurrentToken(Diagnostics.Type_argument_expected); 2561 case ParsingContext.TupleElementTypes: return parseErrorAtCurrentToken(Diagnostics.Type_expected); 2562 case ParsingContext.HeritageClauses: return parseErrorAtCurrentToken(Diagnostics.Unexpected_token_expected); 2563 case ParsingContext.ImportOrExportSpecifiers: return parseErrorAtCurrentToken(Diagnostics.Identifier_expected); 2564 case ParsingContext.JsxAttributes: return parseErrorAtCurrentToken(Diagnostics.Identifier_expected); 2565 case ParsingContext.JsxChildren: return parseErrorAtCurrentToken(Diagnostics.Identifier_expected); 2566 default: return [undefined!]; // TODO: GH#18217 `default: Debug.assertNever(context);` 2567 } 2568 } 2569 2570 // Parses a comma-delimited list of elements 2571 function parseDelimitedList<T extends Node>(kind: ParsingContext, parseElement: () => T, considerSemicolonAsDelimiter?: boolean): NodeArray<T> { 2572 const saveParsingContext = parsingContext; 2573 parsingContext |= 1 << kind; 2574 const list = []; 2575 const listPos = getNodePos(); 2576 2577 let commaStart = -1; // Meaning the previous token was not a comma 2578 while (true) { 2579 if (isListElement(kind, /*inErrorRecovery*/ false)) { 2580 const startPos = scanner.getStartPos(); 2581 list.push(parseListElement(kind, parseElement)); 2582 commaStart = scanner.getTokenPos(); 2583 2584 if (parseOptional(SyntaxKind.CommaToken)) { 2585 // No need to check for a zero length node since we know we parsed a comma 2586 continue; 2587 } 2588 2589 commaStart = -1; // Back to the state where the last token was not a comma 2590 if (isListTerminator(kind)) { 2591 break; 2592 } 2593 2594 // We didn't get a comma, and the list wasn't terminated, explicitly parse 2595 // out a comma so we give a good error message. 2596 parseExpected(SyntaxKind.CommaToken, getExpectedCommaDiagnostic(kind)); 2597 2598 // If the token was a semicolon, and the caller allows that, then skip it and 2599 // continue. This ensures we get back on track and don't result in tons of 2600 // parse errors. For example, this can happen when people do things like use 2601 // a semicolon to delimit object literal members. Note: we'll have already 2602 // reported an error when we called parseExpected above. 2603 if (considerSemicolonAsDelimiter && token() === SyntaxKind.SemicolonToken && !scanner.hasPrecedingLineBreak()) { 2604 nextToken(); 2605 } 2606 if (startPos === scanner.getStartPos()) { 2607 // What we're parsing isn't actually remotely recognizable as a element and we've consumed no tokens whatsoever 2608 // Consume a token to advance the parser in some way and avoid an infinite loop 2609 // This can happen when we're speculatively parsing parenthesized expressions which we think may be arrow functions, 2610 // or when a modifier keyword which is disallowed as a parameter name (ie, `static` in strict mode) is supplied 2611 nextToken(); 2612 } 2613 continue; 2614 } 2615 2616 if (isListTerminator(kind)) { 2617 break; 2618 } 2619 2620 if (abortParsingListOrMoveToNextToken(kind)) { 2621 break; 2622 } 2623 } 2624 2625 parsingContext = saveParsingContext; 2626 // Recording the trailing comma is deliberately done after the previous 2627 // loop, and not just if we see a list terminator. This is because the list 2628 // may have ended incorrectly, but it is still important to know if there 2629 // was a trailing comma. 2630 // Check if the last token was a comma. 2631 // Always preserve a trailing comma by marking it on the NodeArray 2632 return createNodeArray(list, listPos, /*end*/ undefined, commaStart >= 0); 2633 } 2634 2635 function getExpectedCommaDiagnostic(kind: ParsingContext) { 2636 return kind === ParsingContext.EnumMembers ? Diagnostics.An_enum_member_name_must_be_followed_by_a_or : undefined; 2637 } 2638 2639 interface MissingList<T extends Node> extends NodeArray<T> { 2640 isMissingList: true; 2641 } 2642 2643 function createMissingList<T extends Node>(): MissingList<T> { 2644 const list = createNodeArray<T>([], getNodePos()) as MissingList<T>; 2645 list.isMissingList = true; 2646 return list; 2647 } 2648 2649 function isMissingList(arr: NodeArray<Node>): boolean { 2650 return !!(arr as MissingList<Node>).isMissingList; 2651 } 2652 2653 function parseBracketedList<T extends Node>(kind: ParsingContext, parseElement: () => T, open: SyntaxKind, close: SyntaxKind): NodeArray<T> { 2654 if (parseExpected(open)) { 2655 const result = parseDelimitedList(kind, parseElement); 2656 parseExpected(close); 2657 return result; 2658 } 2659 2660 return createMissingList<T>(); 2661 } 2662 2663 function parseEntityName(allowReservedWords: boolean, diagnosticMessage?: DiagnosticMessage): EntityName { 2664 const pos = getNodePos(); 2665 let entity: EntityName = allowReservedWords ? parseIdentifierName(diagnosticMessage) : parseIdentifier(diagnosticMessage); 2666 let dotPos = getNodePos(); 2667 while (parseOptional(SyntaxKind.DotToken)) { 2668 if (token() === SyntaxKind.LessThanToken) { 2669 // the entity is part of a JSDoc-style generic, so record the trailing dot for later error reporting 2670 entity.jsdocDotPos = dotPos; 2671 break; 2672 } 2673 dotPos = getNodePos(); 2674 entity = finishNode( 2675 factory.createQualifiedName( 2676 entity, 2677 parseRightSideOfDot(allowReservedWords, /* allowPrivateIdentifiers */ false) as Identifier 2678 ), 2679 pos 2680 ); 2681 } 2682 return entity; 2683 } 2684 2685 function createQualifiedName(entity: EntityName, name: Identifier): QualifiedName { 2686 return finishNode(factory.createQualifiedName(entity, name), entity.pos); 2687 } 2688 2689 function parseRightSideOfDot(allowIdentifierNames: boolean, allowPrivateIdentifiers: boolean): Identifier | PrivateIdentifier { 2690 // Technically a keyword is valid here as all identifiers and keywords are identifier names. 2691 // However, often we'll encounter this in error situations when the identifier or keyword 2692 // is actually starting another valid construct. 2693 // 2694 // So, we check for the following specific case: 2695 // 2696 // name. 2697 // identifierOrKeyword identifierNameOrKeyword 2698 // 2699 // Note: the newlines are important here. For example, if that above code 2700 // were rewritten into: 2701 // 2702 // name.identifierOrKeyword 2703 // identifierNameOrKeyword 2704 // 2705 // Then we would consider it valid. That's because ASI would take effect and 2706 // the code would be implicitly: "name.identifierOrKeyword; identifierNameOrKeyword". 2707 // In the first case though, ASI will not take effect because there is not a 2708 // line terminator after the identifier or keyword. 2709 if (scanner.hasPrecedingLineBreak() && tokenIsIdentifierOrKeyword(token())) { 2710 const matchesPattern = lookAhead(nextTokenIsIdentifierOrKeywordOnSameLine); 2711 2712 if (matchesPattern) { 2713 // Report that we need an identifier. However, report it right after the dot, 2714 // and not on the next token. This is because the next token might actually 2715 // be an identifier and the error would be quite confusing. 2716 return createMissingNode<Identifier>(SyntaxKind.Identifier, /*reportAtCurrentPosition*/ true, Diagnostics.Identifier_expected); 2717 } 2718 } 2719 2720 if (token() === SyntaxKind.PrivateIdentifier) { 2721 const node = parsePrivateIdentifier(); 2722 return allowPrivateIdentifiers ? node : createMissingNode<Identifier>(SyntaxKind.Identifier, /*reportAtCurrentPosition*/ true, Diagnostics.Identifier_expected); 2723 } 2724 2725 return allowIdentifierNames ? parseIdentifierName() : parseIdentifier(); 2726 } 2727 2728 function parseTemplateSpans(isTaggedTemplate: boolean) { 2729 const pos = getNodePos(); 2730 const list = []; 2731 let node: TemplateSpan; 2732 do { 2733 node = parseTemplateSpan(isTaggedTemplate); 2734 list.push(node); 2735 } 2736 while (node.literal.kind === SyntaxKind.TemplateMiddle); 2737 return createNodeArray(list, pos); 2738 } 2739 2740 function parseTemplateExpression(isTaggedTemplate: boolean): TemplateExpression { 2741 const pos = getNodePos(); 2742 return finishNode( 2743 factory.createTemplateExpression( 2744 parseTemplateHead(isTaggedTemplate), 2745 parseTemplateSpans(isTaggedTemplate) 2746 ), 2747 pos 2748 ); 2749 } 2750 2751 function parseTemplateType(): TemplateLiteralTypeNode { 2752 const pos = getNodePos(); 2753 return finishNode( 2754 factory.createTemplateLiteralType( 2755 parseTemplateHead(/*isTaggedTemplate*/ false), 2756 parseTemplateTypeSpans() 2757 ), 2758 pos 2759 ); 2760 } 2761 2762 function parseTemplateTypeSpans() { 2763 const pos = getNodePos(); 2764 const list = []; 2765 let node: TemplateLiteralTypeSpan; 2766 do { 2767 node = parseTemplateTypeSpan(); 2768 list.push(node); 2769 } 2770 while (node.literal.kind === SyntaxKind.TemplateMiddle); 2771 return createNodeArray(list, pos); 2772 } 2773 2774 function parseTemplateTypeSpan(): TemplateLiteralTypeSpan { 2775 const pos = getNodePos(); 2776 return finishNode( 2777 factory.createTemplateLiteralTypeSpan( 2778 parseType(), 2779 parseLiteralOfTemplateSpan(/*isTaggedTemplate*/ false) 2780 ), 2781 pos 2782 ); 2783 } 2784 2785 function parseLiteralOfTemplateSpan(isTaggedTemplate: boolean) { 2786 if (token() === SyntaxKind.CloseBraceToken) { 2787 reScanTemplateToken(isTaggedTemplate); 2788 return parseTemplateMiddleOrTemplateTail(); 2789 } 2790 else { 2791 // TODO(rbuckton): Do we need to call `parseExpectedToken` or can we just call `createMissingNode` directly? 2792 return <TemplateTail>parseExpectedToken(SyntaxKind.TemplateTail, Diagnostics._0_expected, tokenToString(SyntaxKind.CloseBraceToken)); 2793 } 2794 } 2795 2796 function parseTemplateSpan(isTaggedTemplate: boolean): TemplateSpan { 2797 const pos = getNodePos(); 2798 return finishNode( 2799 factory.createTemplateSpan( 2800 allowInAnd(parseExpression), 2801 parseLiteralOfTemplateSpan(isTaggedTemplate) 2802 ), 2803 pos 2804 ); 2805 } 2806 2807 function parseLiteralNode(): LiteralExpression { 2808 return <LiteralExpression>parseLiteralLikeNode(token()); 2809 } 2810 2811 function parseTemplateHead(isTaggedTemplate: boolean): TemplateHead { 2812 if (isTaggedTemplate) { 2813 reScanTemplateHeadOrNoSubstitutionTemplate(); 2814 } 2815 const fragment = parseLiteralLikeNode(token()); 2816 Debug.assert(fragment.kind === SyntaxKind.TemplateHead, "Template head has wrong token kind"); 2817 return <TemplateHead>fragment; 2818 } 2819 2820 function parseTemplateMiddleOrTemplateTail(): TemplateMiddle | TemplateTail { 2821 const fragment = parseLiteralLikeNode(token()); 2822 Debug.assert(fragment.kind === SyntaxKind.TemplateMiddle || fragment.kind === SyntaxKind.TemplateTail, "Template fragment has wrong token kind"); 2823 return <TemplateMiddle | TemplateTail>fragment; 2824 } 2825 2826 function getTemplateLiteralRawText(kind: TemplateLiteralToken["kind"]) { 2827 const isLast = kind === SyntaxKind.NoSubstitutionTemplateLiteral || kind === SyntaxKind.TemplateTail; 2828 const tokenText = scanner.getTokenText(); 2829 return tokenText.substring(1, tokenText.length - (scanner.isUnterminated() ? 0 : isLast ? 1 : 2)); 2830 } 2831 2832 function parseLiteralLikeNode(kind: SyntaxKind): LiteralLikeNode { 2833 const pos = getNodePos(); 2834 const node = 2835 isTemplateLiteralKind(kind) ? factory.createTemplateLiteralLikeNode(kind, scanner.getTokenValue(), getTemplateLiteralRawText(kind), scanner.getTokenFlags() & TokenFlags.TemplateLiteralLikeFlags) : 2836 // Octal literals are not allowed in strict mode or ES5 2837 // Note that theoretically the following condition would hold true literals like 009, 2838 // which is not octal. But because of how the scanner separates the tokens, we would 2839 // never get a token like this. Instead, we would get 00 and 9 as two separate tokens. 2840 // We also do not need to check for negatives because any prefix operator would be part of a 2841 // parent unary expression. 2842 kind === SyntaxKind.NumericLiteral ? factory.createNumericLiteral(scanner.getTokenValue(), scanner.getNumericLiteralFlags()) : 2843 kind === SyntaxKind.StringLiteral ? factory.createStringLiteral(scanner.getTokenValue(), /*isSingleQuote*/ undefined, scanner.hasExtendedUnicodeEscape()) : 2844 isLiteralKind(kind) ? factory.createLiteralLikeNode(kind, scanner.getTokenValue()) : 2845 Debug.fail(); 2846 2847 if (scanner.hasExtendedUnicodeEscape()) { 2848 node.hasExtendedUnicodeEscape = true; 2849 } 2850 2851 if (scanner.isUnterminated()) { 2852 node.isUnterminated = true; 2853 } 2854 2855 nextToken(); 2856 return finishNode(node, pos); 2857 } 2858 2859 // TYPES 2860 2861 function parseEntityNameOfTypeReference() { 2862 return parseEntityName(/*allowReservedWords*/ true, Diagnostics.Type_expected); 2863 } 2864 2865 function parseTypeArgumentsOfTypeReference() { 2866 if (!scanner.hasPrecedingLineBreak() && reScanLessThanToken() === SyntaxKind.LessThanToken) { 2867 return parseBracketedList(ParsingContext.TypeArguments, parseType, SyntaxKind.LessThanToken, SyntaxKind.GreaterThanToken); 2868 } 2869 } 2870 2871 function parseTypeReference(): TypeReferenceNode { 2872 const pos = getNodePos(); 2873 return finishNode( 2874 factory.createTypeReferenceNode( 2875 parseEntityNameOfTypeReference(), 2876 parseTypeArgumentsOfTypeReference() 2877 ), 2878 pos 2879 ); 2880 } 2881 2882 // If true, we should abort parsing an error function. 2883 function typeHasArrowFunctionBlockingParseError(node: TypeNode): boolean { 2884 switch (node.kind) { 2885 case SyntaxKind.TypeReference: 2886 return nodeIsMissing((node as TypeReferenceNode).typeName); 2887 case SyntaxKind.FunctionType: 2888 case SyntaxKind.ConstructorType: { 2889 const { parameters, type } = node as FunctionOrConstructorTypeNode; 2890 return isMissingList(parameters) || typeHasArrowFunctionBlockingParseError(type); 2891 } 2892 case SyntaxKind.ParenthesizedType: 2893 return typeHasArrowFunctionBlockingParseError((node as ParenthesizedTypeNode).type); 2894 default: 2895 return false; 2896 } 2897 } 2898 2899 function parseThisTypePredicate(lhs: ThisTypeNode): TypePredicateNode { 2900 nextToken(); 2901 return finishNode(factory.createTypePredicateNode(/*assertsModifier*/ undefined, lhs, parseType()), lhs.pos); 2902 } 2903 2904 function parseThisTypeNode(): ThisTypeNode { 2905 const pos = getNodePos(); 2906 nextToken(); 2907 return finishNode(factory.createThisTypeNode(), pos); 2908 } 2909 2910 function parseJSDocAllType(): JSDocAllType | JSDocOptionalType { 2911 const pos = getNodePos(); 2912 nextToken(); 2913 return finishNode(factory.createJSDocAllType(), pos); 2914 } 2915 2916 function parseJSDocNonNullableType(): TypeNode { 2917 const pos = getNodePos(); 2918 nextToken(); 2919 return finishNode(factory.createJSDocNonNullableType(parseNonArrayType()), pos); 2920 } 2921 2922 function parseJSDocUnknownOrNullableType(): JSDocUnknownType | JSDocNullableType { 2923 const pos = getNodePos(); 2924 // skip the ? 2925 nextToken(); 2926 2927 // Need to lookahead to decide if this is a nullable or unknown type. 2928 2929 // Here are cases where we'll pick the unknown type: 2930 // 2931 // Foo(?, 2932 // { a: ? } 2933 // Foo(?) 2934 // Foo<?> 2935 // Foo(?= 2936 // (?| 2937 if (token() === SyntaxKind.CommaToken || 2938 token() === SyntaxKind.CloseBraceToken || 2939 token() === SyntaxKind.CloseParenToken || 2940 token() === SyntaxKind.GreaterThanToken || 2941 token() === SyntaxKind.EqualsToken || 2942 token() === SyntaxKind.BarToken) { 2943 return finishNode(factory.createJSDocUnknownType(), pos); 2944 } 2945 else { 2946 return finishNode(factory.createJSDocNullableType(parseType()), pos); 2947 } 2948 } 2949 2950 function parseJSDocFunctionType(): JSDocFunctionType | TypeReferenceNode { 2951 const pos = getNodePos(); 2952 const hasJSDoc = hasPrecedingJSDocComment(); 2953 if (lookAhead(nextTokenIsOpenParen)) { 2954 nextToken(); 2955 const parameters = parseParameters(SignatureFlags.Type | SignatureFlags.JSDoc); 2956 const type = parseReturnType(SyntaxKind.ColonToken, /*isType*/ false); 2957 return withJSDoc(finishNode(factory.createJSDocFunctionType(parameters, type), pos), hasJSDoc); 2958 } 2959 return finishNode(factory.createTypeReferenceNode(parseIdentifierName(), /*typeArguments*/ undefined), pos); 2960 } 2961 2962 function parseJSDocParameter(): ParameterDeclaration { 2963 const pos = getNodePos(); 2964 let name: Identifier | undefined; 2965 if (token() === SyntaxKind.ThisKeyword || token() === SyntaxKind.NewKeyword) { 2966 name = parseIdentifierName(); 2967 parseExpected(SyntaxKind.ColonToken); 2968 } 2969 return finishNode( 2970 factory.createParameterDeclaration( 2971 /*decorators*/ undefined, 2972 /*modifiers*/ undefined, 2973 /*dotDotDotToken*/ undefined, 2974 // TODO(rbuckton): JSDoc parameters don't have names (except `this`/`new`), should we manufacture an empty identifier? 2975 name!, 2976 /*questionToken*/ undefined, 2977 parseJSDocType(), 2978 /*initializer*/ undefined 2979 ), 2980 pos 2981 ); 2982 } 2983 2984 function parseJSDocType(): TypeNode { 2985 scanner.setInJSDocType(true); 2986 const pos = getNodePos(); 2987 if (parseOptional(SyntaxKind.ModuleKeyword)) { 2988 // TODO(rbuckton): We never set the type for a JSDocNamepathType. What should we put here? 2989 const moduleTag = factory.createJSDocNamepathType(/*type*/ undefined!); 2990 terminate: while (true) { 2991 switch (token()) { 2992 case SyntaxKind.CloseBraceToken: 2993 case SyntaxKind.EndOfFileToken: 2994 case SyntaxKind.CommaToken: 2995 case SyntaxKind.WhitespaceTrivia: 2996 break terminate; 2997 default: 2998 nextTokenJSDoc(); 2999 } 3000 } 3001 3002 scanner.setInJSDocType(false); 3003 return finishNode(moduleTag, pos); 3004 } 3005 3006 const hasDotDotDot = parseOptional(SyntaxKind.DotDotDotToken); 3007 let type = parseTypeOrTypePredicate(); 3008 scanner.setInJSDocType(false); 3009 if (hasDotDotDot) { 3010 type = finishNode(factory.createJSDocVariadicType(type), pos); 3011 } 3012 if (token() === SyntaxKind.EqualsToken) { 3013 nextToken(); 3014 return finishNode(factory.createJSDocOptionalType(type), pos); 3015 } 3016 return type; 3017 } 3018 3019 function parseTypeQuery(): TypeQueryNode { 3020 const pos = getNodePos(); 3021 parseExpected(SyntaxKind.TypeOfKeyword); 3022 return finishNode(factory.createTypeQueryNode(parseEntityName(/*allowReservedWords*/ true)), pos); 3023 } 3024 3025 function parseTypeParameter(position?: number): TypeParameterDeclaration { 3026 const pos = getNodePos(); 3027 const name = position !== undefined ? parseEtsIdentifier(position) : parseIdentifier(); 3028 let constraint: TypeNode | undefined; 3029 let expression: Expression | undefined; 3030 if (parseOptional(SyntaxKind.ExtendsKeyword)) { 3031 // It's not uncommon for people to write improper constraints to a generic. If the 3032 // user writes a constraint that is an expression and not an actual type, then parse 3033 // it out as an expression (so we can recover well), but report that a type is needed 3034 // instead. 3035 if (isStartOfType() || !isStartOfExpression()) { 3036 constraint = parseType(); 3037 } 3038 else { 3039 // It was not a type, and it looked like an expression. Parse out an expression 3040 // here so we recover well. Note: it is important that we call parseUnaryExpression 3041 // and not parseExpression here. If the user has: 3042 // 3043 // <T extends ""> 3044 // 3045 // We do *not* want to consume the `>` as we're consuming the expression for "". 3046 expression = parseUnaryExpressionOrHigher(); 3047 } 3048 } 3049 3050 const defaultType = parseOptional(SyntaxKind.EqualsToken) ? parseType() : undefined; 3051 const node = factory.createTypeParameterDeclaration(name, constraint, defaultType); 3052 node.expression = expression; 3053 return position !== undefined ? finishVirtualNode(node, position, position) : finishNode(node, pos); 3054 } 3055 3056 function parseTypeParameters(): NodeArray<TypeParameterDeclaration> | undefined { 3057 if (token() === SyntaxKind.LessThanToken) { 3058 return parseBracketedList(ParsingContext.TypeParameters, parseTypeParameter, SyntaxKind.LessThanToken, SyntaxKind.GreaterThanToken); 3059 } 3060 } 3061 3062 function parseEtsTypeParameters(pos: number): NodeArray<TypeParameterDeclaration> | undefined { 3063 return createNodeArray([parseTypeParameter(pos)], getNodePos()); 3064 } 3065 3066 function parseEtsTypeArguments(pos: number, name: string): NodeArray<TypeNode> | undefined { 3067 if ((contextFlags & NodeFlags.JavaScriptFile) !== 0) { 3068 // TypeArguments must not be parsed in JavaScript files to avoid ambiguity with binary operators. 3069 return undefined; 3070 } 3071 3072 return createNodeArray([parseEtsType(pos, name)], getNodePos()); 3073 } 3074 3075 function isStartOfParameter(isJSDocParameter: boolean): boolean { 3076 return token() === SyntaxKind.DotDotDotToken || 3077 isBindingIdentifierOrPrivateIdentifierOrPattern() || 3078 isModifierKind(token()) || 3079 token() === SyntaxKind.AtToken || 3080 isStartOfType(/*inStartOfParameter*/ !isJSDocParameter); 3081 } 3082 3083 function parseNameOfParameter(modifiers: ModifiersArray | undefined) { 3084 // FormalParameter [Yield,Await]: 3085 // BindingElement[?Yield,?Await] 3086 const name = parseIdentifierOrPattern(Diagnostics.Private_identifiers_cannot_be_used_as_parameters); 3087 if (getFullWidth(name) === 0 && !some(modifiers) && isModifierKind(token())) { 3088 // in cases like 3089 // 'use strict' 3090 // function foo(static) 3091 // isParameter('static') === true, because of isModifier('static') 3092 // however 'static' is not a legal identifier in a strict mode. 3093 // so result of this function will be ParameterDeclaration (flags = 0, name = missing, type = undefined, initializer = undefined) 3094 // and current token will not change => parsing of the enclosing parameter list will last till the end of time (or OOM) 3095 // to avoid this we'll advance cursor to the next token. 3096 nextToken(); 3097 } 3098 return name; 3099 } 3100 3101 function parseParameterInOuterAwaitContext() { 3102 return parseParameterWorker(/*inOuterAwaitContext*/ true); 3103 } 3104 3105 function parseParameter(): ParameterDeclaration { 3106 return parseParameterWorker(/*inOuterAwaitContext*/ false); 3107 } 3108 3109 function parseParameterWorker(inOuterAwaitContext: boolean): ParameterDeclaration { 3110 const pos = getNodePos(); 3111 const hasJSDoc = hasPrecedingJSDocComment(); 3112 if (token() === SyntaxKind.ThisKeyword) { 3113 const node = factory.createParameterDeclaration( 3114 /*decorators*/ undefined, 3115 /*modifiers*/ undefined, 3116 /*dotDotDotToken*/ undefined, 3117 createIdentifier(/*isIdentifier*/ true), 3118 /*questionToken*/ undefined, 3119 parseTypeAnnotation(), 3120 /*initializer*/ undefined 3121 ); 3122 return withJSDoc(finishNode(node, pos), hasJSDoc); 3123 } 3124 3125 // FormalParameter [Yield,Await]: 3126 // BindingElement[?Yield,?Await] 3127 3128 // Decorators are parsed in the outer [Await] context, the rest of the parameter is parsed in the function's [Await] context. 3129 const decorators = inOuterAwaitContext ? doInAwaitContext(parseDecorators) : parseDecorators(); 3130 const savedTopLevel = topLevel; 3131 topLevel = false; 3132 const modifiers = parseModifiers(); 3133 const node = withJSDoc( 3134 finishNode( 3135 factory.createParameterDeclaration( 3136 decorators, 3137 modifiers, 3138 parseOptionalToken(SyntaxKind.DotDotDotToken), 3139 parseNameOfParameter(modifiers), 3140 parseOptionalToken(SyntaxKind.QuestionToken), 3141 parseTypeAnnotation(), 3142 parseInitializer() 3143 ), 3144 pos 3145 ), 3146 hasJSDoc 3147 ); 3148 topLevel = savedTopLevel; 3149 return node; 3150 } 3151 3152 function parseReturnType(returnToken: SyntaxKind.EqualsGreaterThanToken, isType: boolean): TypeNode; 3153 function parseReturnType(returnToken: SyntaxKind.ColonToken | SyntaxKind.EqualsGreaterThanToken, isType: boolean): TypeNode | undefined; 3154 function parseReturnType(returnToken: SyntaxKind.ColonToken | SyntaxKind.EqualsGreaterThanToken, isType: boolean) { 3155 if (shouldParseReturnType(returnToken, isType)) { 3156 return parseTypeOrTypePredicate(); 3157 } 3158 } 3159 3160 function shouldParseReturnType(returnToken: SyntaxKind.ColonToken | SyntaxKind.EqualsGreaterThanToken, isType: boolean): boolean { 3161 if (returnToken === SyntaxKind.EqualsGreaterThanToken) { 3162 parseExpected(returnToken); 3163 return true; 3164 } 3165 else if (parseOptional(SyntaxKind.ColonToken)) { 3166 return true; 3167 } 3168 else if (isType && token() === SyntaxKind.EqualsGreaterThanToken) { 3169 // This is easy to get backward, especially in type contexts, so parse the type anyway 3170 parseErrorAtCurrentToken(Diagnostics._0_expected, tokenToString(SyntaxKind.ColonToken)); 3171 nextToken(); 3172 return true; 3173 } 3174 return false; 3175 } 3176 3177 function parseParametersWorker(flags: SignatureFlags) { 3178 // FormalParameters [Yield,Await]: (modified) 3179 // [empty] 3180 // FormalParameterList[?Yield,Await] 3181 // 3182 // FormalParameter[Yield,Await]: (modified) 3183 // BindingElement[?Yield,Await] 3184 // 3185 // BindingElement [Yield,Await]: (modified) 3186 // SingleNameBinding[?Yield,?Await] 3187 // BindingPattern[?Yield,?Await]Initializer [In, ?Yield,?Await] opt 3188 // 3189 // SingleNameBinding [Yield,Await]: 3190 // BindingIdentifier[?Yield,?Await]Initializer [In, ?Yield,?Await] opt 3191 const savedYieldContext = inYieldContext(); 3192 const savedAwaitContext = inAwaitContext(); 3193 3194 setYieldContext(!!(flags & SignatureFlags.Yield)); 3195 setAwaitContext(!!(flags & SignatureFlags.Await)); 3196 3197 const parameters = flags & SignatureFlags.JSDoc ? 3198 parseDelimitedList(ParsingContext.JSDocParameters, parseJSDocParameter) : 3199 parseDelimitedList(ParsingContext.Parameters, savedAwaitContext ? parseParameterInOuterAwaitContext : parseParameter); 3200 3201 setYieldContext(savedYieldContext); 3202 setAwaitContext(savedAwaitContext); 3203 3204 return parameters; 3205 } 3206 3207 function parseParameters(flags: SignatureFlags): NodeArray<ParameterDeclaration> { 3208 // FormalParameters [Yield,Await]: (modified) 3209 // [empty] 3210 // FormalParameterList[?Yield,Await] 3211 // 3212 // FormalParameter[Yield,Await]: (modified) 3213 // BindingElement[?Yield,Await] 3214 // 3215 // BindingElement [Yield,Await]: (modified) 3216 // SingleNameBinding[?Yield,?Await] 3217 // BindingPattern[?Yield,?Await]Initializer [In, ?Yield,?Await] opt 3218 // 3219 // SingleNameBinding [Yield,Await]: 3220 // BindingIdentifier[?Yield,?Await]Initializer [In, ?Yield,?Await] opt 3221 if (!parseExpected(SyntaxKind.OpenParenToken)) { 3222 return createMissingList<ParameterDeclaration>(); 3223 } 3224 3225 const parameters = parseParametersWorker(flags); 3226 parseExpected(SyntaxKind.CloseParenToken); 3227 return parameters; 3228 } 3229 3230 function parseTypeMemberSemicolon() { 3231 // We allow type members to be separated by commas or (possibly ASI) semicolons. 3232 // First check if it was a comma. If so, we're done with the member. 3233 if (parseOptional(SyntaxKind.CommaToken)) { 3234 return; 3235 } 3236 3237 // Didn't have a comma. We must have a (possible ASI) semicolon. 3238 parseSemicolon(); 3239 } 3240 3241 function parseSignatureMember(kind: SyntaxKind.CallSignature | SyntaxKind.ConstructSignature): CallSignatureDeclaration | ConstructSignatureDeclaration { 3242 const pos = getNodePos(); 3243 const hasJSDoc = hasPrecedingJSDocComment(); 3244 if (kind === SyntaxKind.ConstructSignature) { 3245 parseExpected(SyntaxKind.NewKeyword); 3246 } 3247 3248 const typeParameters = parseTypeParameters(); 3249 const parameters = parseParameters(SignatureFlags.Type); 3250 const type = parseReturnType(SyntaxKind.ColonToken, /*isType*/ true); 3251 parseTypeMemberSemicolon(); 3252 const node = kind === SyntaxKind.CallSignature 3253 ? factory.createCallSignature(typeParameters, parameters, type) 3254 : factory.createConstructSignature(typeParameters, parameters, type); 3255 return withJSDoc(finishNode(node, pos), hasJSDoc); 3256 } 3257 3258 function isIndexSignature(): boolean { 3259 return token() === SyntaxKind.OpenBracketToken && lookAhead(isUnambiguouslyIndexSignature); 3260 } 3261 3262 function isUnambiguouslyIndexSignature() { 3263 // The only allowed sequence is: 3264 // 3265 // [id: 3266 // 3267 // However, for error recovery, we also check the following cases: 3268 // 3269 // [... 3270 // [id, 3271 // [id?, 3272 // [id?: 3273 // [id?] 3274 // [public id 3275 // [private id 3276 // [protected id 3277 // [] 3278 // 3279 nextToken(); 3280 if (token() === SyntaxKind.DotDotDotToken || token() === SyntaxKind.CloseBracketToken) { 3281 return true; 3282 } 3283 3284 if (isModifierKind(token())) { 3285 nextToken(); 3286 if (isIdentifier()) { 3287 return true; 3288 } 3289 } 3290 else if (!isIdentifier()) { 3291 return false; 3292 } 3293 else { 3294 // Skip the identifier 3295 nextToken(); 3296 } 3297 3298 // A colon signifies a well formed indexer 3299 // A comma should be a badly formed indexer because comma expressions are not allowed 3300 // in computed properties. 3301 if (token() === SyntaxKind.ColonToken || token() === SyntaxKind.CommaToken) { 3302 return true; 3303 } 3304 3305 // Question mark could be an indexer with an optional property, 3306 // or it could be a conditional expression in a computed property. 3307 if (token() !== SyntaxKind.QuestionToken) { 3308 return false; 3309 } 3310 3311 // If any of the following tokens are after the question mark, it cannot 3312 // be a conditional expression, so treat it as an indexer. 3313 nextToken(); 3314 return token() === SyntaxKind.ColonToken || token() === SyntaxKind.CommaToken || token() === SyntaxKind.CloseBracketToken; 3315 } 3316 3317 function parseIndexSignatureDeclaration(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined): IndexSignatureDeclaration { 3318 const parameters = parseBracketedList(ParsingContext.Parameters, parseParameter, SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken); 3319 const type = parseTypeAnnotation(); 3320 parseTypeMemberSemicolon(); 3321 const node = factory.createIndexSignature(decorators, modifiers, parameters, type); 3322 return withJSDoc(finishNode(node, pos), hasJSDoc); 3323 } 3324 3325 function parsePropertyOrMethodSignature(pos: number, hasJSDoc: boolean, modifiers: NodeArray<Modifier> | undefined): PropertySignature | MethodSignature { 3326 const name = parsePropertyName(); 3327 const questionToken = parseOptionalToken(SyntaxKind.QuestionToken); 3328 let node: PropertySignature | MethodSignature; 3329 if (token() === SyntaxKind.OpenParenToken || token() === SyntaxKind.LessThanToken) { 3330 // Method signatures don't exist in expression contexts. So they have neither 3331 // [Yield] nor [Await] 3332 const typeParameters = parseTypeParameters(); 3333 const parameters = parseParameters(SignatureFlags.Type); 3334 const type = parseReturnType(SyntaxKind.ColonToken, /*isType*/ true); 3335 node = factory.createMethodSignature(modifiers, name, questionToken, typeParameters, parameters, type); 3336 } 3337 else { 3338 const type = parseTypeAnnotation(); 3339 node = factory.createPropertySignature(modifiers, name, questionToken, type); 3340 // Although type literal properties cannot not have initializers, we attempt 3341 // to parse an initializer so we can report in the checker that an interface 3342 // property or type literal property cannot have an initializer. 3343 if (token() === SyntaxKind.EqualsToken) node.initializer = parseInitializer(); 3344 } 3345 parseTypeMemberSemicolon(); 3346 return withJSDoc(finishNode(node, pos), hasJSDoc); 3347 } 3348 3349 function isTypeMemberStart(): boolean { 3350 // Return true if we have the start of a signature member 3351 if (token() === SyntaxKind.OpenParenToken || token() === SyntaxKind.LessThanToken) { 3352 return true; 3353 } 3354 let idToken = false; 3355 // Eat up all modifiers, but hold on to the last one in case it is actually an identifier 3356 while (isModifierKind(token())) { 3357 idToken = true; 3358 nextToken(); 3359 } 3360 // Index signatures and computed property names are type members 3361 if (token() === SyntaxKind.OpenBracketToken) { 3362 return true; 3363 } 3364 // Try to get the first property-like token following all modifiers 3365 if (isLiteralPropertyName()) { 3366 idToken = true; 3367 nextToken(); 3368 } 3369 // If we were able to get any potential identifier, check that it is 3370 // the start of a member declaration 3371 if (idToken) { 3372 return token() === SyntaxKind.OpenParenToken || 3373 token() === SyntaxKind.LessThanToken || 3374 token() === SyntaxKind.QuestionToken || 3375 token() === SyntaxKind.ColonToken || 3376 token() === SyntaxKind.CommaToken || 3377 canParseSemicolon(); 3378 } 3379 return false; 3380 } 3381 3382 function parseTypeMember(): TypeElement { 3383 if (token() === SyntaxKind.OpenParenToken || token() === SyntaxKind.LessThanToken) { 3384 return parseSignatureMember(SyntaxKind.CallSignature); 3385 } 3386 if (token() === SyntaxKind.NewKeyword && lookAhead(nextTokenIsOpenParenOrLessThan)) { 3387 return parseSignatureMember(SyntaxKind.ConstructSignature); 3388 } 3389 const pos = getNodePos(); 3390 const hasJSDoc = hasPrecedingJSDocComment(); 3391 const modifiers = parseModifiers(); 3392 if (isIndexSignature()) { 3393 return parseIndexSignatureDeclaration(pos, hasJSDoc, /*decorators*/ undefined, modifiers); 3394 } 3395 return parsePropertyOrMethodSignature(pos, hasJSDoc, modifiers); 3396 } 3397 3398 function nextTokenIsOpenParenOrLessThan() { 3399 nextToken(); 3400 return token() === SyntaxKind.OpenParenToken || token() === SyntaxKind.LessThanToken; 3401 } 3402 3403 function nextTokenIsDot() { 3404 return nextToken() === SyntaxKind.DotToken; 3405 } 3406 3407 function nextTokenIsOpenParenOrLessThanOrDot() { 3408 switch (nextToken()) { 3409 case SyntaxKind.OpenParenToken: 3410 case SyntaxKind.LessThanToken: 3411 case SyntaxKind.DotToken: 3412 return true; 3413 } 3414 return false; 3415 } 3416 3417 function parseTypeLiteral(): TypeLiteralNode { 3418 const pos = getNodePos(); 3419 return finishNode(factory.createTypeLiteralNode(parseObjectTypeMembers()), pos); 3420 } 3421 3422 function parseObjectTypeMembers(): NodeArray<TypeElement> { 3423 let members: NodeArray<TypeElement>; 3424 if (parseExpected(SyntaxKind.OpenBraceToken)) { 3425 members = parseList(ParsingContext.TypeMembers, parseTypeMember); 3426 parseExpected(SyntaxKind.CloseBraceToken); 3427 } 3428 else { 3429 members = createMissingList<TypeElement>(); 3430 } 3431 3432 return members; 3433 } 3434 3435 function isStartOfMappedType() { 3436 nextToken(); 3437 if (token() === SyntaxKind.PlusToken || token() === SyntaxKind.MinusToken) { 3438 return nextToken() === SyntaxKind.ReadonlyKeyword; 3439 } 3440 if (token() === SyntaxKind.ReadonlyKeyword) { 3441 nextToken(); 3442 } 3443 return token() === SyntaxKind.OpenBracketToken && nextTokenIsIdentifier() && nextToken() === SyntaxKind.InKeyword; 3444 } 3445 3446 function parseMappedTypeParameter() { 3447 const pos = getNodePos(); 3448 const name = parseIdentifierName(); 3449 parseExpected(SyntaxKind.InKeyword); 3450 const type = parseType(); 3451 return finishNode(factory.createTypeParameterDeclaration(name, type, /*defaultType*/ undefined), pos); 3452 } 3453 3454 function parseMappedType() { 3455 const pos = getNodePos(); 3456 parseExpected(SyntaxKind.OpenBraceToken); 3457 let readonlyToken: ReadonlyKeyword | PlusToken | MinusToken | undefined; 3458 if (token() === SyntaxKind.ReadonlyKeyword || token() === SyntaxKind.PlusToken || token() === SyntaxKind.MinusToken) { 3459 readonlyToken = parseTokenNode<ReadonlyKeyword | PlusToken | MinusToken>(); 3460 if (readonlyToken.kind !== SyntaxKind.ReadonlyKeyword) { 3461 parseExpected(SyntaxKind.ReadonlyKeyword); 3462 } 3463 } 3464 parseExpected(SyntaxKind.OpenBracketToken); 3465 const typeParameter = parseMappedTypeParameter(); 3466 const nameType = parseOptional(SyntaxKind.AsKeyword) ? parseType() : undefined; 3467 parseExpected(SyntaxKind.CloseBracketToken); 3468 let questionToken: QuestionToken | PlusToken | MinusToken | undefined; 3469 if (token() === SyntaxKind.QuestionToken || token() === SyntaxKind.PlusToken || token() === SyntaxKind.MinusToken) { 3470 questionToken = parseTokenNode<QuestionToken | PlusToken | MinusToken>(); 3471 if (questionToken.kind !== SyntaxKind.QuestionToken) { 3472 parseExpected(SyntaxKind.QuestionToken); 3473 } 3474 } 3475 const type = parseTypeAnnotation(); 3476 parseSemicolon(); 3477 parseExpected(SyntaxKind.CloseBraceToken); 3478 return finishNode(factory.createMappedTypeNode(readonlyToken, typeParameter, nameType, questionToken, type), pos); 3479 } 3480 3481 function parseTupleElementType() { 3482 const pos = getNodePos(); 3483 if (parseOptional(SyntaxKind.DotDotDotToken)) { 3484 return finishNode(factory.createRestTypeNode(parseType()), pos); 3485 } 3486 const type = parseType(); 3487 if (isJSDocNullableType(type) && type.pos === type.type.pos) { 3488 const node = factory.createOptionalTypeNode(type.type); 3489 setTextRange(node, type); 3490 (node as Mutable<Node>).flags = type.flags; 3491 return node; 3492 } 3493 return type; 3494 } 3495 3496 function isNextTokenColonOrQuestionColon() { 3497 return nextToken() === SyntaxKind.ColonToken || (token() === SyntaxKind.QuestionToken && nextToken() === SyntaxKind.ColonToken); 3498 } 3499 3500 function isTupleElementName() { 3501 if (token() === SyntaxKind.DotDotDotToken) { 3502 return tokenIsIdentifierOrKeyword(nextToken()) && isNextTokenColonOrQuestionColon(); 3503 } 3504 return tokenIsIdentifierOrKeyword(token()) && isNextTokenColonOrQuestionColon(); 3505 } 3506 3507 function parseTupleElementNameOrTupleElementType() { 3508 if (lookAhead(isTupleElementName)) { 3509 const pos = getNodePos(); 3510 const hasJSDoc = hasPrecedingJSDocComment(); 3511 const dotDotDotToken = parseOptionalToken(SyntaxKind.DotDotDotToken); 3512 const name = parseIdentifierName(); 3513 const questionToken = parseOptionalToken(SyntaxKind.QuestionToken); 3514 parseExpected(SyntaxKind.ColonToken); 3515 const type = parseTupleElementType(); 3516 const node = factory.createNamedTupleMember(dotDotDotToken, name, questionToken, type); 3517 return withJSDoc(finishNode(node, pos), hasJSDoc); 3518 } 3519 return parseTupleElementType(); 3520 } 3521 3522 function parseTupleType(): TupleTypeNode { 3523 const pos = getNodePos(); 3524 return finishNode( 3525 factory.createTupleTypeNode( 3526 parseBracketedList(ParsingContext.TupleElementTypes, parseTupleElementNameOrTupleElementType, SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken) 3527 ), 3528 pos 3529 ); 3530 } 3531 3532 function parseParenthesizedType(): TypeNode { 3533 const pos = getNodePos(); 3534 parseExpected(SyntaxKind.OpenParenToken); 3535 const type = parseType(); 3536 parseExpected(SyntaxKind.CloseParenToken); 3537 return finishNode(factory.createParenthesizedType(type), pos); 3538 } 3539 3540 function parseModifiersForConstructorType(): NodeArray<Modifier> | undefined { 3541 let modifiers: NodeArray<Modifier> | undefined; 3542 if (token() === SyntaxKind.AbstractKeyword) { 3543 const pos = getNodePos(); 3544 nextToken(); 3545 const modifier = finishNode(factory.createToken(SyntaxKind.AbstractKeyword), pos); 3546 modifiers = createNodeArray<Modifier>([modifier], pos); 3547 } 3548 return modifiers; 3549 } 3550 3551 function parseFunctionOrConstructorType(): TypeNode { 3552 const pos = getNodePos(); 3553 const hasJSDoc = hasPrecedingJSDocComment(); 3554 const modifiers = parseModifiersForConstructorType(); 3555 const isConstructorType = parseOptional(SyntaxKind.NewKeyword); 3556 const typeParameters = parseTypeParameters(); 3557 const parameters = parseParameters(SignatureFlags.Type); 3558 const type = parseReturnType(SyntaxKind.EqualsGreaterThanToken, /*isType*/ false); 3559 const node = isConstructorType 3560 ? factory.createConstructorTypeNode(modifiers, typeParameters, parameters, type) 3561 : factory.createFunctionTypeNode(typeParameters, parameters, type); 3562 if (!isConstructorType) (node as Mutable<Node>).modifiers = modifiers; 3563 return withJSDoc(finishNode(node, pos), hasJSDoc); 3564 } 3565 3566 function parseKeywordAndNoDot(): TypeNode | undefined { 3567 const node = parseTokenNode<TypeNode>(); 3568 return token() === SyntaxKind.DotToken ? undefined : node; 3569 } 3570 3571 function parseLiteralTypeNode(negative?: boolean): LiteralTypeNode { 3572 const pos = getNodePos(); 3573 if (negative) { 3574 nextToken(); 3575 } 3576 let expression: BooleanLiteral | NullLiteral | LiteralExpression | PrefixUnaryExpression = 3577 token() === SyntaxKind.TrueKeyword || token() === SyntaxKind.FalseKeyword || token() === SyntaxKind.NullKeyword ? 3578 parseTokenNode<BooleanLiteral | NullLiteral>() : 3579 parseLiteralLikeNode(token()) as LiteralExpression; 3580 if (negative) { 3581 expression = finishNode(factory.createPrefixUnaryExpression(SyntaxKind.MinusToken, expression), pos); 3582 } 3583 return finishNode(factory.createLiteralTypeNode(expression), pos); 3584 } 3585 3586 function isStartOfTypeOfImportType() { 3587 nextToken(); 3588 return token() === SyntaxKind.ImportKeyword; 3589 } 3590 3591 function parseImportType(): ImportTypeNode { 3592 sourceFlags |= NodeFlags.PossiblyContainsDynamicImport; 3593 const pos = getNodePos(); 3594 const isTypeOf = parseOptional(SyntaxKind.TypeOfKeyword); 3595 parseExpected(SyntaxKind.ImportKeyword); 3596 parseExpected(SyntaxKind.OpenParenToken); 3597 const type = parseType(); 3598 parseExpected(SyntaxKind.CloseParenToken); 3599 const qualifier = parseOptional(SyntaxKind.DotToken) ? parseEntityNameOfTypeReference() : undefined; 3600 const typeArguments = parseTypeArgumentsOfTypeReference(); 3601 return finishNode(factory.createImportTypeNode(type, qualifier, typeArguments, isTypeOf), pos); 3602 } 3603 3604 function nextTokenIsNumericOrBigIntLiteral() { 3605 nextToken(); 3606 return token() === SyntaxKind.NumericLiteral || token() === SyntaxKind.BigIntLiteral; 3607 } 3608 3609 function parseNonArrayType(): TypeNode { 3610 switch (token()) { 3611 case SyntaxKind.AnyKeyword: 3612 case SyntaxKind.UnknownKeyword: 3613 case SyntaxKind.StringKeyword: 3614 case SyntaxKind.NumberKeyword: 3615 case SyntaxKind.BigIntKeyword: 3616 case SyntaxKind.SymbolKeyword: 3617 case SyntaxKind.BooleanKeyword: 3618 case SyntaxKind.UndefinedKeyword: 3619 case SyntaxKind.NeverKeyword: 3620 case SyntaxKind.ObjectKeyword: 3621 // If these are followed by a dot, then parse these out as a dotted type reference instead. 3622 return tryParse(parseKeywordAndNoDot) || parseTypeReference(); 3623 case SyntaxKind.AsteriskEqualsToken: 3624 // If there is '*=', treat it as * followed by postfix = 3625 scanner.reScanAsteriskEqualsToken(); 3626 // falls through 3627 case SyntaxKind.AsteriskToken: 3628 return parseJSDocAllType(); 3629 case SyntaxKind.QuestionQuestionToken: 3630 // If there is '??', treat it as prefix-'?' in JSDoc type. 3631 scanner.reScanQuestionToken(); 3632 // falls through 3633 case SyntaxKind.QuestionToken: 3634 return parseJSDocUnknownOrNullableType(); 3635 case SyntaxKind.FunctionKeyword: 3636 return parseJSDocFunctionType(); 3637 case SyntaxKind.ExclamationToken: 3638 return parseJSDocNonNullableType(); 3639 case SyntaxKind.NoSubstitutionTemplateLiteral: 3640 case SyntaxKind.StringLiteral: 3641 case SyntaxKind.NumericLiteral: 3642 case SyntaxKind.BigIntLiteral: 3643 case SyntaxKind.TrueKeyword: 3644 case SyntaxKind.FalseKeyword: 3645 case SyntaxKind.NullKeyword: 3646 return parseLiteralTypeNode(); 3647 case SyntaxKind.MinusToken: 3648 return lookAhead(nextTokenIsNumericOrBigIntLiteral) ? parseLiteralTypeNode(/*negative*/ true) : parseTypeReference(); 3649 case SyntaxKind.VoidKeyword: 3650 return parseTokenNode<TypeNode>(); 3651 case SyntaxKind.ThisKeyword: { 3652 const thisKeyword = parseThisTypeNode(); 3653 if (token() === SyntaxKind.IsKeyword && !scanner.hasPrecedingLineBreak()) { 3654 return parseThisTypePredicate(thisKeyword); 3655 } 3656 else { 3657 return thisKeyword; 3658 } 3659 } 3660 case SyntaxKind.TypeOfKeyword: 3661 return lookAhead(isStartOfTypeOfImportType) ? parseImportType() : parseTypeQuery(); 3662 case SyntaxKind.OpenBraceToken: 3663 return lookAhead(isStartOfMappedType) ? parseMappedType() : parseTypeLiteral(); 3664 case SyntaxKind.OpenBracketToken: 3665 return parseTupleType(); 3666 case SyntaxKind.OpenParenToken: 3667 return parseParenthesizedType(); 3668 case SyntaxKind.ImportKeyword: 3669 return parseImportType(); 3670 case SyntaxKind.AssertsKeyword: 3671 return lookAhead(nextTokenIsIdentifierOrKeywordOnSameLine) ? parseAssertsTypePredicate() : parseTypeReference(); 3672 case SyntaxKind.TemplateHead: 3673 return parseTemplateType(); 3674 default: 3675 return parseTypeReference(); 3676 } 3677 } 3678 3679 function isStartOfType(inStartOfParameter?: boolean): boolean { 3680 switch (token()) { 3681 case SyntaxKind.AnyKeyword: 3682 case SyntaxKind.UnknownKeyword: 3683 case SyntaxKind.StringKeyword: 3684 case SyntaxKind.NumberKeyword: 3685 case SyntaxKind.BigIntKeyword: 3686 case SyntaxKind.BooleanKeyword: 3687 case SyntaxKind.ReadonlyKeyword: 3688 case SyntaxKind.SymbolKeyword: 3689 case SyntaxKind.UniqueKeyword: 3690 case SyntaxKind.VoidKeyword: 3691 case SyntaxKind.UndefinedKeyword: 3692 case SyntaxKind.NullKeyword: 3693 case SyntaxKind.ThisKeyword: 3694 case SyntaxKind.TypeOfKeyword: 3695 case SyntaxKind.NeverKeyword: 3696 case SyntaxKind.OpenBraceToken: 3697 case SyntaxKind.OpenBracketToken: 3698 case SyntaxKind.LessThanToken: 3699 case SyntaxKind.BarToken: 3700 case SyntaxKind.AmpersandToken: 3701 case SyntaxKind.NewKeyword: 3702 case SyntaxKind.StringLiteral: 3703 case SyntaxKind.NumericLiteral: 3704 case SyntaxKind.BigIntLiteral: 3705 case SyntaxKind.TrueKeyword: 3706 case SyntaxKind.FalseKeyword: 3707 case SyntaxKind.ObjectKeyword: 3708 case SyntaxKind.AsteriskToken: 3709 case SyntaxKind.QuestionToken: 3710 case SyntaxKind.ExclamationToken: 3711 case SyntaxKind.DotDotDotToken: 3712 case SyntaxKind.InferKeyword: 3713 case SyntaxKind.ImportKeyword: 3714 case SyntaxKind.AssertsKeyword: 3715 case SyntaxKind.NoSubstitutionTemplateLiteral: 3716 case SyntaxKind.TemplateHead: 3717 return true; 3718 case SyntaxKind.FunctionKeyword: 3719 return !inStartOfParameter; 3720 case SyntaxKind.MinusToken: 3721 return !inStartOfParameter && lookAhead(nextTokenIsNumericOrBigIntLiteral); 3722 case SyntaxKind.OpenParenToken: 3723 // Only consider '(' the start of a type if followed by ')', '...', an identifier, a modifier, 3724 // or something that starts a type. We don't want to consider things like '(1)' a type. 3725 return !inStartOfParameter && lookAhead(isStartOfParenthesizedOrFunctionType); 3726 default: 3727 return isIdentifier(); 3728 } 3729 } 3730 3731 function isStartOfParenthesizedOrFunctionType() { 3732 nextToken(); 3733 return token() === SyntaxKind.CloseParenToken || isStartOfParameter(/*isJSDocParameter*/ false) || isStartOfType(); 3734 } 3735 3736 function parsePostfixTypeOrHigher(): TypeNode { 3737 const pos = getNodePos(); 3738 let type = parseNonArrayType(); 3739 while (!scanner.hasPrecedingLineBreak()) { 3740 switch (token()) { 3741 case SyntaxKind.ExclamationToken: 3742 nextToken(); 3743 type = finishNode(factory.createJSDocNonNullableType(type), pos); 3744 break; 3745 case SyntaxKind.QuestionToken: 3746 // If next token is start of a type we have a conditional type 3747 if (lookAhead(nextTokenIsStartOfType)) { 3748 return type; 3749 } 3750 nextToken(); 3751 type = finishNode(factory.createJSDocNullableType(type), pos); 3752 break; 3753 case SyntaxKind.OpenBracketToken: 3754 parseExpected(SyntaxKind.OpenBracketToken); 3755 if (isStartOfType()) { 3756 const indexType = parseType(); 3757 parseExpected(SyntaxKind.CloseBracketToken); 3758 type = finishNode(factory.createIndexedAccessTypeNode(type, indexType), pos); 3759 } 3760 else { 3761 parseExpected(SyntaxKind.CloseBracketToken); 3762 type = finishNode(factory.createArrayTypeNode(type), pos); 3763 } 3764 break; 3765 default: 3766 return type; 3767 } 3768 } 3769 return type; 3770 } 3771 3772 function parseTypeOperator(operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.ReadonlyKeyword) { 3773 const pos = getNodePos(); 3774 parseExpected(operator); 3775 return finishNode(factory.createTypeOperatorNode(operator, parseTypeOperatorOrHigher()), pos); 3776 } 3777 3778 function parseTypeParameterOfInferType() { 3779 const pos = getNodePos(); 3780 return finishNode( 3781 factory.createTypeParameterDeclaration( 3782 parseIdentifier(), 3783 /*constraint*/ undefined, 3784 /*defaultType*/ undefined 3785 ), 3786 pos 3787 ); 3788 } 3789 3790 function parseInferType(): InferTypeNode { 3791 const pos = getNodePos(); 3792 parseExpected(SyntaxKind.InferKeyword); 3793 return finishNode(factory.createInferTypeNode(parseTypeParameterOfInferType()), pos); 3794 } 3795 3796 function parseTypeOperatorOrHigher(): TypeNode { 3797 const operator = token(); 3798 switch (operator) { 3799 case SyntaxKind.KeyOfKeyword: 3800 case SyntaxKind.UniqueKeyword: 3801 case SyntaxKind.ReadonlyKeyword: 3802 return parseTypeOperator(operator); 3803 case SyntaxKind.InferKeyword: 3804 return parseInferType(); 3805 } 3806 return parsePostfixTypeOrHigher(); 3807 } 3808 3809 function parseFunctionOrConstructorTypeToError( 3810 isInUnionType: boolean 3811 ): TypeNode | undefined { 3812 // the function type and constructor type shorthand notation 3813 // are not allowed directly in unions and intersections, but we'll 3814 // try to parse them gracefully and issue a helpful message. 3815 if (isStartOfFunctionTypeOrConstructorType()) { 3816 const type = parseFunctionOrConstructorType(); 3817 let diagnostic: DiagnosticMessage; 3818 if (isFunctionTypeNode(type)) { 3819 diagnostic = isInUnionType 3820 ? Diagnostics.Function_type_notation_must_be_parenthesized_when_used_in_a_union_type 3821 : Diagnostics.Function_type_notation_must_be_parenthesized_when_used_in_an_intersection_type; 3822 } 3823 else { 3824 diagnostic = isInUnionType 3825 ? Diagnostics.Constructor_type_notation_must_be_parenthesized_when_used_in_a_union_type 3826 : Diagnostics.Constructor_type_notation_must_be_parenthesized_when_used_in_an_intersection_type; 3827 3828 } 3829 parseErrorAtRange(type, diagnostic); 3830 return type; 3831 } 3832 return undefined; 3833 } 3834 3835 function parseUnionOrIntersectionType( 3836 operator: SyntaxKind.BarToken | SyntaxKind.AmpersandToken, 3837 parseConstituentType: () => TypeNode, 3838 createTypeNode: (types: NodeArray<TypeNode>) => UnionOrIntersectionTypeNode 3839 ): TypeNode { 3840 const pos = getNodePos(); 3841 const isUnionType = operator === SyntaxKind.BarToken; 3842 const hasLeadingOperator = parseOptional(operator); 3843 let type = hasLeadingOperator && parseFunctionOrConstructorTypeToError(isUnionType) 3844 || parseConstituentType(); 3845 if (token() === operator || hasLeadingOperator) { 3846 const types = [type]; 3847 while (parseOptional(operator)) { 3848 types.push(parseFunctionOrConstructorTypeToError(isUnionType) || parseConstituentType()); 3849 } 3850 type = finishNode(createTypeNode(createNodeArray(types, pos)), pos); 3851 } 3852 return type; 3853 } 3854 3855 function parseIntersectionTypeOrHigher(): TypeNode { 3856 return parseUnionOrIntersectionType(SyntaxKind.AmpersandToken, parseTypeOperatorOrHigher, factory.createIntersectionTypeNode); 3857 } 3858 3859 function parseUnionTypeOrHigher(): TypeNode { 3860 return parseUnionOrIntersectionType(SyntaxKind.BarToken, parseIntersectionTypeOrHigher, factory.createUnionTypeNode); 3861 } 3862 3863 function nextTokenIsNewKeyword(): boolean { 3864 nextToken(); 3865 return token() === SyntaxKind.NewKeyword; 3866 } 3867 3868 function isStartOfFunctionTypeOrConstructorType(): boolean { 3869 if (token() === SyntaxKind.LessThanToken) { 3870 return true; 3871 } 3872 if (token() === SyntaxKind.OpenParenToken && lookAhead(isUnambiguouslyStartOfFunctionType)) { 3873 return true; 3874 } 3875 return token() === SyntaxKind.NewKeyword || 3876 token() === SyntaxKind.AbstractKeyword && lookAhead(nextTokenIsNewKeyword); 3877 } 3878 3879 function skipParameterStart(): boolean { 3880 if (isModifierKind(token())) { 3881 // Skip modifiers 3882 parseModifiers(); 3883 } 3884 if (isIdentifier() || token() === SyntaxKind.ThisKeyword) { 3885 nextToken(); 3886 return true; 3887 } 3888 if (token() === SyntaxKind.OpenBracketToken || token() === SyntaxKind.OpenBraceToken) { 3889 // Return true if we can parse an array or object binding pattern with no errors 3890 const previousErrorCount = parseDiagnostics.length; 3891 parseIdentifierOrPattern(); 3892 return previousErrorCount === parseDiagnostics.length; 3893 } 3894 return false; 3895 } 3896 3897 function isUnambiguouslyStartOfFunctionType() { 3898 nextToken(); 3899 if (token() === SyntaxKind.CloseParenToken || token() === SyntaxKind.DotDotDotToken) { 3900 // ( ) 3901 // ( ... 3902 return true; 3903 } 3904 if (skipParameterStart()) { 3905 // We successfully skipped modifiers (if any) and an identifier or binding pattern, 3906 // now see if we have something that indicates a parameter declaration 3907 if (token() === SyntaxKind.ColonToken || token() === SyntaxKind.CommaToken || 3908 token() === SyntaxKind.QuestionToken || token() === SyntaxKind.EqualsToken) { 3909 // ( xxx : 3910 // ( xxx , 3911 // ( xxx ? 3912 // ( xxx = 3913 return true; 3914 } 3915 if (token() === SyntaxKind.CloseParenToken) { 3916 nextToken(); 3917 if (token() === SyntaxKind.EqualsGreaterThanToken) { 3918 // ( xxx ) => 3919 return true; 3920 } 3921 } 3922 } 3923 return false; 3924 } 3925 3926 function parseTypeOrTypePredicate(): TypeNode { 3927 const pos = getNodePos(); 3928 const typePredicateVariable = isIdentifier() && tryParse(parseTypePredicatePrefix); 3929 const type = parseType(); 3930 if (typePredicateVariable) { 3931 return finishNode(factory.createTypePredicateNode(/*assertsModifier*/ undefined, typePredicateVariable, type), pos); 3932 } 3933 else { 3934 return type; 3935 } 3936 } 3937 3938 function parseTypePredicatePrefix() { 3939 const id = parseIdentifier(); 3940 if (token() === SyntaxKind.IsKeyword && !scanner.hasPrecedingLineBreak()) { 3941 nextToken(); 3942 return id; 3943 } 3944 } 3945 3946 function parseAssertsTypePredicate(): TypeNode { 3947 const pos = getNodePos(); 3948 const assertsModifier = parseExpectedToken(SyntaxKind.AssertsKeyword); 3949 const parameterName = token() === SyntaxKind.ThisKeyword ? parseThisTypeNode() : parseIdentifier(); 3950 const type = parseOptional(SyntaxKind.IsKeyword) ? parseType() : undefined; 3951 return finishNode(factory.createTypePredicateNode(assertsModifier, parameterName, type), pos); 3952 } 3953 3954 function parseType(): TypeNode { 3955 // The rules about 'yield' only apply to actual code/expression contexts. They don't 3956 // apply to 'type' contexts. So we disable these parameters here before moving on. 3957 return doOutsideOfContext(NodeFlags.TypeExcludesFlags, parseTypeWorker); 3958 } 3959 3960 function parseEtsType(pos: number, name: string): TypeNode { 3961 const contextFlagsToClear = NodeFlags.TypeExcludesFlags & contextFlags; 3962 if (contextFlagsToClear) { 3963 // clear the requested context flags 3964 setContextFlag(/*val*/ false, contextFlagsToClear); 3965 const result = parseEtsTypeReferenceWorker(pos, name); 3966 // restore the context flags we just cleared 3967 setContextFlag(/*val*/ true, contextFlagsToClear); 3968 return result; 3969 } 3970 3971 return parseEtsTypeReferenceWorker(pos, name); 3972 } 3973 3974 function parseTypeWorker(noConditionalTypes?: boolean): TypeNode { 3975 if (isStartOfFunctionTypeOrConstructorType()) { 3976 return parseFunctionOrConstructorType(); 3977 } 3978 const pos = getNodePos(); 3979 const type = parseUnionTypeOrHigher(); 3980 if (!noConditionalTypes && !scanner.hasPrecedingLineBreak() && parseOptional(SyntaxKind.ExtendsKeyword)) { 3981 // The type following 'extends' is not permitted to be another conditional type 3982 const extendsType = parseTypeWorker(/*noConditionalTypes*/ true); 3983 parseExpected(SyntaxKind.QuestionToken); 3984 const trueType = parseTypeWorker(); 3985 parseExpected(SyntaxKind.ColonToken); 3986 const falseType = parseTypeWorker(); 3987 return finishNode(factory.createConditionalTypeNode(type, extendsType, trueType, falseType), pos); 3988 } 3989 return type; 3990 } 3991 3992 function parseEtsTypeReferenceWorker(pos: number, name: string): TypeNode { 3993 return finishVirtualNode( 3994 factory.createTypeReferenceNode( 3995 finishVirtualNode(factory.createIdentifier(name), pos, pos) 3996 ), 3997 pos, pos 3998 ); 3999 } 4000 4001 function parseTypeAnnotation(): TypeNode | undefined { 4002 return parseOptional(SyntaxKind.ColonToken) ? parseType() : undefined; 4003 } 4004 4005 // EXPRESSIONS 4006 function isStartOfLeftHandSideExpression(): boolean { 4007 switch (token()) { 4008 case SyntaxKind.ThisKeyword: 4009 case SyntaxKind.SuperKeyword: 4010 case SyntaxKind.NullKeyword: 4011 case SyntaxKind.TrueKeyword: 4012 case SyntaxKind.FalseKeyword: 4013 case SyntaxKind.NumericLiteral: 4014 case SyntaxKind.BigIntLiteral: 4015 case SyntaxKind.StringLiteral: 4016 case SyntaxKind.NoSubstitutionTemplateLiteral: 4017 case SyntaxKind.TemplateHead: 4018 case SyntaxKind.OpenParenToken: 4019 case SyntaxKind.OpenBracketToken: 4020 case SyntaxKind.OpenBraceToken: 4021 case SyntaxKind.FunctionKeyword: 4022 case SyntaxKind.ClassKeyword: 4023 case SyntaxKind.NewKeyword: 4024 case SyntaxKind.SlashToken: 4025 case SyntaxKind.SlashEqualsToken: 4026 case SyntaxKind.Identifier: 4027 return true; 4028 case SyntaxKind.StructKeyword: 4029 return inEtsContext(); 4030 case SyntaxKind.ImportKeyword: 4031 return lookAhead(nextTokenIsOpenParenOrLessThanOrDot); 4032 default: 4033 return isIdentifier(); 4034 } 4035 } 4036 4037 function isStartOfExpression(): boolean { 4038 if (isStartOfLeftHandSideExpression()) { 4039 return true; 4040 } 4041 4042 switch (token()) { 4043 case SyntaxKind.PlusToken: 4044 case SyntaxKind.MinusToken: 4045 case SyntaxKind.TildeToken: 4046 case SyntaxKind.ExclamationToken: 4047 case SyntaxKind.DeleteKeyword: 4048 case SyntaxKind.TypeOfKeyword: 4049 case SyntaxKind.VoidKeyword: 4050 case SyntaxKind.PlusPlusToken: 4051 case SyntaxKind.MinusMinusToken: 4052 case SyntaxKind.LessThanToken: 4053 case SyntaxKind.AwaitKeyword: 4054 case SyntaxKind.YieldKeyword: 4055 case SyntaxKind.PrivateIdentifier: 4056 // Yield/await always starts an expression. Either it is an identifier (in which case 4057 // it is definitely an expression). Or it's a keyword (either because we're in 4058 // a generator or async function, or in strict mode (or both)) and it started a yield or await expression. 4059 return true; 4060 case SyntaxKind.DotToken: 4061 return isValidExtendOrStylesContext(); 4062 default: 4063 // Error tolerance. If we see the start of some binary operator, we consider 4064 // that the start of an expression. That way we'll parse out a missing identifier, 4065 // give a good message about an identifier being missing, and then consume the 4066 // rest of the binary expression. 4067 if (isBinaryOperator()) { 4068 return true; 4069 } 4070 4071 return isIdentifier(); 4072 } 4073 } 4074 4075 function isValidExtendOrStylesContext(): boolean { 4076 return (inEtsExtendComponentsContext() && !!extendEtsComponentDeclaration) || 4077 (inEtsStylesComponentsContext() && !!stylesEtsComponentDeclaration); 4078 } 4079 4080 function isStartOfExpressionStatement(): boolean { 4081 // As per the grammar, none of '{' or 'function' or 'class' can start an expression statement. 4082 return token() !== SyntaxKind.OpenBraceToken && 4083 token() !== SyntaxKind.FunctionKeyword && 4084 token() !== SyntaxKind.ClassKeyword && 4085 (!inEtsContext() || token() !== SyntaxKind.StructKeyword) && 4086 token() !== SyntaxKind.AtToken && 4087 isStartOfExpression(); 4088 } 4089 4090 function parseExpression(): Expression { 4091 // Expression[in]: 4092 // AssignmentExpression[in] 4093 // Expression[in] , AssignmentExpression[in] 4094 4095 // clear the decorator context when parsing Expression, as it should be unambiguous when parsing a decorator 4096 const saveDecoratorContext = inDecoratorContext(); 4097 if (saveDecoratorContext) { 4098 setDecoratorContext(/*val*/ false); 4099 } 4100 4101 const pos = getNodePos(); 4102 let expr = parseAssignmentExpressionOrHigher(); 4103 let operatorToken: BinaryOperatorToken; 4104 while ((operatorToken = parseOptionalToken(SyntaxKind.CommaToken))) { 4105 expr = makeBinaryExpression(expr, operatorToken, parseAssignmentExpressionOrHigher(), pos); 4106 } 4107 4108 if (saveDecoratorContext) { 4109 setDecoratorContext(/*val*/ true); 4110 } 4111 return expr; 4112 } 4113 4114 function parseInitializer(): Expression | undefined { 4115 return parseOptional(SyntaxKind.EqualsToken) ? parseAssignmentExpressionOrHigher() : undefined; 4116 } 4117 4118 function parseAssignmentExpressionOrHigher(): Expression { 4119 // AssignmentExpression[in,yield]: 4120 // 1) ConditionalExpression[?in,?yield] 4121 // 2) LeftHandSideExpression = AssignmentExpression[?in,?yield] 4122 // 3) LeftHandSideExpression AssignmentOperator AssignmentExpression[?in,?yield] 4123 // 4) ArrowFunctionExpression[?in,?yield] 4124 // 5) AsyncArrowFunctionExpression[in,yield,await] 4125 // 6) [+Yield] YieldExpression[?In] 4126 // 4127 // Note: for ease of implementation we treat productions '2' and '3' as the same thing. 4128 // (i.e. they're both BinaryExpressions with an assignment operator in it). 4129 4130 // First, do the simple check if we have a YieldExpression (production '6'). 4131 if (isYieldExpression()) { 4132 return parseYieldExpression(); 4133 } 4134 4135 // Then, check if we have an arrow function (production '4' and '5') that starts with a parenthesized 4136 // parameter list or is an async arrow function. 4137 // AsyncArrowFunctionExpression: 4138 // 1) async[no LineTerminator here]AsyncArrowBindingIdentifier[?Yield][no LineTerminator here]=>AsyncConciseBody[?In] 4139 // 2) CoverCallExpressionAndAsyncArrowHead[?Yield, ?Await][no LineTerminator here]=>AsyncConciseBody[?In] 4140 // Production (1) of AsyncArrowFunctionExpression is parsed in "tryParseAsyncSimpleArrowFunctionExpression". 4141 // And production (2) is parsed in "tryParseParenthesizedArrowFunctionExpression". 4142 // 4143 // If we do successfully parse arrow-function, we must *not* recurse for productions 1, 2 or 3. An ArrowFunction is 4144 // not a LeftHandSideExpression, nor does it start a ConditionalExpression. So we are done 4145 // with AssignmentExpression if we see one. 4146 const arrowExpression = tryParseParenthesizedArrowFunctionExpression() || tryParseAsyncSimpleArrowFunctionExpression(); 4147 if (arrowExpression) { 4148 return arrowExpression; 4149 } 4150 4151 // Now try to see if we're in production '1', '2' or '3'. A conditional expression can 4152 // start with a LogicalOrExpression, while the assignment productions can only start with 4153 // LeftHandSideExpressions. 4154 // 4155 // So, first, we try to just parse out a BinaryExpression. If we get something that is a 4156 // LeftHandSide or higher, then we can try to parse out the assignment expression part. 4157 // Otherwise, we try to parse out the conditional expression bit. We want to allow any 4158 // binary expression here, so we pass in the 'lowest' precedence here so that it matches 4159 // and consumes anything. 4160 const pos = getNodePos(); 4161 const expr = parseBinaryExpressionOrHigher(OperatorPrecedence.Lowest); 4162 4163 // To avoid a look-ahead, we did not handle the case of an arrow function with a single un-parenthesized 4164 // parameter ('x => ...') above. We handle it here by checking if the parsed expression was a single 4165 // identifier and the current token is an arrow. 4166 if (expr.kind === SyntaxKind.Identifier && token() === SyntaxKind.EqualsGreaterThanToken) { 4167 return parseSimpleArrowFunctionExpression(pos, <Identifier>expr, /*asyncModifier*/ undefined); 4168 } 4169 4170 // Now see if we might be in cases '2' or '3'. 4171 // If the expression was a LHS expression, and we have an assignment operator, then 4172 // we're in '2' or '3'. Consume the assignment and return. 4173 // 4174 // Note: we call reScanGreaterToken so that we get an appropriately merged token 4175 // for cases like `> > =` becoming `>>=` 4176 if (isLeftHandSideExpression(expr) && isAssignmentOperator(reScanGreaterToken())) { 4177 return makeBinaryExpression(expr, parseTokenNode(), parseAssignmentExpressionOrHigher(), pos); 4178 } 4179 // It's a CallExpression with open brace followed, therefore, we think it's an EtsComponentExpression 4180 if (inEtsContext() && isCallExpression(expr) && token() === SyntaxKind.OpenBraceToken) { 4181 return makeEtsComponentExpression(expr, pos); 4182 } 4183 4184 // It wasn't an assignment or a lambda. This is a conditional expression: 4185 return parseConditionalExpressionRest(expr, pos); 4186 } 4187 4188 function makeEtsComponentExpression(expression: Expression, pos: number): EtsComponentExpression { 4189 const name = (<CallExpression>expression).expression; 4190 const body = parseFunctionBlock(SignatureFlags.None); 4191 return finishNode(factory.createEtsComponentExpression(<Identifier>name, (<CallExpression>expression).arguments, body), pos); 4192 } 4193 4194 function isYieldExpression(): boolean { 4195 if (token() === SyntaxKind.YieldKeyword) { 4196 // If we have a 'yield' keyword, and this is a context where yield expressions are 4197 // allowed, then definitely parse out a yield expression. 4198 if (inYieldContext()) { 4199 return true; 4200 } 4201 4202 // We're in a context where 'yield expr' is not allowed. However, if we can 4203 // definitely tell that the user was trying to parse a 'yield expr' and not 4204 // just a normal expr that start with a 'yield' identifier, then parse out 4205 // a 'yield expr'. We can then report an error later that they are only 4206 // allowed in generator expressions. 4207 // 4208 // for example, if we see 'yield(foo)', then we'll have to treat that as an 4209 // invocation expression of something called 'yield'. However, if we have 4210 // 'yield foo' then that is not legal as a normal expression, so we can 4211 // definitely recognize this as a yield expression. 4212 // 4213 // for now we just check if the next token is an identifier. More heuristics 4214 // can be added here later as necessary. We just need to make sure that we 4215 // don't accidentally consume something legal. 4216 return lookAhead(nextTokenIsIdentifierOrKeywordOrLiteralOnSameLine); 4217 } 4218 4219 return false; 4220 } 4221 4222 function nextTokenIsIdentifierOnSameLine() { 4223 nextToken(); 4224 return !scanner.hasPrecedingLineBreak() && isIdentifier(); 4225 } 4226 4227 function parseYieldExpression(): YieldExpression { 4228 const pos = getNodePos(); 4229 4230 // YieldExpression[In] : 4231 // yield 4232 // yield [no LineTerminator here] [Lexical goal InputElementRegExp]AssignmentExpression[?In, Yield] 4233 // yield [no LineTerminator here] * [Lexical goal InputElementRegExp]AssignmentExpression[?In, Yield] 4234 nextToken(); 4235 4236 if (!scanner.hasPrecedingLineBreak() && 4237 (token() === SyntaxKind.AsteriskToken || isStartOfExpression())) { 4238 return finishNode( 4239 factory.createYieldExpression( 4240 parseOptionalToken(SyntaxKind.AsteriskToken), 4241 parseAssignmentExpressionOrHigher() 4242 ), 4243 pos 4244 ); 4245 } 4246 else { 4247 // if the next token is not on the same line as yield. or we don't have an '*' or 4248 // the start of an expression, then this is just a simple "yield" expression. 4249 return finishNode(factory.createYieldExpression(/*asteriskToken*/ undefined, /*expression*/ undefined), pos); 4250 } 4251 } 4252 4253 function parseSimpleArrowFunctionExpression(pos: number, identifier: Identifier, asyncModifier?: NodeArray<Modifier> | undefined): ArrowFunction { 4254 Debug.assert(token() === SyntaxKind.EqualsGreaterThanToken, "parseSimpleArrowFunctionExpression should only have been called if we had a =>"); 4255 const parameter = factory.createParameterDeclaration( 4256 /*decorators*/ undefined, 4257 /*modifiers*/ undefined, 4258 /*dotDotDotToken*/ undefined, 4259 identifier, 4260 /*questionToken*/ undefined, 4261 /*type*/ undefined, 4262 /*initializer*/ undefined 4263 ); 4264 finishNode(parameter, identifier.pos); 4265 4266 const parameters = createNodeArray<ParameterDeclaration>([parameter], parameter.pos, parameter.end); 4267 const equalsGreaterThanToken = parseExpectedToken(SyntaxKind.EqualsGreaterThanToken); 4268 const body = parseArrowFunctionExpressionBody(/*isAsync*/ !!asyncModifier); 4269 const node = factory.createArrowFunction(asyncModifier, /*typeParameters*/ undefined, parameters, /*type*/ undefined, equalsGreaterThanToken, body); 4270 return addJSDocComment(finishNode(node, pos)); 4271 } 4272 4273 function tryParseParenthesizedArrowFunctionExpression(): Expression | undefined { 4274 const triState = isParenthesizedArrowFunctionExpression(); 4275 if (triState === Tristate.False) { 4276 // It's definitely not a parenthesized arrow function expression. 4277 return undefined; 4278 } 4279 4280 // If we definitely have an arrow function, then we can just parse one, not requiring a 4281 // following => or { token. Otherwise, we *might* have an arrow function. Try to parse 4282 // it out, but don't allow any ambiguity, and return 'undefined' if this could be an 4283 // expression instead. 4284 return triState === Tristate.True ? 4285 parseParenthesizedArrowFunctionExpression(/*allowAmbiguity*/ true) : 4286 tryParse(parsePossibleParenthesizedArrowFunctionExpression); 4287 } 4288 4289 // True -> We definitely expect a parenthesized arrow function here. 4290 // False -> There *cannot* be a parenthesized arrow function here. 4291 // Unknown -> There *might* be a parenthesized arrow function here. 4292 // Speculatively look ahead to be sure, and rollback if not. 4293 function isParenthesizedArrowFunctionExpression(): Tristate { 4294 if (token() === SyntaxKind.OpenParenToken || token() === SyntaxKind.LessThanToken || token() === SyntaxKind.AsyncKeyword) { 4295 return lookAhead(isParenthesizedArrowFunctionExpressionWorker); 4296 } 4297 4298 if (token() === SyntaxKind.EqualsGreaterThanToken) { 4299 // ERROR RECOVERY TWEAK: 4300 // If we see a standalone => try to parse it as an arrow function expression as that's 4301 // likely what the user intended to write. 4302 return Tristate.True; 4303 } 4304 // Definitely not a parenthesized arrow function. 4305 return Tristate.False; 4306 } 4307 4308 function isParenthesizedArrowFunctionExpressionWorker() { 4309 if (token() === SyntaxKind.AsyncKeyword) { 4310 nextToken(); 4311 if (scanner.hasPrecedingLineBreak()) { 4312 return Tristate.False; 4313 } 4314 if (token() !== SyntaxKind.OpenParenToken && token() !== SyntaxKind.LessThanToken) { 4315 return Tristate.False; 4316 } 4317 } 4318 4319 const first = token(); 4320 const second = nextToken(); 4321 4322 if (first === SyntaxKind.OpenParenToken) { 4323 if (second === SyntaxKind.CloseParenToken) { 4324 // Simple cases: "() =>", "(): ", and "() {". 4325 // This is an arrow function with no parameters. 4326 // The last one is not actually an arrow function, 4327 // but this is probably what the user intended. 4328 const third = nextToken(); 4329 switch (third) { 4330 case SyntaxKind.EqualsGreaterThanToken: 4331 case SyntaxKind.ColonToken: 4332 case SyntaxKind.OpenBraceToken: 4333 return Tristate.True; 4334 default: 4335 return Tristate.False; 4336 } 4337 } 4338 4339 // If encounter "([" or "({", this could be the start of a binding pattern. 4340 // Examples: 4341 // ([ x ]) => { } 4342 // ({ x }) => { } 4343 // ([ x ]) 4344 // ({ x }) 4345 if (second === SyntaxKind.OpenBracketToken || second === SyntaxKind.OpenBraceToken) { 4346 return Tristate.Unknown; 4347 } 4348 4349 // Simple case: "(..." 4350 // This is an arrow function with a rest parameter. 4351 if (second === SyntaxKind.DotDotDotToken) { 4352 return Tristate.True; 4353 } 4354 4355 // Check for "(xxx yyy", where xxx is a modifier and yyy is an identifier. This 4356 // isn't actually allowed, but we want to treat it as a lambda so we can provide 4357 // a good error message. 4358 if (isModifierKind(second) && second !== SyntaxKind.AsyncKeyword && lookAhead(nextTokenIsIdentifier)) { 4359 return Tristate.True; 4360 } 4361 4362 // If we had "(" followed by something that's not an identifier, 4363 // then this definitely doesn't look like a lambda. "this" is not 4364 // valid, but we want to parse it and then give a semantic error. 4365 if (!isIdentifier() && second !== SyntaxKind.ThisKeyword) { 4366 return Tristate.False; 4367 } 4368 4369 switch (nextToken()) { 4370 case SyntaxKind.ColonToken: 4371 // If we have something like "(a:", then we must have a 4372 // type-annotated parameter in an arrow function expression. 4373 return Tristate.True; 4374 case SyntaxKind.QuestionToken: 4375 nextToken(); 4376 // If we have "(a?:" or "(a?," or "(a?=" or "(a?)" then it is definitely a lambda. 4377 if (token() === SyntaxKind.ColonToken || token() === SyntaxKind.CommaToken || token() === SyntaxKind.EqualsToken || token() === SyntaxKind.CloseParenToken) { 4378 return Tristate.True; 4379 } 4380 // Otherwise it is definitely not a lambda. 4381 return Tristate.False; 4382 case SyntaxKind.CommaToken: 4383 case SyntaxKind.EqualsToken: 4384 case SyntaxKind.CloseParenToken: 4385 // If we have "(a," or "(a=" or "(a)" this *could* be an arrow function 4386 return Tristate.Unknown; 4387 } 4388 // It is definitely not an arrow function 4389 return Tristate.False; 4390 } 4391 else { 4392 Debug.assert(first === SyntaxKind.LessThanToken); 4393 4394 // If we have "<" not followed by an identifier, 4395 // then this definitely is not an arrow function. 4396 if (!isIdentifier()) { 4397 return Tristate.False; 4398 } 4399 4400 // JSX overrides 4401 if (languageVariant === LanguageVariant.JSX) { 4402 const isArrowFunctionInJsx = lookAhead(() => { 4403 const third = nextToken(); 4404 if (third === SyntaxKind.ExtendsKeyword) { 4405 const fourth = nextToken(); 4406 switch (fourth) { 4407 case SyntaxKind.EqualsToken: 4408 case SyntaxKind.GreaterThanToken: 4409 return false; 4410 default: 4411 return true; 4412 } 4413 } 4414 else if (third === SyntaxKind.CommaToken) { 4415 return true; 4416 } 4417 return false; 4418 }); 4419 4420 if (isArrowFunctionInJsx) { 4421 return Tristate.True; 4422 } 4423 4424 return Tristate.False; 4425 } 4426 4427 // This *could* be a parenthesized arrow function. 4428 return Tristate.Unknown; 4429 } 4430 } 4431 4432 function parsePossibleParenthesizedArrowFunctionExpression(): ArrowFunction | undefined { 4433 const tokenPos = scanner.getTokenPos(); 4434 if (notParenthesizedArrow?.has(tokenPos)) { 4435 return undefined; 4436 } 4437 4438 const result = parseParenthesizedArrowFunctionExpression(/*allowAmbiguity*/ false); 4439 if (!result) { 4440 (notParenthesizedArrow || (notParenthesizedArrow = new Set())).add(tokenPos); 4441 } 4442 4443 return result; 4444 } 4445 4446 function tryParseAsyncSimpleArrowFunctionExpression(): ArrowFunction | undefined { 4447 // We do a check here so that we won't be doing unnecessarily call to "lookAhead" 4448 if (token() === SyntaxKind.AsyncKeyword) { 4449 if (lookAhead(isUnParenthesizedAsyncArrowFunctionWorker) === Tristate.True) { 4450 const pos = getNodePos(); 4451 const asyncModifier = parseModifiersForArrowFunction(); 4452 const expr = parseBinaryExpressionOrHigher(OperatorPrecedence.Lowest); 4453 return parseSimpleArrowFunctionExpression(pos, <Identifier>expr, asyncModifier); 4454 } 4455 } 4456 return undefined; 4457 } 4458 4459 function isUnParenthesizedAsyncArrowFunctionWorker(): Tristate { 4460 // AsyncArrowFunctionExpression: 4461 // 1) async[no LineTerminator here]AsyncArrowBindingIdentifier[?Yield][no LineTerminator here]=>AsyncConciseBody[?In] 4462 // 2) CoverCallExpressionAndAsyncArrowHead[?Yield, ?Await][no LineTerminator here]=>AsyncConciseBody[?In] 4463 if (token() === SyntaxKind.AsyncKeyword) { 4464 nextToken(); 4465 // If the "async" is followed by "=>" token then it is not a beginning of an async arrow-function 4466 // but instead a simple arrow-function which will be parsed inside "parseAssignmentExpressionOrHigher" 4467 if (scanner.hasPrecedingLineBreak() || token() === SyntaxKind.EqualsGreaterThanToken) { 4468 return Tristate.False; 4469 } 4470 // Check for un-parenthesized AsyncArrowFunction 4471 const expr = parseBinaryExpressionOrHigher(OperatorPrecedence.Lowest); 4472 if (!scanner.hasPrecedingLineBreak() && expr.kind === SyntaxKind.Identifier && token() === SyntaxKind.EqualsGreaterThanToken) { 4473 return Tristate.True; 4474 } 4475 } 4476 4477 return Tristate.False; 4478 } 4479 4480 function parseParenthesizedArrowFunctionExpression(allowAmbiguity: boolean): ArrowFunction | undefined { 4481 const pos = getNodePos(); 4482 const hasJSDoc = hasPrecedingJSDocComment(); 4483 const modifiers = parseModifiersForArrowFunction(); 4484 const isAsync = some(modifiers, isAsyncModifier) ? SignatureFlags.Await : SignatureFlags.None; 4485 // Arrow functions are never generators. 4486 // 4487 // If we're speculatively parsing a signature for a parenthesized arrow function, then 4488 // we have to have a complete parameter list. Otherwise we might see something like 4489 // a => (b => c) 4490 // And think that "(b =>" was actually a parenthesized arrow function with a missing 4491 // close paren. 4492 const typeParameters = parseTypeParameters(); 4493 4494 let parameters: NodeArray<ParameterDeclaration>; 4495 if (!parseExpected(SyntaxKind.OpenParenToken)) { 4496 if (!allowAmbiguity) { 4497 return undefined; 4498 } 4499 parameters = createMissingList<ParameterDeclaration>(); 4500 } 4501 else { 4502 parameters = parseParametersWorker(isAsync); 4503 if (!parseExpected(SyntaxKind.CloseParenToken) && !allowAmbiguity) { 4504 return undefined; 4505 } 4506 } 4507 4508 const type = parseReturnType(SyntaxKind.ColonToken, /*isType*/ false); 4509 if (type && !allowAmbiguity && typeHasArrowFunctionBlockingParseError(type)) { 4510 return undefined; 4511 } 4512 4513 // Parsing a signature isn't enough. 4514 // Parenthesized arrow signatures often look like other valid expressions. 4515 // For instance: 4516 // - "(x = 10)" is an assignment expression parsed as a signature with a default parameter value. 4517 // - "(x,y)" is a comma expression parsed as a signature with two parameters. 4518 // - "a ? (b): c" will have "(b):" parsed as a signature with a return type annotation. 4519 // - "a ? (b): function() {}" will too, since function() is a valid JSDoc function type. 4520 // 4521 // So we need just a bit of lookahead to ensure that it can only be a signature. 4522 const hasJSDocFunctionType = type && isJSDocFunctionType(type); 4523 if (!allowAmbiguity && token() !== SyntaxKind.EqualsGreaterThanToken && (hasJSDocFunctionType || token() !== SyntaxKind.OpenBraceToken)) { 4524 // Returning undefined here will cause our caller to rewind to where we started from. 4525 return undefined; 4526 } 4527 4528 // If we have an arrow, then try to parse the body. Even if not, try to parse if we 4529 // have an opening brace, just in case we're in an error state. 4530 const lastToken = token(); 4531 const equalsGreaterThanToken = parseExpectedToken(SyntaxKind.EqualsGreaterThanToken); 4532 const body = (lastToken === SyntaxKind.EqualsGreaterThanToken || lastToken === SyntaxKind.OpenBraceToken) 4533 ? parseArrowFunctionExpressionBody(some(modifiers, isAsyncModifier)) 4534 : parseIdentifier(); 4535 4536 const node = factory.createArrowFunction(modifiers, typeParameters, parameters, type, equalsGreaterThanToken, body); 4537 return withJSDoc(finishNode(node, pos), hasJSDoc); 4538 } 4539 4540 function parseArrowFunctionExpressionBody(isAsync: boolean): Block | Expression { 4541 if (token() === SyntaxKind.OpenBraceToken) { 4542 return parseFunctionBlock(isAsync ? SignatureFlags.Await : SignatureFlags.None); 4543 } 4544 4545 if (token() !== SyntaxKind.SemicolonToken && 4546 token() !== SyntaxKind.FunctionKeyword && 4547 token() !== SyntaxKind.ClassKeyword && 4548 (!inEtsContext() || token() !== SyntaxKind.StructKeyword) && 4549 isStartOfStatement() && 4550 !isStartOfExpressionStatement()) { 4551 // Check if we got a plain statement (i.e. no expression-statements, no function/class expressions/declarations) 4552 // 4553 // Here we try to recover from a potential error situation in the case where the 4554 // user meant to supply a block. For example, if the user wrote: 4555 // 4556 // a => 4557 // let v = 0; 4558 // } 4559 // 4560 // they may be missing an open brace. Check to see if that's the case so we can 4561 // try to recover better. If we don't do this, then the next close curly we see may end 4562 // up preemptively closing the containing construct. 4563 // 4564 // Note: even when 'IgnoreMissingOpenBrace' is passed, parseBody will still error. 4565 return parseFunctionBlock(SignatureFlags.IgnoreMissingOpenBrace | (isAsync ? SignatureFlags.Await : SignatureFlags.None)); 4566 } 4567 4568 const savedTopLevel = topLevel; 4569 topLevel = false; 4570 const node = isAsync 4571 ? doInAwaitContext(parseAssignmentExpressionOrHigher) 4572 : doOutsideOfAwaitContext(parseAssignmentExpressionOrHigher); 4573 topLevel = savedTopLevel; 4574 return node; 4575 } 4576 4577 function parseConditionalExpressionRest(leftOperand: Expression, pos: number): Expression { 4578 // Note: we are passed in an expression which was produced from parseBinaryExpressionOrHigher. 4579 const questionToken = parseOptionalToken(SyntaxKind.QuestionToken); 4580 if (!questionToken) { 4581 return leftOperand; 4582 } 4583 4584 // Note: we explicitly 'allowIn' in the whenTrue part of the condition expression, and 4585 // we do not that for the 'whenFalse' part. 4586 let colonToken; 4587 return finishNode( 4588 factory.createConditionalExpression( 4589 leftOperand, 4590 questionToken, 4591 doOutsideOfContext(disallowInAndDecoratorContext, parseAssignmentExpressionOrHigher), 4592 colonToken = parseExpectedToken(SyntaxKind.ColonToken), 4593 nodeIsPresent(colonToken) 4594 ? parseAssignmentExpressionOrHigher() 4595 : createMissingNode(SyntaxKind.Identifier, /*reportAtCurrentPosition*/ false, Diagnostics._0_expected, tokenToString(SyntaxKind.ColonToken)) 4596 ), 4597 pos 4598 ); 4599 } 4600 4601 function parseBinaryExpressionOrHigher(precedence: OperatorPrecedence): Expression { 4602 const pos = getNodePos(); 4603 const leftOperand = parseUnaryExpressionOrHigher(); 4604 return parseBinaryExpressionRest(precedence, leftOperand, pos); 4605 } 4606 4607 function isInOrOfKeyword(t: SyntaxKind) { 4608 return t === SyntaxKind.InKeyword || t === SyntaxKind.OfKeyword; 4609 } 4610 4611 function parseBinaryExpressionRest(precedence: OperatorPrecedence, leftOperand: Expression, pos: number): Expression { 4612 while (true) { 4613 // We either have a binary operator here, or we're finished. We call 4614 // reScanGreaterToken so that we merge token sequences like > and = into >= 4615 4616 reScanGreaterToken(); 4617 const newPrecedence = getBinaryOperatorPrecedence(token()); 4618 4619 // Check the precedence to see if we should "take" this operator 4620 // - For left associative operator (all operator but **), consume the operator, 4621 // recursively call the function below, and parse binaryExpression as a rightOperand 4622 // of the caller if the new precedence of the operator is greater then or equal to the current precedence. 4623 // For example: 4624 // a - b - c; 4625 // ^token; leftOperand = b. Return b to the caller as a rightOperand 4626 // a * b - c 4627 // ^token; leftOperand = b. Return b to the caller as a rightOperand 4628 // a - b * c; 4629 // ^token; leftOperand = b. Return b * c to the caller as a rightOperand 4630 // - For right associative operator (**), consume the operator, recursively call the function 4631 // and parse binaryExpression as a rightOperand of the caller if the new precedence of 4632 // the operator is strictly grater than the current precedence 4633 // For example: 4634 // a ** b ** c; 4635 // ^^token; leftOperand = b. Return b ** c to the caller as a rightOperand 4636 // a - b ** c; 4637 // ^^token; leftOperand = b. Return b ** c to the caller as a rightOperand 4638 // a ** b - c 4639 // ^token; leftOperand = b. Return b to the caller as a rightOperand 4640 const consumeCurrentOperator = token() === SyntaxKind.AsteriskAsteriskToken ? 4641 newPrecedence >= precedence : 4642 newPrecedence > precedence; 4643 4644 if (!consumeCurrentOperator) { 4645 break; 4646 } 4647 4648 if (token() === SyntaxKind.InKeyword && inDisallowInContext()) { 4649 break; 4650 } 4651 4652 if (token() === SyntaxKind.AsKeyword) { 4653 // Make sure we *do* perform ASI for constructs like this: 4654 // var x = foo 4655 // as (Bar) 4656 // This should be parsed as an initialized variable, followed 4657 // by a function call to 'as' with the argument 'Bar' 4658 if (scanner.hasPrecedingLineBreak()) { 4659 break; 4660 } 4661 else { 4662 nextToken(); 4663 leftOperand = makeAsExpression(leftOperand, parseType()); 4664 } 4665 } 4666 else { 4667 leftOperand = makeBinaryExpression(leftOperand, parseTokenNode(), parseBinaryExpressionOrHigher(newPrecedence), pos); 4668 } 4669 } 4670 4671 return leftOperand; 4672 } 4673 4674 function isBinaryOperator() { 4675 if (inDisallowInContext() && token() === SyntaxKind.InKeyword) { 4676 return false; 4677 } 4678 4679 return getBinaryOperatorPrecedence(token()) > 0; 4680 } 4681 4682 function makeBinaryExpression(left: Expression, operatorToken: BinaryOperatorToken, right: Expression, pos: number): BinaryExpression { 4683 return finishNode(factory.createBinaryExpression(left, operatorToken, right), pos); 4684 } 4685 4686 function makeAsExpression(left: Expression, right: TypeNode): AsExpression { 4687 return finishNode(factory.createAsExpression(left, right), left.pos); 4688 } 4689 4690 function parsePrefixUnaryExpression() { 4691 const pos = getNodePos(); 4692 return finishNode(factory.createPrefixUnaryExpression(<PrefixUnaryOperator>token(), nextTokenAnd(parseSimpleUnaryExpression)), pos); 4693 } 4694 4695 function parseDeleteExpression() { 4696 const pos = getNodePos(); 4697 return finishNode(factory.createDeleteExpression(nextTokenAnd(parseSimpleUnaryExpression)), pos); 4698 } 4699 4700 function parseTypeOfExpression() { 4701 const pos = getNodePos(); 4702 return finishNode(factory.createTypeOfExpression(nextTokenAnd(parseSimpleUnaryExpression)), pos); 4703 } 4704 4705 function parseVoidExpression() { 4706 const pos = getNodePos(); 4707 return finishNode(factory.createVoidExpression(nextTokenAnd(parseSimpleUnaryExpression)), pos); 4708 } 4709 4710 function isAwaitExpression(): boolean { 4711 if (token() === SyntaxKind.AwaitKeyword) { 4712 if (inAwaitContext()) { 4713 return true; 4714 } 4715 4716 // here we are using similar heuristics as 'isYieldExpression' 4717 return lookAhead(nextTokenIsIdentifierOrKeywordOrLiteralOnSameLine); 4718 } 4719 4720 return false; 4721 } 4722 4723 function parseAwaitExpression() { 4724 const pos = getNodePos(); 4725 return finishNode(factory.createAwaitExpression(nextTokenAnd(parseSimpleUnaryExpression)), pos); 4726 } 4727 4728 /** 4729 * Parse ES7 exponential expression and await expression 4730 * 4731 * ES7 ExponentiationExpression: 4732 * 1) UnaryExpression[?Yield] 4733 * 2) UpdateExpression[?Yield] ** ExponentiationExpression[?Yield] 4734 * 4735 */ 4736 function parseUnaryExpressionOrHigher(): UnaryExpression | BinaryExpression { 4737 /** 4738 * ES7 UpdateExpression: 4739 * 1) LeftHandSideExpression[?Yield] 4740 * 2) LeftHandSideExpression[?Yield][no LineTerminator here]++ 4741 * 3) LeftHandSideExpression[?Yield][no LineTerminator here]-- 4742 * 4) ++UnaryExpression[?Yield] 4743 * 5) --UnaryExpression[?Yield] 4744 */ 4745 if (isUpdateExpression()) { 4746 const pos = getNodePos(); 4747 const updateExpression = parseUpdateExpression(); 4748 return token() === SyntaxKind.AsteriskAsteriskToken ? 4749 <BinaryExpression>parseBinaryExpressionRest(getBinaryOperatorPrecedence(token()), updateExpression, pos) : 4750 updateExpression; 4751 } 4752 4753 /** 4754 * ES7 UnaryExpression: 4755 * 1) UpdateExpression[?yield] 4756 * 2) delete UpdateExpression[?yield] 4757 * 3) void UpdateExpression[?yield] 4758 * 4) typeof UpdateExpression[?yield] 4759 * 5) + UpdateExpression[?yield] 4760 * 6) - UpdateExpression[?yield] 4761 * 7) ~ UpdateExpression[?yield] 4762 * 8) ! UpdateExpression[?yield] 4763 */ 4764 const unaryOperator = token(); 4765 const simpleUnaryExpression = parseSimpleUnaryExpression(); 4766 if (token() === SyntaxKind.AsteriskAsteriskToken) { 4767 const pos = skipTrivia(sourceText, simpleUnaryExpression.pos); 4768 const { end } = simpleUnaryExpression; 4769 if (simpleUnaryExpression.kind === SyntaxKind.TypeAssertionExpression) { 4770 parseErrorAt(pos, end, Diagnostics.A_type_assertion_expression_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_expression_Consider_enclosing_the_expression_in_parentheses); 4771 } 4772 else { 4773 parseErrorAt(pos, end, Diagnostics.An_unary_expression_with_the_0_operator_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_expression_Consider_enclosing_the_expression_in_parentheses, tokenToString(unaryOperator)); 4774 } 4775 } 4776 return simpleUnaryExpression; 4777 } 4778 4779 /** 4780 * Parse ES7 simple-unary expression or higher: 4781 * 4782 * ES7 UnaryExpression: 4783 * 1) UpdateExpression[?yield] 4784 * 2) delete UnaryExpression[?yield] 4785 * 3) void UnaryExpression[?yield] 4786 * 4) typeof UnaryExpression[?yield] 4787 * 5) + UnaryExpression[?yield] 4788 * 6) - UnaryExpression[?yield] 4789 * 7) ~ UnaryExpression[?yield] 4790 * 8) ! UnaryExpression[?yield] 4791 * 9) [+Await] await UnaryExpression[?yield] 4792 */ 4793 function parseSimpleUnaryExpression(): UnaryExpression { 4794 switch (token()) { 4795 case SyntaxKind.PlusToken: 4796 case SyntaxKind.MinusToken: 4797 case SyntaxKind.TildeToken: 4798 case SyntaxKind.ExclamationToken: 4799 return parsePrefixUnaryExpression(); 4800 case SyntaxKind.DeleteKeyword: 4801 return parseDeleteExpression(); 4802 case SyntaxKind.TypeOfKeyword: 4803 return parseTypeOfExpression(); 4804 case SyntaxKind.VoidKeyword: 4805 return parseVoidExpression(); 4806 case SyntaxKind.LessThanToken: 4807 // This is modified UnaryExpression grammar in TypeScript 4808 // UnaryExpression (modified): 4809 // < type > UnaryExpression 4810 return parseTypeAssertion(); 4811 case SyntaxKind.AwaitKeyword: 4812 if (isAwaitExpression()) { 4813 return parseAwaitExpression(); 4814 } 4815 // falls through 4816 default: 4817 return parseUpdateExpression(); 4818 } 4819 } 4820 4821 /** 4822 * Check if the current token can possibly be an ES7 increment expression. 4823 * 4824 * ES7 UpdateExpression: 4825 * LeftHandSideExpression[?Yield] 4826 * LeftHandSideExpression[?Yield][no LineTerminator here]++ 4827 * LeftHandSideExpression[?Yield][no LineTerminator here]-- 4828 * ++LeftHandSideExpression[?Yield] 4829 * --LeftHandSideExpression[?Yield] 4830 */ 4831 function isUpdateExpression(): boolean { 4832 // This function is called inside parseUnaryExpression to decide 4833 // whether to call parseSimpleUnaryExpression or call parseUpdateExpression directly 4834 switch (token()) { 4835 case SyntaxKind.PlusToken: 4836 case SyntaxKind.MinusToken: 4837 case SyntaxKind.TildeToken: 4838 case SyntaxKind.ExclamationToken: 4839 case SyntaxKind.DeleteKeyword: 4840 case SyntaxKind.TypeOfKeyword: 4841 case SyntaxKind.VoidKeyword: 4842 case SyntaxKind.AwaitKeyword: 4843 return false; 4844 case SyntaxKind.LessThanToken: 4845 // If we are not in JSX context, we are parsing TypeAssertion which is an UnaryExpression 4846 if (languageVariant !== LanguageVariant.JSX) { 4847 return false; 4848 } 4849 // We are in JSX context and the token is part of JSXElement. 4850 // falls through 4851 default: 4852 return true; 4853 } 4854 } 4855 4856 /** 4857 * Parse ES7 UpdateExpression. UpdateExpression is used instead of ES6's PostFixExpression. 4858 * 4859 * ES7 UpdateExpression[yield]: 4860 * 1) LeftHandSideExpression[?yield] 4861 * 2) LeftHandSideExpression[?yield] [[no LineTerminator here]]++ 4862 * 3) LeftHandSideExpression[?yield] [[no LineTerminator here]]-- 4863 * 4) ++LeftHandSideExpression[?yield] 4864 * 5) --LeftHandSideExpression[?yield] 4865 * In TypeScript (2), (3) are parsed as PostfixUnaryExpression. (4), (5) are parsed as PrefixUnaryExpression 4866 */ 4867 function parseUpdateExpression(): UpdateExpression { 4868 if (token() === SyntaxKind.PlusPlusToken || token() === SyntaxKind.MinusMinusToken) { 4869 const pos = getNodePos(); 4870 return finishNode(factory.createPrefixUnaryExpression(<PrefixUnaryOperator>token(), nextTokenAnd(parseLeftHandSideExpressionOrHigher)), pos); 4871 } 4872 else if (languageVariant === LanguageVariant.JSX && token() === SyntaxKind.LessThanToken && lookAhead(nextTokenIsIdentifierOrKeywordOrGreaterThan)) { 4873 // JSXElement is part of primaryExpression 4874 return parseJsxElementOrSelfClosingElementOrFragment(/*inExpressionContext*/ true); 4875 } 4876 4877 const expression = parseLeftHandSideExpressionOrHigher(); 4878 4879 Debug.assert(isLeftHandSideExpression(expression)); 4880 if ((token() === SyntaxKind.PlusPlusToken || token() === SyntaxKind.MinusMinusToken) && !scanner.hasPrecedingLineBreak()) { 4881 const operator = <PostfixUnaryOperator>token(); 4882 nextToken(); 4883 return finishNode(factory.createPostfixUnaryExpression(expression, operator), expression.pos); 4884 } 4885 4886 return expression; 4887 } 4888 4889 function parseLeftHandSideExpressionOrHigher(): LeftHandSideExpression { 4890 // Original Ecma: 4891 // LeftHandSideExpression: See 11.2 4892 // NewExpression 4893 // CallExpression 4894 // 4895 // Our simplification: 4896 // 4897 // LeftHandSideExpression: See 11.2 4898 // MemberExpression 4899 // CallExpression 4900 // 4901 // See comment in parseMemberExpressionOrHigher on how we replaced NewExpression with 4902 // MemberExpression to make our lives easier. 4903 // 4904 // to best understand the below code, it's important to see how CallExpression expands 4905 // out into its own productions: 4906 // 4907 // CallExpression: 4908 // MemberExpression Arguments 4909 // CallExpression Arguments 4910 // CallExpression[Expression] 4911 // CallExpression.IdentifierName 4912 // import (AssignmentExpression) 4913 // super Arguments 4914 // super.IdentifierName 4915 // 4916 // Because of the recursion in these calls, we need to bottom out first. There are three 4917 // bottom out states we can run into: 1) We see 'super' which must start either of 4918 // the last two CallExpression productions. 2) We see 'import' which must start import call. 4919 // 3)we have a MemberExpression which either completes the LeftHandSideExpression, 4920 // or starts the beginning of the first four CallExpression productions. 4921 const pos = getNodePos(); 4922 let expression: MemberExpression; 4923 if (token() === SyntaxKind.ImportKeyword) { 4924 if (lookAhead(nextTokenIsOpenParenOrLessThan)) { 4925 // We don't want to eagerly consume all import keyword as import call expression so we look ahead to find "(" 4926 // For example: 4927 // var foo3 = require("subfolder 4928 // import * as foo1 from "module-from-node 4929 // We want this import to be a statement rather than import call expression 4930 sourceFlags |= NodeFlags.PossiblyContainsDynamicImport; 4931 expression = parseTokenNode<PrimaryExpression>(); 4932 } 4933 else if (lookAhead(nextTokenIsDot)) { 4934 // This is an 'import.*' metaproperty (i.e. 'import.meta') 4935 nextToken(); // advance past the 'import' 4936 nextToken(); // advance past the dot 4937 expression = finishNode(factory.createMetaProperty(SyntaxKind.ImportKeyword, parseIdentifierName()), pos); 4938 sourceFlags |= NodeFlags.PossiblyContainsImportMeta; 4939 } 4940 else { 4941 expression = parseMemberExpressionOrHigher(); 4942 } 4943 } 4944 else { 4945 expression = token() === SyntaxKind.SuperKeyword ? parseSuperExpression() : parseMemberExpressionOrHigher(); 4946 } 4947 4948 // Now, we *may* be complete. However, we might have consumed the start of a 4949 // CallExpression or OptionalExpression. As such, we need to consume the rest 4950 // of it here to be complete. 4951 return parseCallExpressionRest(pos, expression); 4952 } 4953 4954 function parseMemberExpressionOrHigher(): MemberExpression { 4955 // Note: to make our lives simpler, we decompose the NewExpression productions and 4956 // place ObjectCreationExpression and FunctionExpression into PrimaryExpression. 4957 // like so: 4958 // 4959 // PrimaryExpression : See 11.1 4960 // this 4961 // Identifier 4962 // Literal 4963 // ArrayLiteral 4964 // ObjectLiteral 4965 // (Expression) 4966 // FunctionExpression 4967 // new MemberExpression Arguments? 4968 // 4969 // MemberExpression : See 11.2 4970 // PrimaryExpression 4971 // MemberExpression[Expression] 4972 // MemberExpression.IdentifierName 4973 // 4974 // CallExpression : See 11.2 4975 // MemberExpression 4976 // CallExpression Arguments 4977 // CallExpression[Expression] 4978 // CallExpression.IdentifierName 4979 // 4980 // Technically this is ambiguous. i.e. CallExpression defines: 4981 // 4982 // CallExpression: 4983 // CallExpression Arguments 4984 // 4985 // If you see: "new Foo()" 4986 // 4987 // Then that could be treated as a single ObjectCreationExpression, or it could be 4988 // treated as the invocation of "new Foo". We disambiguate that in code (to match 4989 // the original grammar) by making sure that if we see an ObjectCreationExpression 4990 // we always consume arguments if they are there. So we treat "new Foo()" as an 4991 // object creation only, and not at all as an invocation. Another way to think 4992 // about this is that for every "new" that we see, we will consume an argument list if 4993 // it is there as part of the *associated* object creation node. Any additional 4994 // argument lists we see, will become invocation expressions. 4995 // 4996 // Because there are no other places in the grammar now that refer to FunctionExpression 4997 // or ObjectCreationExpression, it is safe to push down into the PrimaryExpression 4998 // production. 4999 // 5000 // Because CallExpression and MemberExpression are left recursive, we need to bottom out 5001 // of the recursion immediately. So we parse out a primary expression to start with. 5002 const pos = getNodePos(); 5003 let expression; 5004 5005 if (inEtsExtendComponentsContext() && extendEtsComponentDeclaration && token() === SyntaxKind.DotToken) { 5006 expression = finishVirtualNode(factory.createIdentifier(extendEtsComponentDeclaration.instance, /*typeArguments*/ undefined, SyntaxKind.Identifier), pos, pos); 5007 } 5008 else if (inEtsStylesComponentsContext() && stylesEtsComponentDeclaration && token() === SyntaxKind.DotToken) { 5009 expression = finishVirtualNode(factory.createIdentifier(stylesEtsComponentDeclaration.instance, /*typeArguments*/ undefined, SyntaxKind.Identifier), pos, pos); 5010 } 5011 else if (inEtsStateStylesContext() && stateStylesRootNode && token() === SyntaxKind.DotToken) { 5012 expression = finishVirtualNode(factory.createIdentifier(`${stateStylesRootNode}Instance`, /*typeArguments*/ undefined, SyntaxKind.Identifier), pos, pos); 5013 } 5014 else { 5015 expression = parsePrimaryExpression(); 5016 } 5017 return parseMemberExpressionRest(pos, expression, /*allowOptionalChain*/ true); 5018 } 5019 5020 function parseSuperExpression(): MemberExpression { 5021 const pos = getNodePos(); 5022 const expression = parseTokenNode<PrimaryExpression>(); 5023 if (token() === SyntaxKind.LessThanToken) { 5024 const startPos = getNodePos(); 5025 const typeArguments = tryParse(parseTypeArgumentsInExpression); 5026 if (typeArguments !== undefined) { 5027 parseErrorAt(startPos, getNodePos(), Diagnostics.super_may_not_use_type_arguments); 5028 } 5029 } 5030 5031 if (token() === SyntaxKind.OpenParenToken || token() === SyntaxKind.DotToken || token() === SyntaxKind.OpenBracketToken) { 5032 return expression; 5033 } 5034 5035 // If we have seen "super" it must be followed by '(' or '.'. 5036 // If it wasn't then just try to parse out a '.' and report an error. 5037 parseExpectedToken(SyntaxKind.DotToken, Diagnostics.super_must_be_followed_by_an_argument_list_or_member_access); 5038 // private names will never work with `super` (`super.#foo`), but that's a semantic error, not syntactic 5039 return finishNode(factory.createPropertyAccessExpression(expression, parseRightSideOfDot(/*allowIdentifierNames*/ true, /*allowPrivateIdentifiers*/ true)), pos); 5040 } 5041 5042 function parseJsxElementOrSelfClosingElementOrFragment(inExpressionContext: boolean, topInvalidNodePosition?: number): JsxElement | JsxSelfClosingElement | JsxFragment { 5043 const pos = getNodePos(); 5044 const opening = parseJsxOpeningOrSelfClosingElementOrOpeningFragment(inExpressionContext); 5045 let result: JsxElement | JsxSelfClosingElement | JsxFragment; 5046 if (opening.kind === SyntaxKind.JsxOpeningElement) { 5047 const children = parseJsxChildren(opening); 5048 const closingElement = parseJsxClosingElement(inExpressionContext); 5049 5050 if (!tagNamesAreEquivalent(opening.tagName, closingElement.tagName)) { 5051 parseErrorAtRange(closingElement, Diagnostics.Expected_corresponding_JSX_closing_tag_for_0, getTextOfNodeFromSourceText(sourceText, opening.tagName)); 5052 } 5053 5054 result = finishNode(factory.createJsxElement(opening, children, closingElement), pos); 5055 } 5056 else if (opening.kind === SyntaxKind.JsxOpeningFragment) { 5057 result = finishNode(factory.createJsxFragment(opening, parseJsxChildren(opening), parseJsxClosingFragment(inExpressionContext)), pos); 5058 } 5059 else { 5060 Debug.assert(opening.kind === SyntaxKind.JsxSelfClosingElement); 5061 // Nothing else to do for self-closing elements 5062 result = opening; 5063 } 5064 5065 // If the user writes the invalid code '<div></div><div></div>' in an expression context (i.e. not wrapped in 5066 // an enclosing tag), we'll naively try to parse ^ this as a 'less than' operator and the remainder of the tag 5067 // as garbage, which will cause the formatter to badly mangle the JSX. Perform a speculative parse of a JSX 5068 // element if we see a < token so that we can wrap it in a synthetic binary expression so the formatter 5069 // does less damage and we can report a better error. 5070 // Since JSX elements are invalid < operands anyway, this lookahead parse will only occur in error scenarios 5071 // of one sort or another. 5072 if (inExpressionContext && token() === SyntaxKind.LessThanToken) { 5073 const topBadPos = typeof topInvalidNodePosition === "undefined" ? result.pos : topInvalidNodePosition; 5074 const invalidElement = tryParse(() => parseJsxElementOrSelfClosingElementOrFragment(/*inExpressionContext*/ true, topBadPos)); 5075 if (invalidElement) { 5076 const operatorToken = createMissingNode(SyntaxKind.CommaToken, /*reportAtCurrentPosition*/ false); 5077 setTextRangePosWidth(operatorToken, invalidElement.pos, 0); 5078 parseErrorAt(skipTrivia(sourceText, topBadPos), invalidElement.end, Diagnostics.JSX_expressions_must_have_one_parent_element); 5079 return <JsxElement><Node>finishNode(factory.createBinaryExpression(result, operatorToken as Token<SyntaxKind.CommaToken>, invalidElement), pos); 5080 } 5081 } 5082 5083 return result; 5084 } 5085 5086 function parseJsxText(): JsxText { 5087 const pos = getNodePos(); 5088 const node = factory.createJsxText(scanner.getTokenValue(), currentToken === SyntaxKind.JsxTextAllWhiteSpaces); 5089 currentToken = scanner.scanJsxToken(); 5090 return finishNode(node, pos); 5091 } 5092 5093 function parseJsxChild(openingTag: JsxOpeningElement | JsxOpeningFragment, token: JsxTokenSyntaxKind): JsxChild | undefined { 5094 switch (token) { 5095 case SyntaxKind.EndOfFileToken: 5096 // If we hit EOF, issue the error at the tag that lacks the closing element 5097 // rather than at the end of the file (which is useless) 5098 if (isJsxOpeningFragment(openingTag)) { 5099 parseErrorAtRange(openingTag, Diagnostics.JSX_fragment_has_no_corresponding_closing_tag); 5100 } 5101 else { 5102 // We want the error span to cover only 'Foo.Bar' in < Foo.Bar > 5103 // or to cover only 'Foo' in < Foo > 5104 const tag = openingTag.tagName; 5105 const start = skipTrivia(sourceText, tag.pos); 5106 parseErrorAt(start, tag.end, Diagnostics.JSX_element_0_has_no_corresponding_closing_tag, getTextOfNodeFromSourceText(sourceText, openingTag.tagName)); 5107 } 5108 return undefined; 5109 case SyntaxKind.LessThanSlashToken: 5110 case SyntaxKind.ConflictMarkerTrivia: 5111 return undefined; 5112 case SyntaxKind.JsxText: 5113 case SyntaxKind.JsxTextAllWhiteSpaces: 5114 return parseJsxText(); 5115 case SyntaxKind.OpenBraceToken: 5116 return parseJsxExpression(/*inExpressionContext*/ false); 5117 case SyntaxKind.LessThanToken: 5118 return parseJsxElementOrSelfClosingElementOrFragment(/*inExpressionContext*/ false); 5119 default: 5120 return Debug.assertNever(token); 5121 } 5122 } 5123 5124 function parseJsxChildren(openingTag: JsxOpeningElement | JsxOpeningFragment): NodeArray<JsxChild> { 5125 const list = []; 5126 const listPos = getNodePos(); 5127 const saveParsingContext = parsingContext; 5128 parsingContext |= 1 << ParsingContext.JsxChildren; 5129 5130 while (true) { 5131 const child = parseJsxChild(openingTag, currentToken = scanner.reScanJsxToken()); 5132 if (!child) break; 5133 list.push(child); 5134 } 5135 5136 parsingContext = saveParsingContext; 5137 return createNodeArray(list, listPos); 5138 } 5139 5140 function parseJsxAttributes(): JsxAttributes { 5141 const pos = getNodePos(); 5142 return finishNode(factory.createJsxAttributes(parseList(ParsingContext.JsxAttributes, parseJsxAttribute)), pos); 5143 } 5144 5145 function parseJsxOpeningOrSelfClosingElementOrOpeningFragment(inExpressionContext: boolean): JsxOpeningElement | JsxSelfClosingElement | JsxOpeningFragment { 5146 const pos = getNodePos(); 5147 5148 parseExpected(SyntaxKind.LessThanToken); 5149 5150 if (token() === SyntaxKind.GreaterThanToken) { 5151 // See below for explanation of scanJsxText 5152 scanJsxText(); 5153 return finishNode(factory.createJsxOpeningFragment(), pos); 5154 } 5155 5156 const tagName = parseJsxElementName(); 5157 const typeArguments = (contextFlags & NodeFlags.JavaScriptFile) === 0 ? tryParseTypeArguments() : undefined; 5158 const attributes = parseJsxAttributes(); 5159 5160 let node: JsxOpeningLikeElement; 5161 5162 if (token() === SyntaxKind.GreaterThanToken) { 5163 // Closing tag, so scan the immediately-following text with the JSX scanning instead 5164 // of regular scanning to avoid treating illegal characters (e.g. '#') as immediate 5165 // scanning errors 5166 scanJsxText(); 5167 node = factory.createJsxOpeningElement(tagName, typeArguments, attributes); 5168 } 5169 else { 5170 parseExpected(SyntaxKind.SlashToken); 5171 if (inExpressionContext) { 5172 parseExpected(SyntaxKind.GreaterThanToken); 5173 } 5174 else { 5175 parseExpected(SyntaxKind.GreaterThanToken, /*diagnostic*/ undefined, /*shouldAdvance*/ false); 5176 scanJsxText(); 5177 } 5178 node = factory.createJsxSelfClosingElement(tagName, typeArguments, attributes); 5179 } 5180 5181 return finishNode(node, pos); 5182 } 5183 5184 function parseJsxElementName(): JsxTagNameExpression { 5185 const pos = getNodePos(); 5186 scanJsxIdentifier(); 5187 // JsxElement can have name in the form of 5188 // propertyAccessExpression 5189 // primaryExpression in the form of an identifier and "this" keyword 5190 // We can't just simply use parseLeftHandSideExpressionOrHigher because then we will start consider class,function etc as a keyword 5191 // We only want to consider "this" as a primaryExpression 5192 let expression: JsxTagNameExpression = token() === SyntaxKind.ThisKeyword ? 5193 parseTokenNode<ThisExpression>() : parseIdentifierName(); 5194 while (parseOptional(SyntaxKind.DotToken)) { 5195 expression = finishNode(factory.createPropertyAccessExpression(expression, parseRightSideOfDot(/*allowIdentifierNames*/ true, /*allowPrivateIdentifiers*/ false)), pos) as JsxTagNamePropertyAccess; 5196 } 5197 return expression; 5198 } 5199 5200 function parseJsxExpression(inExpressionContext: boolean): JsxExpression | undefined { 5201 const pos = getNodePos(); 5202 if (!parseExpected(SyntaxKind.OpenBraceToken)) { 5203 return undefined; 5204 } 5205 5206 let dotDotDotToken: DotDotDotToken | undefined; 5207 let expression: Expression | undefined; 5208 if (token() !== SyntaxKind.CloseBraceToken) { 5209 dotDotDotToken = parseOptionalToken(SyntaxKind.DotDotDotToken); 5210 // Only an AssignmentExpression is valid here per the JSX spec, 5211 // but we can unambiguously parse a comma sequence and provide 5212 // a better error message in grammar checking. 5213 expression = parseExpression(); 5214 } 5215 if (inExpressionContext) { 5216 parseExpected(SyntaxKind.CloseBraceToken); 5217 } 5218 else { 5219 if (parseExpected(SyntaxKind.CloseBraceToken, /*message*/ undefined, /*shouldAdvance*/ false)) { 5220 scanJsxText(); 5221 } 5222 } 5223 5224 return finishNode(factory.createJsxExpression(dotDotDotToken, expression), pos); 5225 } 5226 5227 function parseJsxAttribute(): JsxAttribute | JsxSpreadAttribute { 5228 if (token() === SyntaxKind.OpenBraceToken) { 5229 return parseJsxSpreadAttribute(); 5230 } 5231 5232 scanJsxIdentifier(); 5233 const pos = getNodePos(); 5234 return finishNode( 5235 factory.createJsxAttribute( 5236 parseIdentifierName(), 5237 token() !== SyntaxKind.EqualsToken ? undefined : 5238 scanJsxAttributeValue() === SyntaxKind.StringLiteral ? parseLiteralNode() as StringLiteral : 5239 parseJsxExpression(/*inExpressionContext*/ true) 5240 ), 5241 pos 5242 ); 5243 } 5244 5245 function parseJsxSpreadAttribute(): JsxSpreadAttribute { 5246 const pos = getNodePos(); 5247 parseExpected(SyntaxKind.OpenBraceToken); 5248 parseExpected(SyntaxKind.DotDotDotToken); 5249 const expression = parseExpression(); 5250 parseExpected(SyntaxKind.CloseBraceToken); 5251 return finishNode(factory.createJsxSpreadAttribute(expression), pos); 5252 } 5253 5254 function parseJsxClosingElement(inExpressionContext: boolean): JsxClosingElement { 5255 const pos = getNodePos(); 5256 parseExpected(SyntaxKind.LessThanSlashToken); 5257 const tagName = parseJsxElementName(); 5258 if (inExpressionContext) { 5259 parseExpected(SyntaxKind.GreaterThanToken); 5260 } 5261 else { 5262 parseExpected(SyntaxKind.GreaterThanToken, /*diagnostic*/ undefined, /*shouldAdvance*/ false); 5263 scanJsxText(); 5264 } 5265 return finishNode(factory.createJsxClosingElement(tagName), pos); 5266 } 5267 5268 function parseJsxClosingFragment(inExpressionContext: boolean): JsxClosingFragment { 5269 const pos = getNodePos(); 5270 parseExpected(SyntaxKind.LessThanSlashToken); 5271 if (tokenIsIdentifierOrKeyword(token())) { 5272 parseErrorAtRange(parseJsxElementName(), Diagnostics.Expected_corresponding_closing_tag_for_JSX_fragment); 5273 } 5274 if (inExpressionContext) { 5275 parseExpected(SyntaxKind.GreaterThanToken); 5276 } 5277 else { 5278 parseExpected(SyntaxKind.GreaterThanToken, /*diagnostic*/ undefined, /*shouldAdvance*/ false); 5279 scanJsxText(); 5280 } 5281 return finishNode(factory.createJsxJsxClosingFragment(), pos); 5282 } 5283 5284 function parseTypeAssertion(): TypeAssertion { 5285 const pos = getNodePos(); 5286 parseExpected(SyntaxKind.LessThanToken); 5287 const type = parseType(); 5288 parseExpected(SyntaxKind.GreaterThanToken); 5289 const expression = parseSimpleUnaryExpression(); 5290 return finishNode(factory.createTypeAssertion(type, expression), pos); 5291 } 5292 5293 function nextTokenIsIdentifierOrKeywordOrOpenBracketOrTemplate() { 5294 nextToken(); 5295 return tokenIsIdentifierOrKeyword(token()) 5296 || token() === SyntaxKind.OpenBracketToken 5297 || isTemplateStartOfTaggedTemplate(); 5298 } 5299 5300 function isStartOfOptionalPropertyOrElementAccessChain() { 5301 return token() === SyntaxKind.QuestionDotToken 5302 && lookAhead(nextTokenIsIdentifierOrKeywordOrOpenBracketOrTemplate); 5303 } 5304 5305 function tryReparseOptionalChain(node: Expression) { 5306 if (node.flags & NodeFlags.OptionalChain) { 5307 return true; 5308 } 5309 // check for an optional chain in a non-null expression 5310 if (isNonNullExpression(node)) { 5311 let expr = node.expression; 5312 while (isNonNullExpression(expr) && !(expr.flags & NodeFlags.OptionalChain)) { 5313 expr = expr.expression; 5314 } 5315 if (expr.flags & NodeFlags.OptionalChain) { 5316 // this is part of an optional chain. Walk down from `node` to `expression` and set the flag. 5317 while (isNonNullExpression(node)) { 5318 (node as Mutable<NonNullExpression>).flags |= NodeFlags.OptionalChain; 5319 node = node.expression; 5320 } 5321 return true; 5322 } 5323 } 5324 return false; 5325 } 5326 5327 function parsePropertyAccessExpressionRest(pos: number, expression: LeftHandSideExpression, questionDotToken: QuestionDotToken | undefined) { 5328 const name = parseRightSideOfDot(/*allowIdentifierNames*/ true, /*allowPrivateIdentifiers*/ true); 5329 const isOptionalChain = questionDotToken || tryReparseOptionalChain(expression); 5330 const propertyAccess = isOptionalChain ? 5331 factory.createPropertyAccessChain(expression, questionDotToken, name) : 5332 factory.createPropertyAccessExpression(expression, name); 5333 if (isOptionalChain && isPrivateIdentifier(propertyAccess.name)) { 5334 parseErrorAtRange(propertyAccess.name, Diagnostics.An_optional_chain_cannot_contain_private_identifiers); 5335 } 5336 return finishNode(propertyAccess, pos); 5337 } 5338 5339 function parseElementAccessExpressionRest(pos: number, expression: LeftHandSideExpression, questionDotToken: QuestionDotToken | undefined) { 5340 let argumentExpression: Expression; 5341 if (token() === SyntaxKind.CloseBracketToken) { 5342 argumentExpression = createMissingNode(SyntaxKind.Identifier, /*reportAtCurrentPosition*/ true, Diagnostics.An_element_access_expression_should_take_an_argument); 5343 } 5344 else { 5345 const argument = allowInAnd(parseExpression); 5346 if (isStringOrNumericLiteralLike(argument)) { 5347 argument.text = internIdentifier(argument.text); 5348 } 5349 argumentExpression = argument; 5350 } 5351 5352 parseExpected(SyntaxKind.CloseBracketToken); 5353 5354 const indexedAccess = questionDotToken || tryReparseOptionalChain(expression) ? 5355 factory.createElementAccessChain(expression, questionDotToken, argumentExpression) : 5356 factory.createElementAccessExpression(expression, argumentExpression); 5357 return finishNode(indexedAccess, pos); 5358 } 5359 5360 function parseMemberExpressionRest(pos: number, expression: LeftHandSideExpression, allowOptionalChain: boolean): MemberExpression { 5361 while (true) { 5362 let questionDotToken: QuestionDotToken | undefined; 5363 let isPropertyAccess = false; 5364 if (allowOptionalChain && isStartOfOptionalPropertyOrElementAccessChain()) { 5365 questionDotToken = parseExpectedToken(SyntaxKind.QuestionDotToken); 5366 isPropertyAccess = tokenIsIdentifierOrKeyword(token()); 5367 } 5368 else { 5369 isPropertyAccess = parseOptional(SyntaxKind.DotToken); 5370 } 5371 5372 if (isPropertyAccess) { 5373 expression = parsePropertyAccessExpressionRest(pos, expression, questionDotToken); 5374 continue; 5375 } 5376 5377 if (!questionDotToken && token() === SyntaxKind.ExclamationToken && !scanner.hasPrecedingLineBreak()) { 5378 nextToken(); 5379 expression = finishNode(factory.createNonNullExpression(expression), pos); 5380 continue; 5381 } 5382 5383 // when in the [Decorator] context, we do not parse ElementAccess as it could be part of a ComputedPropertyName 5384 if ((questionDotToken || !inDecoratorContext()) && parseOptional(SyntaxKind.OpenBracketToken)) { 5385 expression = parseElementAccessExpressionRest(pos, expression, questionDotToken); 5386 continue; 5387 } 5388 5389 if (isTemplateStartOfTaggedTemplate()) { 5390 expression = parseTaggedTemplateRest(pos, expression, questionDotToken, /*typeArguments*/ undefined); 5391 continue; 5392 } 5393 5394 return <MemberExpression>expression; 5395 } 5396 } 5397 5398 function isTemplateStartOfTaggedTemplate() { 5399 return token() === SyntaxKind.NoSubstitutionTemplateLiteral || token() === SyntaxKind.TemplateHead; 5400 } 5401 5402 function parseTaggedTemplateRest(pos: number, tag: LeftHandSideExpression, questionDotToken: QuestionDotToken | undefined, typeArguments: NodeArray<TypeNode> | undefined) { 5403 const tagExpression = factory.createTaggedTemplateExpression( 5404 tag, 5405 typeArguments, 5406 token() === SyntaxKind.NoSubstitutionTemplateLiteral ? 5407 (reScanTemplateHeadOrNoSubstitutionTemplate(), parseLiteralNode() as NoSubstitutionTemplateLiteral) : 5408 parseTemplateExpression(/*isTaggedTemplate*/ true) 5409 ); 5410 if (questionDotToken || tag.flags & NodeFlags.OptionalChain) { 5411 (tagExpression as Mutable<Node>).flags |= NodeFlags.OptionalChain; 5412 } 5413 tagExpression.questionDotToken = questionDotToken; 5414 return finishNode(tagExpression, pos); 5415 } 5416 5417 function parseCallExpressionRest(pos: number, expression: LeftHandSideExpression): LeftHandSideExpression { 5418 let currentNodeName: string | undefined; 5419 while (true) { 5420 expression = parseMemberExpressionRest(pos, expression, /*allowOptionalChain*/ true); 5421 const questionDotToken = parseOptionalToken(SyntaxKind.QuestionDotToken); 5422 // handle 'foo<<T>()' 5423 // parse template arguments only in TypeScript files (not in JavaScript files). 5424 if ((contextFlags & NodeFlags.JavaScriptFile) === 0 && (token() === SyntaxKind.LessThanToken || token() === SyntaxKind.LessThanLessThanToken)) { 5425 // See if this is the start of a generic invocation. If so, consume it and 5426 // keep checking for postfix expressions. Otherwise, it's just a '<' that's 5427 // part of an arithmetic expression. Break out so we consume it higher in the 5428 // stack. 5429 const typeArguments = tryParse(parseTypeArgumentsInExpression); 5430 if (typeArguments) { 5431 if (isTemplateStartOfTaggedTemplate()) { 5432 expression = parseTaggedTemplateRest(pos, expression, questionDotToken, typeArguments); 5433 continue; 5434 } 5435 5436 const argumentList = parseArgumentList(); 5437 const callExpr = questionDotToken || tryReparseOptionalChain(expression) ? 5438 factory.createCallChain(expression, questionDotToken, typeArguments, argumentList) : 5439 factory.createCallExpression(expression, typeArguments, argumentList); 5440 expression = finishNode(callExpr, pos); 5441 continue; 5442 } 5443 } 5444 else if (token() === SyntaxKind.OpenParenToken) { 5445 let typeArguments: NodeArray<TypeNode> | undefined; 5446 if ((isValidVirtualTypeArgumentsContext() || inBuilderContext()) && isPropertyAccessExpression(expression)) { 5447 const rootNode = getRootEtsComponent(expression); 5448 if (rootNode) { 5449 const rootNodeName = (<Identifier>(rootNode.expression)).escapedText.toString(); 5450 currentNodeName = getTextOfPropertyName(expression.name).toString(); 5451 if (currentNodeName === sourceFileCompilerOptions?.ets?.styles?.property) { 5452 setEtsStateStylesContext(true); 5453 stateStylesRootNode = rootNodeName; 5454 } 5455 else { 5456 setEtsStateStylesContext(false); 5457 stateStylesRootNode = undefined; 5458 } 5459 typeArguments = parseEtsTypeArguments(pos, `${rootNodeName}Attribute`); 5460 } 5461 else if (inEtsStateStylesContext() && stateStylesRootNode) { 5462 typeArguments = parseEtsTypeArguments(pos, `${stateStylesRootNode}Attribute`); 5463 } 5464 } 5465 const argumentList = parseArgumentList(); 5466 const callExpr = questionDotToken || tryReparseOptionalChain(expression) ? 5467 factory.createCallChain(expression, questionDotToken, typeArguments, argumentList) : 5468 factory.createCallExpression(expression, typeArguments, argumentList); 5469 expression = finishNode(callExpr, pos); 5470 continue; 5471 } 5472 if (questionDotToken) { 5473 // We failed to parse anything, so report a missing identifier here. 5474 const name = createMissingNode<Identifier>(SyntaxKind.Identifier, /*reportAtCurrentPosition*/ false, Diagnostics.Identifier_expected); 5475 expression = finishNode(factory.createPropertyAccessChain(expression, questionDotToken, name), pos); 5476 } 5477 break; 5478 } 5479 if (currentNodeName === sourceFileCompilerOptions?.ets?.styles?.property) { 5480 setEtsStateStylesContext(false); 5481 stateStylesRootNode = undefined; 5482 } 5483 return expression; 5484 } 5485 5486 function isValidVirtualTypeArgumentsContext(): boolean { 5487 return (inBuildContext() || inBuilderContext()) && inStructContext(); 5488 } 5489 5490 function parseArgumentList() { 5491 parseExpected(SyntaxKind.OpenParenToken); 5492 const result = parseDelimitedList(ParsingContext.ArgumentExpressions, parseArgumentExpression); 5493 parseExpected(SyntaxKind.CloseParenToken); 5494 return result; 5495 } 5496 5497 function parseTypeArgumentsInExpression() { 5498 if ((contextFlags & NodeFlags.JavaScriptFile) !== 0) { 5499 // TypeArguments must not be parsed in JavaScript files to avoid ambiguity with binary operators. 5500 return undefined; 5501 } 5502 5503 if (reScanLessThanToken() !== SyntaxKind.LessThanToken) { 5504 return undefined; 5505 } 5506 nextToken(); 5507 5508 const typeArguments = parseDelimitedList(ParsingContext.TypeArguments, parseType); 5509 if (!parseExpected(SyntaxKind.GreaterThanToken)) { 5510 // If it doesn't have the closing `>` then it's definitely not an type argument list. 5511 return undefined; 5512 } 5513 5514 // If we have a '<', then only parse this as a argument list if the type arguments 5515 // are complete and we have an open paren. if we don't, rewind and return nothing. 5516 return typeArguments && canFollowTypeArgumentsInExpression() 5517 ? typeArguments 5518 : undefined; 5519 } 5520 5521 function canFollowTypeArgumentsInExpression(): boolean { 5522 switch (token()) { 5523 case SyntaxKind.OpenParenToken: // foo<x>( 5524 case SyntaxKind.NoSubstitutionTemplateLiteral: // foo<T> `...` 5525 case SyntaxKind.TemplateHead: // foo<T> `...${100}...` 5526 // these are the only tokens can legally follow a type argument 5527 // list. So we definitely want to treat them as type arg lists. 5528 // falls through 5529 case SyntaxKind.DotToken: // foo<x>. 5530 case SyntaxKind.CloseParenToken: // foo<x>) 5531 case SyntaxKind.CloseBracketToken: // foo<x>] 5532 case SyntaxKind.ColonToken: // foo<x>: 5533 case SyntaxKind.SemicolonToken: // foo<x>; 5534 case SyntaxKind.QuestionToken: // foo<x>? 5535 case SyntaxKind.EqualsEqualsToken: // foo<x> == 5536 case SyntaxKind.EqualsEqualsEqualsToken: // foo<x> === 5537 case SyntaxKind.ExclamationEqualsToken: // foo<x> != 5538 case SyntaxKind.ExclamationEqualsEqualsToken: // foo<x> !== 5539 case SyntaxKind.AmpersandAmpersandToken: // foo<x> && 5540 case SyntaxKind.BarBarToken: // foo<x> || 5541 case SyntaxKind.QuestionQuestionToken: // foo<x> ?? 5542 case SyntaxKind.CaretToken: // foo<x> ^ 5543 case SyntaxKind.AmpersandToken: // foo<x> & 5544 case SyntaxKind.BarToken: // foo<x> | 5545 case SyntaxKind.CloseBraceToken: // foo<x> } 5546 case SyntaxKind.EndOfFileToken: // foo<x> 5547 // these cases can't legally follow a type arg list. However, they're not legal 5548 // expressions either. The user is probably in the middle of a generic type. So 5549 // treat it as such. 5550 return true; 5551 5552 case SyntaxKind.CommaToken: // foo<x>, 5553 case SyntaxKind.OpenBraceToken: // foo<x> { 5554 // We don't want to treat these as type arguments. Otherwise we'll parse this 5555 // as an invocation expression. Instead, we want to parse out the expression 5556 // in isolation from the type arguments. 5557 // falls through 5558 default: 5559 // Anything else treat as an expression. 5560 return false; 5561 } 5562 } 5563 5564 function isCurrentTokenAnEtsComponentExpression(): boolean { 5565 if (!inEtsComponentsContext()) { 5566 return false; 5567 } 5568 const components = sourceFileCompilerOptions.ets?.components ?? []; 5569 return components.includes(scanner.getTokenText()); 5570 } 5571 5572 function parsePrimaryExpression(): PrimaryExpression { 5573 switch (token()) { 5574 case SyntaxKind.NumericLiteral: 5575 case SyntaxKind.BigIntLiteral: 5576 case SyntaxKind.StringLiteral: 5577 case SyntaxKind.NoSubstitutionTemplateLiteral: 5578 return parseLiteralNode(); 5579 case SyntaxKind.ThisKeyword: 5580 case SyntaxKind.SuperKeyword: 5581 case SyntaxKind.NullKeyword: 5582 case SyntaxKind.TrueKeyword: 5583 case SyntaxKind.FalseKeyword: 5584 return parseTokenNode<PrimaryExpression>(); 5585 case SyntaxKind.OpenParenToken: 5586 return parseParenthesizedExpression(); 5587 case SyntaxKind.OpenBracketToken: 5588 return parseArrayLiteralExpression(); 5589 case SyntaxKind.OpenBraceToken: 5590 return parseObjectLiteralExpression(); 5591 case SyntaxKind.AsyncKeyword: 5592 // Async arrow functions are parsed earlier in parseAssignmentExpressionOrHigher. 5593 // If we encounter `async [no LineTerminator here] function` then this is an async 5594 // function; otherwise, its an identifier. 5595 if (!lookAhead(nextTokenIsFunctionKeywordOnSameLine)) { 5596 break; 5597 } 5598 5599 return parseFunctionExpression(); 5600 case SyntaxKind.ClassKeyword: 5601 return parseClassExpression(); 5602 case SyntaxKind.FunctionKeyword: 5603 return parseFunctionExpression(); 5604 case SyntaxKind.NewKeyword: 5605 return parseNewExpressionOrNewDotTarget(); 5606 case SyntaxKind.SlashToken: 5607 case SyntaxKind.SlashEqualsToken: 5608 if (reScanSlashToken() === SyntaxKind.RegularExpressionLiteral) { 5609 return parseLiteralNode(); 5610 } 5611 break; 5612 case SyntaxKind.TemplateHead: 5613 return parseTemplateExpression(/* isTaggedTemplate */ false); 5614 } 5615 5616 if(isCurrentTokenAnEtsComponentExpression() && !inEtsNewExpressionContext()){ 5617 return parseEtsComponentExpression(); 5618 } 5619 5620 return parseIdentifier(Diagnostics.Expression_expected); 5621 } 5622 5623 function parseParenthesizedExpression(): ParenthesizedExpression { 5624 const pos = getNodePos(); 5625 const hasJSDoc = hasPrecedingJSDocComment(); 5626 parseExpected(SyntaxKind.OpenParenToken); 5627 const expression = allowInAnd(parseExpression); 5628 parseExpected(SyntaxKind.CloseParenToken); 5629 return withJSDoc(finishNode(factory.createParenthesizedExpression(expression), pos), hasJSDoc); 5630 } 5631 5632 function parseSpreadElement(): Expression { 5633 const pos = getNodePos(); 5634 parseExpected(SyntaxKind.DotDotDotToken); 5635 const expression = parseAssignmentExpressionOrHigher(); 5636 return finishNode(factory.createSpreadElement(expression), pos); 5637 } 5638 5639 function parseArgumentOrArrayLiteralElement(): Expression { 5640 return token() === SyntaxKind.DotDotDotToken ? parseSpreadElement() : 5641 token() === SyntaxKind.CommaToken ? finishNode(factory.createOmittedExpression(), getNodePos()) : 5642 parseAssignmentExpressionOrHigher(); 5643 } 5644 5645 function parseArgumentExpression(): Expression { 5646 return doOutsideOfContext(disallowInAndDecoratorContext, parseArgumentOrArrayLiteralElement); 5647 } 5648 5649 function parseArrayLiteralExpression(): ArrayLiteralExpression { 5650 const pos = getNodePos(); 5651 parseExpected(SyntaxKind.OpenBracketToken); 5652 const multiLine = scanner.hasPrecedingLineBreak(); 5653 const elements = parseDelimitedList(ParsingContext.ArrayLiteralMembers, parseArgumentOrArrayLiteralElement); 5654 parseExpected(SyntaxKind.CloseBracketToken); 5655 return finishNode(factory.createArrayLiteralExpression(elements, multiLine), pos); 5656 } 5657 5658 function parseObjectLiteralElement(): ObjectLiteralElementLike { 5659 const pos = getNodePos(); 5660 const hasJSDoc = hasPrecedingJSDocComment(); 5661 5662 if (parseOptionalToken(SyntaxKind.DotDotDotToken)) { 5663 const expression = parseAssignmentExpressionOrHigher(); 5664 return withJSDoc(finishNode(factory.createSpreadAssignment(expression), pos), hasJSDoc); 5665 } 5666 5667 const decorators = parseDecorators(); 5668 const modifiers = parseModifiers(); 5669 5670 if (parseContextualModifier(SyntaxKind.GetKeyword)) { 5671 return parseAccessorDeclaration(pos, hasJSDoc, decorators, modifiers, SyntaxKind.GetAccessor); 5672 } 5673 if (parseContextualModifier(SyntaxKind.SetKeyword)) { 5674 return parseAccessorDeclaration(pos, hasJSDoc, decorators, modifiers, SyntaxKind.SetAccessor); 5675 } 5676 5677 const asteriskToken = parseOptionalToken(SyntaxKind.AsteriskToken); 5678 const tokenIsIdentifier = isIdentifier(); 5679 const name = parsePropertyName(); 5680 5681 // Disallowing of optional property assignments and definite assignment assertion happens in the grammar checker. 5682 const questionToken = parseOptionalToken(SyntaxKind.QuestionToken); 5683 const exclamationToken = parseOptionalToken(SyntaxKind.ExclamationToken); 5684 5685 if (asteriskToken || token() === SyntaxKind.OpenParenToken || token() === SyntaxKind.LessThanToken) { 5686 return parseMethodDeclaration(pos, hasJSDoc, decorators, modifiers, asteriskToken, name, questionToken, exclamationToken); 5687 } 5688 5689 // check if it is short-hand property assignment or normal property assignment 5690 // NOTE: if token is EqualsToken it is interpreted as CoverInitializedName production 5691 // CoverInitializedName[Yield] : 5692 // IdentifierReference[?Yield] Initializer[In, ?Yield] 5693 // this is necessary because ObjectLiteral productions are also used to cover grammar for ObjectAssignmentPattern 5694 let node: Mutable<ShorthandPropertyAssignment | PropertyAssignment>; 5695 const isShorthandPropertyAssignment = tokenIsIdentifier && (token() !== SyntaxKind.ColonToken); 5696 if (isShorthandPropertyAssignment) { 5697 const equalsToken = parseOptionalToken(SyntaxKind.EqualsToken); 5698 const objectAssignmentInitializer = equalsToken ? allowInAnd(parseAssignmentExpressionOrHigher) : undefined; 5699 node = factory.createShorthandPropertyAssignment(name as Identifier, objectAssignmentInitializer); 5700 // Save equals token for error reporting. 5701 // TODO(rbuckton): Consider manufacturing this when we need to report an error as it is otherwise not useful. 5702 node.equalsToken = equalsToken; 5703 } 5704 else { 5705 parseExpected(SyntaxKind.ColonToken); 5706 const initializer = allowInAnd(parseAssignmentExpressionOrHigher); 5707 node = factory.createPropertyAssignment(name, initializer); 5708 } 5709 // Decorators, Modifiers, questionToken, and exclamationToken are not supported by property assignments and are reported in the grammar checker 5710 node.decorators = decorators; 5711 node.modifiers = modifiers; 5712 node.questionToken = questionToken; 5713 node.exclamationToken = exclamationToken; 5714 return withJSDoc(finishNode(node, pos), hasJSDoc); 5715 } 5716 5717 function parseObjectLiteralExpression(): ObjectLiteralExpression { 5718 const pos = getNodePos(); 5719 const openBracePosition = scanner.getTokenPos(); 5720 parseExpected(SyntaxKind.OpenBraceToken); 5721 const multiLine = scanner.hasPrecedingLineBreak(); 5722 const properties = parseDelimitedList(ParsingContext.ObjectLiteralMembers, parseObjectLiteralElement, /*considerSemicolonAsDelimiter*/ true); 5723 if (!parseExpected(SyntaxKind.CloseBraceToken)) { 5724 const lastError = lastOrUndefined(parseDiagnostics); 5725 if (lastError && lastError.code === Diagnostics._0_expected.code) { 5726 addRelatedInfo( 5727 lastError, 5728 createDetachedDiagnostic(fileName, openBracePosition, 1, Diagnostics.The_parser_expected_to_find_a_to_match_the_token_here) 5729 ); 5730 } 5731 } 5732 return finishNode(factory.createObjectLiteralExpression(properties, multiLine), pos); 5733 } 5734 5735 function parseFunctionExpression(): FunctionExpression { 5736 // GeneratorExpression: 5737 // function* BindingIdentifier [Yield][opt](FormalParameters[Yield]){ GeneratorBody } 5738 // 5739 // FunctionExpression: 5740 // function BindingIdentifier[opt](FormalParameters){ FunctionBody } 5741 const saveDecoratorContext = inDecoratorContext(); 5742 if (saveDecoratorContext) { 5743 setDecoratorContext(/*val*/ false); 5744 } 5745 5746 const pos = getNodePos(); 5747 const hasJSDoc = hasPrecedingJSDocComment(); 5748 const modifiers = parseModifiers(); 5749 parseExpected(SyntaxKind.FunctionKeyword); 5750 const asteriskToken = parseOptionalToken(SyntaxKind.AsteriskToken); 5751 const isGenerator = asteriskToken ? SignatureFlags.Yield : SignatureFlags.None; 5752 const isAsync = some(modifiers, isAsyncModifier) ? SignatureFlags.Await : SignatureFlags.None; 5753 const name = 5754 isGenerator && isAsync ? doInYieldAndAwaitContext(parseOptionalBindingIdentifier) : 5755 isGenerator ? doInYieldContext(parseOptionalBindingIdentifier) : 5756 isAsync ? doInAwaitContext(parseOptionalBindingIdentifier) : 5757 parseOptionalBindingIdentifier(); 5758 5759 const typeParameters = parseTypeParameters(); 5760 const parameters = parseParameters(isGenerator | isAsync); 5761 const type = parseReturnType(SyntaxKind.ColonToken, /*isType*/ false); 5762 const body = parseFunctionBlock(isGenerator | isAsync); 5763 5764 if (saveDecoratorContext) { 5765 setDecoratorContext(/*val*/ true); 5766 } 5767 5768 const node = factory.createFunctionExpression(modifiers, asteriskToken, name, typeParameters, parameters, type, body); 5769 return withJSDoc(finishNode(node, pos), hasJSDoc); 5770 } 5771 5772 function parseEtsComponentExpression(): EtsComponentExpression { 5773 const pos = getNodePos(); 5774 const name = parseBindingIdentifier(); 5775 const argumentList = parseArgumentList(); 5776 const body = token() === SyntaxKind.OpenBraceToken ? parseFunctionBlock(SignatureFlags.None) : undefined; 5777 const node = factory.createEtsComponentExpression(name, argumentList, body); 5778 return finishNode(node, pos); 5779 } 5780 5781 function parseOptionalBindingIdentifier(): Identifier | undefined { 5782 return isBindingIdentifier() ? parseBindingIdentifier() : undefined; 5783 } 5784 5785 function parseNewExpressionOrNewDotTarget(): NewExpression | MetaProperty { 5786 setEtsNewExpressionContext(inEtsComponentsContext()); 5787 const pos = getNodePos(); 5788 parseExpected(SyntaxKind.NewKeyword); 5789 if (parseOptional(SyntaxKind.DotToken)) { 5790 const name = parseIdentifierName(); 5791 return finishNode(factory.createMetaProperty(SyntaxKind.NewKeyword, name), pos); 5792 } 5793 5794 const expressionPos = getNodePos(); 5795 let expression: MemberExpression = parsePrimaryExpression(); 5796 let typeArguments; 5797 while (true) { 5798 expression = parseMemberExpressionRest(expressionPos, expression, /*allowOptionalChain*/ false); 5799 typeArguments = tryParse(parseTypeArgumentsInExpression); 5800 if (isTemplateStartOfTaggedTemplate()) { 5801 Debug.assert(!!typeArguments, 5802 "Expected a type argument list; all plain tagged template starts should be consumed in 'parseMemberExpressionRest'"); 5803 expression = parseTaggedTemplateRest(expressionPos, expression, /*optionalChain*/ undefined, typeArguments); 5804 typeArguments = undefined; 5805 } 5806 break; 5807 } 5808 5809 let argumentsArray: NodeArray<Expression> | undefined; 5810 if (token() === SyntaxKind.OpenParenToken) { 5811 argumentsArray = parseArgumentList(); 5812 } 5813 else if (typeArguments) { 5814 parseErrorAt(pos, scanner.getStartPos(), Diagnostics.A_new_expression_with_type_arguments_must_always_be_followed_by_a_parenthesized_argument_list); 5815 } 5816 setEtsNewExpressionContext(false); 5817 return finishNode(factory.createNewExpression(expression, typeArguments, argumentsArray), pos); 5818 } 5819 5820 // STATEMENTS 5821 function parseBlock(ignoreMissingOpenBrace: boolean, diagnosticMessage?: DiagnosticMessage): Block { 5822 const pos = getNodePos(); 5823 const openBracePosition = scanner.getTokenPos(); 5824 if (parseExpected(SyntaxKind.OpenBraceToken, diagnosticMessage) || ignoreMissingOpenBrace) { 5825 const multiLine = scanner.hasPrecedingLineBreak(); 5826 const statements = parseList(ParsingContext.BlockStatements, parseStatement); 5827 if (!parseExpected(SyntaxKind.CloseBraceToken)) { 5828 const lastError = lastOrUndefined(parseDiagnostics); 5829 if (lastError && lastError.code === Diagnostics._0_expected.code) { 5830 addRelatedInfo( 5831 lastError, 5832 createDetachedDiagnostic(fileName, openBracePosition, 1, Diagnostics.The_parser_expected_to_find_a_to_match_the_token_here) 5833 ); 5834 } 5835 } 5836 return finishNode(factory.createBlock(statements, multiLine), pos); 5837 } 5838 else { 5839 const statements = createMissingList<Statement>(); 5840 return finishNode(factory.createBlock(statements, /*multiLine*/ undefined), pos); 5841 } 5842 } 5843 5844 function parseFunctionBlock(flags: SignatureFlags, diagnosticMessage?: DiagnosticMessage): Block { 5845 const savedYieldContext = inYieldContext(); 5846 setYieldContext(!!(flags & SignatureFlags.Yield)); 5847 5848 const savedAwaitContext = inAwaitContext(); 5849 setAwaitContext(!!(flags & SignatureFlags.Await)); 5850 5851 const savedTopLevel = topLevel; 5852 topLevel = false; 5853 5854 // We may be in a [Decorator] context when parsing a function expression or 5855 // arrow function. The body of the function is not in [Decorator] context. 5856 const saveDecoratorContext = inDecoratorContext(); 5857 if (saveDecoratorContext) { 5858 setDecoratorContext(/*val*/ false); 5859 } 5860 5861 const block = parseBlock(!!(flags & SignatureFlags.IgnoreMissingOpenBrace), diagnosticMessage); 5862 5863 if (saveDecoratorContext) { 5864 setDecoratorContext(/*val*/ true); 5865 } 5866 5867 topLevel = savedTopLevel; 5868 setYieldContext(savedYieldContext); 5869 setAwaitContext(savedAwaitContext); 5870 5871 return block; 5872 } 5873 5874 function parseEmptyStatement(): Statement { 5875 const pos = getNodePos(); 5876 parseExpected(SyntaxKind.SemicolonToken); 5877 return finishNode(factory.createEmptyStatement(), pos); 5878 } 5879 5880 function parseIfStatement(): IfStatement { 5881 const pos = getNodePos(); 5882 parseExpected(SyntaxKind.IfKeyword); 5883 parseExpected(SyntaxKind.OpenParenToken); 5884 const expression = allowInAnd(parseExpression); 5885 parseExpected(SyntaxKind.CloseParenToken); 5886 const thenStatement = parseStatement(); 5887 const elseStatement = parseOptional(SyntaxKind.ElseKeyword) ? parseStatement() : undefined; 5888 return finishNode(factory.createIfStatement(expression, thenStatement, elseStatement), pos); 5889 } 5890 5891 function parseDoStatement(): DoStatement { 5892 const pos = getNodePos(); 5893 parseExpected(SyntaxKind.DoKeyword); 5894 const statement = parseStatement(); 5895 parseExpected(SyntaxKind.WhileKeyword); 5896 parseExpected(SyntaxKind.OpenParenToken); 5897 const expression = allowInAnd(parseExpression); 5898 parseExpected(SyntaxKind.CloseParenToken); 5899 5900 // From: https://mail.mozilla.org/pipermail/es-discuss/2011-August/016188.html 5901 // 157 min --- All allen at wirfs-brock.com CONF --- "do{;}while(false)false" prohibited in 5902 // spec but allowed in consensus reality. Approved -- this is the de-facto standard whereby 5903 // do;while(0)x will have a semicolon inserted before x. 5904 parseOptional(SyntaxKind.SemicolonToken); 5905 return finishNode(factory.createDoStatement(statement, expression), pos); 5906 } 5907 5908 function parseWhileStatement(): WhileStatement { 5909 const pos = getNodePos(); 5910 parseExpected(SyntaxKind.WhileKeyword); 5911 parseExpected(SyntaxKind.OpenParenToken); 5912 const expression = allowInAnd(parseExpression); 5913 parseExpected(SyntaxKind.CloseParenToken); 5914 const statement = parseStatement(); 5915 return finishNode(factory.createWhileStatement(expression, statement), pos); 5916 } 5917 5918 function parseForOrForInOrForOfStatement(): Statement { 5919 const pos = getNodePos(); 5920 parseExpected(SyntaxKind.ForKeyword); 5921 const awaitToken = parseOptionalToken(SyntaxKind.AwaitKeyword); 5922 parseExpected(SyntaxKind.OpenParenToken); 5923 5924 let initializer!: VariableDeclarationList | Expression; 5925 if (token() !== SyntaxKind.SemicolonToken) { 5926 if (token() === SyntaxKind.VarKeyword || token() === SyntaxKind.LetKeyword || token() === SyntaxKind.ConstKeyword) { 5927 initializer = parseVariableDeclarationList(/*inForStatementInitializer*/ true); 5928 } 5929 else { 5930 initializer = disallowInAnd(parseExpression); 5931 } 5932 } 5933 5934 let node: IterationStatement; 5935 if (awaitToken ? parseExpected(SyntaxKind.OfKeyword) : parseOptional(SyntaxKind.OfKeyword)) { 5936 const expression = allowInAnd(parseAssignmentExpressionOrHigher); 5937 parseExpected(SyntaxKind.CloseParenToken); 5938 node = factory.createForOfStatement(awaitToken, initializer, expression, parseStatement()); 5939 } 5940 else if (parseOptional(SyntaxKind.InKeyword)) { 5941 const expression = allowInAnd(parseExpression); 5942 parseExpected(SyntaxKind.CloseParenToken); 5943 node = factory.createForInStatement(initializer, expression, parseStatement()); 5944 } 5945 else { 5946 parseExpected(SyntaxKind.SemicolonToken); 5947 const condition = token() !== SyntaxKind.SemicolonToken && token() !== SyntaxKind.CloseParenToken 5948 ? allowInAnd(parseExpression) 5949 : undefined; 5950 parseExpected(SyntaxKind.SemicolonToken); 5951 const incrementor = token() !== SyntaxKind.CloseParenToken 5952 ? allowInAnd(parseExpression) 5953 : undefined; 5954 parseExpected(SyntaxKind.CloseParenToken); 5955 node = factory.createForStatement(initializer, condition, incrementor, parseStatement()); 5956 } 5957 5958 return finishNode(node, pos); 5959 } 5960 5961 function parseBreakOrContinueStatement(kind: SyntaxKind): BreakOrContinueStatement { 5962 const pos = getNodePos(); 5963 5964 parseExpected(kind === SyntaxKind.BreakStatement ? SyntaxKind.BreakKeyword : SyntaxKind.ContinueKeyword); 5965 const label = canParseSemicolon() ? undefined : parseIdentifier(); 5966 5967 parseSemicolon(); 5968 const node = kind === SyntaxKind.BreakStatement 5969 ? factory.createBreakStatement(label) 5970 : factory.createContinueStatement(label); 5971 return finishNode(node, pos); 5972 } 5973 5974 function parseReturnStatement(): ReturnStatement { 5975 const pos = getNodePos(); 5976 parseExpected(SyntaxKind.ReturnKeyword); 5977 const expression = canParseSemicolon() ? undefined : allowInAnd(parseExpression); 5978 parseSemicolon(); 5979 return finishNode(factory.createReturnStatement(expression), pos); 5980 } 5981 5982 function parseWithStatement(): WithStatement { 5983 const pos = getNodePos(); 5984 parseExpected(SyntaxKind.WithKeyword); 5985 parseExpected(SyntaxKind.OpenParenToken); 5986 const expression = allowInAnd(parseExpression); 5987 parseExpected(SyntaxKind.CloseParenToken); 5988 const statement = doInsideOfContext(NodeFlags.InWithStatement, parseStatement); 5989 return finishNode(factory.createWithStatement(expression, statement), pos); 5990 } 5991 5992 function parseCaseClause(): CaseClause { 5993 const pos = getNodePos(); 5994 parseExpected(SyntaxKind.CaseKeyword); 5995 const expression = allowInAnd(parseExpression); 5996 parseExpected(SyntaxKind.ColonToken); 5997 const statements = parseList(ParsingContext.SwitchClauseStatements, parseStatement); 5998 return finishNode(factory.createCaseClause(expression, statements), pos); 5999 } 6000 6001 function parseDefaultClause(): DefaultClause { 6002 const pos = getNodePos(); 6003 parseExpected(SyntaxKind.DefaultKeyword); 6004 parseExpected(SyntaxKind.ColonToken); 6005 const statements = parseList(ParsingContext.SwitchClauseStatements, parseStatement); 6006 return finishNode(factory.createDefaultClause(statements), pos); 6007 } 6008 6009 function parseCaseOrDefaultClause(): CaseOrDefaultClause { 6010 return token() === SyntaxKind.CaseKeyword ? parseCaseClause() : parseDefaultClause(); 6011 } 6012 6013 function parseCaseBlock(): CaseBlock { 6014 const pos = getNodePos(); 6015 parseExpected(SyntaxKind.OpenBraceToken); 6016 const clauses = parseList(ParsingContext.SwitchClauses, parseCaseOrDefaultClause); 6017 parseExpected(SyntaxKind.CloseBraceToken); 6018 return finishNode(factory.createCaseBlock(clauses), pos); 6019 } 6020 6021 function parseSwitchStatement(): SwitchStatement { 6022 const pos = getNodePos(); 6023 parseExpected(SyntaxKind.SwitchKeyword); 6024 parseExpected(SyntaxKind.OpenParenToken); 6025 const expression = allowInAnd(parseExpression); 6026 parseExpected(SyntaxKind.CloseParenToken); 6027 const caseBlock = parseCaseBlock(); 6028 return finishNode(factory.createSwitchStatement(expression, caseBlock), pos); 6029 } 6030 6031 function parseThrowStatement(): ThrowStatement { 6032 // ThrowStatement[Yield] : 6033 // throw [no LineTerminator here]Expression[In, ?Yield]; 6034 6035 const pos = getNodePos(); 6036 parseExpected(SyntaxKind.ThrowKeyword); 6037 6038 // Because of automatic semicolon insertion, we need to report error if this 6039 // throw could be terminated with a semicolon. Note: we can't call 'parseExpression' 6040 // directly as that might consume an expression on the following line. 6041 // Instead, we create a "missing" identifier, but don't report an error. The actual error 6042 // will be reported in the grammar walker. 6043 let expression = scanner.hasPrecedingLineBreak() ? undefined : allowInAnd(parseExpression); 6044 if (expression === undefined) { 6045 identifierCount++; 6046 expression = finishNode(factory.createIdentifier(""), getNodePos()); 6047 } 6048 parseSemicolon(); 6049 return finishNode(factory.createThrowStatement(expression), pos); 6050 } 6051 6052 // TODO: Review for error recovery 6053 function parseTryStatement(): TryStatement { 6054 const pos = getNodePos(); 6055 6056 parseExpected(SyntaxKind.TryKeyword); 6057 const tryBlock = parseBlock(/*ignoreMissingOpenBrace*/ false); 6058 const catchClause = token() === SyntaxKind.CatchKeyword ? parseCatchClause() : undefined; 6059 6060 // If we don't have a catch clause, then we must have a finally clause. Try to parse 6061 // one out no matter what. 6062 let finallyBlock: Block | undefined; 6063 if (!catchClause || token() === SyntaxKind.FinallyKeyword) { 6064 parseExpected(SyntaxKind.FinallyKeyword); 6065 finallyBlock = parseBlock(/*ignoreMissingOpenBrace*/ false); 6066 } 6067 6068 return finishNode(factory.createTryStatement(tryBlock, catchClause, finallyBlock), pos); 6069 } 6070 6071 function parseCatchClause(): CatchClause { 6072 const pos = getNodePos(); 6073 parseExpected(SyntaxKind.CatchKeyword); 6074 6075 let variableDeclaration; 6076 if (parseOptional(SyntaxKind.OpenParenToken)) { 6077 variableDeclaration = parseVariableDeclaration(); 6078 parseExpected(SyntaxKind.CloseParenToken); 6079 } 6080 else { 6081 // Keep shape of node to avoid degrading performance. 6082 variableDeclaration = undefined; 6083 } 6084 6085 const block = parseBlock(/*ignoreMissingOpenBrace*/ false); 6086 return finishNode(factory.createCatchClause(variableDeclaration, block), pos); 6087 } 6088 6089 function parseDebuggerStatement(): Statement { 6090 const pos = getNodePos(); 6091 parseExpected(SyntaxKind.DebuggerKeyword); 6092 parseSemicolon(); 6093 return finishNode(factory.createDebuggerStatement(), pos); 6094 } 6095 6096 function parseExpressionOrLabeledStatement(): ExpressionStatement | LabeledStatement { 6097 // Avoiding having to do the lookahead for a labeled statement by just trying to parse 6098 // out an expression, seeing if it is identifier and then seeing if it is followed by 6099 // a colon. 6100 const pos = getNodePos(); 6101 let hasJSDoc = hasPrecedingJSDocComment(); 6102 let node: ExpressionStatement | LabeledStatement; 6103 const hasParen = token() === SyntaxKind.OpenParenToken; 6104 const expression = allowInAnd(parseExpression); 6105 if (ts.isIdentifier(expression) && parseOptional(SyntaxKind.ColonToken)) { 6106 node = factory.createLabeledStatement(expression, parseStatement()); 6107 } 6108 else { 6109 parseSemicolon(); 6110 node = factory.createExpressionStatement(expression); 6111 if (hasParen) { 6112 // do not parse the same jsdoc twice 6113 hasJSDoc = false; 6114 } 6115 } 6116 return withJSDoc(finishNode(node, pos), hasJSDoc); 6117 } 6118 6119 function nextTokenIsIdentifierOrKeywordOnSameLine() { 6120 nextToken(); 6121 return tokenIsIdentifierOrKeyword(token()) && !scanner.hasPrecedingLineBreak(); 6122 } 6123 6124 function nextTokenIsClassKeywordOnSameLine() { 6125 nextToken(); 6126 return token() === SyntaxKind.ClassKeyword && !scanner.hasPrecedingLineBreak(); 6127 } 6128 6129 function nextTokenIsFunctionKeywordOnSameLine() { 6130 nextToken(); 6131 return token() === SyntaxKind.FunctionKeyword && !scanner.hasPrecedingLineBreak(); 6132 } 6133 6134 function nextTokenIsIdentifierOrKeywordOrLiteralOnSameLine() { 6135 nextToken(); 6136 return (tokenIsIdentifierOrKeyword(token()) || token() === SyntaxKind.NumericLiteral || token() === SyntaxKind.BigIntLiteral || token() === SyntaxKind.StringLiteral) && !scanner.hasPrecedingLineBreak(); 6137 } 6138 6139 function isDeclaration(): boolean { 6140 while (true) { 6141 switch (token()) { 6142 case SyntaxKind.VarKeyword: 6143 case SyntaxKind.LetKeyword: 6144 case SyntaxKind.ConstKeyword: 6145 case SyntaxKind.FunctionKeyword: 6146 case SyntaxKind.ClassKeyword: 6147 case SyntaxKind.EnumKeyword: 6148 return true; 6149 case SyntaxKind.StructKeyword: 6150 return inEtsContext(); 6151 6152 6153 // 'declare', 'module', 'namespace', 'interface'* and 'type' are all legal JavaScript identifiers; 6154 // however, an identifier cannot be followed by another identifier on the same line. This is what we 6155 // count on to parse out the respective declarations. For instance, we exploit this to say that 6156 // 6157 // namespace n 6158 // 6159 // can be none other than the beginning of a namespace declaration, but need to respect that JavaScript sees 6160 // 6161 // namespace 6162 // n 6163 // 6164 // as the identifier 'namespace' on one line followed by the identifier 'n' on another. 6165 // We need to look one token ahead to see if it permissible to try parsing a declaration. 6166 // 6167 // *Note*: 'interface' is actually a strict mode reserved word. So while 6168 // 6169 // "use strict" 6170 // interface 6171 // I {} 6172 // 6173 // could be legal, it would add complexity for very little gain. 6174 case SyntaxKind.InterfaceKeyword: 6175 case SyntaxKind.TypeKeyword: 6176 return nextTokenIsIdentifierOnSameLine(); 6177 case SyntaxKind.ModuleKeyword: 6178 case SyntaxKind.NamespaceKeyword: 6179 return nextTokenIsIdentifierOrStringLiteralOnSameLine(); 6180 case SyntaxKind.AbstractKeyword: 6181 case SyntaxKind.AsyncKeyword: 6182 case SyntaxKind.DeclareKeyword: 6183 case SyntaxKind.PrivateKeyword: 6184 case SyntaxKind.ProtectedKeyword: 6185 case SyntaxKind.PublicKeyword: 6186 case SyntaxKind.ReadonlyKeyword: 6187 nextToken(); 6188 // ASI takes effect for this modifier. 6189 if (scanner.hasPrecedingLineBreak()) { 6190 return false; 6191 } 6192 continue; 6193 6194 case SyntaxKind.GlobalKeyword: 6195 nextToken(); 6196 return token() === SyntaxKind.OpenBraceToken || token() === SyntaxKind.Identifier || token() === SyntaxKind.ExportKeyword; 6197 6198 case SyntaxKind.ImportKeyword: 6199 nextToken(); 6200 return token() === SyntaxKind.StringLiteral || token() === SyntaxKind.AsteriskToken || 6201 token() === SyntaxKind.OpenBraceToken || tokenIsIdentifierOrKeyword(token()); 6202 case SyntaxKind.ExportKeyword: 6203 let currentToken = nextToken(); 6204 if (currentToken === SyntaxKind.TypeKeyword) { 6205 currentToken = lookAhead(nextToken); 6206 } 6207 if (currentToken === SyntaxKind.EqualsToken || currentToken === SyntaxKind.AsteriskToken || 6208 currentToken === SyntaxKind.OpenBraceToken || currentToken === SyntaxKind.DefaultKeyword || 6209 currentToken === SyntaxKind.AsKeyword) { 6210 return true; 6211 } 6212 continue; 6213 6214 case SyntaxKind.StaticKeyword: 6215 nextToken(); 6216 continue; 6217 default: 6218 return false; 6219 } 6220 } 6221 } 6222 6223 function isStartOfDeclaration(): boolean { 6224 return lookAhead(isDeclaration); 6225 } 6226 6227 function isStartOfStatement(): boolean { 6228 switch (token()) { 6229 case SyntaxKind.AtToken: 6230 case SyntaxKind.SemicolonToken: 6231 case SyntaxKind.OpenBraceToken: 6232 case SyntaxKind.VarKeyword: 6233 case SyntaxKind.LetKeyword: 6234 case SyntaxKind.FunctionKeyword: 6235 case SyntaxKind.ClassKeyword: 6236 case SyntaxKind.EnumKeyword: 6237 case SyntaxKind.IfKeyword: 6238 case SyntaxKind.DoKeyword: 6239 case SyntaxKind.WhileKeyword: 6240 case SyntaxKind.ForKeyword: 6241 case SyntaxKind.ContinueKeyword: 6242 case SyntaxKind.BreakKeyword: 6243 case SyntaxKind.ReturnKeyword: 6244 case SyntaxKind.WithKeyword: 6245 case SyntaxKind.SwitchKeyword: 6246 case SyntaxKind.ThrowKeyword: 6247 case SyntaxKind.TryKeyword: 6248 case SyntaxKind.DebuggerKeyword: 6249 // 'catch' and 'finally' do not actually indicate that the code is part of a statement, 6250 // however, we say they are here so that we may gracefully parse them and error later. 6251 // falls through 6252 case SyntaxKind.CatchKeyword: 6253 case SyntaxKind.FinallyKeyword: 6254 return true; 6255 case SyntaxKind.StructKeyword: 6256 return inEtsContext(); 6257 6258 case SyntaxKind.ImportKeyword: 6259 return isStartOfDeclaration() || lookAhead(nextTokenIsOpenParenOrLessThanOrDot); 6260 6261 case SyntaxKind.ConstKeyword: 6262 case SyntaxKind.ExportKeyword: 6263 return isStartOfDeclaration(); 6264 6265 case SyntaxKind.AsyncKeyword: 6266 case SyntaxKind.DeclareKeyword: 6267 case SyntaxKind.InterfaceKeyword: 6268 case SyntaxKind.ModuleKeyword: 6269 case SyntaxKind.NamespaceKeyword: 6270 case SyntaxKind.TypeKeyword: 6271 case SyntaxKind.GlobalKeyword: 6272 // When these don't start a declaration, they're an identifier in an expression statement 6273 return true; 6274 6275 case SyntaxKind.PublicKeyword: 6276 case SyntaxKind.PrivateKeyword: 6277 case SyntaxKind.ProtectedKeyword: 6278 case SyntaxKind.StaticKeyword: 6279 case SyntaxKind.ReadonlyKeyword: 6280 // When these don't start a declaration, they may be the start of a class member if an identifier 6281 // immediately follows. Otherwise they're an identifier in an expression statement. 6282 return isStartOfDeclaration() || !lookAhead(nextTokenIsIdentifierOrKeywordOnSameLine); 6283 6284 default: 6285 return isStartOfExpression(); 6286 } 6287 } 6288 6289 function nextTokenIsIdentifierOrStartOfDestructuring() { 6290 nextToken(); 6291 return isIdentifier() || token() === SyntaxKind.OpenBraceToken || token() === SyntaxKind.OpenBracketToken; 6292 } 6293 6294 function isLetDeclaration() { 6295 // In ES6 'let' always starts a lexical declaration if followed by an identifier or { 6296 // or [. 6297 return lookAhead(nextTokenIsIdentifierOrStartOfDestructuring); 6298 } 6299 6300 function parseStatement(): Statement { 6301 switch (token()) { 6302 case SyntaxKind.SemicolonToken: 6303 return parseEmptyStatement(); 6304 case SyntaxKind.OpenBraceToken: 6305 return parseBlock(/*ignoreMissingOpenBrace*/ false); 6306 case SyntaxKind.VarKeyword: 6307 return parseVariableStatement(getNodePos(), hasPrecedingJSDocComment(), /*decorators*/ undefined, /*modifiers*/ undefined); 6308 case SyntaxKind.LetKeyword: 6309 if (isLetDeclaration()) { 6310 return parseVariableStatement(getNodePos(), hasPrecedingJSDocComment(), /*decorators*/ undefined, /*modifiers*/ undefined); 6311 } 6312 break; 6313 case SyntaxKind.FunctionKeyword: 6314 return parseFunctionDeclaration(getNodePos(), hasPrecedingJSDocComment(), /*decorators*/ undefined, /*modifiers*/ undefined); 6315 case SyntaxKind.StructKeyword: 6316 if (inEtsContext()) { 6317 return parseStructDeclaration(getNodePos(), hasPrecedingJSDocComment(), /*decorators*/ undefined, /*modifiers*/ undefined); 6318 } 6319 break; 6320 case SyntaxKind.ClassKeyword: 6321 return parseClassDeclaration(getNodePos(), hasPrecedingJSDocComment(), /*decorators*/ undefined, /*modifiers*/ undefined); 6322 case SyntaxKind.IfKeyword: 6323 return parseIfStatement(); 6324 case SyntaxKind.DoKeyword: 6325 return parseDoStatement(); 6326 case SyntaxKind.WhileKeyword: 6327 return parseWhileStatement(); 6328 case SyntaxKind.ForKeyword: 6329 return parseForOrForInOrForOfStatement(); 6330 case SyntaxKind.ContinueKeyword: 6331 return parseBreakOrContinueStatement(SyntaxKind.ContinueStatement); 6332 case SyntaxKind.BreakKeyword: 6333 return parseBreakOrContinueStatement(SyntaxKind.BreakStatement); 6334 case SyntaxKind.ReturnKeyword: 6335 return parseReturnStatement(); 6336 case SyntaxKind.WithKeyword: 6337 return parseWithStatement(); 6338 case SyntaxKind.SwitchKeyword: 6339 return parseSwitchStatement(); 6340 case SyntaxKind.ThrowKeyword: 6341 return parseThrowStatement(); 6342 case SyntaxKind.TryKeyword: 6343 // Include 'catch' and 'finally' for error recovery. 6344 // falls through 6345 case SyntaxKind.CatchKeyword: 6346 case SyntaxKind.FinallyKeyword: 6347 return parseTryStatement(); 6348 case SyntaxKind.DebuggerKeyword: 6349 return parseDebuggerStatement(); 6350 case SyntaxKind.AtToken: 6351 return parseDeclaration(); 6352 case SyntaxKind.AsyncKeyword: 6353 case SyntaxKind.InterfaceKeyword: 6354 case SyntaxKind.TypeKeyword: 6355 case SyntaxKind.ModuleKeyword: 6356 case SyntaxKind.NamespaceKeyword: 6357 case SyntaxKind.DeclareKeyword: 6358 case SyntaxKind.ConstKeyword: 6359 case SyntaxKind.EnumKeyword: 6360 case SyntaxKind.ExportKeyword: 6361 case SyntaxKind.ImportKeyword: 6362 case SyntaxKind.PrivateKeyword: 6363 case SyntaxKind.ProtectedKeyword: 6364 case SyntaxKind.PublicKeyword: 6365 case SyntaxKind.AbstractKeyword: 6366 case SyntaxKind.StaticKeyword: 6367 case SyntaxKind.ReadonlyKeyword: 6368 case SyntaxKind.GlobalKeyword: 6369 if (isStartOfDeclaration()) { 6370 return parseDeclaration(); 6371 } 6372 break; 6373 } 6374 return parseExpressionOrLabeledStatement(); 6375 } 6376 6377 function isDeclareModifier(modifier: Modifier) { 6378 return modifier.kind === SyntaxKind.DeclareKeyword; 6379 } 6380 6381 function parseDeclaration(): Statement { 6382 // TODO: Can we hold onto the parsed decorators/modifiers and advance the scanner 6383 // if we can't reuse the declaration, so that we don't do this work twice? 6384 // 6385 // `parseListElement` attempted to get the reused node at this position, 6386 // but the ambient context flag was not yet set, so the node appeared 6387 // not reusable in that context. 6388 const isAmbient = some(lookAhead(() => (parseDecorators(), parseModifiers())), isDeclareModifier); 6389 if (isAmbient) { 6390 const node = tryReuseAmbientDeclaration(); 6391 if (node) { 6392 return node; 6393 } 6394 } 6395 6396 const pos = getNodePos(); 6397 const hasJSDoc = hasPrecedingJSDocComment(); 6398 const decorators = parseDecorators(); 6399 6400 if (token() === SyntaxKind.FunctionKeyword || token() === SyntaxKind.ExportKeyword) { 6401 if (hasEtsExtendDecoratorNames(decorators, sourceFileCompilerOptions)) { 6402 const extendEtsComponentDecoratorNames = getEtsExtendDecoratorComponentNames(decorators, sourceFileCompilerOptions); 6403 if (extendEtsComponentDecoratorNames.length > 0) { 6404 sourceFileCompilerOptions.ets?.extend.components.forEach(({ name, type, instance }) => { 6405 if (name === last(extendEtsComponentDecoratorNames)) { 6406 extendEtsComponentDeclaration = { name, type, instance }; 6407 } 6408 }); 6409 } 6410 setEtsExtendComponentsContext(!!extendEtsComponentDeclaration); 6411 } 6412 else if (hasEtsStylesDecoratorNames(decorators, sourceFileCompilerOptions)) { 6413 const stylesEtsComponentDecoratorNames = getEtsStylesDecoratorComponentNames(decorators, sourceFileCompilerOptions); 6414 if (stylesEtsComponentDecoratorNames.length > 0) { 6415 stylesEtsComponentDeclaration = sourceFileCompilerOptions.ets?.styles.component; 6416 } 6417 setEtsStylesComponentsContext(!!stylesEtsComponentDeclaration); 6418 } 6419 else { 6420 setEtsComponentsContext(isTokenInsideBuilder(decorators, sourceFileCompilerOptions)); 6421 } 6422 } 6423 6424 const modifiers = parseModifiers(); 6425 if (isAmbient) { 6426 for (const m of modifiers!) { 6427 (m as Mutable<Node>).flags |= NodeFlags.Ambient; 6428 } 6429 return doInsideOfContext(NodeFlags.Ambient, () => parseDeclarationWorker(pos, hasJSDoc, decorators, modifiers)); 6430 } 6431 else { 6432 return parseDeclarationWorker(pos, hasJSDoc, decorators, modifiers); 6433 } 6434 } 6435 6436 function tryReuseAmbientDeclaration(): Statement | undefined { 6437 return doInsideOfContext(NodeFlags.Ambient, () => { 6438 const node = currentNode(parsingContext); 6439 if (node) { 6440 return consumeNode(node) as Statement; 6441 } 6442 }); 6443 } 6444 6445 function parseDeclarationWorker(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined): Statement { 6446 switch (token()) { 6447 case SyntaxKind.VarKeyword: 6448 case SyntaxKind.LetKeyword: 6449 case SyntaxKind.ConstKeyword: 6450 return parseVariableStatement(pos, hasJSDoc, decorators, modifiers); 6451 case SyntaxKind.FunctionKeyword: 6452 return parseFunctionDeclaration(pos, hasJSDoc, decorators, modifiers); 6453 case SyntaxKind.ClassKeyword: 6454 return parseClassDeclaration(pos, hasJSDoc, decorators, modifiers); 6455 case SyntaxKind.StructKeyword: 6456 if (inEtsContext()) { 6457 return parseStructDeclaration(pos, hasJSDoc, decorators, modifiers); 6458 } 6459 return parseDeclarationDefault(pos,decorators, modifiers); 6460 case SyntaxKind.InterfaceKeyword: 6461 return parseInterfaceDeclaration(pos, hasJSDoc, decorators, modifiers); 6462 case SyntaxKind.TypeKeyword: 6463 return parseTypeAliasDeclaration(pos, hasJSDoc, decorators, modifiers); 6464 case SyntaxKind.EnumKeyword: 6465 return parseEnumDeclaration(pos, hasJSDoc, decorators, modifiers); 6466 case SyntaxKind.GlobalKeyword: 6467 case SyntaxKind.ModuleKeyword: 6468 case SyntaxKind.NamespaceKeyword: 6469 return parseModuleDeclaration(pos, hasJSDoc, decorators, modifiers); 6470 case SyntaxKind.ImportKeyword: 6471 return parseImportDeclarationOrImportEqualsDeclaration(pos, hasJSDoc, decorators, modifiers); 6472 case SyntaxKind.ExportKeyword: 6473 nextToken(); 6474 switch (token()) { 6475 case SyntaxKind.DefaultKeyword: 6476 case SyntaxKind.EqualsToken: 6477 return parseExportAssignment(pos, hasJSDoc, decorators, modifiers); 6478 case SyntaxKind.AsKeyword: 6479 return parseNamespaceExportDeclaration(pos, hasJSDoc, decorators, modifiers); 6480 default: 6481 return parseExportDeclaration(pos, hasJSDoc, decorators, modifiers); 6482 } 6483 default: 6484 return parseDeclarationDefault(pos,decorators, modifiers); 6485 } 6486 } 6487 6488 function parseDeclarationDefault(pos: number,decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined): Statement { 6489 if (decorators || modifiers) { 6490 // We reached this point because we encountered decorators and/or modifiers and assumed a declaration 6491 // would follow. For recovery and error reporting purposes, return an incomplete declaration. 6492 const missing = createMissingNode<MissingDeclaration>(SyntaxKind.MissingDeclaration, /*reportAtCurrentPosition*/ true, Diagnostics.Declaration_expected); 6493 setTextRangePos(missing, pos); 6494 missing.decorators = decorators; 6495 missing.modifiers = modifiers; 6496 return missing; 6497 } 6498 return undefined!; // TODO: GH#18217 6499 } 6500 6501 function nextTokenIsIdentifierOrStringLiteralOnSameLine() { 6502 nextToken(); 6503 return !scanner.hasPrecedingLineBreak() && (isIdentifier() || token() === SyntaxKind.StringLiteral); 6504 } 6505 6506 function parseFunctionBlockOrSemicolon(flags: SignatureFlags, diagnosticMessage?: DiagnosticMessage): Block | undefined { 6507 if (token() !== SyntaxKind.OpenBraceToken && canParseSemicolon()) { 6508 parseSemicolon(); 6509 return; 6510 } 6511 6512 return parseFunctionBlock(flags, diagnosticMessage); 6513 } 6514 6515 // DECLARATIONS 6516 6517 function parseArrayBindingElement(): ArrayBindingElement { 6518 const pos = getNodePos(); 6519 if (token() === SyntaxKind.CommaToken) { 6520 return finishNode(factory.createOmittedExpression(), pos); 6521 } 6522 const dotDotDotToken = parseOptionalToken(SyntaxKind.DotDotDotToken); 6523 const name = parseIdentifierOrPattern(); 6524 const initializer = parseInitializer(); 6525 return finishNode(factory.createBindingElement(dotDotDotToken, /*propertyName*/ undefined, name, initializer), pos); 6526 } 6527 6528 function parseObjectBindingElement(): BindingElement { 6529 const pos = getNodePos(); 6530 const dotDotDotToken = parseOptionalToken(SyntaxKind.DotDotDotToken); 6531 const tokenIsIdentifier = isBindingIdentifier(); 6532 let propertyName: PropertyName | undefined = parsePropertyName(); 6533 let name: BindingName; 6534 if (tokenIsIdentifier && token() !== SyntaxKind.ColonToken) { 6535 name = <Identifier>propertyName; 6536 propertyName = undefined; 6537 } 6538 else { 6539 parseExpected(SyntaxKind.ColonToken); 6540 name = parseIdentifierOrPattern(); 6541 } 6542 const initializer = parseInitializer(); 6543 return finishNode(factory.createBindingElement(dotDotDotToken, propertyName, name, initializer), pos); 6544 } 6545 6546 function parseObjectBindingPattern(): ObjectBindingPattern { 6547 const pos = getNodePos(); 6548 parseExpected(SyntaxKind.OpenBraceToken); 6549 const elements = parseDelimitedList(ParsingContext.ObjectBindingElements, parseObjectBindingElement); 6550 parseExpected(SyntaxKind.CloseBraceToken); 6551 return finishNode(factory.createObjectBindingPattern(elements), pos); 6552 } 6553 6554 function parseArrayBindingPattern(): ArrayBindingPattern { 6555 const pos = getNodePos(); 6556 parseExpected(SyntaxKind.OpenBracketToken); 6557 const elements = parseDelimitedList(ParsingContext.ArrayBindingElements, parseArrayBindingElement); 6558 parseExpected(SyntaxKind.CloseBracketToken); 6559 return finishNode(factory.createArrayBindingPattern(elements), pos); 6560 } 6561 6562 function isBindingIdentifierOrPrivateIdentifierOrPattern() { 6563 return token() === SyntaxKind.OpenBraceToken 6564 || token() === SyntaxKind.OpenBracketToken 6565 || token() === SyntaxKind.PrivateIdentifier 6566 || isBindingIdentifier(); 6567 } 6568 6569 function parseIdentifierOrPattern(privateIdentifierDiagnosticMessage?: DiagnosticMessage): Identifier | BindingPattern { 6570 if (token() === SyntaxKind.OpenBracketToken) { 6571 return parseArrayBindingPattern(); 6572 } 6573 if (token() === SyntaxKind.OpenBraceToken) { 6574 return parseObjectBindingPattern(); 6575 } 6576 return parseBindingIdentifier(privateIdentifierDiagnosticMessage); 6577 } 6578 6579 function parseVariableDeclarationAllowExclamation() { 6580 return parseVariableDeclaration(/*allowExclamation*/ true); 6581 } 6582 6583 function parseVariableDeclaration(allowExclamation?: boolean): VariableDeclaration { 6584 const pos = getNodePos(); 6585 const name = parseIdentifierOrPattern(Diagnostics.Private_identifiers_are_not_allowed_in_variable_declarations); 6586 let exclamationToken: ExclamationToken | undefined; 6587 if (allowExclamation && name.kind === SyntaxKind.Identifier && 6588 token() === SyntaxKind.ExclamationToken && !scanner.hasPrecedingLineBreak()) { 6589 exclamationToken = parseTokenNode<Token<SyntaxKind.ExclamationToken>>(); 6590 } 6591 const type = parseTypeAnnotation(); 6592 const initializer = isInOrOfKeyword(token()) ? undefined : parseInitializer(); 6593 const node = factory.createVariableDeclaration(name, exclamationToken, type, initializer); 6594 return finishNode(node, pos); 6595 } 6596 6597 function parseVariableDeclarationList(inForStatementInitializer: boolean): VariableDeclarationList { 6598 const pos = getNodePos(); 6599 6600 let flags: NodeFlags = 0; 6601 switch (token()) { 6602 case SyntaxKind.VarKeyword: 6603 break; 6604 case SyntaxKind.LetKeyword: 6605 flags |= NodeFlags.Let; 6606 break; 6607 case SyntaxKind.ConstKeyword: 6608 flags |= NodeFlags.Const; 6609 break; 6610 default: 6611 Debug.fail(); 6612 } 6613 6614 nextToken(); 6615 6616 // The user may have written the following: 6617 // 6618 // for (let of X) { } 6619 // 6620 // In this case, we want to parse an empty declaration list, and then parse 'of' 6621 // as a keyword. The reason this is not automatic is that 'of' is a valid identifier. 6622 // So we need to look ahead to determine if 'of' should be treated as a keyword in 6623 // this context. 6624 // The checker will then give an error that there is an empty declaration list. 6625 let declarations; 6626 if (token() === SyntaxKind.OfKeyword && lookAhead(canFollowContextualOfKeyword)) { 6627 declarations = createMissingList<VariableDeclaration>(); 6628 } 6629 else { 6630 const savedDisallowIn = inDisallowInContext(); 6631 setDisallowInContext(inForStatementInitializer); 6632 6633 declarations = parseDelimitedList(ParsingContext.VariableDeclarations, 6634 inForStatementInitializer ? parseVariableDeclaration : parseVariableDeclarationAllowExclamation); 6635 6636 setDisallowInContext(savedDisallowIn); 6637 } 6638 6639 return finishNode(factory.createVariableDeclarationList(declarations, flags), pos); 6640 } 6641 6642 function canFollowContextualOfKeyword(): boolean { 6643 return nextTokenIsIdentifier() && nextToken() === SyntaxKind.CloseParenToken; 6644 } 6645 6646 function parseVariableStatement(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined): VariableStatement { 6647 const declarationList = parseVariableDeclarationList(/*inForStatementInitializer*/ false); 6648 parseSemicolon(); 6649 const node = factory.createVariableStatement(modifiers, declarationList); 6650 // Decorators are not allowed on a variable statement, so we keep track of them to report them in the grammar checker. 6651 node.decorators = decorators; 6652 return withJSDoc(finishNode(node, pos), hasJSDoc); 6653 } 6654 6655 function parseFunctionDeclaration(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined): FunctionDeclaration { 6656 const savedAwaitContext = inAwaitContext(); 6657 const modifierFlags = modifiersToFlags(modifiers); 6658 parseExpected(SyntaxKind.FunctionKeyword); 6659 const asteriskToken = parseOptionalToken(SyntaxKind.AsteriskToken); 6660 // We don't parse the name here in await context, instead we will report a grammar error in the checker. 6661 const name = modifierFlags & ModifierFlags.Default ? parseOptionalBindingIdentifier() : parseBindingIdentifier(); 6662 if(name && hasEtsStylesDecoratorNames(decorators, sourceFileCompilerOptions)) { 6663 fileStylesComponents.set(name.escapedText.toString(), SyntaxKind.FunctionDeclaration); 6664 } 6665 setEtsBuilderContext(hasEtsBuilderDecoratorNames(decorators, sourceFileCompilerOptions)); 6666 const isGenerator = asteriskToken ? SignatureFlags.Yield : SignatureFlags.None; 6667 const isAsync = modifierFlags & ModifierFlags.Async ? SignatureFlags.Await : SignatureFlags.None; 6668 const typeParameters = inEtsStylesComponentsContext() && stylesEtsComponentDeclaration ? parseEtsTypeParameters(scanner.getStartPos()) : parseTypeParameters(); 6669 if (modifierFlags & ModifierFlags.Export) setAwaitContext(/*value*/ true); 6670 const parameters = parseParameters(isGenerator | isAsync); 6671 const typeStartPos = scanner.getStartPos(); 6672 const type = getFunctionDeclarationReturnType(); 6673 const body = parseFunctionBlockOrSemicolon(isGenerator | isAsync, Diagnostics.or_expected); 6674 setEtsBuilderContext(false); 6675 setEtsExtendComponentsContext(false); 6676 extendEtsComponentDeclaration = undefined; 6677 setEtsStylesComponentsContext(false); 6678 stylesEtsComponentDeclaration = undefined; 6679 setEtsComponentsContext(inBuildContext()); 6680 setAwaitContext(savedAwaitContext); 6681 const node = factory.createFunctionDeclaration(decorators, modifiers, asteriskToken, name, typeParameters, parameters, type, body); 6682 return withJSDoc(finishNode(node, pos), hasJSDoc); 6683 6684 function getFunctionDeclarationReturnType(): TypeNode | undefined { 6685 let returnType = parseReturnType(SyntaxKind.ColonToken, /*isType*/ false); 6686 // If function decorated by Extend and type is not defined, then will use Ets Extend Components Type 6687 if (!returnType && extendEtsComponentDeclaration && inEtsExtendComponentsContext()) { 6688 returnType = finishVirtualNode(factory.createTypeReferenceNode( 6689 finishVirtualNode(factory.createIdentifier(extendEtsComponentDeclaration.type), typeStartPos, typeStartPos)), 6690 typeStartPos, typeStartPos); 6691 } 6692 // If function decorated by Styles and type is not defined, then will use Ets Styles Components Type 6693 if (!returnType && stylesEtsComponentDeclaration && inEtsStylesComponentsContext()) { 6694 returnType = finishVirtualNode(factory.createTypeReferenceNode( 6695 finishVirtualNode(factory.createIdentifier(stylesEtsComponentDeclaration.type), typeStartPos, typeStartPos)), 6696 typeStartPos, typeStartPos); 6697 } 6698 return returnType; 6699 } 6700 } 6701 6702 function parseConstructorName() { 6703 if (token() === SyntaxKind.ConstructorKeyword) { 6704 return parseExpected(SyntaxKind.ConstructorKeyword); 6705 } 6706 if (token() === SyntaxKind.StringLiteral && lookAhead(nextToken) === SyntaxKind.OpenParenToken) { 6707 return tryParse(() => { 6708 const literalNode = parseLiteralNode(); 6709 return literalNode.text === "constructor" ? literalNode : undefined; 6710 }); 6711 } 6712 } 6713 6714 function tryParseConstructorDeclaration(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined): ConstructorDeclaration | undefined { 6715 return tryParse(() => { 6716 if (parseConstructorName()) { 6717 const typeParameters = parseTypeParameters(); 6718 const parameters = parseParameters(SignatureFlags.None); 6719 const type = parseReturnType(SyntaxKind.ColonToken, /*isType*/ false); 6720 const body = parseFunctionBlockOrSemicolon(SignatureFlags.None, Diagnostics.or_expected); 6721 const node = factory.createConstructorDeclaration(decorators, modifiers, parameters, body); 6722 // Attach `typeParameters` and `type` if they exist so that we can report them in the grammar checker. 6723 node.typeParameters = typeParameters; 6724 node.type = type; 6725 return withJSDoc(finishNode(node, pos), hasJSDoc); 6726 } 6727 }); 6728 } 6729 6730 6731 function isTokenInsideStructBuild(methodName: PropertyName): boolean { 6732 const renderMethod = sourceFileCompilerOptions.ets?.render?.method?.find(render => render === "build") ?? "build"; 6733 6734 if (methodName.kind === SyntaxKind.Identifier && methodName.escapedText === renderMethod) { 6735 return true; 6736 } 6737 return false; 6738 } 6739 6740 function isTokenInsideStructBuilder(decorators: NodeArray<Decorator> | undefined): boolean { 6741 return isTokenInsideBuilder(decorators, sourceFileCompilerOptions); 6742 } 6743 6744 function isTokenInsideStructPageTransition(methodName: PropertyName): boolean { 6745 const renderMethod = sourceFileCompilerOptions.ets?.render?.method?.find(render => render === "pageTransition") ?? "pageTransition"; 6746 6747 if (methodName.kind === SyntaxKind.Identifier && methodName.escapedText === renderMethod) { 6748 return true; 6749 } 6750 return false; 6751 } 6752 6753 function parseMethodDeclaration( 6754 pos: number, 6755 hasJSDoc: boolean, 6756 decorators: NodeArray<Decorator> | undefined, 6757 modifiers: NodeArray<Modifier> | undefined, 6758 asteriskToken: AsteriskToken | undefined, 6759 name: PropertyName, 6760 questionToken: QuestionToken | undefined, 6761 exclamationToken: ExclamationToken | undefined, 6762 diagnosticMessage?: DiagnosticMessage 6763 ): MethodDeclaration { 6764 const methodName = getPropertyNameForPropertyNameNode(name)?.toString(); 6765 const orignalEtsBuildContext = inBuildContext(); 6766 const orignalEtsBuilderContext = inBuilderContext(); 6767 setEtsBuildContext(methodName === sourceFileCompilerOptions?.ets?.render?.method?.find(render => render === "build")); 6768 setEtsBuilderContext(hasEtsBuilderDecoratorNames(decorators, sourceFileCompilerOptions)); 6769 if (inStructContext() && hasEtsStylesDecoratorNames(decorators, sourceFileCompilerOptions)) { 6770 if (methodName && currentStructName) { 6771 structStylesComponents.set(methodName, { structName: currentStructName, kind: SyntaxKind.MethodDeclaration }); 6772 } 6773 const stylesEtsComponentDecoratorNames = getEtsStylesDecoratorComponentNames(decorators, sourceFileCompilerOptions); 6774 if (stylesEtsComponentDecoratorNames.length > 0) { 6775 stylesEtsComponentDeclaration = sourceFileCompilerOptions.ets?.styles.component; 6776 } 6777 setEtsStylesComponentsContext(!!stylesEtsComponentDeclaration); 6778 } 6779 const orignalEtsComponentsContext: boolean = inEtsComponentsContext(); 6780 setEtsComponentsContext(inStructContext() && (isTokenInsideStructBuild(name) || isTokenInsideStructBuilder(decorators) || 6781 isTokenInsideStructPageTransition(name))); 6782 const isGenerator = asteriskToken ? SignatureFlags.Yield : SignatureFlags.None; 6783 const isAsync = some(modifiers, isAsyncModifier) ? SignatureFlags.Await : SignatureFlags.None; 6784 const typeParameters = inEtsStylesComponentsContext() && stylesEtsComponentDeclaration ? parseEtsTypeParameters(pos) : parseTypeParameters(); 6785 const parameters = parseParameters(isGenerator | isAsync); 6786 const typeStartPos = scanner.getStartPos(); 6787 const type = getMethodDeclarationReturnType(); 6788 const body = parseFunctionBlockOrSemicolon(isGenerator | isAsync, diagnosticMessage); 6789 const node = factory.createMethodDeclaration( 6790 decorators, 6791 modifiers, 6792 asteriskToken, 6793 name, 6794 questionToken, 6795 typeParameters, 6796 parameters, 6797 type, 6798 body 6799 ); 6800 // An exclamation token on a method is invalid syntax and will be handled by the grammar checker 6801 node.exclamationToken = exclamationToken; 6802 setEtsBuildContext(orignalEtsBuildContext); 6803 setEtsBuilderContext(orignalEtsBuilderContext); 6804 setEtsStylesComponentsContext(false); 6805 stylesEtsComponentDeclaration = undefined; 6806 setEtsComponentsContext(orignalEtsComponentsContext); 6807 return withJSDoc(finishNode(node, pos), hasJSDoc); 6808 6809 function getMethodDeclarationReturnType(): TypeNode | undefined { 6810 let returnType = parseReturnType(SyntaxKind.ColonToken, /*isType*/ false); 6811 // If function decorated by Styles and type is not defined, then will use Ets Styles Components Type 6812 if (!returnType && stylesEtsComponentDeclaration && inEtsStylesComponentsContext()) { 6813 returnType = finishVirtualNode(factory.createTypeReferenceNode( 6814 finishVirtualNode(factory.createIdentifier(stylesEtsComponentDeclaration.type), typeStartPos, typeStartPos)), 6815 typeStartPos, typeStartPos); 6816 } 6817 return returnType; 6818 } 6819 } 6820 6821 function parsePropertyDeclaration( 6822 pos: number, 6823 hasJSDoc: boolean, 6824 decorators: NodeArray<Decorator> | undefined, 6825 modifiers: NodeArray<Modifier> | undefined, 6826 name: PropertyName, 6827 questionToken: QuestionToken | undefined 6828 ): PropertyDeclaration { 6829 const exclamationToken = !questionToken && !scanner.hasPrecedingLineBreak() ? parseOptionalToken(SyntaxKind.ExclamationToken) : undefined; 6830 const type = parseTypeAnnotation(); 6831 const initializer = doOutsideOfContext(NodeFlags.YieldContext | NodeFlags.AwaitContext | NodeFlags.DisallowInContext, parseInitializer); 6832 parseSemicolon(); 6833 const node = factory.createPropertyDeclaration(decorators, modifiers, name, questionToken || exclamationToken, type, initializer); 6834 return withJSDoc(finishNode(node, pos), hasJSDoc); 6835 } 6836 6837 function parsePropertyOrMethodDeclaration( 6838 pos: number, 6839 hasJSDoc: boolean, 6840 decorators: NodeArray<Decorator> | undefined, 6841 modifiers: NodeArray<Modifier> | undefined 6842 ): PropertyDeclaration | MethodDeclaration { 6843 const asteriskToken = parseOptionalToken(SyntaxKind.AsteriskToken); 6844 const name = parsePropertyName(); 6845 // Note: this is not legal as per the grammar. But we allow it in the parser and 6846 // report an error in the grammar checker. 6847 const questionToken = parseOptionalToken(SyntaxKind.QuestionToken); 6848 if (asteriskToken || token() === SyntaxKind.OpenParenToken || token() === SyntaxKind.LessThanToken) { 6849 return parseMethodDeclaration(pos, hasJSDoc, decorators, modifiers, asteriskToken, name, questionToken, /*exclamationToken*/ undefined, Diagnostics.or_expected); 6850 } 6851 return parsePropertyDeclaration(pos, hasJSDoc, decorators, modifiers, name, questionToken); 6852 } 6853 6854 function parseAccessorDeclaration(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined, kind: AccessorDeclaration["kind"]): AccessorDeclaration { 6855 const name = parsePropertyName(); 6856 const typeParameters = parseTypeParameters(); 6857 const parameters = parseParameters(SignatureFlags.None); 6858 const type = parseReturnType(SyntaxKind.ColonToken, /*isType*/ false); 6859 const body = parseFunctionBlockOrSemicolon(SignatureFlags.None); 6860 const node = kind === SyntaxKind.GetAccessor 6861 ? factory.createGetAccessorDeclaration(decorators, modifiers, name, parameters, type, body) 6862 : factory.createSetAccessorDeclaration(decorators, modifiers, name, parameters, body); 6863 // Keep track of `typeParameters` (for both) and `type` (for setters) if they were parsed those indicate grammar errors 6864 node.typeParameters = typeParameters; 6865 if (type && node.kind === SyntaxKind.SetAccessor) (node as Mutable<SetAccessorDeclaration>).type = type; 6866 return withJSDoc(finishNode(node, pos), hasJSDoc); 6867 } 6868 6869 function isClassMemberStart(): boolean { 6870 let idToken: SyntaxKind | undefined; 6871 6872 if (token() === SyntaxKind.AtToken) { 6873 return true; 6874 } 6875 6876 // Eat up all modifiers, but hold on to the last one in case it is actually an identifier. 6877 while (isModifierKind(token())) { 6878 idToken = token(); 6879 // If the idToken is a class modifier (protected, private, public, and static), it is 6880 // certain that we are starting to parse class member. This allows better error recovery 6881 // Example: 6882 // public foo() ... // true 6883 // public @dec blah ... // true; we will then report an error later 6884 // export public ... // true; we will then report an error later 6885 if (isClassMemberModifier(idToken)) { 6886 return true; 6887 } 6888 6889 nextToken(); 6890 } 6891 6892 if (token() === SyntaxKind.AsteriskToken) { 6893 return true; 6894 } 6895 6896 // Try to get the first property-like token following all modifiers. 6897 // This can either be an identifier or the 'get' or 'set' keywords. 6898 if (isLiteralPropertyName()) { 6899 idToken = token(); 6900 nextToken(); 6901 } 6902 6903 // Index signatures and computed properties are class members; we can parse. 6904 if (token() === SyntaxKind.OpenBracketToken) { 6905 return true; 6906 } 6907 6908 // If we were able to get any potential identifier... 6909 if (idToken !== undefined) { 6910 // If we have a non-keyword identifier, or if we have an accessor, then it's safe to parse. 6911 if (!isKeyword(idToken) || idToken === SyntaxKind.SetKeyword || idToken === SyntaxKind.GetKeyword) { 6912 return true; 6913 } 6914 6915 // If it *is* a keyword, but not an accessor, check a little farther along 6916 // to see if it should actually be parsed as a class member. 6917 switch (token()) { 6918 case SyntaxKind.OpenParenToken: // Method declaration 6919 case SyntaxKind.LessThanToken: // Generic Method declaration 6920 case SyntaxKind.ExclamationToken: // Non-null assertion on property name 6921 case SyntaxKind.ColonToken: // Type Annotation for declaration 6922 case SyntaxKind.EqualsToken: // Initializer for declaration 6923 case SyntaxKind.QuestionToken: // Not valid, but permitted so that it gets caught later on. 6924 return true; 6925 default: 6926 // Covers 6927 // - Semicolons (declaration termination) 6928 // - Closing braces (end-of-class, must be declaration) 6929 // - End-of-files (not valid, but permitted so that it gets caught later on) 6930 // - Line-breaks (enabling *automatic semicolon insertion*) 6931 return canParseSemicolon(); 6932 } 6933 } 6934 6935 return false; 6936 } 6937 6938 function parseDecoratorExpression() { 6939 if (inAwaitContext() && token() === SyntaxKind.AwaitKeyword) { 6940 // `@await` is is disallowed in an [Await] context, but can cause parsing to go off the rails 6941 // This simply parses the missing identifier and moves on. 6942 const pos = getNodePos(); 6943 const awaitExpression = parseIdentifier(Diagnostics.Expression_expected); 6944 nextToken(); 6945 const memberExpression = parseMemberExpressionRest(pos, awaitExpression, /*allowOptionalChain*/ true); 6946 return parseCallExpressionRest(pos, memberExpression); 6947 } 6948 return parseLeftHandSideExpressionOrHigher(); 6949 } 6950 6951 function tryParseDecorator(): Decorator | undefined { 6952 const pos = getNodePos(); 6953 if (!parseOptional(SyntaxKind.AtToken)) { 6954 return undefined; 6955 } 6956 const expression = doInDecoratorContext(parseDecoratorExpression); 6957 return finishNode(factory.createDecorator(expression), pos); 6958 } 6959 6960 function parseDecorators(): NodeArray<Decorator> | undefined { 6961 const pos = getNodePos(); 6962 let list, decorator; 6963 while (decorator = tryParseDecorator()) { 6964 list = append(list, decorator); 6965 } 6966 return list && createNodeArray(list, pos); 6967 } 6968 6969 function tryParseModifier(permitInvalidConstAsModifier?: boolean): Modifier | undefined { 6970 const pos = getNodePos(); 6971 const kind = token(); 6972 6973 if (token() === SyntaxKind.ConstKeyword && permitInvalidConstAsModifier) { 6974 // We need to ensure that any subsequent modifiers appear on the same line 6975 // so that when 'const' is a standalone declaration, we don't issue an error. 6976 if (!tryParse(nextTokenIsOnSameLineAndCanFollowModifier)) { 6977 return undefined; 6978 } 6979 } 6980 else { 6981 if (!parseAnyContextualModifier()) { 6982 return undefined; 6983 } 6984 } 6985 6986 return finishNode(factory.createToken(kind as Modifier["kind"]), pos); 6987 } 6988 6989 /* 6990 * There are situations in which a modifier like 'const' will appear unexpectedly, such as on a class member. 6991 * In those situations, if we are entirely sure that 'const' is not valid on its own (such as when ASI takes effect 6992 * and turns it into a standalone declaration), then it is better to parse it and report an error later. 6993 * 6994 * In such situations, 'permitInvalidConstAsModifier' should be set to true. 6995 */ 6996 function parseModifiers(permitInvalidConstAsModifier?: boolean): NodeArray<Modifier> | undefined { 6997 const pos = getNodePos(); 6998 let list, modifier; 6999 while (modifier = tryParseModifier(permitInvalidConstAsModifier)) { 7000 list = append(list, modifier); 7001 } 7002 return list && createNodeArray(list, pos); 7003 } 7004 7005 function parseModifiersForArrowFunction(): NodeArray<Modifier> | undefined { 7006 let modifiers: NodeArray<Modifier> | undefined; 7007 if (token() === SyntaxKind.AsyncKeyword) { 7008 const pos = getNodePos(); 7009 nextToken(); 7010 const modifier = finishNode(factory.createToken(SyntaxKind.AsyncKeyword), pos); 7011 modifiers = createNodeArray<Modifier>([modifier], pos); 7012 } 7013 return modifiers; 7014 } 7015 7016 function parseClassElement(): ClassElement { 7017 const pos = getNodePos(); 7018 if (token() === SyntaxKind.SemicolonToken) { 7019 nextToken(); 7020 return finishNode(factory.createSemicolonClassElement(), pos); 7021 } 7022 7023 const hasJSDoc = hasPrecedingJSDocComment(); 7024 const decorators = parseDecorators(); 7025 const modifiers = parseModifiers(/*permitInvalidConstAsModifier*/ true); 7026 7027 if (parseContextualModifier(SyntaxKind.GetKeyword)) { 7028 return parseAccessorDeclaration(pos, hasJSDoc, decorators, modifiers, SyntaxKind.GetAccessor); 7029 } 7030 7031 if (parseContextualModifier(SyntaxKind.SetKeyword)) { 7032 return parseAccessorDeclaration(pos, hasJSDoc, decorators, modifiers, SyntaxKind.SetAccessor); 7033 } 7034 7035 if (token() === SyntaxKind.ConstructorKeyword || token() === SyntaxKind.StringLiteral) { 7036 const constructorDeclaration = tryParseConstructorDeclaration(pos, hasJSDoc, decorators, modifiers); 7037 if (constructorDeclaration) { 7038 return constructorDeclaration; 7039 } 7040 } 7041 7042 if (isIndexSignature()) { 7043 return parseIndexSignatureDeclaration(pos, hasJSDoc, decorators, modifiers); 7044 } 7045 7046 // It is very important that we check this *after* checking indexers because 7047 // the [ token can start an index signature or a computed property name 7048 if (tokenIsIdentifierOrKeyword(token()) || 7049 token() === SyntaxKind.StringLiteral || 7050 token() === SyntaxKind.NumericLiteral || 7051 token() === SyntaxKind.AsteriskToken || 7052 token() === SyntaxKind.OpenBracketToken) { 7053 const isAmbient = some(modifiers, isDeclareModifier); 7054 if (isAmbient) { 7055 for (const m of modifiers!) { 7056 (m as Mutable<Node>).flags |= NodeFlags.Ambient; 7057 } 7058 return doInsideOfContext(NodeFlags.Ambient, () => parsePropertyOrMethodDeclaration(pos, hasJSDoc, decorators, modifiers)); 7059 } 7060 else { 7061 return parsePropertyOrMethodDeclaration(pos, hasJSDoc, decorators, modifiers); 7062 } 7063 } 7064 7065 if (decorators || modifiers) { 7066 // treat this as a property declaration with a missing name. 7067 const name = createMissingNode<Identifier>(SyntaxKind.Identifier, /*reportAtCurrentPosition*/ true, Diagnostics.Declaration_expected); 7068 return parsePropertyDeclaration(pos, hasJSDoc, decorators, modifiers, name, /*questionToken*/ undefined); 7069 } 7070 7071 // 'isClassMemberStart' should have hinted not to attempt parsing. 7072 return Debug.fail("Should not have attempted to parse class member declaration."); 7073 } 7074 7075 function parseClassExpression(): ClassExpression { 7076 return <ClassExpression>parseClassDeclarationOrExpression(getNodePos(), hasPrecedingJSDocComment(), /*decorators*/ undefined, /*modifiers*/ undefined, SyntaxKind.ClassExpression); 7077 } 7078 7079 function parseClassDeclaration(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined): ClassDeclaration { 7080 return <ClassDeclaration>parseClassDeclarationOrExpression(pos, hasJSDoc, decorators, modifiers, SyntaxKind.ClassDeclaration); 7081 } 7082 7083 function parseStructDeclaration(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined): StructDeclaration { 7084 return parseStructDeclarationOrExpression(pos, hasJSDoc, decorators, modifiers); 7085 } 7086 7087 function parseClassDeclarationOrExpression(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined, kind: ClassLikeDeclaration["kind"]): ClassLikeDeclaration { 7088 const savedAwaitContext = inAwaitContext(); 7089 parseExpected(SyntaxKind.ClassKeyword); 7090 // We don't parse the name here in await context, instead we will report a grammar error in the checker. 7091 const name = parseNameOfClassDeclarationOrExpression(); 7092 const typeParameters = parseTypeParameters(); 7093 if (some(modifiers, isExportModifier)) setAwaitContext(/*value*/ true); 7094 const heritageClauses = parseHeritageClauses(); 7095 7096 let members; 7097 if (parseExpected(SyntaxKind.OpenBraceToken)) { 7098 // ClassTail[Yield,Await] : (Modified) See 14.5 7099 // ClassHeritage[?Yield,?Await]opt { ClassBody[?Yield,?Await]opt } 7100 members = parseClassMembers(); 7101 parseExpected(SyntaxKind.CloseBraceToken); 7102 } 7103 else { 7104 members = createMissingList<ClassElement>(); 7105 } 7106 setAwaitContext(savedAwaitContext); 7107 const node = kind === SyntaxKind.ClassDeclaration 7108 ? factory.createClassDeclaration(decorators, modifiers, name, typeParameters, heritageClauses, members) 7109 : factory.createClassExpression(decorators, modifiers, name, typeParameters, heritageClauses, members); 7110 return withJSDoc(finishNode(node, pos), hasJSDoc); 7111 } 7112 7113 function parseStructDeclarationOrExpression(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined): StructDeclaration { 7114 const savedAwaitContext = inAwaitContext(); 7115 parseExpected(SyntaxKind.StructKeyword); 7116 setStructContext(true); 7117 // We don't parse the name here in await context, instead we will report a grammar error in the checker. 7118 // struct Identifier logic is same to class Identifier 7119 const name = parseNameOfClassDeclarationOrExpression(); 7120 currentStructName = name?.escapedText.toString(); 7121 const typeParameters = parseTypeParameters(); 7122 if (some(modifiers, isExportModifier)) setAwaitContext(/*value*/ true); 7123 let heritageClauses = parseHeritageClauses(); 7124 const customComponent = sourceFileCompilerOptions.ets?.customComponent; 7125 if (!heritageClauses && customComponent) { 7126 heritageClauses = createVirtualHeritageClauses(customComponent); 7127 } 7128 let members; 7129 if (parseExpected(SyntaxKind.OpenBraceToken)) { 7130 // ClassTail[Yield,Await] : (Modified) See 14.5 7131 // ClassHeritage[?Yield,?Await]opt { ClassBody[?Yield,?Await]opt } 7132 members = parseStructMembers(pos); 7133 parseExpected(SyntaxKind.CloseBraceToken); 7134 } 7135 else { 7136 members = createMissingList<ClassElement>(); 7137 } 7138 setAwaitContext(savedAwaitContext); 7139 const node = factory.createStructDeclaration(decorators, modifiers, name, typeParameters, heritageClauses, members); 7140 currentStructName = undefined; 7141 structStylesComponents.clear(); 7142 setStructContext(false); 7143 return withJSDoc(finishNode(node, pos), hasJSDoc); 7144 } 7145 7146 function createVirtualHeritageClauses(customComponent: string): NodeArray<HeritageClause> { 7147 const curPos = getNodePos(); 7148 const clause = factory.createHeritageClause( 7149 SyntaxKind.ExtendsKeyword, 7150 createNodeArray([finishNode(factory.createExpressionWithTypeArguments( 7151 finishNode(factory.createIdentifier(/*text*/ customComponent), curPos, /*end*/ undefined, /*virtual*/ true), 7152 /*typeArguments*/ undefined 7153 ), curPos)], curPos, /*end*/ undefined, /*hasTrailingComma*/ false) 7154 ); 7155 return createNodeArray([finishNode(clause, curPos, /*end*/ undefined, /*virtual*/ true)], curPos, /*end*/ undefined, /*hasTrailingComma*/ false); 7156 } 7157 7158 function parseNameOfClassDeclarationOrExpression(): Identifier | undefined { 7159 // implements is a future reserved word so 7160 // 'class implements' might mean either 7161 // - class expression with omitted name, 'implements' starts heritage clause 7162 // - class with name 'implements' 7163 // 'isImplementsClause' helps to disambiguate between these two cases 7164 return isBindingIdentifier() && !isImplementsClause() 7165 ? createIdentifier(isBindingIdentifier()) 7166 : undefined; 7167 } 7168 7169 function isImplementsClause() { 7170 return token() === SyntaxKind.ImplementsKeyword && lookAhead(nextTokenIsIdentifierOrKeyword); 7171 } 7172 7173 function parseHeritageClauses(): NodeArray<HeritageClause> | undefined { 7174 // ClassTail[Yield,Await] : (Modified) See 14.5 7175 // ClassHeritage[?Yield,?Await]opt { ClassBody[?Yield,?Await]opt } 7176 7177 if (isHeritageClause()) { 7178 return parseList(ParsingContext.HeritageClauses, parseHeritageClause); 7179 } 7180 7181 return undefined; 7182 } 7183 7184 function parseHeritageClause(): HeritageClause { 7185 const pos = getNodePos(); 7186 const tok = token(); 7187 Debug.assert(tok === SyntaxKind.ExtendsKeyword || tok === SyntaxKind.ImplementsKeyword); // isListElement() should ensure this. 7188 nextToken(); 7189 const types = parseDelimitedList(ParsingContext.HeritageClauseElement, parseExpressionWithTypeArguments); 7190 return finishNode(factory.createHeritageClause(tok, types), pos); 7191 } 7192 7193 function parseExpressionWithTypeArguments(): ExpressionWithTypeArguments { 7194 const pos = getNodePos(); 7195 const expression = parseLeftHandSideExpressionOrHigher(); 7196 const typeArguments = tryParseTypeArguments(); 7197 return finishNode(factory.createExpressionWithTypeArguments(expression, typeArguments), pos); 7198 } 7199 7200 function tryParseTypeArguments(): NodeArray<TypeNode> | undefined { 7201 return token() === SyntaxKind.LessThanToken ? 7202 parseBracketedList(ParsingContext.TypeArguments, parseType, SyntaxKind.LessThanToken, SyntaxKind.GreaterThanToken) : undefined; 7203 } 7204 7205 function isHeritageClause(): boolean { 7206 return token() === SyntaxKind.ExtendsKeyword || token() === SyntaxKind.ImplementsKeyword; 7207 } 7208 7209 function parseClassMembers(): NodeArray<ClassElement> { 7210 return parseList(ParsingContext.ClassMembers, parseClassElement); 7211 } 7212 7213 function parseStructMembers(pos: number): NodeArray<ClassElement> { 7214 const structMembers = parseList(ParsingContext.ClassMembers, parseClassElement); 7215 7216 const virtualStructMembers: ClassElement[] = []; 7217 // create constructor function argument object properties 7218 const virtualParameterProperties: TypeElement[] = []; 7219 structMembers.forEach(member => { 7220 virtualStructMembers.push(member); 7221 if (member.kind === SyntaxKind.PropertyDeclaration) { 7222 const property = <PropertyDeclaration>member; 7223 virtualParameterProperties.push( 7224 finishVirtualNode( 7225 factory.createPropertySignature(property.modifiers, property.name, factory.createToken(SyntaxKind.QuestionToken), property.type) 7226 ) 7227 ); 7228 } 7229 }); 7230 const parameters: ParameterDeclaration[] = []; 7231 if (virtualParameterProperties.length) { 7232 const type = finishVirtualNode(factory.createTypeLiteralNode(createNodeArray(virtualParameterProperties, 0, 0))); 7233 parameters.push( 7234 finishVirtualNode( 7235 factory.createParameterDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, 7236 finishVirtualNode(factory.createIdentifier("value")), factory.createToken(SyntaxKind.QuestionToken), type 7237 ) 7238 ) 7239 ); 7240 } 7241 const emptyBody = finishVirtualNode(factory.createBlock(createNodeArray([], 0, 0))); 7242 const virtualConstructor = factory.createConstructorDeclaration(/*decorators*/ undefined, /*modifier*/ undefined, createNodeArray(parameters, 0, 0), emptyBody); 7243 7244 virtualStructMembers.unshift(finishVirtualNode(virtualConstructor, pos, pos)); 7245 7246 return createNodeArray(virtualStructMembers, structMembers.pos); 7247 } 7248 7249 function finishVirtualNode<T extends Node>(node: T, start = 0, end = 0) { 7250 return finishNode(node, start, end, /*virtual*/ true); 7251 } 7252 7253 function parseInterfaceDeclaration(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined): InterfaceDeclaration { 7254 parseExpected(SyntaxKind.InterfaceKeyword); 7255 const name = parseIdentifier(); 7256 const typeParameters = parseTypeParameters(); 7257 const heritageClauses = parseHeritageClauses(); 7258 const members = parseObjectTypeMembers(); 7259 const node = factory.createInterfaceDeclaration(decorators, modifiers, name, typeParameters, heritageClauses, members); 7260 return withJSDoc(finishNode(node, pos), hasJSDoc); 7261 } 7262 7263 function parseTypeAliasDeclaration(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined): TypeAliasDeclaration { 7264 parseExpected(SyntaxKind.TypeKeyword); 7265 const name = parseIdentifier(); 7266 const typeParameters = parseTypeParameters(); 7267 parseExpected(SyntaxKind.EqualsToken); 7268 const type = token() === SyntaxKind.IntrinsicKeyword && tryParse(parseKeywordAndNoDot) || parseType(); 7269 parseSemicolon(); 7270 const node = factory.createTypeAliasDeclaration(decorators, modifiers, name, typeParameters, type); 7271 return withJSDoc(finishNode(node, pos), hasJSDoc); 7272 } 7273 7274 // In an ambient declaration, the grammar only allows integer literals as initializers. 7275 // In a non-ambient declaration, the grammar allows uninitialized members only in a 7276 // ConstantEnumMemberSection, which starts at the beginning of an enum declaration 7277 // or any time an integer literal initializer is encountered. 7278 function parseEnumMember(): EnumMember { 7279 const pos = getNodePos(); 7280 const hasJSDoc = hasPrecedingJSDocComment(); 7281 const name = parsePropertyName(); 7282 const initializer = allowInAnd(parseInitializer); 7283 return withJSDoc(finishNode(factory.createEnumMember(name, initializer), pos), hasJSDoc); 7284 } 7285 7286 function parseEnumDeclaration(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined): EnumDeclaration { 7287 parseExpected(SyntaxKind.EnumKeyword); 7288 const name = parseIdentifier(); 7289 let members; 7290 if (parseExpected(SyntaxKind.OpenBraceToken)) { 7291 members = doOutsideOfYieldAndAwaitContext(() => parseDelimitedList(ParsingContext.EnumMembers, parseEnumMember)); 7292 parseExpected(SyntaxKind.CloseBraceToken); 7293 } 7294 else { 7295 members = createMissingList<EnumMember>(); 7296 } 7297 const node = factory.createEnumDeclaration(decorators, modifiers, name, members); 7298 return withJSDoc(finishNode(node, pos), hasJSDoc); 7299 } 7300 7301 function parseModuleBlock(): ModuleBlock { 7302 const pos = getNodePos(); 7303 let statements; 7304 if (parseExpected(SyntaxKind.OpenBraceToken)) { 7305 statements = parseList(ParsingContext.BlockStatements, parseStatement); 7306 parseExpected(SyntaxKind.CloseBraceToken); 7307 } 7308 else { 7309 statements = createMissingList<Statement>(); 7310 } 7311 return finishNode(factory.createModuleBlock(statements), pos); 7312 } 7313 7314 function parseModuleOrNamespaceDeclaration(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined, flags: NodeFlags): ModuleDeclaration { 7315 // If we are parsing a dotted namespace name, we want to 7316 // propagate the 'Namespace' flag across the names if set. 7317 const namespaceFlag = flags & NodeFlags.Namespace; 7318 const name = parseIdentifier(); 7319 const body = parseOptional(SyntaxKind.DotToken) 7320 ? <NamespaceDeclaration>parseModuleOrNamespaceDeclaration(getNodePos(), /*hasJSDoc*/ false, /*decorators*/ undefined, /*modifiers*/ undefined, NodeFlags.NestedNamespace | namespaceFlag) 7321 : parseModuleBlock(); 7322 const node = factory.createModuleDeclaration(decorators, modifiers, name, body, flags); 7323 return withJSDoc(finishNode(node, pos), hasJSDoc); 7324 } 7325 7326 function parseAmbientExternalModuleDeclaration(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined): ModuleDeclaration { 7327 let flags: NodeFlags = 0; 7328 let name; 7329 if (token() === SyntaxKind.GlobalKeyword) { 7330 // parse 'global' as name of global scope augmentation 7331 name = parseIdentifier(); 7332 flags |= NodeFlags.GlobalAugmentation; 7333 } 7334 else { 7335 name = <StringLiteral>parseLiteralNode(); 7336 name.text = internIdentifier(name.text); 7337 } 7338 let body: ModuleBlock | undefined; 7339 if (token() === SyntaxKind.OpenBraceToken) { 7340 body = parseModuleBlock(); 7341 } 7342 else { 7343 parseSemicolon(); 7344 } 7345 const node = factory.createModuleDeclaration(decorators, modifiers, name, body, flags); 7346 return withJSDoc(finishNode(node, pos), hasJSDoc); 7347 } 7348 7349 function parseModuleDeclaration(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined): ModuleDeclaration { 7350 let flags: NodeFlags = 0; 7351 if (token() === SyntaxKind.GlobalKeyword) { 7352 // global augmentation 7353 return parseAmbientExternalModuleDeclaration(pos, hasJSDoc, decorators, modifiers); 7354 } 7355 else if (parseOptional(SyntaxKind.NamespaceKeyword)) { 7356 flags |= NodeFlags.Namespace; 7357 } 7358 else { 7359 parseExpected(SyntaxKind.ModuleKeyword); 7360 if (token() === SyntaxKind.StringLiteral) { 7361 return parseAmbientExternalModuleDeclaration(pos, hasJSDoc, decorators, modifiers); 7362 } 7363 } 7364 return parseModuleOrNamespaceDeclaration(pos, hasJSDoc, decorators, modifiers, flags); 7365 } 7366 7367 function isExternalModuleReference() { 7368 return token() === SyntaxKind.RequireKeyword && 7369 lookAhead(nextTokenIsOpenParen); 7370 } 7371 7372 function nextTokenIsOpenParen() { 7373 return nextToken() === SyntaxKind.OpenParenToken; 7374 } 7375 7376 function nextTokenIsSlash() { 7377 return nextToken() === SyntaxKind.SlashToken; 7378 } 7379 7380 function parseNamespaceExportDeclaration(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined): NamespaceExportDeclaration { 7381 parseExpected(SyntaxKind.AsKeyword); 7382 parseExpected(SyntaxKind.NamespaceKeyword); 7383 const name = parseIdentifier(); 7384 parseSemicolon(); 7385 const node = factory.createNamespaceExportDeclaration(name); 7386 // NamespaceExportDeclaration nodes cannot have decorators or modifiers, so we attach them here so we can report them in the grammar checker 7387 node.decorators = decorators; 7388 node.modifiers = modifiers; 7389 return withJSDoc(finishNode(node, pos), hasJSDoc); 7390 } 7391 7392 function parseImportDeclarationOrImportEqualsDeclaration(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined): ImportEqualsDeclaration | ImportDeclaration { 7393 parseExpected(SyntaxKind.ImportKeyword); 7394 7395 const afterImportPos = scanner.getStartPos(); 7396 7397 // We don't parse the identifier here in await context, instead we will report a grammar error in the checker. 7398 let identifier: Identifier | undefined; 7399 if (isIdentifier()) { 7400 identifier = parseIdentifier(); 7401 } 7402 7403 let isTypeOnly = false; 7404 if (token() !== SyntaxKind.FromKeyword && 7405 identifier?.escapedText === "type" && 7406 (isIdentifier() || tokenAfterImportDefinitelyProducesImportDeclaration()) 7407 ) { 7408 isTypeOnly = true; 7409 identifier = isIdentifier() ? parseIdentifier() : undefined; 7410 } 7411 7412 if (identifier && !tokenAfterImportedIdentifierDefinitelyProducesImportDeclaration()) { 7413 return parseImportEqualsDeclaration(pos, hasJSDoc, decorators, modifiers, identifier, isTypeOnly); 7414 } 7415 7416 // ImportDeclaration: 7417 // import ImportClause from ModuleSpecifier ; 7418 // import ModuleSpecifier; 7419 let importClause: ImportClause | undefined; 7420 if (identifier || // import id 7421 token() === SyntaxKind.AsteriskToken || // import * 7422 token() === SyntaxKind.OpenBraceToken // import { 7423 ) { 7424 importClause = parseImportClause(identifier, afterImportPos, isTypeOnly); 7425 parseExpected(SyntaxKind.FromKeyword); 7426 } 7427 7428 const moduleSpecifier = parseModuleSpecifier(); 7429 parseSemicolon(); 7430 const node = factory.createImportDeclaration(decorators, modifiers, importClause, moduleSpecifier); 7431 return withJSDoc(finishNode(node, pos), hasJSDoc); 7432 } 7433 7434 function tokenAfterImportDefinitelyProducesImportDeclaration() { 7435 return token() === SyntaxKind.AsteriskToken || token() === SyntaxKind.OpenBraceToken; 7436 } 7437 7438 function tokenAfterImportedIdentifierDefinitelyProducesImportDeclaration() { 7439 // In `import id ___`, the current token decides whether to produce 7440 // an ImportDeclaration or ImportEqualsDeclaration. 7441 return token() === SyntaxKind.CommaToken || token() === SyntaxKind.FromKeyword; 7442 } 7443 7444 function parseImportEqualsDeclaration(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined, identifier: Identifier, isTypeOnly: boolean): ImportEqualsDeclaration { 7445 parseExpected(SyntaxKind.EqualsToken); 7446 const moduleReference = parseModuleReference(); 7447 parseSemicolon(); 7448 const node = factory.createImportEqualsDeclaration(decorators, modifiers, isTypeOnly, identifier, moduleReference); 7449 const finished = withJSDoc(finishNode(node, pos), hasJSDoc); 7450 return finished; 7451 } 7452 7453 function parseImportClause(identifier: Identifier | undefined, pos: number, isTypeOnly: boolean) { 7454 // ImportClause: 7455 // ImportedDefaultBinding 7456 // NameSpaceImport 7457 // NamedImports 7458 // ImportedDefaultBinding, NameSpaceImport 7459 // ImportedDefaultBinding, NamedImports 7460 7461 // If there was no default import or if there is comma token after default import 7462 // parse namespace or named imports 7463 let namedBindings: NamespaceImport | NamedImports | undefined; 7464 if (!identifier || 7465 parseOptional(SyntaxKind.CommaToken)) { 7466 namedBindings = token() === SyntaxKind.AsteriskToken ? parseNamespaceImport() : parseNamedImportsOrExports(SyntaxKind.NamedImports); 7467 } 7468 7469 return finishNode(factory.createImportClause(isTypeOnly, identifier, namedBindings), pos); 7470 } 7471 7472 function parseModuleReference() { 7473 return isExternalModuleReference() 7474 ? parseExternalModuleReference() 7475 : parseEntityName(/*allowReservedWords*/ false); 7476 } 7477 7478 function parseExternalModuleReference() { 7479 const pos = getNodePos(); 7480 parseExpected(SyntaxKind.RequireKeyword); 7481 parseExpected(SyntaxKind.OpenParenToken); 7482 const expression = parseModuleSpecifier(); 7483 parseExpected(SyntaxKind.CloseParenToken); 7484 return finishNode(factory.createExternalModuleReference(expression), pos); 7485 } 7486 7487 function parseModuleSpecifier(): Expression { 7488 if (token() === SyntaxKind.StringLiteral) { 7489 const result = parseLiteralNode(); 7490 result.text = internIdentifier(result.text); 7491 return result; 7492 } 7493 else { 7494 // We allow arbitrary expressions here, even though the grammar only allows string 7495 // literals. We check to ensure that it is only a string literal later in the grammar 7496 // check pass. 7497 return parseExpression(); 7498 } 7499 } 7500 7501 function parseNamespaceImport(): NamespaceImport { 7502 // NameSpaceImport: 7503 // * as ImportedBinding 7504 const pos = getNodePos(); 7505 parseExpected(SyntaxKind.AsteriskToken); 7506 parseExpected(SyntaxKind.AsKeyword); 7507 const name = parseIdentifier(); 7508 return finishNode(factory.createNamespaceImport(name), pos); 7509 } 7510 7511 function parseNamedImportsOrExports(kind: SyntaxKind.NamedImports): NamedImports; 7512 function parseNamedImportsOrExports(kind: SyntaxKind.NamedExports): NamedExports; 7513 function parseNamedImportsOrExports(kind: SyntaxKind): NamedImportsOrExports { 7514 const pos = getNodePos(); 7515 7516 // NamedImports: 7517 // { } 7518 // { ImportsList } 7519 // { ImportsList, } 7520 7521 // ImportsList: 7522 // ImportSpecifier 7523 // ImportsList, ImportSpecifier 7524 const node = kind === SyntaxKind.NamedImports 7525 ? factory.createNamedImports(parseBracketedList(ParsingContext.ImportOrExportSpecifiers, parseImportSpecifier, SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken)) 7526 : factory.createNamedExports(parseBracketedList(ParsingContext.ImportOrExportSpecifiers, parseExportSpecifier, SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken)); 7527 return finishNode(node, pos); 7528 } 7529 7530 function parseExportSpecifier() { 7531 return parseImportOrExportSpecifier(SyntaxKind.ExportSpecifier) as ExportSpecifier; 7532 } 7533 7534 function parseImportSpecifier() { 7535 return parseImportOrExportSpecifier(SyntaxKind.ImportSpecifier) as ImportSpecifier; 7536 } 7537 7538 function parseImportOrExportSpecifier(kind: SyntaxKind): ImportOrExportSpecifier { 7539 const pos = getNodePos(); 7540 // ImportSpecifier: 7541 // BindingIdentifier 7542 // IdentifierName as BindingIdentifier 7543 // ExportSpecifier: 7544 // IdentifierName 7545 // IdentifierName as IdentifierName 7546 let checkIdentifierIsKeyword = isKeyword(token()) && !isIdentifier(); 7547 let checkIdentifierStart = scanner.getTokenPos(); 7548 let checkIdentifierEnd = scanner.getTextPos(); 7549 const identifierName = parseIdentifierName(); 7550 let propertyName: Identifier | undefined; 7551 let name: Identifier; 7552 if (token() === SyntaxKind.AsKeyword) { 7553 propertyName = identifierName; 7554 parseExpected(SyntaxKind.AsKeyword); 7555 checkIdentifierIsKeyword = isKeyword(token()) && !isIdentifier(); 7556 checkIdentifierStart = scanner.getTokenPos(); 7557 checkIdentifierEnd = scanner.getTextPos(); 7558 name = parseIdentifierName(); 7559 } 7560 else { 7561 name = identifierName; 7562 } 7563 if (kind === SyntaxKind.ImportSpecifier && checkIdentifierIsKeyword) { 7564 parseErrorAt(checkIdentifierStart, checkIdentifierEnd, Diagnostics.Identifier_expected); 7565 } 7566 const node = kind === SyntaxKind.ImportSpecifier 7567 ? factory.createImportSpecifier(propertyName, name) 7568 : factory.createExportSpecifier(propertyName, name); 7569 return finishNode(node, pos); 7570 } 7571 7572 function parseNamespaceExport(pos: number): NamespaceExport { 7573 return finishNode(factory.createNamespaceExport(parseIdentifierName()), pos); 7574 } 7575 7576 function parseExportDeclaration(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined): ExportDeclaration { 7577 const savedAwaitContext = inAwaitContext(); 7578 setAwaitContext(/*value*/ true); 7579 let exportClause: NamedExportBindings | undefined; 7580 let moduleSpecifier: Expression | undefined; 7581 const isTypeOnly = parseOptional(SyntaxKind.TypeKeyword); 7582 const namespaceExportPos = getNodePos(); 7583 if (parseOptional(SyntaxKind.AsteriskToken)) { 7584 if (parseOptional(SyntaxKind.AsKeyword)) { 7585 exportClause = parseNamespaceExport(namespaceExportPos); 7586 } 7587 parseExpected(SyntaxKind.FromKeyword); 7588 moduleSpecifier = parseModuleSpecifier(); 7589 } 7590 else { 7591 exportClause = parseNamedImportsOrExports(SyntaxKind.NamedExports); 7592 // It is not uncommon to accidentally omit the 'from' keyword. Additionally, in editing scenarios, 7593 // the 'from' keyword can be parsed as a named export when the export clause is unterminated (i.e. `export { from "moduleName";`) 7594 // If we don't have a 'from' keyword, see if we have a string literal such that ASI won't take effect. 7595 if (token() === SyntaxKind.FromKeyword || (token() === SyntaxKind.StringLiteral && !scanner.hasPrecedingLineBreak())) { 7596 parseExpected(SyntaxKind.FromKeyword); 7597 moduleSpecifier = parseModuleSpecifier(); 7598 } 7599 } 7600 parseSemicolon(); 7601 setAwaitContext(savedAwaitContext); 7602 const node = factory.createExportDeclaration(decorators, modifiers, isTypeOnly, exportClause, moduleSpecifier); 7603 return withJSDoc(finishNode(node, pos), hasJSDoc); 7604 } 7605 7606 function parseExportAssignment(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined): ExportAssignment { 7607 const savedAwaitContext = inAwaitContext(); 7608 setAwaitContext(/*value*/ true); 7609 let isExportEquals: boolean | undefined; 7610 if (parseOptional(SyntaxKind.EqualsToken)) { 7611 isExportEquals = true; 7612 } 7613 else { 7614 parseExpected(SyntaxKind.DefaultKeyword); 7615 } 7616 const expression = parseAssignmentExpressionOrHigher(); 7617 parseSemicolon(); 7618 setAwaitContext(savedAwaitContext); 7619 const node = factory.createExportAssignment(decorators, modifiers, isExportEquals, expression); 7620 return withJSDoc(finishNode(node, pos), hasJSDoc); 7621 } 7622 7623 function setExternalModuleIndicator(sourceFile: SourceFile) { 7624 // Try to use the first top-level import/export when available, then 7625 // fall back to looking for an 'import.meta' somewhere in the tree if necessary. 7626 sourceFile.externalModuleIndicator = 7627 forEach(sourceFile.statements, isAnExternalModuleIndicatorNode) || 7628 getImportMetaIfNecessary(sourceFile); 7629 } 7630 7631 function isAnExternalModuleIndicatorNode(node: Node) { 7632 return hasModifierOfKind(node, SyntaxKind.ExportKeyword) 7633 || isImportEqualsDeclaration(node) && ts.isExternalModuleReference(node.moduleReference) 7634 || isImportDeclaration(node) 7635 || isExportAssignment(node) 7636 || isExportDeclaration(node) ? node : undefined; 7637 } 7638 7639 function getImportMetaIfNecessary(sourceFile: SourceFile) { 7640 return sourceFile.flags & NodeFlags.PossiblyContainsImportMeta ? 7641 walkTreeForExternalModuleIndicators(sourceFile) : 7642 undefined; 7643 } 7644 7645 function walkTreeForExternalModuleIndicators(node: Node): Node | undefined { 7646 return isImportMeta(node) ? node : forEachChild(node, walkTreeForExternalModuleIndicators); 7647 } 7648 7649 /** Do not use hasModifier inside the parser; it relies on parent pointers. Use this instead. */ 7650 function hasModifierOfKind(node: Node, kind: SyntaxKind) { 7651 return some(node.modifiers, m => m.kind === kind); 7652 } 7653 7654 function isImportMeta(node: Node): boolean { 7655 return isMetaProperty(node) && node.keywordToken === SyntaxKind.ImportKeyword && node.name.escapedText === "meta"; 7656 } 7657 7658 const enum ParsingContext { 7659 SourceElements, // Elements in source file 7660 BlockStatements, // Statements in block 7661 SwitchClauses, // Clauses in switch statement 7662 SwitchClauseStatements, // Statements in switch clause 7663 TypeMembers, // Members in interface or type literal 7664 ClassMembers, // Members in class declaration 7665 EnumMembers, // Members in enum declaration 7666 HeritageClauseElement, // Elements in a heritage clause 7667 VariableDeclarations, // Variable declarations in variable statement 7668 ObjectBindingElements, // Binding elements in object binding list 7669 ArrayBindingElements, // Binding elements in array binding list 7670 ArgumentExpressions, // Expressions in argument list 7671 ObjectLiteralMembers, // Members in object literal 7672 JsxAttributes, // Attributes in jsx element 7673 JsxChildren, // Things between opening and closing JSX tags 7674 ArrayLiteralMembers, // Members in array literal 7675 Parameters, // Parameters in parameter list 7676 JSDocParameters, // JSDoc parameters in parameter list of JSDoc function type 7677 RestProperties, // Property names in a rest type list 7678 TypeParameters, // Type parameters in type parameter list 7679 TypeArguments, // Type arguments in type argument list 7680 TupleElementTypes, // Element types in tuple element type list 7681 HeritageClauses, // Heritage clauses for a class or interface declaration. 7682 ImportOrExportSpecifiers, // Named import clause's import specifier list 7683 Count // Number of parsing contexts 7684 } 7685 7686 const enum Tristate { 7687 False, 7688 True, 7689 Unknown 7690 } 7691 7692 export namespace JSDocParser { 7693 export function parseJSDocTypeExpressionForTests(content: string, start: number | undefined, length: number | undefined): { jsDocTypeExpression: JSDocTypeExpression, diagnostics: Diagnostic[] } | undefined { 7694 initializeState("file.js", content, ScriptTarget.Latest, /*_syntaxCursor:*/ undefined, ScriptKind.JS); 7695 scanner.setText(content, start, length); 7696 currentToken = scanner.scan(); 7697 const jsDocTypeExpression = parseJSDocTypeExpression(); 7698 7699 const sourceFile = createSourceFile("file.js", ScriptTarget.Latest, ScriptKind.JS, /*isDeclarationFile*/ false, [], factory.createToken(SyntaxKind.EndOfFileToken), NodeFlags.None); 7700 const diagnostics = attachFileToDiagnostics(parseDiagnostics, sourceFile); 7701 if (jsDocDiagnostics) { 7702 sourceFile.jsDocDiagnostics = attachFileToDiagnostics(jsDocDiagnostics, sourceFile); 7703 } 7704 7705 clearState(); 7706 7707 return jsDocTypeExpression ? { jsDocTypeExpression, diagnostics } : undefined; 7708 } 7709 7710 // Parses out a JSDoc type expression. 7711 export function parseJSDocTypeExpression(mayOmitBraces?: boolean): JSDocTypeExpression { 7712 const pos = getNodePos(); 7713 const hasBrace = (mayOmitBraces ? parseOptional : parseExpected)(SyntaxKind.OpenBraceToken); 7714 const type = doInsideOfContext(NodeFlags.JSDoc, parseJSDocType); 7715 if (!mayOmitBraces || hasBrace) { 7716 parseExpectedJSDoc(SyntaxKind.CloseBraceToken); 7717 } 7718 7719 const result = factory.createJSDocTypeExpression(type); 7720 fixupParentReferences(result); 7721 return finishNode(result, pos); 7722 } 7723 7724 export function parseJSDocNameReference(): JSDocNameReference { 7725 const pos = getNodePos(); 7726 const hasBrace = parseOptional(SyntaxKind.OpenBraceToken); 7727 const entityName = parseEntityName(/* allowReservedWords*/ false); 7728 if (hasBrace) { 7729 parseExpectedJSDoc(SyntaxKind.CloseBraceToken); 7730 } 7731 7732 const result = factory.createJSDocNameReference(entityName); 7733 fixupParentReferences(result); 7734 return finishNode(result, pos); 7735 } 7736 7737 export function parseIsolatedJSDocComment(content: string, start: number | undefined, length: number | undefined): { jsDoc: JSDoc, diagnostics: Diagnostic[] } | undefined { 7738 initializeState("", content, ScriptTarget.Latest, /*_syntaxCursor:*/ undefined, ScriptKind.JS); 7739 const jsDoc = doInsideOfContext(NodeFlags.JSDoc, () => parseJSDocCommentWorker(start, length)); 7740 7741 const sourceFile = <SourceFile>{ languageVariant: LanguageVariant.Standard, text: content }; 7742 const diagnostics = attachFileToDiagnostics(parseDiagnostics, sourceFile); 7743 clearState(); 7744 7745 return jsDoc ? { jsDoc, diagnostics } : undefined; 7746 } 7747 7748 export function parseJSDocComment(parent: HasJSDoc, start: number, length: number): JSDoc | undefined { 7749 const saveToken = currentToken; 7750 const saveParseDiagnosticsLength = parseDiagnostics.length; 7751 const saveParseErrorBeforeNextFinishedNode = parseErrorBeforeNextFinishedNode; 7752 7753 const comment = doInsideOfContext(NodeFlags.JSDoc, () => parseJSDocCommentWorker(start, length)); 7754 setParent(comment, parent); 7755 7756 if (contextFlags & NodeFlags.JavaScriptFile) { 7757 if (!jsDocDiagnostics) { 7758 jsDocDiagnostics = []; 7759 } 7760 jsDocDiagnostics.push(...parseDiagnostics); 7761 } 7762 currentToken = saveToken; 7763 parseDiagnostics.length = saveParseDiagnosticsLength; 7764 parseErrorBeforeNextFinishedNode = saveParseErrorBeforeNextFinishedNode; 7765 return comment; 7766 } 7767 7768 const enum JSDocState { 7769 BeginningOfLine, 7770 SawAsterisk, 7771 SavingComments, 7772 SavingBackticks, // NOTE: Only used when parsing tag comments 7773 } 7774 7775 const enum PropertyLikeParse { 7776 Property = 1 << 0, 7777 Parameter = 1 << 1, 7778 CallbackParameter = 1 << 2, 7779 } 7780 7781 function parseJSDocCommentWorker(start = 0, length: number | undefined): JSDoc | undefined { 7782 const content = sourceText; 7783 const end = length === undefined ? content.length : start + length; 7784 length = end - start; 7785 7786 Debug.assert(start >= 0); 7787 Debug.assert(start <= end); 7788 Debug.assert(end <= content.length); 7789 7790 // Check for /** (JSDoc opening part) 7791 if (!isJSDocLikeText(content, start)) { 7792 return undefined; 7793 } 7794 7795 let tags: JSDocTag[]; 7796 let tagsPos: number; 7797 let tagsEnd: number; 7798 const comments: string[] = []; 7799 7800 // + 3 for leading /**, - 5 in total for /** */ 7801 return scanner.scanRange(start + 3, length - 5, () => { 7802 // Initially we can parse out a tag. We also have seen a starting asterisk. 7803 // This is so that /** * @type */ doesn't parse. 7804 let state = JSDocState.SawAsterisk; 7805 let margin: number | undefined; 7806 // + 4 for leading '/** ' 7807 // + 1 because the last index of \n is always one index before the first character in the line and coincidentally, if there is no \n before start, it is -1, which is also one index before the first character 7808 let indent = start - (content.lastIndexOf("\n", start) + 1) + 4; 7809 function pushComment(text: string) { 7810 if (!margin) { 7811 margin = indent; 7812 } 7813 comments.push(text); 7814 indent += text.length; 7815 } 7816 7817 nextTokenJSDoc(); 7818 while (parseOptionalJsdoc(SyntaxKind.WhitespaceTrivia)); 7819 if (parseOptionalJsdoc(SyntaxKind.NewLineTrivia)) { 7820 state = JSDocState.BeginningOfLine; 7821 indent = 0; 7822 } 7823 loop: while (true) { 7824 switch (token()) { 7825 case SyntaxKind.AtToken: 7826 if (state === JSDocState.BeginningOfLine || state === JSDocState.SawAsterisk) { 7827 removeTrailingWhitespace(comments); 7828 addTag(parseTag(indent)); 7829 // NOTE: According to usejsdoc.org, a tag goes to end of line, except the last tag. 7830 // Real-world comments may break this rule, so "BeginningOfLine" will not be a real line beginning 7831 // for malformed examples like `/** @param {string} x @returns {number} the length */` 7832 state = JSDocState.BeginningOfLine; 7833 margin = undefined; 7834 } 7835 else { 7836 pushComment(scanner.getTokenText()); 7837 } 7838 break; 7839 case SyntaxKind.NewLineTrivia: 7840 comments.push(scanner.getTokenText()); 7841 state = JSDocState.BeginningOfLine; 7842 indent = 0; 7843 break; 7844 case SyntaxKind.AsteriskToken: 7845 const asterisk = scanner.getTokenText(); 7846 if (state === JSDocState.SawAsterisk || state === JSDocState.SavingComments) { 7847 // If we've already seen an asterisk, then we can no longer parse a tag on this line 7848 state = JSDocState.SavingComments; 7849 pushComment(asterisk); 7850 } 7851 else { 7852 // Ignore the first asterisk on a line 7853 state = JSDocState.SawAsterisk; 7854 indent += asterisk.length; 7855 } 7856 break; 7857 case SyntaxKind.WhitespaceTrivia: 7858 // only collect whitespace if we're already saving comments or have just crossed the comment indent margin 7859 const whitespace = scanner.getTokenText(); 7860 if (state === JSDocState.SavingComments) { 7861 comments.push(whitespace); 7862 } 7863 else if (margin !== undefined && indent + whitespace.length > margin) { 7864 comments.push(whitespace.slice(margin - indent)); 7865 } 7866 indent += whitespace.length; 7867 break; 7868 case SyntaxKind.EndOfFileToken: 7869 break loop; 7870 default: 7871 // Anything else is doc comment text. We just save it. Because it 7872 // wasn't a tag, we can no longer parse a tag on this line until we hit the next 7873 // line break. 7874 state = JSDocState.SavingComments; 7875 pushComment(scanner.getTokenText()); 7876 break; 7877 } 7878 nextTokenJSDoc(); 7879 } 7880 removeLeadingNewlines(comments); 7881 removeTrailingWhitespace(comments); 7882 return createJSDocComment(); 7883 }); 7884 7885 function removeLeadingNewlines(comments: string[]) { 7886 while (comments.length && (comments[0] === "\n" || comments[0] === "\r")) { 7887 comments.shift(); 7888 } 7889 } 7890 7891 function removeTrailingWhitespace(comments: string[]) { 7892 while (comments.length && comments[comments.length - 1].trim() === "") { 7893 comments.pop(); 7894 } 7895 } 7896 7897 function createJSDocComment(): JSDoc { 7898 const comment = comments.length ? comments.join("") : undefined; 7899 const tagsArray = tags && createNodeArray(tags, tagsPos, tagsEnd); 7900 return finishNode(factory.createJSDocComment(comment, tagsArray), start, end); 7901 } 7902 7903 function isNextNonwhitespaceTokenEndOfFile(): boolean { 7904 // We must use infinite lookahead, as there could be any number of newlines :( 7905 while (true) { 7906 nextTokenJSDoc(); 7907 if (token() === SyntaxKind.EndOfFileToken) { 7908 return true; 7909 } 7910 if (!(token() === SyntaxKind.WhitespaceTrivia || token() === SyntaxKind.NewLineTrivia)) { 7911 return false; 7912 } 7913 } 7914 } 7915 7916 function skipWhitespace(): void { 7917 if (token() === SyntaxKind.WhitespaceTrivia || token() === SyntaxKind.NewLineTrivia) { 7918 if (lookAhead(isNextNonwhitespaceTokenEndOfFile)) { 7919 return; // Don't skip whitespace prior to EoF (or end of comment) - that shouldn't be included in any node's range 7920 } 7921 } 7922 while (token() === SyntaxKind.WhitespaceTrivia || token() === SyntaxKind.NewLineTrivia) { 7923 nextTokenJSDoc(); 7924 } 7925 } 7926 7927 function skipWhitespaceOrAsterisk(): string { 7928 if (token() === SyntaxKind.WhitespaceTrivia || token() === SyntaxKind.NewLineTrivia) { 7929 if (lookAhead(isNextNonwhitespaceTokenEndOfFile)) { 7930 return ""; // Don't skip whitespace prior to EoF (or end of comment) - that shouldn't be included in any node's range 7931 } 7932 } 7933 7934 let precedingLineBreak = scanner.hasPrecedingLineBreak(); 7935 let seenLineBreak = false; 7936 let indentText = ""; 7937 while ((precedingLineBreak && token() === SyntaxKind.AsteriskToken) || token() === SyntaxKind.WhitespaceTrivia || token() === SyntaxKind.NewLineTrivia) { 7938 indentText += scanner.getTokenText(); 7939 if (token() === SyntaxKind.NewLineTrivia) { 7940 precedingLineBreak = true; 7941 seenLineBreak = true; 7942 indentText = ""; 7943 } 7944 else if (token() === SyntaxKind.AsteriskToken) { 7945 precedingLineBreak = false; 7946 } 7947 nextTokenJSDoc(); 7948 } 7949 return seenLineBreak ? indentText : ""; 7950 } 7951 7952 function parseTag(margin: number) { 7953 Debug.assert(token() === SyntaxKind.AtToken); 7954 const start = scanner.getTokenPos(); 7955 nextTokenJSDoc(); 7956 7957 const tagName = parseJSDocIdentifierName(/*message*/ undefined); 7958 const indentText = skipWhitespaceOrAsterisk(); 7959 7960 let tag: JSDocTag | undefined; 7961 switch (tagName.escapedText) { 7962 case "author": 7963 tag = parseAuthorTag(start, tagName, margin, indentText); 7964 break; 7965 case "implements": 7966 tag = parseImplementsTag(start, tagName, margin, indentText); 7967 break; 7968 case "augments": 7969 case "extends": 7970 tag = parseAugmentsTag(start, tagName, margin, indentText); 7971 break; 7972 case "class": 7973 case "constructor": 7974 tag = parseSimpleTag(start, factory.createJSDocClassTag, tagName, margin, indentText); 7975 break; 7976 case "public": 7977 tag = parseSimpleTag(start, factory.createJSDocPublicTag, tagName, margin, indentText); 7978 break; 7979 case "private": 7980 tag = parseSimpleTag(start, factory.createJSDocPrivateTag, tagName, margin, indentText); 7981 break; 7982 case "protected": 7983 tag = parseSimpleTag(start, factory.createJSDocProtectedTag, tagName, margin, indentText); 7984 break; 7985 case "readonly": 7986 tag = parseSimpleTag(start, factory.createJSDocReadonlyTag, tagName, margin, indentText); 7987 break; 7988 case "deprecated": 7989 hasDeprecatedTag = true; 7990 tag = parseSimpleTag(start, factory.createJSDocDeprecatedTag, tagName, margin, indentText); 7991 break; 7992 case "this": 7993 tag = parseThisTag(start, tagName, margin, indentText); 7994 break; 7995 case "enum": 7996 tag = parseEnumTag(start, tagName, margin, indentText); 7997 break; 7998 case "arg": 7999 case "argument": 8000 case "param": 8001 return parseParameterOrPropertyTag(start, tagName, PropertyLikeParse.Parameter, margin); 8002 case "return": 8003 case "returns": 8004 tag = parseReturnTag(start, tagName, margin, indentText); 8005 break; 8006 case "template": 8007 tag = parseTemplateTag(start, tagName, margin, indentText); 8008 break; 8009 case "type": 8010 tag = parseTypeTag(start, tagName, margin, indentText); 8011 break; 8012 case "typedef": 8013 tag = parseTypedefTag(start, tagName, margin, indentText); 8014 break; 8015 case "callback": 8016 tag = parseCallbackTag(start, tagName, margin, indentText); 8017 break; 8018 case "see": 8019 tag = parseSeeTag(start, tagName, margin, indentText); 8020 break; 8021 default: 8022 tag = parseUnknownTag(start, tagName, margin, indentText); 8023 break; 8024 } 8025 return tag; 8026 } 8027 8028 function parseTrailingTagComments(pos: number, end: number, margin: number, indentText: string) { 8029 // some tags, like typedef and callback, have already parsed their comments earlier 8030 if (!indentText) { 8031 margin += end - pos; 8032 } 8033 return parseTagComments(margin, indentText.slice(margin)); 8034 } 8035 8036 function parseTagComments(indent: number, initialMargin?: string): string | undefined { 8037 const comments: string[] = []; 8038 let state = JSDocState.BeginningOfLine; 8039 let previousWhitespace = true; 8040 let margin: number | undefined; 8041 function pushComment(text: string) { 8042 if (!margin) { 8043 margin = indent; 8044 } 8045 comments.push(text); 8046 indent += text.length; 8047 } 8048 if (initialMargin !== undefined) { 8049 // jump straight to saving comments if there is some initial indentation 8050 if (initialMargin !== "") { 8051 pushComment(initialMargin); 8052 } 8053 state = JSDocState.SawAsterisk; 8054 } 8055 let tok = token() as JSDocSyntaxKind; 8056 loop: while (true) { 8057 switch (tok) { 8058 case SyntaxKind.NewLineTrivia: 8059 state = JSDocState.BeginningOfLine; 8060 // don't use pushComment here because we want to keep the margin unchanged 8061 comments.push(scanner.getTokenText()); 8062 indent = 0; 8063 break; 8064 case SyntaxKind.AtToken: 8065 if (state === JSDocState.SavingBackticks || !previousWhitespace && state === JSDocState.SavingComments) { 8066 // @ doesn't start a new tag inside ``, and inside a comment, only after whitespace 8067 comments.push(scanner.getTokenText()); 8068 break; 8069 } 8070 scanner.setTextPos(scanner.getTextPos() - 1); 8071 // falls through 8072 case SyntaxKind.EndOfFileToken: 8073 // Done 8074 break loop; 8075 case SyntaxKind.WhitespaceTrivia: 8076 if (state === JSDocState.SavingComments || state === JSDocState.SavingBackticks) { 8077 pushComment(scanner.getTokenText()); 8078 } 8079 else { 8080 const whitespace = scanner.getTokenText(); 8081 // if the whitespace crosses the margin, take only the whitespace that passes the margin 8082 if (margin !== undefined && indent + whitespace.length > margin) { 8083 comments.push(whitespace.slice(margin - indent)); 8084 } 8085 indent += whitespace.length; 8086 } 8087 break; 8088 case SyntaxKind.OpenBraceToken: 8089 state = JSDocState.SavingComments; 8090 if (lookAhead(() => nextTokenJSDoc() === SyntaxKind.AtToken && tokenIsIdentifierOrKeyword(nextTokenJSDoc()) && scanner.getTokenText() === "link")) { 8091 pushComment(scanner.getTokenText()); 8092 nextTokenJSDoc(); 8093 pushComment(scanner.getTokenText()); 8094 nextTokenJSDoc(); 8095 } 8096 pushComment(scanner.getTokenText()); 8097 break; 8098 case SyntaxKind.BacktickToken: 8099 if (state === JSDocState.SavingBackticks) { 8100 state = JSDocState.SavingComments; 8101 } 8102 else { 8103 state = JSDocState.SavingBackticks; 8104 } 8105 pushComment(scanner.getTokenText()); 8106 break; 8107 case SyntaxKind.AsteriskToken: 8108 if (state === JSDocState.BeginningOfLine) { 8109 // leading asterisks start recording on the *next* (non-whitespace) token 8110 state = JSDocState.SawAsterisk; 8111 indent += 1; 8112 break; 8113 } 8114 // record the * as a comment 8115 // falls through 8116 default: 8117 if (state !== JSDocState.SavingBackticks) { 8118 state = JSDocState.SavingComments; // leading identifiers start recording as well 8119 } 8120 pushComment(scanner.getTokenText()); 8121 break; 8122 } 8123 previousWhitespace = token() === SyntaxKind.WhitespaceTrivia; 8124 tok = nextTokenJSDoc(); 8125 } 8126 8127 removeLeadingNewlines(comments); 8128 removeTrailingWhitespace(comments); 8129 return comments.length === 0 ? undefined : comments.join(""); 8130 } 8131 8132 function parseUnknownTag(start: number, tagName: Identifier, indent: number, indentText: string) { 8133 const end = getNodePos(); 8134 return finishNode(factory.createJSDocUnknownTag(tagName, parseTrailingTagComments(start, end, indent, indentText)), start, end); 8135 } 8136 8137 function addTag(tag: JSDocTag | undefined): void { 8138 if (!tag) { 8139 return; 8140 } 8141 if (!tags) { 8142 tags = [tag]; 8143 tagsPos = tag.pos; 8144 } 8145 else { 8146 tags.push(tag); 8147 } 8148 tagsEnd = tag.end; 8149 } 8150 8151 function tryParseTypeExpression(): JSDocTypeExpression | undefined { 8152 skipWhitespaceOrAsterisk(); 8153 return token() === SyntaxKind.OpenBraceToken ? parseJSDocTypeExpression() : undefined; 8154 } 8155 8156 function parseBracketNameInPropertyAndParamTag(): { name: EntityName, isBracketed: boolean } { 8157 // Looking for something like '[foo]', 'foo', '[foo.bar]' or 'foo.bar' 8158 const isBracketed = parseOptionalJsdoc(SyntaxKind.OpenBracketToken); 8159 if (isBracketed) { 8160 skipWhitespace(); 8161 } 8162 // a markdown-quoted name: `arg` is not legal jsdoc, but occurs in the wild 8163 const isBackquoted = parseOptionalJsdoc(SyntaxKind.BacktickToken); 8164 const name = parseJSDocEntityName(); 8165 if (isBackquoted) { 8166 parseExpectedTokenJSDoc(SyntaxKind.BacktickToken); 8167 } 8168 if (isBracketed) { 8169 skipWhitespace(); 8170 // May have an optional default, e.g. '[foo = 42]' 8171 if (parseOptionalToken(SyntaxKind.EqualsToken)) { 8172 parseExpression(); 8173 } 8174 8175 parseExpected(SyntaxKind.CloseBracketToken); 8176 } 8177 8178 return { name, isBracketed }; 8179 } 8180 8181 function isObjectOrObjectArrayTypeReference(node: TypeNode): boolean { 8182 switch (node.kind) { 8183 case SyntaxKind.ObjectKeyword: 8184 return true; 8185 case SyntaxKind.ArrayType: 8186 return isObjectOrObjectArrayTypeReference((node as ArrayTypeNode).elementType); 8187 default: 8188 return isTypeReferenceNode(node) && ts.isIdentifier(node.typeName) && node.typeName.escapedText === "Object" && !node.typeArguments; 8189 } 8190 } 8191 8192 function parseParameterOrPropertyTag(start: number, tagName: Identifier, target: PropertyLikeParse, indent: number): JSDocParameterTag | JSDocPropertyTag { 8193 let typeExpression = tryParseTypeExpression(); 8194 let isNameFirst = !typeExpression; 8195 skipWhitespaceOrAsterisk(); 8196 8197 const { name, isBracketed } = parseBracketNameInPropertyAndParamTag(); 8198 const indentText = skipWhitespaceOrAsterisk(); 8199 8200 if (isNameFirst) { 8201 typeExpression = tryParseTypeExpression(); 8202 } 8203 8204 const comment = parseTrailingTagComments(start, getNodePos(), indent, indentText); 8205 8206 const nestedTypeLiteral = target !== PropertyLikeParse.CallbackParameter && parseNestedTypeLiteral(typeExpression, name, target, indent); 8207 if (nestedTypeLiteral) { 8208 typeExpression = nestedTypeLiteral; 8209 isNameFirst = true; 8210 } 8211 const result = target === PropertyLikeParse.Property 8212 ? factory.createJSDocPropertyTag(tagName, name, isBracketed, typeExpression, isNameFirst, comment) 8213 : factory.createJSDocParameterTag(tagName, name, isBracketed, typeExpression, isNameFirst, comment); 8214 return finishNode(result, start); 8215 } 8216 8217 function parseNestedTypeLiteral(typeExpression: JSDocTypeExpression | undefined, name: EntityName, target: PropertyLikeParse, indent: number) { 8218 if (typeExpression && isObjectOrObjectArrayTypeReference(typeExpression.type)) { 8219 const pos = getNodePos(); 8220 let child: JSDocPropertyLikeTag | JSDocTypeTag | false; 8221 let children: JSDocPropertyLikeTag[] | undefined; 8222 while (child = tryParse(() => parseChildParameterOrPropertyTag(target, indent, name))) { 8223 if (child.kind === SyntaxKind.JSDocParameterTag || child.kind === SyntaxKind.JSDocPropertyTag) { 8224 children = append(children, child); 8225 } 8226 } 8227 if (children) { 8228 const literal = finishNode(factory.createJSDocTypeLiteral(children, typeExpression.type.kind === SyntaxKind.ArrayType), pos); 8229 return finishNode(factory.createJSDocTypeExpression(literal), pos); 8230 } 8231 } 8232 } 8233 8234 function parseReturnTag(start: number, tagName: Identifier, indent: number, indentText: string): JSDocReturnTag { 8235 if (some(tags, isJSDocReturnTag)) { 8236 parseErrorAt(tagName.pos, scanner.getTokenPos(), Diagnostics._0_tag_already_specified, tagName.escapedText); 8237 } 8238 8239 const typeExpression = tryParseTypeExpression(); 8240 const end = getNodePos(); 8241 return finishNode(factory.createJSDocReturnTag(tagName, typeExpression, parseTrailingTagComments(start, end, indent, indentText)), start, end); 8242 } 8243 8244 function parseTypeTag(start: number, tagName: Identifier, indent?: number, indentText?: string): JSDocTypeTag { 8245 if (some(tags, isJSDocTypeTag)) { 8246 parseErrorAt(tagName.pos, scanner.getTokenPos(), Diagnostics._0_tag_already_specified, tagName.escapedText); 8247 } 8248 8249 const typeExpression = parseJSDocTypeExpression(/*mayOmitBraces*/ true); 8250 const end = getNodePos(); 8251 const comments = indent !== undefined && indentText !== undefined ? parseTrailingTagComments(start, end, indent, indentText) : undefined; 8252 return finishNode(factory.createJSDocTypeTag(tagName, typeExpression, comments), start, end); 8253 } 8254 8255 function parseSeeTag(start: number, tagName: Identifier, indent?: number, indentText?: string): JSDocSeeTag { 8256 const nameExpression = parseJSDocNameReference(); 8257 const end = getNodePos(); 8258 const comments = indent !== undefined && indentText !== undefined ? parseTrailingTagComments(start, end, indent, indentText) : undefined; 8259 return finishNode(factory.createJSDocSeeTag(tagName, nameExpression, comments), start, end); 8260 } 8261 8262 function parseAuthorTag(start: number, tagName: Identifier, indent: number, indentText: string): JSDocAuthorTag { 8263 const comments = parseAuthorNameAndEmail() + (parseTrailingTagComments(start, end, indent, indentText) || ""); 8264 return finishNode(factory.createJSDocAuthorTag(tagName, comments || undefined), start); 8265 } 8266 8267 function parseAuthorNameAndEmail(): string { 8268 const comments: string[] = []; 8269 let inEmail = false; 8270 let token = scanner.getToken(); 8271 while (token !== SyntaxKind.EndOfFileToken && token !== SyntaxKind.NewLineTrivia) { 8272 if (token === SyntaxKind.LessThanToken) { 8273 inEmail = true; 8274 } 8275 else if (token === SyntaxKind.AtToken && !inEmail) { 8276 break; 8277 } 8278 else if (token === SyntaxKind.GreaterThanToken && inEmail) { 8279 comments.push(scanner.getTokenText()); 8280 scanner.setTextPos(scanner.getTokenPos() + 1); 8281 break; 8282 } 8283 comments.push(scanner.getTokenText()); 8284 token = nextTokenJSDoc(); 8285 } 8286 8287 return comments.join(""); 8288 } 8289 8290 function parseImplementsTag(start: number, tagName: Identifier, margin: number, indentText: string): JSDocImplementsTag { 8291 const className = parseExpressionWithTypeArgumentsForAugments(); 8292 const end = getNodePos(); 8293 return finishNode(factory.createJSDocImplementsTag(tagName, className, parseTrailingTagComments(start, end, margin, indentText)), start, end); 8294 } 8295 8296 function parseAugmentsTag(start: number, tagName: Identifier, margin: number, indentText: string): JSDocAugmentsTag { 8297 const className = parseExpressionWithTypeArgumentsForAugments(); 8298 const end = getNodePos(); 8299 return finishNode(factory.createJSDocAugmentsTag(tagName, className, parseTrailingTagComments(start, end, margin, indentText)), start, end); 8300 } 8301 8302 function parseExpressionWithTypeArgumentsForAugments(): ExpressionWithTypeArguments & { expression: Identifier | PropertyAccessEntityNameExpression } { 8303 const usedBrace = parseOptional(SyntaxKind.OpenBraceToken); 8304 const pos = getNodePos(); 8305 const expression = parsePropertyAccessEntityNameExpression(); 8306 const typeArguments = tryParseTypeArguments(); 8307 const node = factory.createExpressionWithTypeArguments(expression, typeArguments) as ExpressionWithTypeArguments & { expression: Identifier | PropertyAccessEntityNameExpression }; 8308 const res = finishNode(node, pos); 8309 if (usedBrace) { 8310 parseExpected(SyntaxKind.CloseBraceToken); 8311 } 8312 return res; 8313 } 8314 8315 function parsePropertyAccessEntityNameExpression() { 8316 const pos = getNodePos(); 8317 let node: Identifier | PropertyAccessEntityNameExpression = parseJSDocIdentifierName(); 8318 while (parseOptional(SyntaxKind.DotToken)) { 8319 const name = parseJSDocIdentifierName(); 8320 node = finishNode(factory.createPropertyAccessExpression(node, name), pos) as PropertyAccessEntityNameExpression; 8321 } 8322 return node; 8323 } 8324 8325 function parseSimpleTag(start: number, createTag: (tagName: Identifier | undefined, comment?: string) => JSDocTag, tagName: Identifier, margin: number, indentText: string): JSDocTag { 8326 const end = getNodePos(); 8327 return finishNode(createTag(tagName, parseTrailingTagComments(start, end, margin, indentText)), start, end); 8328 } 8329 8330 function parseThisTag(start: number, tagName: Identifier, margin: number, indentText: string): JSDocThisTag { 8331 const typeExpression = parseJSDocTypeExpression(/*mayOmitBraces*/ true); 8332 skipWhitespace(); 8333 const end = getNodePos(); 8334 return finishNode(factory.createJSDocThisTag(tagName, typeExpression, parseTrailingTagComments(start, end, margin, indentText)), start, end); 8335 } 8336 8337 function parseEnumTag(start: number, tagName: Identifier, margin: number, indentText: string): JSDocEnumTag { 8338 const typeExpression = parseJSDocTypeExpression(/*mayOmitBraces*/ true); 8339 skipWhitespace(); 8340 const end = getNodePos(); 8341 return finishNode(factory.createJSDocEnumTag(tagName, typeExpression, parseTrailingTagComments(start, end, margin, indentText)), start, end); 8342 } 8343 8344 function parseTypedefTag(start: number, tagName: Identifier, indent: number, indentText: string): JSDocTypedefTag { 8345 let typeExpression: JSDocTypeExpression | JSDocTypeLiteral | undefined = tryParseTypeExpression(); 8346 skipWhitespaceOrAsterisk(); 8347 8348 const fullName = parseJSDocTypeNameWithNamespace(); 8349 skipWhitespace(); 8350 let comment = parseTagComments(indent); 8351 8352 let end: number | undefined; 8353 if (!typeExpression || isObjectOrObjectArrayTypeReference(typeExpression.type)) { 8354 let child: JSDocTypeTag | JSDocPropertyTag | false; 8355 let childTypeTag: JSDocTypeTag | undefined; 8356 let jsDocPropertyTags: JSDocPropertyTag[] | undefined; 8357 let hasChildren = false; 8358 while (child = tryParse(() => parseChildPropertyTag(indent))) { 8359 hasChildren = true; 8360 if (child.kind === SyntaxKind.JSDocTypeTag) { 8361 if (childTypeTag) { 8362 parseErrorAtCurrentToken(Diagnostics.A_JSDoc_typedef_comment_may_not_contain_multiple_type_tags); 8363 const lastError = lastOrUndefined(parseDiagnostics); 8364 if (lastError) { 8365 addRelatedInfo( 8366 lastError, 8367 createDetachedDiagnostic(fileName, 0, 0, Diagnostics.The_tag_was_first_specified_here) 8368 ); 8369 } 8370 break; 8371 } 8372 else { 8373 childTypeTag = child; 8374 } 8375 } 8376 else { 8377 jsDocPropertyTags = append(jsDocPropertyTags, child); 8378 } 8379 } 8380 if (hasChildren) { 8381 const isArrayType = typeExpression && typeExpression.type.kind === SyntaxKind.ArrayType; 8382 const jsdocTypeLiteral = factory.createJSDocTypeLiteral(jsDocPropertyTags, isArrayType); 8383 typeExpression = childTypeTag && childTypeTag.typeExpression && !isObjectOrObjectArrayTypeReference(childTypeTag.typeExpression.type) ? 8384 childTypeTag.typeExpression : 8385 finishNode(jsdocTypeLiteral, start); 8386 end = typeExpression.end; 8387 } 8388 } 8389 8390 // Only include the characters between the name end and the next token if a comment was actually parsed out - otherwise it's just whitespace 8391 end = end || comment !== undefined ? 8392 getNodePos() : 8393 (fullName ?? typeExpression ?? tagName).end; 8394 8395 if (!comment) { 8396 comment = parseTrailingTagComments(start, end, indent, indentText); 8397 } 8398 8399 const typedefTag = factory.createJSDocTypedefTag(tagName, typeExpression, fullName, comment); 8400 return finishNode(typedefTag, start, end); 8401 } 8402 8403 function parseJSDocTypeNameWithNamespace(nested?: boolean) { 8404 const pos = scanner.getTokenPos(); 8405 if (!tokenIsIdentifierOrKeyword(token())) { 8406 return undefined; 8407 } 8408 const typeNameOrNamespaceName = parseJSDocIdentifierName(); 8409 if (parseOptional(SyntaxKind.DotToken)) { 8410 const body = parseJSDocTypeNameWithNamespace(/*nested*/ true); 8411 const jsDocNamespaceNode = factory.createModuleDeclaration( 8412 /*decorators*/ undefined, 8413 /*modifiers*/ undefined, 8414 typeNameOrNamespaceName, 8415 body, 8416 nested ? NodeFlags.NestedNamespace : undefined 8417 ) as JSDocNamespaceDeclaration; 8418 return finishNode(jsDocNamespaceNode, pos); 8419 } 8420 8421 if (nested) { 8422 typeNameOrNamespaceName.isInJSDocNamespace = true; 8423 } 8424 return typeNameOrNamespaceName; 8425 } 8426 8427 8428 function parseCallbackTagParameters(indent: number) { 8429 const pos = getNodePos(); 8430 let child: JSDocParameterTag | false; 8431 let parameters; 8432 while (child = tryParse(() => parseChildParameterOrPropertyTag(PropertyLikeParse.CallbackParameter, indent) as JSDocParameterTag)) { 8433 parameters = append(parameters, child); 8434 } 8435 return createNodeArray(parameters || [], pos); 8436 } 8437 8438 function parseCallbackTag(start: number, tagName: Identifier, indent: number, indentText: string): JSDocCallbackTag { 8439 const fullName = parseJSDocTypeNameWithNamespace(); 8440 skipWhitespace(); 8441 let comment = parseTagComments(indent); 8442 const parameters = parseCallbackTagParameters(indent); 8443 const returnTag = tryParse(() => { 8444 if (parseOptionalJsdoc(SyntaxKind.AtToken)) { 8445 const tag = parseTag(indent); 8446 if (tag && tag.kind === SyntaxKind.JSDocReturnTag) { 8447 return tag as JSDocReturnTag; 8448 } 8449 } 8450 }); 8451 const typeExpression = finishNode(factory.createJSDocSignature(/*typeParameters*/ undefined, parameters, returnTag), start); 8452 const end = getNodePos(); 8453 if (!comment) { 8454 comment = parseTrailingTagComments(start, end, indent, indentText); 8455 } 8456 return finishNode(factory.createJSDocCallbackTag(tagName, typeExpression, fullName, comment), start, end); 8457 } 8458 8459 function escapedTextsEqual(a: EntityName, b: EntityName): boolean { 8460 while (!ts.isIdentifier(a) || !ts.isIdentifier(b)) { 8461 if (!ts.isIdentifier(a) && !ts.isIdentifier(b) && a.right.escapedText === b.right.escapedText) { 8462 a = a.left; 8463 b = b.left; 8464 } 8465 else { 8466 return false; 8467 } 8468 } 8469 return a.escapedText === b.escapedText; 8470 } 8471 8472 function parseChildPropertyTag(indent: number) { 8473 return parseChildParameterOrPropertyTag(PropertyLikeParse.Property, indent) as JSDocTypeTag | JSDocPropertyTag | false; 8474 } 8475 8476 function parseChildParameterOrPropertyTag(target: PropertyLikeParse, indent: number, name?: EntityName): JSDocTypeTag | JSDocPropertyTag | JSDocParameterTag | false { 8477 let canParseTag = true; 8478 let seenAsterisk = false; 8479 while (true) { 8480 switch (nextTokenJSDoc()) { 8481 case SyntaxKind.AtToken: 8482 if (canParseTag) { 8483 const child = tryParseChildTag(target, indent); 8484 if (child && (child.kind === SyntaxKind.JSDocParameterTag || child.kind === SyntaxKind.JSDocPropertyTag) && 8485 target !== PropertyLikeParse.CallbackParameter && 8486 name && (ts.isIdentifier(child.name) || !escapedTextsEqual(name, child.name.left))) { 8487 return false; 8488 } 8489 return child; 8490 } 8491 seenAsterisk = false; 8492 break; 8493 case SyntaxKind.NewLineTrivia: 8494 canParseTag = true; 8495 seenAsterisk = false; 8496 break; 8497 case SyntaxKind.AsteriskToken: 8498 if (seenAsterisk) { 8499 canParseTag = false; 8500 } 8501 seenAsterisk = true; 8502 break; 8503 case SyntaxKind.Identifier: 8504 canParseTag = false; 8505 break; 8506 case SyntaxKind.EndOfFileToken: 8507 return false; 8508 } 8509 } 8510 } 8511 8512 function tryParseChildTag(target: PropertyLikeParse, indent: number): JSDocTypeTag | JSDocPropertyTag | JSDocParameterTag | false { 8513 Debug.assert(token() === SyntaxKind.AtToken); 8514 const start = scanner.getStartPos(); 8515 nextTokenJSDoc(); 8516 8517 const tagName = parseJSDocIdentifierName(); 8518 skipWhitespace(); 8519 let t: PropertyLikeParse; 8520 switch (tagName.escapedText) { 8521 case "type": 8522 return target === PropertyLikeParse.Property && parseTypeTag(start, tagName); 8523 case "prop": 8524 case "property": 8525 t = PropertyLikeParse.Property; 8526 break; 8527 case "arg": 8528 case "argument": 8529 case "param": 8530 t = PropertyLikeParse.Parameter | PropertyLikeParse.CallbackParameter; 8531 break; 8532 default: 8533 return false; 8534 } 8535 if (!(target & t)) { 8536 return false; 8537 } 8538 return parseParameterOrPropertyTag(start, tagName, target, indent); 8539 } 8540 8541 function parseTemplateTagTypeParameter() { 8542 const typeParameterPos = getNodePos(); 8543 const name = parseJSDocIdentifierName(Diagnostics.Unexpected_token_A_type_parameter_name_was_expected_without_curly_braces); 8544 return finishNode(factory.createTypeParameterDeclaration(name, /*constraint*/ undefined, /*defaultType*/ undefined), typeParameterPos); 8545 } 8546 8547 function parseTemplateTagTypeParameters() { 8548 const pos = getNodePos(); 8549 const typeParameters = []; 8550 do { 8551 skipWhitespace(); 8552 typeParameters.push(parseTemplateTagTypeParameter()); 8553 skipWhitespaceOrAsterisk(); 8554 } while (parseOptionalJsdoc(SyntaxKind.CommaToken)); 8555 return createNodeArray(typeParameters, pos); 8556 } 8557 8558 function parseTemplateTag(start: number, tagName: Identifier, indent: number, indentText: string): JSDocTemplateTag { 8559 // The template tag looks like one of the following: 8560 // @template T,U,V 8561 // @template {Constraint} T 8562 // 8563 // According to the [closure docs](https://github.com/google/closure-compiler/wiki/Generic-Types#multiple-bounded-template-types): 8564 // > Multiple bounded generics cannot be declared on the same line. For the sake of clarity, if multiple templates share the same 8565 // > type bound they must be declared on separate lines. 8566 // 8567 // TODO: Determine whether we should enforce this in the checker. 8568 // TODO: Consider moving the `constraint` to the first type parameter as we could then remove `getEffectiveConstraintOfTypeParameter`. 8569 // TODO: Consider only parsing a single type parameter if there is a constraint. 8570 const constraint = token() === SyntaxKind.OpenBraceToken ? parseJSDocTypeExpression() : undefined; 8571 const typeParameters = parseTemplateTagTypeParameters(); 8572 const end = getNodePos(); 8573 return finishNode(factory.createJSDocTemplateTag(tagName, constraint, typeParameters, parseTrailingTagComments(start, end, indent, indentText)), start, end); 8574 } 8575 8576 function parseOptionalJsdoc(t: JSDocSyntaxKind): boolean { 8577 if (token() === t) { 8578 nextTokenJSDoc(); 8579 return true; 8580 } 8581 return false; 8582 } 8583 8584 function parseJSDocEntityName(): EntityName { 8585 let entity: EntityName = parseJSDocIdentifierName(); 8586 if (parseOptional(SyntaxKind.OpenBracketToken)) { 8587 parseExpected(SyntaxKind.CloseBracketToken); 8588 // Note that y[] is accepted as an entity name, but the postfix brackets are not saved for checking. 8589 // Technically usejsdoc.org requires them for specifying a property of a type equivalent to Array<{ x: ...}> 8590 // but it's not worth it to enforce that restriction. 8591 } 8592 while (parseOptional(SyntaxKind.DotToken)) { 8593 const name = parseJSDocIdentifierName(); 8594 if (parseOptional(SyntaxKind.OpenBracketToken)) { 8595 parseExpected(SyntaxKind.CloseBracketToken); 8596 } 8597 entity = createQualifiedName(entity, name); 8598 } 8599 return entity; 8600 } 8601 8602 function parseJSDocIdentifierName(message?: DiagnosticMessage): Identifier { 8603 if (!tokenIsIdentifierOrKeyword(token())) { 8604 return createMissingNode<Identifier>(SyntaxKind.Identifier, /*reportAtCurrentPosition*/ !message, message || Diagnostics.Identifier_expected); 8605 } 8606 8607 identifierCount++; 8608 const pos = scanner.getTokenPos(); 8609 const end = scanner.getTextPos(); 8610 const originalKeywordKind = token(); 8611 const text = internIdentifier(scanner.getTokenValue()); 8612 const result = finishNode(factory.createIdentifier(text, /*typeArguments*/ undefined, originalKeywordKind), pos, end); 8613 nextTokenJSDoc(); 8614 return result; 8615 } 8616 } 8617 } 8618 } 8619 8620 namespace IncrementalParser { 8621 export function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks: boolean): SourceFile { 8622 aggressiveChecks = aggressiveChecks || Debug.shouldAssert(AssertionLevel.Aggressive); 8623 8624 checkChangeRange(sourceFile, newText, textChangeRange, aggressiveChecks); 8625 if (textChangeRangeIsUnchanged(textChangeRange)) { 8626 // if the text didn't change, then we can just return our current source file as-is. 8627 return sourceFile; 8628 } 8629 8630 if (sourceFile.statements.length === 0) { 8631 // If we don't have any statements in the current source file, then there's no real 8632 // way to incrementally parse. So just do a full parse instead. 8633 return Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, /*syntaxCursor*/ undefined, /*setParentNodes*/ true, sourceFile.scriptKind); 8634 } 8635 8636 // Make sure we're not trying to incrementally update a source file more than once. Once 8637 // we do an update the original source file is considered unusable from that point onwards. 8638 // 8639 // This is because we do incremental parsing in-place. i.e. we take nodes from the old 8640 // tree and give them new positions and parents. From that point on, trusting the old 8641 // tree at all is not possible as far too much of it may violate invariants. 8642 const incrementalSourceFile = <IncrementalNode><Node>sourceFile; 8643 Debug.assert(!incrementalSourceFile.hasBeenIncrementallyParsed); 8644 incrementalSourceFile.hasBeenIncrementallyParsed = true; 8645 Parser.fixupParentReferences(incrementalSourceFile); 8646 const oldText = sourceFile.text; 8647 const syntaxCursor = createSyntaxCursor(sourceFile); 8648 8649 // Make the actual change larger so that we know to reparse anything whose lookahead 8650 // might have intersected the change. 8651 const changeRange = extendToAffectedRange(sourceFile, textChangeRange); 8652 checkChangeRange(sourceFile, newText, changeRange, aggressiveChecks); 8653 8654 // Ensure that extending the affected range only moved the start of the change range 8655 // earlier in the file. 8656 Debug.assert(changeRange.span.start <= textChangeRange.span.start); 8657 Debug.assert(textSpanEnd(changeRange.span) === textSpanEnd(textChangeRange.span)); 8658 Debug.assert(textSpanEnd(textChangeRangeNewSpan(changeRange)) === textSpanEnd(textChangeRangeNewSpan(textChangeRange))); 8659 8660 // The is the amount the nodes after the edit range need to be adjusted. It can be 8661 // positive (if the edit added characters), negative (if the edit deleted characters) 8662 // or zero (if this was a pure overwrite with nothing added/removed). 8663 const delta = textChangeRangeNewSpan(changeRange).length - changeRange.span.length; 8664 8665 // If we added or removed characters during the edit, then we need to go and adjust all 8666 // the nodes after the edit. Those nodes may move forward (if we inserted chars) or they 8667 // may move backward (if we deleted chars). 8668 // 8669 // Doing this helps us out in two ways. First, it means that any nodes/tokens we want 8670 // to reuse are already at the appropriate position in the new text. That way when we 8671 // reuse them, we don't have to figure out if they need to be adjusted. Second, it makes 8672 // it very easy to determine if we can reuse a node. If the node's position is at where 8673 // we are in the text, then we can reuse it. Otherwise we can't. If the node's position 8674 // is ahead of us, then we'll need to rescan tokens. If the node's position is behind 8675 // us, then we'll need to skip it or crumble it as appropriate 8676 // 8677 // We will also adjust the positions of nodes that intersect the change range as well. 8678 // By doing this, we ensure that all the positions in the old tree are consistent, not 8679 // just the positions of nodes entirely before/after the change range. By being 8680 // consistent, we can then easily map from positions to nodes in the old tree easily. 8681 // 8682 // Also, mark any syntax elements that intersect the changed span. We know, up front, 8683 // that we cannot reuse these elements. 8684 updateTokenPositionsAndMarkElements(incrementalSourceFile, 8685 changeRange.span.start, textSpanEnd(changeRange.span), textSpanEnd(textChangeRangeNewSpan(changeRange)), delta, oldText, newText, aggressiveChecks); 8686 8687 // Now that we've set up our internal incremental state just proceed and parse the 8688 // source file in the normal fashion. When possible the parser will retrieve and 8689 // reuse nodes from the old tree. 8690 // 8691 // Note: passing in 'true' for setNodeParents is very important. When incrementally 8692 // parsing, we will be reusing nodes from the old tree, and placing it into new 8693 // parents. If we don't set the parents now, we'll end up with an observably 8694 // inconsistent tree. Setting the parents on the new tree should be very fast. We 8695 // will immediately bail out of walking any subtrees when we can see that their parents 8696 // are already correct. 8697 const result = Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, syntaxCursor, /*setParentNodes*/ true, sourceFile.scriptKind); 8698 result.commentDirectives = getNewCommentDirectives( 8699 sourceFile.commentDirectives, 8700 result.commentDirectives, 8701 changeRange.span.start, 8702 textSpanEnd(changeRange.span), 8703 delta, 8704 oldText, 8705 newText, 8706 aggressiveChecks 8707 ); 8708 return result; 8709 } 8710 8711 function getNewCommentDirectives( 8712 oldDirectives: CommentDirective[] | undefined, 8713 newDirectives: CommentDirective[] | undefined, 8714 changeStart: number, 8715 changeRangeOldEnd: number, 8716 delta: number, 8717 oldText: string, 8718 newText: string, 8719 aggressiveChecks: boolean 8720 ): CommentDirective[] | undefined { 8721 if (!oldDirectives) return newDirectives; 8722 let commentDirectives: CommentDirective[] | undefined; 8723 let addedNewlyScannedDirectives = false; 8724 for (const directive of oldDirectives) { 8725 const { range, type } = directive; 8726 // Range before the change 8727 if (range.end < changeStart) { 8728 commentDirectives = append(commentDirectives, directive); 8729 } 8730 else if (range.pos > changeRangeOldEnd) { 8731 addNewlyScannedDirectives(); 8732 // Node is entirely past the change range. We need to move both its pos and 8733 // end, forward or backward appropriately. 8734 const updatedDirective: CommentDirective = { 8735 range: { pos: range.pos + delta, end: range.end + delta }, 8736 type 8737 }; 8738 commentDirectives = append(commentDirectives, updatedDirective); 8739 if (aggressiveChecks) { 8740 Debug.assert(oldText.substring(range.pos, range.end) === newText.substring(updatedDirective.range.pos, updatedDirective.range.end)); 8741 } 8742 } 8743 // Ignore ranges that fall in change range 8744 } 8745 addNewlyScannedDirectives(); 8746 return commentDirectives; 8747 8748 function addNewlyScannedDirectives() { 8749 if (addedNewlyScannedDirectives) return; 8750 addedNewlyScannedDirectives = true; 8751 if (!commentDirectives) { 8752 commentDirectives = newDirectives; 8753 } 8754 else if (newDirectives) { 8755 commentDirectives.push(...newDirectives); 8756 } 8757 } 8758 } 8759 8760 function moveElementEntirelyPastChangeRange(element: IncrementalElement, isArray: boolean, delta: number, oldText: string, newText: string, aggressiveChecks: boolean) { 8761 if (isArray) { 8762 visitArray(<IncrementalNodeArray>element); 8763 } 8764 else { 8765 visitNode(<IncrementalNode>element); 8766 } 8767 return; 8768 8769 function visitNode(node: IncrementalNode) { 8770 let text = ""; 8771 if (aggressiveChecks && shouldCheckNode(node)) { 8772 text = oldText.substring(node.pos, node.end); 8773 } 8774 8775 // Ditch any existing LS children we may have created. This way we can avoid 8776 // moving them forward. 8777 if (node._children) { 8778 node._children = undefined; 8779 } 8780 8781 setTextRangePosEnd(node, node.pos + delta, node.end + delta); 8782 8783 if (aggressiveChecks && shouldCheckNode(node)) { 8784 Debug.assert(text === newText.substring(node.pos, node.end)); 8785 } 8786 8787 forEachChild(node, visitNode, visitArray); 8788 if (hasJSDocNodes(node)) { 8789 for (const jsDocComment of node.jsDoc!) { 8790 visitNode(<IncrementalNode><Node>jsDocComment); 8791 } 8792 } 8793 checkNodePositions(node, aggressiveChecks); 8794 } 8795 8796 function visitArray(array: IncrementalNodeArray) { 8797 array._children = undefined; 8798 setTextRangePosEnd(array, array.pos + delta, array.end + delta); 8799 8800 for (const node of array) { 8801 visitNode(node); 8802 } 8803 } 8804 } 8805 8806 function shouldCheckNode(node: Node) { 8807 switch (node.kind) { 8808 case SyntaxKind.StringLiteral: 8809 case SyntaxKind.NumericLiteral: 8810 case SyntaxKind.Identifier: 8811 return true; 8812 } 8813 8814 return false; 8815 } 8816 8817 function adjustIntersectingElement(element: IncrementalElement, changeStart: number, changeRangeOldEnd: number, changeRangeNewEnd: number, delta: number) { 8818 Debug.assert(element.end >= changeStart, "Adjusting an element that was entirely before the change range"); 8819 Debug.assert(element.pos <= changeRangeOldEnd, "Adjusting an element that was entirely after the change range"); 8820 Debug.assert(element.pos <= element.end); 8821 8822 // We have an element that intersects the change range in some way. It may have its 8823 // start, or its end (or both) in the changed range. We want to adjust any part 8824 // that intersects such that the final tree is in a consistent state. i.e. all 8825 // children have spans within the span of their parent, and all siblings are ordered 8826 // properly. 8827 8828 // We may need to update both the 'pos' and the 'end' of the element. 8829 8830 // If the 'pos' is before the start of the change, then we don't need to touch it. 8831 // If it isn't, then the 'pos' must be inside the change. How we update it will 8832 // depend if delta is positive or negative. If delta is positive then we have 8833 // something like: 8834 // 8835 // -------------------AAA----------------- 8836 // -------------------BBBCCCCCCC----------------- 8837 // 8838 // In this case, we consider any node that started in the change range to still be 8839 // starting at the same position. 8840 // 8841 // however, if the delta is negative, then we instead have something like this: 8842 // 8843 // -------------------XXXYYYYYYY----------------- 8844 // -------------------ZZZ----------------- 8845 // 8846 // In this case, any element that started in the 'X' range will keep its position. 8847 // However any element that started after that will have their pos adjusted to be 8848 // at the end of the new range. i.e. any node that started in the 'Y' range will 8849 // be adjusted to have their start at the end of the 'Z' range. 8850 // 8851 // The element will keep its position if possible. Or Move backward to the new-end 8852 // if it's in the 'Y' range. 8853 const pos = Math.min(element.pos, changeRangeNewEnd); 8854 8855 // If the 'end' is after the change range, then we always adjust it by the delta 8856 // amount. However, if the end is in the change range, then how we adjust it 8857 // will depend on if delta is positive or negative. If delta is positive then we 8858 // have something like: 8859 // 8860 // -------------------AAA----------------- 8861 // -------------------BBBCCCCCCC----------------- 8862 // 8863 // In this case, we consider any node that ended inside the change range to keep its 8864 // end position. 8865 // 8866 // however, if the delta is negative, then we instead have something like this: 8867 // 8868 // -------------------XXXYYYYYYY----------------- 8869 // -------------------ZZZ----------------- 8870 // 8871 // In this case, any element that ended in the 'X' range will keep its position. 8872 // However any element that ended after that will have their pos adjusted to be 8873 // at the end of the new range. i.e. any node that ended in the 'Y' range will 8874 // be adjusted to have their end at the end of the 'Z' range. 8875 const end = element.end >= changeRangeOldEnd ? 8876 // Element ends after the change range. Always adjust the end pos. 8877 element.end + delta : 8878 // Element ends in the change range. The element will keep its position if 8879 // possible. Or Move backward to the new-end if it's in the 'Y' range. 8880 Math.min(element.end, changeRangeNewEnd); 8881 8882 Debug.assert(pos <= end); 8883 if (element.parent) { 8884 Debug.assertGreaterThanOrEqual(pos, element.parent.pos); 8885 Debug.assertLessThanOrEqual(end, element.parent.end); 8886 } 8887 8888 setTextRangePosEnd(element, pos, end); 8889 } 8890 8891 function checkNodePositions(node: Node, aggressiveChecks: boolean) { 8892 if (aggressiveChecks) { 8893 let pos = node.pos; 8894 const visitNode = (child: Node) => { 8895 Debug.assert(child.pos >= pos); 8896 pos = child.end; 8897 }; 8898 if (hasJSDocNodes(node)) { 8899 for (const jsDocComment of node.jsDoc!) { 8900 visitNode(jsDocComment); 8901 } 8902 } 8903 forEachChild(node, visitNode); 8904 Debug.assert(pos <= node.end); 8905 } 8906 } 8907 8908 function updateTokenPositionsAndMarkElements( 8909 sourceFile: IncrementalNode, 8910 changeStart: number, 8911 changeRangeOldEnd: number, 8912 changeRangeNewEnd: number, 8913 delta: number, 8914 oldText: string, 8915 newText: string, 8916 aggressiveChecks: boolean): void { 8917 8918 visitNode(sourceFile); 8919 return; 8920 8921 function visitNode(child: IncrementalNode) { 8922 Debug.assert(child.pos <= child.end); 8923 if (child.pos > changeRangeOldEnd) { 8924 // Node is entirely past the change range. We need to move both its pos and 8925 // end, forward or backward appropriately. 8926 moveElementEntirelyPastChangeRange(child, /*isArray*/ false, delta, oldText, newText, aggressiveChecks); 8927 return; 8928 } 8929 8930 // Check if the element intersects the change range. If it does, then it is not 8931 // reusable. Also, we'll need to recurse to see what constituent portions we may 8932 // be able to use. 8933 const fullEnd = child.end; 8934 if (fullEnd >= changeStart) { 8935 child.intersectsChange = true; 8936 child._children = undefined; 8937 8938 // Adjust the pos or end (or both) of the intersecting element accordingly. 8939 adjustIntersectingElement(child, changeStart, changeRangeOldEnd, changeRangeNewEnd, delta); 8940 forEachChild(child, visitNode, visitArray); 8941 if (hasJSDocNodes(child)) { 8942 for (const jsDocComment of child.jsDoc!) { 8943 visitNode(<IncrementalNode><Node>jsDocComment); 8944 } 8945 } 8946 checkNodePositions(child, aggressiveChecks); 8947 return; 8948 } 8949 8950 // Otherwise, the node is entirely before the change range. No need to do anything with it. 8951 Debug.assert(fullEnd < changeStart); 8952 } 8953 8954 function visitArray(array: IncrementalNodeArray) { 8955 Debug.assert(array.pos <= array.end); 8956 if (array.pos > changeRangeOldEnd) { 8957 // Array is entirely after the change range. We need to move it, and move any of 8958 // its children. 8959 moveElementEntirelyPastChangeRange(array, /*isArray*/ true, delta, oldText, newText, aggressiveChecks); 8960 return; 8961 } 8962 8963 // Check if the element intersects the change range. If it does, then it is not 8964 // reusable. Also, we'll need to recurse to see what constituent portions we may 8965 // be able to use. 8966 const fullEnd = array.end; 8967 if (fullEnd >= changeStart) { 8968 array.intersectsChange = true; 8969 array._children = undefined; 8970 8971 // Adjust the pos or end (or both) of the intersecting array accordingly. 8972 adjustIntersectingElement(array, changeStart, changeRangeOldEnd, changeRangeNewEnd, delta); 8973 for (const node of array) { 8974 if (!node.virtual) { 8975 visitNode(node); 8976 } 8977 } 8978 return; 8979 } 8980 8981 // Otherwise, the array is entirely before the change range. No need to do anything with it. 8982 Debug.assert(fullEnd < changeStart); 8983 } 8984 } 8985 8986 function extendToAffectedRange(sourceFile: SourceFile, changeRange: TextChangeRange): TextChangeRange { 8987 // Consider the following code: 8988 // void foo() { /; } 8989 // 8990 // If the text changes with an insertion of / just before the semicolon then we end up with: 8991 // void foo() { //; } 8992 // 8993 // If we were to just use the changeRange a is, then we would not rescan the { token 8994 // (as it does not intersect the actual original change range). Because an edit may 8995 // change the token touching it, we actually need to look back *at least* one token so 8996 // that the prior token sees that change. 8997 const maxLookahead = 1; 8998 8999 let start = changeRange.span.start; 9000 9001 // the first iteration aligns us with the change start. subsequent iteration move us to 9002 // the left by maxLookahead tokens. We only need to do this as long as we're not at the 9003 // start of the tree. 9004 for (let i = 0; start > 0 && i <= maxLookahead; i++) { 9005 const nearestNode = findNearestNodeStartingBeforeOrAtPosition(sourceFile, start); 9006 Debug.assert(nearestNode.pos <= start); 9007 const position = nearestNode.pos; 9008 9009 start = Math.max(0, position - 1); 9010 } 9011 9012 const finalSpan = createTextSpanFromBounds(start, textSpanEnd(changeRange.span)); 9013 const finalLength = changeRange.newLength + (changeRange.span.start - start); 9014 9015 return createTextChangeRange(finalSpan, finalLength); 9016 } 9017 9018 function findNearestNodeStartingBeforeOrAtPosition(sourceFile: SourceFile, position: number): Node { 9019 let bestResult: Node = sourceFile; 9020 let lastNodeEntirelyBeforePosition: Node | undefined; 9021 9022 forEachChild(sourceFile, visit); 9023 9024 if (lastNodeEntirelyBeforePosition) { 9025 const lastChildOfLastEntireNodeBeforePosition = getLastDescendant(lastNodeEntirelyBeforePosition); 9026 if (lastChildOfLastEntireNodeBeforePosition.pos > bestResult.pos) { 9027 bestResult = lastChildOfLastEntireNodeBeforePosition; 9028 } 9029 } 9030 9031 return bestResult; 9032 9033 function getLastDescendant(node: Node): Node { 9034 while (true) { 9035 const lastChild = getLastChild(node); 9036 if (lastChild) { 9037 node = lastChild; 9038 } 9039 else { 9040 return node; 9041 } 9042 } 9043 } 9044 9045 function visit(child: Node) { 9046 if (nodeIsMissing(child)) { 9047 // Missing nodes are effectively invisible to us. We never even consider them 9048 // When trying to find the nearest node before us. 9049 return; 9050 } 9051 9052 // If the child intersects this position, then this node is currently the nearest 9053 // node that starts before the position. 9054 if (child.pos <= position) { 9055 if (child.pos >= bestResult.pos) { 9056 // This node starts before the position, and is closer to the position than 9057 // the previous best node we found. It is now the new best node. 9058 bestResult = child; 9059 } 9060 9061 // Now, the node may overlap the position, or it may end entirely before the 9062 // position. If it overlaps with the position, then either it, or one of its 9063 // children must be the nearest node before the position. So we can just 9064 // recurse into this child to see if we can find something better. 9065 if (position < child.end) { 9066 // The nearest node is either this child, or one of the children inside 9067 // of it. We've already marked this child as the best so far. Recurse 9068 // in case one of the children is better. 9069 forEachChild(child, visit); 9070 9071 // Once we look at the children of this node, then there's no need to 9072 // continue any further. 9073 return true; 9074 } 9075 else { 9076 Debug.assert(child.end <= position); 9077 // The child ends entirely before this position. Say you have the following 9078 // (where $ is the position) 9079 // 9080 // <complex expr 1> ? <complex expr 2> $ : <...> <...> 9081 // 9082 // We would want to find the nearest preceding node in "complex expr 2". 9083 // To support that, we keep track of this node, and once we're done searching 9084 // for a best node, we recurse down this node to see if we can find a good 9085 // result in it. 9086 // 9087 // This approach allows us to quickly skip over nodes that are entirely 9088 // before the position, while still allowing us to find any nodes in the 9089 // last one that might be what we want. 9090 lastNodeEntirelyBeforePosition = child; 9091 } 9092 } 9093 else { 9094 Debug.assert(child.pos > position); 9095 // We're now at a node that is entirely past the position we're searching for. 9096 // This node (and all following nodes) could never contribute to the result, 9097 // so just skip them by returning 'true' here. 9098 return true; 9099 } 9100 } 9101 } 9102 9103 function checkChangeRange(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks: boolean) { 9104 const oldText = sourceFile.text; 9105 if (textChangeRange) { 9106 Debug.assert((oldText.length - textChangeRange.span.length + textChangeRange.newLength) === newText.length); 9107 9108 if (aggressiveChecks || Debug.shouldAssert(AssertionLevel.VeryAggressive)) { 9109 const oldTextPrefix = oldText.substr(0, textChangeRange.span.start); 9110 const newTextPrefix = newText.substr(0, textChangeRange.span.start); 9111 Debug.assert(oldTextPrefix === newTextPrefix); 9112 9113 const oldTextSuffix = oldText.substring(textSpanEnd(textChangeRange.span), oldText.length); 9114 const newTextSuffix = newText.substring(textSpanEnd(textChangeRangeNewSpan(textChangeRange)), newText.length); 9115 Debug.assert(oldTextSuffix === newTextSuffix); 9116 } 9117 } 9118 } 9119 9120 interface IncrementalElement extends ReadonlyTextRange { 9121 readonly parent: Node; 9122 intersectsChange: boolean; 9123 length?: number; 9124 _children: Node[] | undefined; 9125 } 9126 9127 export interface IncrementalNode extends Node, IncrementalElement { 9128 hasBeenIncrementallyParsed: boolean; 9129 } 9130 9131 interface IncrementalNodeArray extends NodeArray<IncrementalNode>, IncrementalElement { 9132 length: number; 9133 } 9134 9135 // Allows finding nodes in the source file at a certain position in an efficient manner. 9136 // The implementation takes advantage of the calling pattern it knows the parser will 9137 // make in order to optimize finding nodes as quickly as possible. 9138 export interface SyntaxCursor { 9139 currentNode(position: number): IncrementalNode; 9140 } 9141 9142 export function createSyntaxCursor(sourceFile: SourceFile): SyntaxCursor { 9143 let currentArray: NodeArray<Node> = sourceFile.statements; 9144 let currentArrayIndex = 0; 9145 9146 Debug.assert(currentArrayIndex < currentArray.length); 9147 let current = currentArray[currentArrayIndex]; 9148 let lastQueriedPosition = InvalidPosition.Value; 9149 9150 return { 9151 currentNode(position: number) { 9152 // Only compute the current node if the position is different than the last time 9153 // we were asked. The parser commonly asks for the node at the same position 9154 // twice. Once to know if can read an appropriate list element at a certain point, 9155 // and then to actually read and consume the node. 9156 if (position !== lastQueriedPosition) { 9157 // Much of the time the parser will need the very next node in the array that 9158 // we just returned a node from.So just simply check for that case and move 9159 // forward in the array instead of searching for the node again. 9160 if (current && current.end === position && currentArrayIndex < (currentArray.length - 1)) { 9161 currentArrayIndex++; 9162 current = currentArray[currentArrayIndex]; 9163 } 9164 9165 // If we don't have a node, or the node we have isn't in the right position, 9166 // then try to find a viable node at the position requested. 9167 if (!current || current.pos !== position) { 9168 findHighestListElementThatStartsAtPosition(position); 9169 } 9170 } 9171 9172 // Cache this query so that we don't do any extra work if the parser calls back 9173 // into us. Note: this is very common as the parser will make pairs of calls like 9174 // 'isListElement -> parseListElement'. If we were unable to find a node when 9175 // called with 'isListElement', we don't want to redo the work when parseListElement 9176 // is called immediately after. 9177 lastQueriedPosition = position; 9178 9179 // Either we don'd have a node, or we have a node at the position being asked for. 9180 Debug.assert(!current || current.pos === position); 9181 return <IncrementalNode>current; 9182 } 9183 }; 9184 9185 // Finds the highest element in the tree we can find that starts at the provided position. 9186 // The element must be a direct child of some node list in the tree. This way after we 9187 // return it, we can easily return its next sibling in the list. 9188 function findHighestListElementThatStartsAtPosition(position: number) { 9189 // Clear out any cached state about the last node we found. 9190 currentArray = undefined!; 9191 currentArrayIndex = InvalidPosition.Value; 9192 current = undefined!; 9193 9194 // Recurse into the source file to find the highest node at this position. 9195 forEachChild(sourceFile, visitNode, visitArray); 9196 return; 9197 9198 function visitNode(node: Node) { 9199 if (position >= node.pos && position < node.end) { 9200 // Position was within this node. Keep searching deeper to find the node. 9201 forEachChild(node, visitNode, visitArray); 9202 9203 // don't proceed any further in the search. 9204 return true; 9205 } 9206 9207 // position wasn't in this node, have to keep searching. 9208 return false; 9209 } 9210 9211 function visitArray(array: NodeArray<Node>) { 9212 if (position >= array.pos && position < array.end) { 9213 // position was in this array. Search through this array to see if we find a 9214 // viable element. 9215 for (let i = 0; i < array.length; i++) { 9216 const child = array[i]; 9217 if (child) { 9218 if (child.pos === position) { 9219 // Found the right node. We're done. 9220 currentArray = array; 9221 currentArrayIndex = i; 9222 current = child; 9223 return true; 9224 } 9225 else { 9226 if (child.pos < position && position < child.end) { 9227 // Position in somewhere within this child. Search in it and 9228 // stop searching in this array. 9229 forEachChild(child, visitNode, visitArray); 9230 return true; 9231 } 9232 } 9233 } 9234 } 9235 } 9236 9237 // position wasn't in this array, have to keep searching. 9238 return false; 9239 } 9240 } 9241 } 9242 9243 const enum InvalidPosition { 9244 Value = -1 9245 } 9246 } 9247 9248 /** @internal */ 9249 export function isDeclarationFileName(fileName: string): boolean { 9250 return fileExtensionIs(fileName, Extension.Dts) || fileExtensionIs(fileName, Extension.Dets); 9251 } 9252 9253 /*@internal*/ 9254 export interface PragmaContext { 9255 languageVersion: ScriptTarget; 9256 pragmas?: PragmaMap; 9257 checkJsDirective?: CheckJsDirective; 9258 referencedFiles: FileReference[]; 9259 typeReferenceDirectives: FileReference[]; 9260 libReferenceDirectives: FileReference[]; 9261 amdDependencies: AmdDependency[]; 9262 hasNoDefaultLib?: boolean; 9263 moduleName?: string; 9264 } 9265 9266 /*@internal*/ 9267 export function processCommentPragmas(context: PragmaContext, sourceText: string): void { 9268 const pragmas: PragmaPseudoMapEntry[] = []; 9269 9270 for (const range of getLeadingCommentRanges(sourceText, 0) || emptyArray) { 9271 const comment = sourceText.substring(range.pos, range.end); 9272 extractPragmas(pragmas, range, comment); 9273 } 9274 9275 context.pragmas = new Map() as PragmaMap; 9276 for (const pragma of pragmas) { 9277 if (context.pragmas.has(pragma.name)) { 9278 const currentValue = context.pragmas.get(pragma.name); 9279 if (currentValue instanceof Array) { 9280 currentValue.push(pragma.args); 9281 } 9282 else { 9283 context.pragmas.set(pragma.name, [currentValue, pragma.args]); 9284 } 9285 continue; 9286 } 9287 context.pragmas.set(pragma.name, pragma.args); 9288 } 9289 } 9290 9291 /*@internal*/ 9292 type PragmaDiagnosticReporter = (pos: number, length: number, message: DiagnosticMessage) => void; 9293 9294 /*@internal*/ 9295 export function processPragmasIntoFields(context: PragmaContext, reportDiagnostic: PragmaDiagnosticReporter): void { 9296 context.checkJsDirective = undefined; 9297 context.referencedFiles = []; 9298 context.typeReferenceDirectives = []; 9299 context.libReferenceDirectives = []; 9300 context.amdDependencies = []; 9301 context.hasNoDefaultLib = false; 9302 context.pragmas!.forEach((entryOrList, key) => { // TODO: GH#18217 9303 // TODO: The below should be strongly type-guarded and not need casts/explicit annotations, since entryOrList is related to 9304 // key and key is constrained to a union; but it's not (see GH#21483 for at least partial fix) :( 9305 switch (key) { 9306 case "reference": { 9307 const referencedFiles = context.referencedFiles; 9308 const typeReferenceDirectives = context.typeReferenceDirectives; 9309 const libReferenceDirectives = context.libReferenceDirectives; 9310 forEach(toArray(entryOrList) as PragmaPseudoMap["reference"][], arg => { 9311 const { types, lib, path } = arg.arguments; 9312 if (arg.arguments["no-default-lib"]) { 9313 context.hasNoDefaultLib = true; 9314 } 9315 else if (types) { 9316 typeReferenceDirectives.push({ pos: types.pos, end: types.end, fileName: types.value }); 9317 } 9318 else if (lib) { 9319 libReferenceDirectives.push({ pos: lib.pos, end: lib.end, fileName: lib.value }); 9320 } 9321 else if (path) { 9322 referencedFiles.push({ pos: path.pos, end: path.end, fileName: path.value }); 9323 } 9324 else { 9325 reportDiagnostic(arg.range.pos, arg.range.end - arg.range.pos, Diagnostics.Invalid_reference_directive_syntax); 9326 } 9327 }); 9328 break; 9329 } 9330 case "amd-dependency": { 9331 context.amdDependencies = map( 9332 toArray(entryOrList) as PragmaPseudoMap["amd-dependency"][], 9333 x => ({ name: x.arguments.name, path: x.arguments.path })); 9334 break; 9335 } 9336 case "amd-module": { 9337 if (entryOrList instanceof Array) { 9338 for (const entry of entryOrList) { 9339 if (context.moduleName) { 9340 // TODO: It's probably fine to issue this diagnostic on all instances of the pragma 9341 reportDiagnostic(entry.range.pos, entry.range.end - entry.range.pos, Diagnostics.An_AMD_module_cannot_have_multiple_name_assignments); 9342 } 9343 context.moduleName = (entry as PragmaPseudoMap["amd-module"]).arguments.name; 9344 } 9345 } 9346 else { 9347 context.moduleName = (entryOrList as PragmaPseudoMap["amd-module"]).arguments.name; 9348 } 9349 break; 9350 } 9351 case "ts-nocheck": 9352 case "ts-check": { 9353 // _last_ of either nocheck or check in a file is the "winner" 9354 forEach(toArray(entryOrList), entry => { 9355 if (!context.checkJsDirective || entry.range.pos > context.checkJsDirective.pos) { 9356 context.checkJsDirective = { 9357 enabled: key === "ts-check", 9358 end: entry.range.end, 9359 pos: entry.range.pos 9360 }; 9361 } 9362 }); 9363 break; 9364 } 9365 case "jsx": 9366 case "jsxfrag": 9367 case "jsximportsource": 9368 case "jsxruntime": 9369 return; // Accessed directly 9370 default: Debug.fail("Unhandled pragma kind"); // Can this be made into an assertNever in the future? 9371 } 9372 }); 9373 } 9374 9375 const namedArgRegExCache = new Map<string, RegExp>(); 9376 function getNamedArgRegEx(name: string): RegExp { 9377 if (namedArgRegExCache.has(name)) { 9378 return namedArgRegExCache.get(name)!; 9379 } 9380 const result = new RegExp(`(\\s${name}\\s*=\\s*)('|")(.+?)\\2`, "im"); 9381 namedArgRegExCache.set(name, result); 9382 return result; 9383 } 9384 9385 const tripleSlashXMLCommentStartRegEx = /^\/\/\/\s*<(\S+)\s.*?\/>/im; 9386 const singleLinePragmaRegEx = /^\/\/\/?\s*@(\S+)\s*(.*)\s*$/im; 9387 function extractPragmas(pragmas: PragmaPseudoMapEntry[], range: CommentRange, text: string) { 9388 const tripleSlash = range.kind === SyntaxKind.SingleLineCommentTrivia && tripleSlashXMLCommentStartRegEx.exec(text); 9389 if (tripleSlash) { 9390 const name = tripleSlash[1].toLowerCase() as keyof PragmaPseudoMap; // Technically unsafe cast, but we do it so the below check to make it safe typechecks 9391 const pragma = commentPragmas[name] as PragmaDefinition; 9392 if (!pragma || !(pragma.kind! & PragmaKindFlags.TripleSlashXML)) { 9393 return; 9394 } 9395 if (pragma.args) { 9396 const argument: {[index: string]: string | {value: string, pos: number, end: number}} = {}; 9397 for (const arg of pragma.args) { 9398 const matcher = getNamedArgRegEx(arg.name); 9399 const matchResult = matcher.exec(text); 9400 if (!matchResult && !arg.optional) { 9401 return; // Missing required argument, don't parse 9402 } 9403 else if (matchResult) { 9404 if (arg.captureSpan) { 9405 const startPos = range.pos + matchResult.index + matchResult[1].length + matchResult[2].length; 9406 argument[arg.name] = { 9407 value: matchResult[3], 9408 pos: startPos, 9409 end: startPos + matchResult[3].length 9410 }; 9411 } 9412 else { 9413 argument[arg.name] = matchResult[3]; 9414 } 9415 } 9416 } 9417 pragmas.push({ name, args: { arguments: argument, range } } as PragmaPseudoMapEntry); 9418 } 9419 else { 9420 pragmas.push({ name, args: { arguments: {}, range } } as PragmaPseudoMapEntry); 9421 } 9422 return; 9423 } 9424 9425 const singleLine = range.kind === SyntaxKind.SingleLineCommentTrivia && singleLinePragmaRegEx.exec(text); 9426 if (singleLine) { 9427 return addPragmaForMatch(pragmas, range, PragmaKindFlags.SingleLine, singleLine); 9428 } 9429 9430 if (range.kind === SyntaxKind.MultiLineCommentTrivia) { 9431 const multiLinePragmaRegEx = /\s*@(\S+)\s*(.*)\s*$/gim; // Defined inline since it uses the "g" flag, which keeps a persistent index (for iterating) 9432 let multiLineMatch: RegExpExecArray | null; 9433 while (multiLineMatch = multiLinePragmaRegEx.exec(text)) { 9434 addPragmaForMatch(pragmas, range, PragmaKindFlags.MultiLine, multiLineMatch); 9435 } 9436 } 9437 } 9438 9439 function addPragmaForMatch(pragmas: PragmaPseudoMapEntry[], range: CommentRange, kind: PragmaKindFlags, match: RegExpExecArray) { 9440 if (!match) return; 9441 const name = match[1].toLowerCase() as keyof PragmaPseudoMap; // Technically unsafe cast, but we do it so they below check to make it safe typechecks 9442 const pragma = commentPragmas[name] as PragmaDefinition; 9443 if (!pragma || !(pragma.kind! & kind)) { 9444 return; 9445 } 9446 const args = match[2]; // Split on spaces and match up positionally with definition 9447 const argument = getNamedPragmaArguments(pragma, args); 9448 if (argument === "fail") return; // Missing required argument, fail to parse it 9449 pragmas.push({ name, args: { arguments: argument, range } } as PragmaPseudoMapEntry); 9450 return; 9451 } 9452 9453 function getNamedPragmaArguments(pragma: PragmaDefinition, text: string | undefined): {[index: string]: string} | "fail" { 9454 if (!text) return {}; 9455 if (!pragma.args) return {}; 9456 const args = text.split(/\s+/); 9457 const argMap: {[index: string]: string} = {}; 9458 for (let i = 0; i < pragma.args.length; i++) { 9459 const argument = pragma.args[i]; 9460 if (!args[i] && !argument.optional) { 9461 return "fail"; 9462 } 9463 if (argument.captureSpan) { 9464 return Debug.fail("Capture spans not yet implemented for non-xml pragmas"); 9465 } 9466 argMap[argument.name] = args[i]; 9467 } 9468 return argMap; 9469 } 9470 9471 /** @internal */ 9472 export function tagNamesAreEquivalent(lhs: JsxTagNameExpression, rhs: JsxTagNameExpression): boolean { 9473 if (lhs.kind !== rhs.kind) { 9474 return false; 9475 } 9476 9477 if (lhs.kind === SyntaxKind.Identifier) { 9478 return lhs.escapedText === (<Identifier>rhs).escapedText; 9479 } 9480 9481 if (lhs.kind === SyntaxKind.ThisKeyword) { 9482 return true; 9483 } 9484 9485 // If we are at this statement then we must have PropertyAccessExpression and because tag name in Jsx element can only 9486 // take forms of JsxTagNameExpression which includes an identifier, "this" expression, or another propertyAccessExpression 9487 // it is safe to case the expression property as such. See parseJsxElementName for how we parse tag name in Jsx element 9488 return (<PropertyAccessExpression>lhs).name.escapedText === (<PropertyAccessExpression>rhs).name.escapedText && 9489 tagNamesAreEquivalent((<PropertyAccessExpression>lhs).expression as JsxTagNameExpression, (<PropertyAccessExpression>rhs).expression as JsxTagNameExpression); 9490 } 9491} 9492