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 setEtsBuildContext(methodName === sourceFileCompilerOptions?.ets?.render?.method?.find(render => render === "build")); 6766 setEtsBuilderContext(hasEtsBuilderDecoratorNames(decorators, sourceFileCompilerOptions)); 6767 if (inStructContext() && hasEtsStylesDecoratorNames(decorators, sourceFileCompilerOptions)) { 6768 if (methodName && currentStructName) { 6769 structStylesComponents.set(methodName, { structName: currentStructName, kind: SyntaxKind.MethodDeclaration }); 6770 } 6771 const stylesEtsComponentDecoratorNames = getEtsStylesDecoratorComponentNames(decorators, sourceFileCompilerOptions); 6772 if (stylesEtsComponentDecoratorNames.length > 0) { 6773 stylesEtsComponentDeclaration = sourceFileCompilerOptions.ets?.styles.component; 6774 } 6775 setEtsStylesComponentsContext(!!stylesEtsComponentDeclaration); 6776 } 6777 setEtsComponentsContext(inStructContext() && (isTokenInsideStructBuild(name) || isTokenInsideStructBuilder(decorators) || 6778 isTokenInsideStructPageTransition(name))); 6779 const isGenerator = asteriskToken ? SignatureFlags.Yield : SignatureFlags.None; 6780 const isAsync = some(modifiers, isAsyncModifier) ? SignatureFlags.Await : SignatureFlags.None; 6781 const typeParameters = inEtsStylesComponentsContext() && stylesEtsComponentDeclaration ? parseEtsTypeParameters(pos) : parseTypeParameters(); 6782 const parameters = parseParameters(isGenerator | isAsync); 6783 const typeStartPos = scanner.getStartPos(); 6784 const type = getMethodDeclarationReturnType(); 6785 const body = parseFunctionBlockOrSemicolon(isGenerator | isAsync, diagnosticMessage); 6786 const node = factory.createMethodDeclaration( 6787 decorators, 6788 modifiers, 6789 asteriskToken, 6790 name, 6791 questionToken, 6792 typeParameters, 6793 parameters, 6794 type, 6795 body 6796 ); 6797 // An exclamation token on a method is invalid syntax and will be handled by the grammar checker 6798 node.exclamationToken = exclamationToken; 6799 setEtsBuildContext(false); 6800 setEtsBuilderContext(false); 6801 setEtsStylesComponentsContext(false); 6802 stylesEtsComponentDeclaration = undefined; 6803 setEtsComponentsContext(false); 6804 return withJSDoc(finishNode(node, pos), hasJSDoc); 6805 6806 function getMethodDeclarationReturnType(): TypeNode | undefined { 6807 let returnType = parseReturnType(SyntaxKind.ColonToken, /*isType*/ false); 6808 // If function decorated by Styles and type is not defined, then will use Ets Styles Components Type 6809 if (!returnType && stylesEtsComponentDeclaration && inEtsStylesComponentsContext()) { 6810 returnType = finishVirtualNode(factory.createTypeReferenceNode( 6811 finishVirtualNode(factory.createIdentifier(stylesEtsComponentDeclaration.type), typeStartPos, typeStartPos)), 6812 typeStartPos, typeStartPos); 6813 } 6814 return returnType; 6815 } 6816 } 6817 6818 function parsePropertyDeclaration( 6819 pos: number, 6820 hasJSDoc: boolean, 6821 decorators: NodeArray<Decorator> | undefined, 6822 modifiers: NodeArray<Modifier> | undefined, 6823 name: PropertyName, 6824 questionToken: QuestionToken | undefined 6825 ): PropertyDeclaration { 6826 const exclamationToken = !questionToken && !scanner.hasPrecedingLineBreak() ? parseOptionalToken(SyntaxKind.ExclamationToken) : undefined; 6827 const type = parseTypeAnnotation(); 6828 const initializer = doOutsideOfContext(NodeFlags.YieldContext | NodeFlags.AwaitContext | NodeFlags.DisallowInContext, parseInitializer); 6829 parseSemicolon(); 6830 const node = factory.createPropertyDeclaration(decorators, modifiers, name, questionToken || exclamationToken, type, initializer); 6831 return withJSDoc(finishNode(node, pos), hasJSDoc); 6832 } 6833 6834 function parsePropertyOrMethodDeclaration( 6835 pos: number, 6836 hasJSDoc: boolean, 6837 decorators: NodeArray<Decorator> | undefined, 6838 modifiers: NodeArray<Modifier> | undefined 6839 ): PropertyDeclaration | MethodDeclaration { 6840 const asteriskToken = parseOptionalToken(SyntaxKind.AsteriskToken); 6841 const name = parsePropertyName(); 6842 // Note: this is not legal as per the grammar. But we allow it in the parser and 6843 // report an error in the grammar checker. 6844 const questionToken = parseOptionalToken(SyntaxKind.QuestionToken); 6845 if (asteriskToken || token() === SyntaxKind.OpenParenToken || token() === SyntaxKind.LessThanToken) { 6846 return parseMethodDeclaration(pos, hasJSDoc, decorators, modifiers, asteriskToken, name, questionToken, /*exclamationToken*/ undefined, Diagnostics.or_expected); 6847 } 6848 return parsePropertyDeclaration(pos, hasJSDoc, decorators, modifiers, name, questionToken); 6849 } 6850 6851 function parseAccessorDeclaration(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined, kind: AccessorDeclaration["kind"]): AccessorDeclaration { 6852 const name = parsePropertyName(); 6853 const typeParameters = parseTypeParameters(); 6854 const parameters = parseParameters(SignatureFlags.None); 6855 const type = parseReturnType(SyntaxKind.ColonToken, /*isType*/ false); 6856 const body = parseFunctionBlockOrSemicolon(SignatureFlags.None); 6857 const node = kind === SyntaxKind.GetAccessor 6858 ? factory.createGetAccessorDeclaration(decorators, modifiers, name, parameters, type, body) 6859 : factory.createSetAccessorDeclaration(decorators, modifiers, name, parameters, body); 6860 // Keep track of `typeParameters` (for both) and `type` (for setters) if they were parsed those indicate grammar errors 6861 node.typeParameters = typeParameters; 6862 if (type && node.kind === SyntaxKind.SetAccessor) (node as Mutable<SetAccessorDeclaration>).type = type; 6863 return withJSDoc(finishNode(node, pos), hasJSDoc); 6864 } 6865 6866 function isClassMemberStart(): boolean { 6867 let idToken: SyntaxKind | undefined; 6868 6869 if (token() === SyntaxKind.AtToken) { 6870 return true; 6871 } 6872 6873 // Eat up all modifiers, but hold on to the last one in case it is actually an identifier. 6874 while (isModifierKind(token())) { 6875 idToken = token(); 6876 // If the idToken is a class modifier (protected, private, public, and static), it is 6877 // certain that we are starting to parse class member. This allows better error recovery 6878 // Example: 6879 // public foo() ... // true 6880 // public @dec blah ... // true; we will then report an error later 6881 // export public ... // true; we will then report an error later 6882 if (isClassMemberModifier(idToken)) { 6883 return true; 6884 } 6885 6886 nextToken(); 6887 } 6888 6889 if (token() === SyntaxKind.AsteriskToken) { 6890 return true; 6891 } 6892 6893 // Try to get the first property-like token following all modifiers. 6894 // This can either be an identifier or the 'get' or 'set' keywords. 6895 if (isLiteralPropertyName()) { 6896 idToken = token(); 6897 nextToken(); 6898 } 6899 6900 // Index signatures and computed properties are class members; we can parse. 6901 if (token() === SyntaxKind.OpenBracketToken) { 6902 return true; 6903 } 6904 6905 // If we were able to get any potential identifier... 6906 if (idToken !== undefined) { 6907 // If we have a non-keyword identifier, or if we have an accessor, then it's safe to parse. 6908 if (!isKeyword(idToken) || idToken === SyntaxKind.SetKeyword || idToken === SyntaxKind.GetKeyword) { 6909 return true; 6910 } 6911 6912 // If it *is* a keyword, but not an accessor, check a little farther along 6913 // to see if it should actually be parsed as a class member. 6914 switch (token()) { 6915 case SyntaxKind.OpenParenToken: // Method declaration 6916 case SyntaxKind.LessThanToken: // Generic Method declaration 6917 case SyntaxKind.ExclamationToken: // Non-null assertion on property name 6918 case SyntaxKind.ColonToken: // Type Annotation for declaration 6919 case SyntaxKind.EqualsToken: // Initializer for declaration 6920 case SyntaxKind.QuestionToken: // Not valid, but permitted so that it gets caught later on. 6921 return true; 6922 default: 6923 // Covers 6924 // - Semicolons (declaration termination) 6925 // - Closing braces (end-of-class, must be declaration) 6926 // - End-of-files (not valid, but permitted so that it gets caught later on) 6927 // - Line-breaks (enabling *automatic semicolon insertion*) 6928 return canParseSemicolon(); 6929 } 6930 } 6931 6932 return false; 6933 } 6934 6935 function parseDecoratorExpression() { 6936 if (inAwaitContext() && token() === SyntaxKind.AwaitKeyword) { 6937 // `@await` is is disallowed in an [Await] context, but can cause parsing to go off the rails 6938 // This simply parses the missing identifier and moves on. 6939 const pos = getNodePos(); 6940 const awaitExpression = parseIdentifier(Diagnostics.Expression_expected); 6941 nextToken(); 6942 const memberExpression = parseMemberExpressionRest(pos, awaitExpression, /*allowOptionalChain*/ true); 6943 return parseCallExpressionRest(pos, memberExpression); 6944 } 6945 return parseLeftHandSideExpressionOrHigher(); 6946 } 6947 6948 function tryParseDecorator(): Decorator | undefined { 6949 const pos = getNodePos(); 6950 if (!parseOptional(SyntaxKind.AtToken)) { 6951 return undefined; 6952 } 6953 const expression = doInDecoratorContext(parseDecoratorExpression); 6954 return finishNode(factory.createDecorator(expression), pos); 6955 } 6956 6957 function parseDecorators(): NodeArray<Decorator> | undefined { 6958 const pos = getNodePos(); 6959 let list, decorator; 6960 while (decorator = tryParseDecorator()) { 6961 list = append(list, decorator); 6962 } 6963 return list && createNodeArray(list, pos); 6964 } 6965 6966 function tryParseModifier(permitInvalidConstAsModifier?: boolean): Modifier | undefined { 6967 const pos = getNodePos(); 6968 const kind = token(); 6969 6970 if (token() === SyntaxKind.ConstKeyword && permitInvalidConstAsModifier) { 6971 // We need to ensure that any subsequent modifiers appear on the same line 6972 // so that when 'const' is a standalone declaration, we don't issue an error. 6973 if (!tryParse(nextTokenIsOnSameLineAndCanFollowModifier)) { 6974 return undefined; 6975 } 6976 } 6977 else { 6978 if (!parseAnyContextualModifier()) { 6979 return undefined; 6980 } 6981 } 6982 6983 return finishNode(factory.createToken(kind as Modifier["kind"]), pos); 6984 } 6985 6986 /* 6987 * There are situations in which a modifier like 'const' will appear unexpectedly, such as on a class member. 6988 * In those situations, if we are entirely sure that 'const' is not valid on its own (such as when ASI takes effect 6989 * and turns it into a standalone declaration), then it is better to parse it and report an error later. 6990 * 6991 * In such situations, 'permitInvalidConstAsModifier' should be set to true. 6992 */ 6993 function parseModifiers(permitInvalidConstAsModifier?: boolean): NodeArray<Modifier> | undefined { 6994 const pos = getNodePos(); 6995 let list, modifier; 6996 while (modifier = tryParseModifier(permitInvalidConstAsModifier)) { 6997 list = append(list, modifier); 6998 } 6999 return list && createNodeArray(list, pos); 7000 } 7001 7002 function parseModifiersForArrowFunction(): NodeArray<Modifier> | undefined { 7003 let modifiers: NodeArray<Modifier> | undefined; 7004 if (token() === SyntaxKind.AsyncKeyword) { 7005 const pos = getNodePos(); 7006 nextToken(); 7007 const modifier = finishNode(factory.createToken(SyntaxKind.AsyncKeyword), pos); 7008 modifiers = createNodeArray<Modifier>([modifier], pos); 7009 } 7010 return modifiers; 7011 } 7012 7013 function parseClassElement(): ClassElement { 7014 const pos = getNodePos(); 7015 if (token() === SyntaxKind.SemicolonToken) { 7016 nextToken(); 7017 return finishNode(factory.createSemicolonClassElement(), pos); 7018 } 7019 7020 const hasJSDoc = hasPrecedingJSDocComment(); 7021 const decorators = parseDecorators(); 7022 const modifiers = parseModifiers(/*permitInvalidConstAsModifier*/ true); 7023 7024 if (parseContextualModifier(SyntaxKind.GetKeyword)) { 7025 return parseAccessorDeclaration(pos, hasJSDoc, decorators, modifiers, SyntaxKind.GetAccessor); 7026 } 7027 7028 if (parseContextualModifier(SyntaxKind.SetKeyword)) { 7029 return parseAccessorDeclaration(pos, hasJSDoc, decorators, modifiers, SyntaxKind.SetAccessor); 7030 } 7031 7032 if (token() === SyntaxKind.ConstructorKeyword || token() === SyntaxKind.StringLiteral) { 7033 const constructorDeclaration = tryParseConstructorDeclaration(pos, hasJSDoc, decorators, modifiers); 7034 if (constructorDeclaration) { 7035 return constructorDeclaration; 7036 } 7037 } 7038 7039 if (isIndexSignature()) { 7040 return parseIndexSignatureDeclaration(pos, hasJSDoc, decorators, modifiers); 7041 } 7042 7043 // It is very important that we check this *after* checking indexers because 7044 // the [ token can start an index signature or a computed property name 7045 if (tokenIsIdentifierOrKeyword(token()) || 7046 token() === SyntaxKind.StringLiteral || 7047 token() === SyntaxKind.NumericLiteral || 7048 token() === SyntaxKind.AsteriskToken || 7049 token() === SyntaxKind.OpenBracketToken) { 7050 const isAmbient = some(modifiers, isDeclareModifier); 7051 if (isAmbient) { 7052 for (const m of modifiers!) { 7053 (m as Mutable<Node>).flags |= NodeFlags.Ambient; 7054 } 7055 return doInsideOfContext(NodeFlags.Ambient, () => parsePropertyOrMethodDeclaration(pos, hasJSDoc, decorators, modifiers)); 7056 } 7057 else { 7058 return parsePropertyOrMethodDeclaration(pos, hasJSDoc, decorators, modifiers); 7059 } 7060 } 7061 7062 if (decorators || modifiers) { 7063 // treat this as a property declaration with a missing name. 7064 const name = createMissingNode<Identifier>(SyntaxKind.Identifier, /*reportAtCurrentPosition*/ true, Diagnostics.Declaration_expected); 7065 return parsePropertyDeclaration(pos, hasJSDoc, decorators, modifiers, name, /*questionToken*/ undefined); 7066 } 7067 7068 // 'isClassMemberStart' should have hinted not to attempt parsing. 7069 return Debug.fail("Should not have attempted to parse class member declaration."); 7070 } 7071 7072 function parseClassExpression(): ClassExpression { 7073 return <ClassExpression>parseClassDeclarationOrExpression(getNodePos(), hasPrecedingJSDocComment(), /*decorators*/ undefined, /*modifiers*/ undefined, SyntaxKind.ClassExpression); 7074 } 7075 7076 function parseClassDeclaration(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined): ClassDeclaration { 7077 return <ClassDeclaration>parseClassDeclarationOrExpression(pos, hasJSDoc, decorators, modifiers, SyntaxKind.ClassDeclaration); 7078 } 7079 7080 function parseStructDeclaration(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined): StructDeclaration { 7081 return parseStructDeclarationOrExpression(pos, hasJSDoc, decorators, modifiers); 7082 } 7083 7084 function parseClassDeclarationOrExpression(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined, kind: ClassLikeDeclaration["kind"]): ClassLikeDeclaration { 7085 const savedAwaitContext = inAwaitContext(); 7086 parseExpected(SyntaxKind.ClassKeyword); 7087 // We don't parse the name here in await context, instead we will report a grammar error in the checker. 7088 const name = parseNameOfClassDeclarationOrExpression(); 7089 const typeParameters = parseTypeParameters(); 7090 if (some(modifiers, isExportModifier)) setAwaitContext(/*value*/ true); 7091 const heritageClauses = parseHeritageClauses(); 7092 7093 let members; 7094 if (parseExpected(SyntaxKind.OpenBraceToken)) { 7095 // ClassTail[Yield,Await] : (Modified) See 14.5 7096 // ClassHeritage[?Yield,?Await]opt { ClassBody[?Yield,?Await]opt } 7097 members = parseClassMembers(); 7098 parseExpected(SyntaxKind.CloseBraceToken); 7099 } 7100 else { 7101 members = createMissingList<ClassElement>(); 7102 } 7103 setAwaitContext(savedAwaitContext); 7104 const node = kind === SyntaxKind.ClassDeclaration 7105 ? factory.createClassDeclaration(decorators, modifiers, name, typeParameters, heritageClauses, members) 7106 : factory.createClassExpression(decorators, modifiers, name, typeParameters, heritageClauses, members); 7107 return withJSDoc(finishNode(node, pos), hasJSDoc); 7108 } 7109 7110 function parseStructDeclarationOrExpression(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined): StructDeclaration { 7111 const savedAwaitContext = inAwaitContext(); 7112 parseExpected(SyntaxKind.StructKeyword); 7113 setStructContext(true); 7114 // We don't parse the name here in await context, instead we will report a grammar error in the checker. 7115 // struct Identifier logic is same to class Identifier 7116 const name = parseNameOfClassDeclarationOrExpression(); 7117 currentStructName = name?.escapedText.toString(); 7118 const typeParameters = parseTypeParameters(); 7119 if (some(modifiers, isExportModifier)) setAwaitContext(/*value*/ true); 7120 let heritageClauses = parseHeritageClauses(); 7121 const customComponent = sourceFileCompilerOptions.ets?.customComponent; 7122 if (!heritageClauses && customComponent) { 7123 heritageClauses = createVirtualHeritageClauses(customComponent); 7124 } 7125 let members; 7126 if (parseExpected(SyntaxKind.OpenBraceToken)) { 7127 // ClassTail[Yield,Await] : (Modified) See 14.5 7128 // ClassHeritage[?Yield,?Await]opt { ClassBody[?Yield,?Await]opt } 7129 members = parseStructMembers(pos); 7130 parseExpected(SyntaxKind.CloseBraceToken); 7131 } 7132 else { 7133 members = createMissingList<ClassElement>(); 7134 } 7135 setAwaitContext(savedAwaitContext); 7136 const node = factory.createStructDeclaration(decorators, modifiers, name, typeParameters, heritageClauses, members); 7137 currentStructName = undefined; 7138 structStylesComponents.clear(); 7139 setStructContext(false); 7140 return withJSDoc(finishNode(node, pos), hasJSDoc); 7141 } 7142 7143 function createVirtualHeritageClauses(customComponent: string): NodeArray<HeritageClause> { 7144 const curPos = getNodePos(); 7145 const clause = factory.createHeritageClause( 7146 SyntaxKind.ExtendsKeyword, 7147 createNodeArray([finishNode(factory.createExpressionWithTypeArguments( 7148 finishNode(factory.createIdentifier(/*text*/ customComponent), curPos, /*end*/ undefined, /*virtual*/ true), 7149 /*typeArguments*/ undefined 7150 ), curPos)], curPos, /*end*/ undefined, /*hasTrailingComma*/ false) 7151 ); 7152 return createNodeArray([finishNode(clause, curPos, /*end*/ undefined, /*virtual*/ true)], curPos, /*end*/ undefined, /*hasTrailingComma*/ false); 7153 } 7154 7155 function parseNameOfClassDeclarationOrExpression(): Identifier | undefined { 7156 // implements is a future reserved word so 7157 // 'class implements' might mean either 7158 // - class expression with omitted name, 'implements' starts heritage clause 7159 // - class with name 'implements' 7160 // 'isImplementsClause' helps to disambiguate between these two cases 7161 return isBindingIdentifier() && !isImplementsClause() 7162 ? createIdentifier(isBindingIdentifier()) 7163 : undefined; 7164 } 7165 7166 function isImplementsClause() { 7167 return token() === SyntaxKind.ImplementsKeyword && lookAhead(nextTokenIsIdentifierOrKeyword); 7168 } 7169 7170 function parseHeritageClauses(): NodeArray<HeritageClause> | undefined { 7171 // ClassTail[Yield,Await] : (Modified) See 14.5 7172 // ClassHeritage[?Yield,?Await]opt { ClassBody[?Yield,?Await]opt } 7173 7174 if (isHeritageClause()) { 7175 return parseList(ParsingContext.HeritageClauses, parseHeritageClause); 7176 } 7177 7178 return undefined; 7179 } 7180 7181 function parseHeritageClause(): HeritageClause { 7182 const pos = getNodePos(); 7183 const tok = token(); 7184 Debug.assert(tok === SyntaxKind.ExtendsKeyword || tok === SyntaxKind.ImplementsKeyword); // isListElement() should ensure this. 7185 nextToken(); 7186 const types = parseDelimitedList(ParsingContext.HeritageClauseElement, parseExpressionWithTypeArguments); 7187 return finishNode(factory.createHeritageClause(tok, types), pos); 7188 } 7189 7190 function parseExpressionWithTypeArguments(): ExpressionWithTypeArguments { 7191 const pos = getNodePos(); 7192 const expression = parseLeftHandSideExpressionOrHigher(); 7193 const typeArguments = tryParseTypeArguments(); 7194 return finishNode(factory.createExpressionWithTypeArguments(expression, typeArguments), pos); 7195 } 7196 7197 function tryParseTypeArguments(): NodeArray<TypeNode> | undefined { 7198 return token() === SyntaxKind.LessThanToken ? 7199 parseBracketedList(ParsingContext.TypeArguments, parseType, SyntaxKind.LessThanToken, SyntaxKind.GreaterThanToken) : undefined; 7200 } 7201 7202 function isHeritageClause(): boolean { 7203 return token() === SyntaxKind.ExtendsKeyword || token() === SyntaxKind.ImplementsKeyword; 7204 } 7205 7206 function parseClassMembers(): NodeArray<ClassElement> { 7207 return parseList(ParsingContext.ClassMembers, parseClassElement); 7208 } 7209 7210 function parseStructMembers(pos: number): NodeArray<ClassElement> { 7211 const structMembers = parseList(ParsingContext.ClassMembers, parseClassElement); 7212 7213 const virtualStructMembers: ClassElement[] = []; 7214 // create constructor function argument object properties 7215 const virtualParameterProperties: TypeElement[] = []; 7216 structMembers.forEach(member => { 7217 virtualStructMembers.push(member); 7218 if (member.kind === SyntaxKind.PropertyDeclaration) { 7219 const property = <PropertyDeclaration>member; 7220 virtualParameterProperties.push( 7221 finishVirtualNode( 7222 factory.createPropertySignature(property.modifiers, property.name, factory.createToken(SyntaxKind.QuestionToken), property.type) 7223 ) 7224 ); 7225 } 7226 }); 7227 const parameters: ParameterDeclaration[] = []; 7228 if (virtualParameterProperties.length) { 7229 const type = finishVirtualNode(factory.createTypeLiteralNode(createNodeArray(virtualParameterProperties, 0, 0))); 7230 parameters.push( 7231 finishVirtualNode( 7232 factory.createParameterDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, 7233 finishVirtualNode(factory.createIdentifier("value")), factory.createToken(SyntaxKind.QuestionToken), type 7234 ) 7235 ) 7236 ); 7237 } 7238 const emptyBody = finishVirtualNode(factory.createBlock(createNodeArray([], 0, 0))); 7239 const virtualConstructor = factory.createConstructorDeclaration(/*decorators*/ undefined, /*modifier*/ undefined, createNodeArray(parameters, 0, 0), emptyBody); 7240 7241 virtualStructMembers.unshift(finishVirtualNode(virtualConstructor, pos, pos)); 7242 7243 return createNodeArray(virtualStructMembers, structMembers.pos); 7244 } 7245 7246 function finishVirtualNode<T extends Node>(node: T, start = 0, end = 0) { 7247 return finishNode(node, start, end, /*virtual*/ true); 7248 } 7249 7250 function parseInterfaceDeclaration(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined): InterfaceDeclaration { 7251 parseExpected(SyntaxKind.InterfaceKeyword); 7252 const name = parseIdentifier(); 7253 const typeParameters = parseTypeParameters(); 7254 const heritageClauses = parseHeritageClauses(); 7255 const members = parseObjectTypeMembers(); 7256 const node = factory.createInterfaceDeclaration(decorators, modifiers, name, typeParameters, heritageClauses, members); 7257 return withJSDoc(finishNode(node, pos), hasJSDoc); 7258 } 7259 7260 function parseTypeAliasDeclaration(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined): TypeAliasDeclaration { 7261 parseExpected(SyntaxKind.TypeKeyword); 7262 const name = parseIdentifier(); 7263 const typeParameters = parseTypeParameters(); 7264 parseExpected(SyntaxKind.EqualsToken); 7265 const type = token() === SyntaxKind.IntrinsicKeyword && tryParse(parseKeywordAndNoDot) || parseType(); 7266 parseSemicolon(); 7267 const node = factory.createTypeAliasDeclaration(decorators, modifiers, name, typeParameters, type); 7268 return withJSDoc(finishNode(node, pos), hasJSDoc); 7269 } 7270 7271 // In an ambient declaration, the grammar only allows integer literals as initializers. 7272 // In a non-ambient declaration, the grammar allows uninitialized members only in a 7273 // ConstantEnumMemberSection, which starts at the beginning of an enum declaration 7274 // or any time an integer literal initializer is encountered. 7275 function parseEnumMember(): EnumMember { 7276 const pos = getNodePos(); 7277 const hasJSDoc = hasPrecedingJSDocComment(); 7278 const name = parsePropertyName(); 7279 const initializer = allowInAnd(parseInitializer); 7280 return withJSDoc(finishNode(factory.createEnumMember(name, initializer), pos), hasJSDoc); 7281 } 7282 7283 function parseEnumDeclaration(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined): EnumDeclaration { 7284 parseExpected(SyntaxKind.EnumKeyword); 7285 const name = parseIdentifier(); 7286 let members; 7287 if (parseExpected(SyntaxKind.OpenBraceToken)) { 7288 members = doOutsideOfYieldAndAwaitContext(() => parseDelimitedList(ParsingContext.EnumMembers, parseEnumMember)); 7289 parseExpected(SyntaxKind.CloseBraceToken); 7290 } 7291 else { 7292 members = createMissingList<EnumMember>(); 7293 } 7294 const node = factory.createEnumDeclaration(decorators, modifiers, name, members); 7295 return withJSDoc(finishNode(node, pos), hasJSDoc); 7296 } 7297 7298 function parseModuleBlock(): ModuleBlock { 7299 const pos = getNodePos(); 7300 let statements; 7301 if (parseExpected(SyntaxKind.OpenBraceToken)) { 7302 statements = parseList(ParsingContext.BlockStatements, parseStatement); 7303 parseExpected(SyntaxKind.CloseBraceToken); 7304 } 7305 else { 7306 statements = createMissingList<Statement>(); 7307 } 7308 return finishNode(factory.createModuleBlock(statements), pos); 7309 } 7310 7311 function parseModuleOrNamespaceDeclaration(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined, flags: NodeFlags): ModuleDeclaration { 7312 // If we are parsing a dotted namespace name, we want to 7313 // propagate the 'Namespace' flag across the names if set. 7314 const namespaceFlag = flags & NodeFlags.Namespace; 7315 const name = parseIdentifier(); 7316 const body = parseOptional(SyntaxKind.DotToken) 7317 ? <NamespaceDeclaration>parseModuleOrNamespaceDeclaration(getNodePos(), /*hasJSDoc*/ false, /*decorators*/ undefined, /*modifiers*/ undefined, NodeFlags.NestedNamespace | namespaceFlag) 7318 : parseModuleBlock(); 7319 const node = factory.createModuleDeclaration(decorators, modifiers, name, body, flags); 7320 return withJSDoc(finishNode(node, pos), hasJSDoc); 7321 } 7322 7323 function parseAmbientExternalModuleDeclaration(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined): ModuleDeclaration { 7324 let flags: NodeFlags = 0; 7325 let name; 7326 if (token() === SyntaxKind.GlobalKeyword) { 7327 // parse 'global' as name of global scope augmentation 7328 name = parseIdentifier(); 7329 flags |= NodeFlags.GlobalAugmentation; 7330 } 7331 else { 7332 name = <StringLiteral>parseLiteralNode(); 7333 name.text = internIdentifier(name.text); 7334 } 7335 let body: ModuleBlock | undefined; 7336 if (token() === SyntaxKind.OpenBraceToken) { 7337 body = parseModuleBlock(); 7338 } 7339 else { 7340 parseSemicolon(); 7341 } 7342 const node = factory.createModuleDeclaration(decorators, modifiers, name, body, flags); 7343 return withJSDoc(finishNode(node, pos), hasJSDoc); 7344 } 7345 7346 function parseModuleDeclaration(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined): ModuleDeclaration { 7347 let flags: NodeFlags = 0; 7348 if (token() === SyntaxKind.GlobalKeyword) { 7349 // global augmentation 7350 return parseAmbientExternalModuleDeclaration(pos, hasJSDoc, decorators, modifiers); 7351 } 7352 else if (parseOptional(SyntaxKind.NamespaceKeyword)) { 7353 flags |= NodeFlags.Namespace; 7354 } 7355 else { 7356 parseExpected(SyntaxKind.ModuleKeyword); 7357 if (token() === SyntaxKind.StringLiteral) { 7358 return parseAmbientExternalModuleDeclaration(pos, hasJSDoc, decorators, modifiers); 7359 } 7360 } 7361 return parseModuleOrNamespaceDeclaration(pos, hasJSDoc, decorators, modifiers, flags); 7362 } 7363 7364 function isExternalModuleReference() { 7365 return token() === SyntaxKind.RequireKeyword && 7366 lookAhead(nextTokenIsOpenParen); 7367 } 7368 7369 function nextTokenIsOpenParen() { 7370 return nextToken() === SyntaxKind.OpenParenToken; 7371 } 7372 7373 function nextTokenIsSlash() { 7374 return nextToken() === SyntaxKind.SlashToken; 7375 } 7376 7377 function parseNamespaceExportDeclaration(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined): NamespaceExportDeclaration { 7378 parseExpected(SyntaxKind.AsKeyword); 7379 parseExpected(SyntaxKind.NamespaceKeyword); 7380 const name = parseIdentifier(); 7381 parseSemicolon(); 7382 const node = factory.createNamespaceExportDeclaration(name); 7383 // NamespaceExportDeclaration nodes cannot have decorators or modifiers, so we attach them here so we can report them in the grammar checker 7384 node.decorators = decorators; 7385 node.modifiers = modifiers; 7386 return withJSDoc(finishNode(node, pos), hasJSDoc); 7387 } 7388 7389 function parseImportDeclarationOrImportEqualsDeclaration(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined): ImportEqualsDeclaration | ImportDeclaration { 7390 parseExpected(SyntaxKind.ImportKeyword); 7391 7392 const afterImportPos = scanner.getStartPos(); 7393 7394 // We don't parse the identifier here in await context, instead we will report a grammar error in the checker. 7395 let identifier: Identifier | undefined; 7396 if (isIdentifier()) { 7397 identifier = parseIdentifier(); 7398 } 7399 7400 let isTypeOnly = false; 7401 if (token() !== SyntaxKind.FromKeyword && 7402 identifier?.escapedText === "type" && 7403 (isIdentifier() || tokenAfterImportDefinitelyProducesImportDeclaration()) 7404 ) { 7405 isTypeOnly = true; 7406 identifier = isIdentifier() ? parseIdentifier() : undefined; 7407 } 7408 7409 if (identifier && !tokenAfterImportedIdentifierDefinitelyProducesImportDeclaration()) { 7410 return parseImportEqualsDeclaration(pos, hasJSDoc, decorators, modifiers, identifier, isTypeOnly); 7411 } 7412 7413 // ImportDeclaration: 7414 // import ImportClause from ModuleSpecifier ; 7415 // import ModuleSpecifier; 7416 let importClause: ImportClause | undefined; 7417 if (identifier || // import id 7418 token() === SyntaxKind.AsteriskToken || // import * 7419 token() === SyntaxKind.OpenBraceToken // import { 7420 ) { 7421 importClause = parseImportClause(identifier, afterImportPos, isTypeOnly); 7422 parseExpected(SyntaxKind.FromKeyword); 7423 } 7424 7425 const moduleSpecifier = parseModuleSpecifier(); 7426 parseSemicolon(); 7427 const node = factory.createImportDeclaration(decorators, modifiers, importClause, moduleSpecifier); 7428 return withJSDoc(finishNode(node, pos), hasJSDoc); 7429 } 7430 7431 function tokenAfterImportDefinitelyProducesImportDeclaration() { 7432 return token() === SyntaxKind.AsteriskToken || token() === SyntaxKind.OpenBraceToken; 7433 } 7434 7435 function tokenAfterImportedIdentifierDefinitelyProducesImportDeclaration() { 7436 // In `import id ___`, the current token decides whether to produce 7437 // an ImportDeclaration or ImportEqualsDeclaration. 7438 return token() === SyntaxKind.CommaToken || token() === SyntaxKind.FromKeyword; 7439 } 7440 7441 function parseImportEqualsDeclaration(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined, identifier: Identifier, isTypeOnly: boolean): ImportEqualsDeclaration { 7442 parseExpected(SyntaxKind.EqualsToken); 7443 const moduleReference = parseModuleReference(); 7444 parseSemicolon(); 7445 const node = factory.createImportEqualsDeclaration(decorators, modifiers, isTypeOnly, identifier, moduleReference); 7446 const finished = withJSDoc(finishNode(node, pos), hasJSDoc); 7447 return finished; 7448 } 7449 7450 function parseImportClause(identifier: Identifier | undefined, pos: number, isTypeOnly: boolean) { 7451 // ImportClause: 7452 // ImportedDefaultBinding 7453 // NameSpaceImport 7454 // NamedImports 7455 // ImportedDefaultBinding, NameSpaceImport 7456 // ImportedDefaultBinding, NamedImports 7457 7458 // If there was no default import or if there is comma token after default import 7459 // parse namespace or named imports 7460 let namedBindings: NamespaceImport | NamedImports | undefined; 7461 if (!identifier || 7462 parseOptional(SyntaxKind.CommaToken)) { 7463 namedBindings = token() === SyntaxKind.AsteriskToken ? parseNamespaceImport() : parseNamedImportsOrExports(SyntaxKind.NamedImports); 7464 } 7465 7466 return finishNode(factory.createImportClause(isTypeOnly, identifier, namedBindings), pos); 7467 } 7468 7469 function parseModuleReference() { 7470 return isExternalModuleReference() 7471 ? parseExternalModuleReference() 7472 : parseEntityName(/*allowReservedWords*/ false); 7473 } 7474 7475 function parseExternalModuleReference() { 7476 const pos = getNodePos(); 7477 parseExpected(SyntaxKind.RequireKeyword); 7478 parseExpected(SyntaxKind.OpenParenToken); 7479 const expression = parseModuleSpecifier(); 7480 parseExpected(SyntaxKind.CloseParenToken); 7481 return finishNode(factory.createExternalModuleReference(expression), pos); 7482 } 7483 7484 function parseModuleSpecifier(): Expression { 7485 if (token() === SyntaxKind.StringLiteral) { 7486 const result = parseLiteralNode(); 7487 result.text = internIdentifier(result.text); 7488 return result; 7489 } 7490 else { 7491 // We allow arbitrary expressions here, even though the grammar only allows string 7492 // literals. We check to ensure that it is only a string literal later in the grammar 7493 // check pass. 7494 return parseExpression(); 7495 } 7496 } 7497 7498 function parseNamespaceImport(): NamespaceImport { 7499 // NameSpaceImport: 7500 // * as ImportedBinding 7501 const pos = getNodePos(); 7502 parseExpected(SyntaxKind.AsteriskToken); 7503 parseExpected(SyntaxKind.AsKeyword); 7504 const name = parseIdentifier(); 7505 return finishNode(factory.createNamespaceImport(name), pos); 7506 } 7507 7508 function parseNamedImportsOrExports(kind: SyntaxKind.NamedImports): NamedImports; 7509 function parseNamedImportsOrExports(kind: SyntaxKind.NamedExports): NamedExports; 7510 function parseNamedImportsOrExports(kind: SyntaxKind): NamedImportsOrExports { 7511 const pos = getNodePos(); 7512 7513 // NamedImports: 7514 // { } 7515 // { ImportsList } 7516 // { ImportsList, } 7517 7518 // ImportsList: 7519 // ImportSpecifier 7520 // ImportsList, ImportSpecifier 7521 const node = kind === SyntaxKind.NamedImports 7522 ? factory.createNamedImports(parseBracketedList(ParsingContext.ImportOrExportSpecifiers, parseImportSpecifier, SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken)) 7523 : factory.createNamedExports(parseBracketedList(ParsingContext.ImportOrExportSpecifiers, parseExportSpecifier, SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken)); 7524 return finishNode(node, pos); 7525 } 7526 7527 function parseExportSpecifier() { 7528 return parseImportOrExportSpecifier(SyntaxKind.ExportSpecifier) as ExportSpecifier; 7529 } 7530 7531 function parseImportSpecifier() { 7532 return parseImportOrExportSpecifier(SyntaxKind.ImportSpecifier) as ImportSpecifier; 7533 } 7534 7535 function parseImportOrExportSpecifier(kind: SyntaxKind): ImportOrExportSpecifier { 7536 const pos = getNodePos(); 7537 // ImportSpecifier: 7538 // BindingIdentifier 7539 // IdentifierName as BindingIdentifier 7540 // ExportSpecifier: 7541 // IdentifierName 7542 // IdentifierName as IdentifierName 7543 let checkIdentifierIsKeyword = isKeyword(token()) && !isIdentifier(); 7544 let checkIdentifierStart = scanner.getTokenPos(); 7545 let checkIdentifierEnd = scanner.getTextPos(); 7546 const identifierName = parseIdentifierName(); 7547 let propertyName: Identifier | undefined; 7548 let name: Identifier; 7549 if (token() === SyntaxKind.AsKeyword) { 7550 propertyName = identifierName; 7551 parseExpected(SyntaxKind.AsKeyword); 7552 checkIdentifierIsKeyword = isKeyword(token()) && !isIdentifier(); 7553 checkIdentifierStart = scanner.getTokenPos(); 7554 checkIdentifierEnd = scanner.getTextPos(); 7555 name = parseIdentifierName(); 7556 } 7557 else { 7558 name = identifierName; 7559 } 7560 if (kind === SyntaxKind.ImportSpecifier && checkIdentifierIsKeyword) { 7561 parseErrorAt(checkIdentifierStart, checkIdentifierEnd, Diagnostics.Identifier_expected); 7562 } 7563 const node = kind === SyntaxKind.ImportSpecifier 7564 ? factory.createImportSpecifier(propertyName, name) 7565 : factory.createExportSpecifier(propertyName, name); 7566 return finishNode(node, pos); 7567 } 7568 7569 function parseNamespaceExport(pos: number): NamespaceExport { 7570 return finishNode(factory.createNamespaceExport(parseIdentifierName()), pos); 7571 } 7572 7573 function parseExportDeclaration(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined): ExportDeclaration { 7574 const savedAwaitContext = inAwaitContext(); 7575 setAwaitContext(/*value*/ true); 7576 let exportClause: NamedExportBindings | undefined; 7577 let moduleSpecifier: Expression | undefined; 7578 const isTypeOnly = parseOptional(SyntaxKind.TypeKeyword); 7579 const namespaceExportPos = getNodePos(); 7580 if (parseOptional(SyntaxKind.AsteriskToken)) { 7581 if (parseOptional(SyntaxKind.AsKeyword)) { 7582 exportClause = parseNamespaceExport(namespaceExportPos); 7583 } 7584 parseExpected(SyntaxKind.FromKeyword); 7585 moduleSpecifier = parseModuleSpecifier(); 7586 } 7587 else { 7588 exportClause = parseNamedImportsOrExports(SyntaxKind.NamedExports); 7589 // It is not uncommon to accidentally omit the 'from' keyword. Additionally, in editing scenarios, 7590 // the 'from' keyword can be parsed as a named export when the export clause is unterminated (i.e. `export { from "moduleName";`) 7591 // If we don't have a 'from' keyword, see if we have a string literal such that ASI won't take effect. 7592 if (token() === SyntaxKind.FromKeyword || (token() === SyntaxKind.StringLiteral && !scanner.hasPrecedingLineBreak())) { 7593 parseExpected(SyntaxKind.FromKeyword); 7594 moduleSpecifier = parseModuleSpecifier(); 7595 } 7596 } 7597 parseSemicolon(); 7598 setAwaitContext(savedAwaitContext); 7599 const node = factory.createExportDeclaration(decorators, modifiers, isTypeOnly, exportClause, moduleSpecifier); 7600 return withJSDoc(finishNode(node, pos), hasJSDoc); 7601 } 7602 7603 function parseExportAssignment(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined): ExportAssignment { 7604 const savedAwaitContext = inAwaitContext(); 7605 setAwaitContext(/*value*/ true); 7606 let isExportEquals: boolean | undefined; 7607 if (parseOptional(SyntaxKind.EqualsToken)) { 7608 isExportEquals = true; 7609 } 7610 else { 7611 parseExpected(SyntaxKind.DefaultKeyword); 7612 } 7613 const expression = parseAssignmentExpressionOrHigher(); 7614 parseSemicolon(); 7615 setAwaitContext(savedAwaitContext); 7616 const node = factory.createExportAssignment(decorators, modifiers, isExportEquals, expression); 7617 return withJSDoc(finishNode(node, pos), hasJSDoc); 7618 } 7619 7620 function setExternalModuleIndicator(sourceFile: SourceFile) { 7621 // Try to use the first top-level import/export when available, then 7622 // fall back to looking for an 'import.meta' somewhere in the tree if necessary. 7623 sourceFile.externalModuleIndicator = 7624 forEach(sourceFile.statements, isAnExternalModuleIndicatorNode) || 7625 getImportMetaIfNecessary(sourceFile); 7626 } 7627 7628 function isAnExternalModuleIndicatorNode(node: Node) { 7629 return hasModifierOfKind(node, SyntaxKind.ExportKeyword) 7630 || isImportEqualsDeclaration(node) && ts.isExternalModuleReference(node.moduleReference) 7631 || isImportDeclaration(node) 7632 || isExportAssignment(node) 7633 || isExportDeclaration(node) ? node : undefined; 7634 } 7635 7636 function getImportMetaIfNecessary(sourceFile: SourceFile) { 7637 return sourceFile.flags & NodeFlags.PossiblyContainsImportMeta ? 7638 walkTreeForExternalModuleIndicators(sourceFile) : 7639 undefined; 7640 } 7641 7642 function walkTreeForExternalModuleIndicators(node: Node): Node | undefined { 7643 return isImportMeta(node) ? node : forEachChild(node, walkTreeForExternalModuleIndicators); 7644 } 7645 7646 /** Do not use hasModifier inside the parser; it relies on parent pointers. Use this instead. */ 7647 function hasModifierOfKind(node: Node, kind: SyntaxKind) { 7648 return some(node.modifiers, m => m.kind === kind); 7649 } 7650 7651 function isImportMeta(node: Node): boolean { 7652 return isMetaProperty(node) && node.keywordToken === SyntaxKind.ImportKeyword && node.name.escapedText === "meta"; 7653 } 7654 7655 const enum ParsingContext { 7656 SourceElements, // Elements in source file 7657 BlockStatements, // Statements in block 7658 SwitchClauses, // Clauses in switch statement 7659 SwitchClauseStatements, // Statements in switch clause 7660 TypeMembers, // Members in interface or type literal 7661 ClassMembers, // Members in class declaration 7662 EnumMembers, // Members in enum declaration 7663 HeritageClauseElement, // Elements in a heritage clause 7664 VariableDeclarations, // Variable declarations in variable statement 7665 ObjectBindingElements, // Binding elements in object binding list 7666 ArrayBindingElements, // Binding elements in array binding list 7667 ArgumentExpressions, // Expressions in argument list 7668 ObjectLiteralMembers, // Members in object literal 7669 JsxAttributes, // Attributes in jsx element 7670 JsxChildren, // Things between opening and closing JSX tags 7671 ArrayLiteralMembers, // Members in array literal 7672 Parameters, // Parameters in parameter list 7673 JSDocParameters, // JSDoc parameters in parameter list of JSDoc function type 7674 RestProperties, // Property names in a rest type list 7675 TypeParameters, // Type parameters in type parameter list 7676 TypeArguments, // Type arguments in type argument list 7677 TupleElementTypes, // Element types in tuple element type list 7678 HeritageClauses, // Heritage clauses for a class or interface declaration. 7679 ImportOrExportSpecifiers, // Named import clause's import specifier list 7680 Count // Number of parsing contexts 7681 } 7682 7683 const enum Tristate { 7684 False, 7685 True, 7686 Unknown 7687 } 7688 7689 export namespace JSDocParser { 7690 export function parseJSDocTypeExpressionForTests(content: string, start: number | undefined, length: number | undefined): { jsDocTypeExpression: JSDocTypeExpression, diagnostics: Diagnostic[] } | undefined { 7691 initializeState("file.js", content, ScriptTarget.Latest, /*_syntaxCursor:*/ undefined, ScriptKind.JS); 7692 scanner.setText(content, start, length); 7693 currentToken = scanner.scan(); 7694 const jsDocTypeExpression = parseJSDocTypeExpression(); 7695 7696 const sourceFile = createSourceFile("file.js", ScriptTarget.Latest, ScriptKind.JS, /*isDeclarationFile*/ false, [], factory.createToken(SyntaxKind.EndOfFileToken), NodeFlags.None); 7697 const diagnostics = attachFileToDiagnostics(parseDiagnostics, sourceFile); 7698 if (jsDocDiagnostics) { 7699 sourceFile.jsDocDiagnostics = attachFileToDiagnostics(jsDocDiagnostics, sourceFile); 7700 } 7701 7702 clearState(); 7703 7704 return jsDocTypeExpression ? { jsDocTypeExpression, diagnostics } : undefined; 7705 } 7706 7707 // Parses out a JSDoc type expression. 7708 export function parseJSDocTypeExpression(mayOmitBraces?: boolean): JSDocTypeExpression { 7709 const pos = getNodePos(); 7710 const hasBrace = (mayOmitBraces ? parseOptional : parseExpected)(SyntaxKind.OpenBraceToken); 7711 const type = doInsideOfContext(NodeFlags.JSDoc, parseJSDocType); 7712 if (!mayOmitBraces || hasBrace) { 7713 parseExpectedJSDoc(SyntaxKind.CloseBraceToken); 7714 } 7715 7716 const result = factory.createJSDocTypeExpression(type); 7717 fixupParentReferences(result); 7718 return finishNode(result, pos); 7719 } 7720 7721 export function parseJSDocNameReference(): JSDocNameReference { 7722 const pos = getNodePos(); 7723 const hasBrace = parseOptional(SyntaxKind.OpenBraceToken); 7724 const entityName = parseEntityName(/* allowReservedWords*/ false); 7725 if (hasBrace) { 7726 parseExpectedJSDoc(SyntaxKind.CloseBraceToken); 7727 } 7728 7729 const result = factory.createJSDocNameReference(entityName); 7730 fixupParentReferences(result); 7731 return finishNode(result, pos); 7732 } 7733 7734 export function parseIsolatedJSDocComment(content: string, start: number | undefined, length: number | undefined): { jsDoc: JSDoc, diagnostics: Diagnostic[] } | undefined { 7735 initializeState("", content, ScriptTarget.Latest, /*_syntaxCursor:*/ undefined, ScriptKind.JS); 7736 const jsDoc = doInsideOfContext(NodeFlags.JSDoc, () => parseJSDocCommentWorker(start, length)); 7737 7738 const sourceFile = <SourceFile>{ languageVariant: LanguageVariant.Standard, text: content }; 7739 const diagnostics = attachFileToDiagnostics(parseDiagnostics, sourceFile); 7740 clearState(); 7741 7742 return jsDoc ? { jsDoc, diagnostics } : undefined; 7743 } 7744 7745 export function parseJSDocComment(parent: HasJSDoc, start: number, length: number): JSDoc | undefined { 7746 const saveToken = currentToken; 7747 const saveParseDiagnosticsLength = parseDiagnostics.length; 7748 const saveParseErrorBeforeNextFinishedNode = parseErrorBeforeNextFinishedNode; 7749 7750 const comment = doInsideOfContext(NodeFlags.JSDoc, () => parseJSDocCommentWorker(start, length)); 7751 setParent(comment, parent); 7752 7753 if (contextFlags & NodeFlags.JavaScriptFile) { 7754 if (!jsDocDiagnostics) { 7755 jsDocDiagnostics = []; 7756 } 7757 jsDocDiagnostics.push(...parseDiagnostics); 7758 } 7759 currentToken = saveToken; 7760 parseDiagnostics.length = saveParseDiagnosticsLength; 7761 parseErrorBeforeNextFinishedNode = saveParseErrorBeforeNextFinishedNode; 7762 return comment; 7763 } 7764 7765 const enum JSDocState { 7766 BeginningOfLine, 7767 SawAsterisk, 7768 SavingComments, 7769 SavingBackticks, // NOTE: Only used when parsing tag comments 7770 } 7771 7772 const enum PropertyLikeParse { 7773 Property = 1 << 0, 7774 Parameter = 1 << 1, 7775 CallbackParameter = 1 << 2, 7776 } 7777 7778 function parseJSDocCommentWorker(start = 0, length: number | undefined): JSDoc | undefined { 7779 const content = sourceText; 7780 const end = length === undefined ? content.length : start + length; 7781 length = end - start; 7782 7783 Debug.assert(start >= 0); 7784 Debug.assert(start <= end); 7785 Debug.assert(end <= content.length); 7786 7787 // Check for /** (JSDoc opening part) 7788 if (!isJSDocLikeText(content, start)) { 7789 return undefined; 7790 } 7791 7792 let tags: JSDocTag[]; 7793 let tagsPos: number; 7794 let tagsEnd: number; 7795 const comments: string[] = []; 7796 7797 // + 3 for leading /**, - 5 in total for /** */ 7798 return scanner.scanRange(start + 3, length - 5, () => { 7799 // Initially we can parse out a tag. We also have seen a starting asterisk. 7800 // This is so that /** * @type */ doesn't parse. 7801 let state = JSDocState.SawAsterisk; 7802 let margin: number | undefined; 7803 // + 4 for leading '/** ' 7804 // + 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 7805 let indent = start - (content.lastIndexOf("\n", start) + 1) + 4; 7806 function pushComment(text: string) { 7807 if (!margin) { 7808 margin = indent; 7809 } 7810 comments.push(text); 7811 indent += text.length; 7812 } 7813 7814 nextTokenJSDoc(); 7815 while (parseOptionalJsdoc(SyntaxKind.WhitespaceTrivia)); 7816 if (parseOptionalJsdoc(SyntaxKind.NewLineTrivia)) { 7817 state = JSDocState.BeginningOfLine; 7818 indent = 0; 7819 } 7820 loop: while (true) { 7821 switch (token()) { 7822 case SyntaxKind.AtToken: 7823 if (state === JSDocState.BeginningOfLine || state === JSDocState.SawAsterisk) { 7824 removeTrailingWhitespace(comments); 7825 addTag(parseTag(indent)); 7826 // NOTE: According to usejsdoc.org, a tag goes to end of line, except the last tag. 7827 // Real-world comments may break this rule, so "BeginningOfLine" will not be a real line beginning 7828 // for malformed examples like `/** @param {string} x @returns {number} the length */` 7829 state = JSDocState.BeginningOfLine; 7830 margin = undefined; 7831 } 7832 else { 7833 pushComment(scanner.getTokenText()); 7834 } 7835 break; 7836 case SyntaxKind.NewLineTrivia: 7837 comments.push(scanner.getTokenText()); 7838 state = JSDocState.BeginningOfLine; 7839 indent = 0; 7840 break; 7841 case SyntaxKind.AsteriskToken: 7842 const asterisk = scanner.getTokenText(); 7843 if (state === JSDocState.SawAsterisk || state === JSDocState.SavingComments) { 7844 // If we've already seen an asterisk, then we can no longer parse a tag on this line 7845 state = JSDocState.SavingComments; 7846 pushComment(asterisk); 7847 } 7848 else { 7849 // Ignore the first asterisk on a line 7850 state = JSDocState.SawAsterisk; 7851 indent += asterisk.length; 7852 } 7853 break; 7854 case SyntaxKind.WhitespaceTrivia: 7855 // only collect whitespace if we're already saving comments or have just crossed the comment indent margin 7856 const whitespace = scanner.getTokenText(); 7857 if (state === JSDocState.SavingComments) { 7858 comments.push(whitespace); 7859 } 7860 else if (margin !== undefined && indent + whitespace.length > margin) { 7861 comments.push(whitespace.slice(margin - indent)); 7862 } 7863 indent += whitespace.length; 7864 break; 7865 case SyntaxKind.EndOfFileToken: 7866 break loop; 7867 default: 7868 // Anything else is doc comment text. We just save it. Because it 7869 // wasn't a tag, we can no longer parse a tag on this line until we hit the next 7870 // line break. 7871 state = JSDocState.SavingComments; 7872 pushComment(scanner.getTokenText()); 7873 break; 7874 } 7875 nextTokenJSDoc(); 7876 } 7877 removeLeadingNewlines(comments); 7878 removeTrailingWhitespace(comments); 7879 return createJSDocComment(); 7880 }); 7881 7882 function removeLeadingNewlines(comments: string[]) { 7883 while (comments.length && (comments[0] === "\n" || comments[0] === "\r")) { 7884 comments.shift(); 7885 } 7886 } 7887 7888 function removeTrailingWhitespace(comments: string[]) { 7889 while (comments.length && comments[comments.length - 1].trim() === "") { 7890 comments.pop(); 7891 } 7892 } 7893 7894 function createJSDocComment(): JSDoc { 7895 const comment = comments.length ? comments.join("") : undefined; 7896 const tagsArray = tags && createNodeArray(tags, tagsPos, tagsEnd); 7897 return finishNode(factory.createJSDocComment(comment, tagsArray), start, end); 7898 } 7899 7900 function isNextNonwhitespaceTokenEndOfFile(): boolean { 7901 // We must use infinite lookahead, as there could be any number of newlines :( 7902 while (true) { 7903 nextTokenJSDoc(); 7904 if (token() === SyntaxKind.EndOfFileToken) { 7905 return true; 7906 } 7907 if (!(token() === SyntaxKind.WhitespaceTrivia || token() === SyntaxKind.NewLineTrivia)) { 7908 return false; 7909 } 7910 } 7911 } 7912 7913 function skipWhitespace(): void { 7914 if (token() === SyntaxKind.WhitespaceTrivia || token() === SyntaxKind.NewLineTrivia) { 7915 if (lookAhead(isNextNonwhitespaceTokenEndOfFile)) { 7916 return; // Don't skip whitespace prior to EoF (or end of comment) - that shouldn't be included in any node's range 7917 } 7918 } 7919 while (token() === SyntaxKind.WhitespaceTrivia || token() === SyntaxKind.NewLineTrivia) { 7920 nextTokenJSDoc(); 7921 } 7922 } 7923 7924 function skipWhitespaceOrAsterisk(): string { 7925 if (token() === SyntaxKind.WhitespaceTrivia || token() === SyntaxKind.NewLineTrivia) { 7926 if (lookAhead(isNextNonwhitespaceTokenEndOfFile)) { 7927 return ""; // Don't skip whitespace prior to EoF (or end of comment) - that shouldn't be included in any node's range 7928 } 7929 } 7930 7931 let precedingLineBreak = scanner.hasPrecedingLineBreak(); 7932 let seenLineBreak = false; 7933 let indentText = ""; 7934 while ((precedingLineBreak && token() === SyntaxKind.AsteriskToken) || token() === SyntaxKind.WhitespaceTrivia || token() === SyntaxKind.NewLineTrivia) { 7935 indentText += scanner.getTokenText(); 7936 if (token() === SyntaxKind.NewLineTrivia) { 7937 precedingLineBreak = true; 7938 seenLineBreak = true; 7939 indentText = ""; 7940 } 7941 else if (token() === SyntaxKind.AsteriskToken) { 7942 precedingLineBreak = false; 7943 } 7944 nextTokenJSDoc(); 7945 } 7946 return seenLineBreak ? indentText : ""; 7947 } 7948 7949 function parseTag(margin: number) { 7950 Debug.assert(token() === SyntaxKind.AtToken); 7951 const start = scanner.getTokenPos(); 7952 nextTokenJSDoc(); 7953 7954 const tagName = parseJSDocIdentifierName(/*message*/ undefined); 7955 const indentText = skipWhitespaceOrAsterisk(); 7956 7957 let tag: JSDocTag | undefined; 7958 switch (tagName.escapedText) { 7959 case "author": 7960 tag = parseAuthorTag(start, tagName, margin, indentText); 7961 break; 7962 case "implements": 7963 tag = parseImplementsTag(start, tagName, margin, indentText); 7964 break; 7965 case "augments": 7966 case "extends": 7967 tag = parseAugmentsTag(start, tagName, margin, indentText); 7968 break; 7969 case "class": 7970 case "constructor": 7971 tag = parseSimpleTag(start, factory.createJSDocClassTag, tagName, margin, indentText); 7972 break; 7973 case "public": 7974 tag = parseSimpleTag(start, factory.createJSDocPublicTag, tagName, margin, indentText); 7975 break; 7976 case "private": 7977 tag = parseSimpleTag(start, factory.createJSDocPrivateTag, tagName, margin, indentText); 7978 break; 7979 case "protected": 7980 tag = parseSimpleTag(start, factory.createJSDocProtectedTag, tagName, margin, indentText); 7981 break; 7982 case "readonly": 7983 tag = parseSimpleTag(start, factory.createJSDocReadonlyTag, tagName, margin, indentText); 7984 break; 7985 case "deprecated": 7986 hasDeprecatedTag = true; 7987 tag = parseSimpleTag(start, factory.createJSDocDeprecatedTag, tagName, margin, indentText); 7988 break; 7989 case "this": 7990 tag = parseThisTag(start, tagName, margin, indentText); 7991 break; 7992 case "enum": 7993 tag = parseEnumTag(start, tagName, margin, indentText); 7994 break; 7995 case "arg": 7996 case "argument": 7997 case "param": 7998 return parseParameterOrPropertyTag(start, tagName, PropertyLikeParse.Parameter, margin); 7999 case "return": 8000 case "returns": 8001 tag = parseReturnTag(start, tagName, margin, indentText); 8002 break; 8003 case "template": 8004 tag = parseTemplateTag(start, tagName, margin, indentText); 8005 break; 8006 case "type": 8007 tag = parseTypeTag(start, tagName, margin, indentText); 8008 break; 8009 case "typedef": 8010 tag = parseTypedefTag(start, tagName, margin, indentText); 8011 break; 8012 case "callback": 8013 tag = parseCallbackTag(start, tagName, margin, indentText); 8014 break; 8015 case "see": 8016 tag = parseSeeTag(start, tagName, margin, indentText); 8017 break; 8018 default: 8019 tag = parseUnknownTag(start, tagName, margin, indentText); 8020 break; 8021 } 8022 return tag; 8023 } 8024 8025 function parseTrailingTagComments(pos: number, end: number, margin: number, indentText: string) { 8026 // some tags, like typedef and callback, have already parsed their comments earlier 8027 if (!indentText) { 8028 margin += end - pos; 8029 } 8030 return parseTagComments(margin, indentText.slice(margin)); 8031 } 8032 8033 function parseTagComments(indent: number, initialMargin?: string): string | undefined { 8034 const comments: string[] = []; 8035 let state = JSDocState.BeginningOfLine; 8036 let previousWhitespace = true; 8037 let margin: number | undefined; 8038 function pushComment(text: string) { 8039 if (!margin) { 8040 margin = indent; 8041 } 8042 comments.push(text); 8043 indent += text.length; 8044 } 8045 if (initialMargin !== undefined) { 8046 // jump straight to saving comments if there is some initial indentation 8047 if (initialMargin !== "") { 8048 pushComment(initialMargin); 8049 } 8050 state = JSDocState.SawAsterisk; 8051 } 8052 let tok = token() as JSDocSyntaxKind; 8053 loop: while (true) { 8054 switch (tok) { 8055 case SyntaxKind.NewLineTrivia: 8056 state = JSDocState.BeginningOfLine; 8057 // don't use pushComment here because we want to keep the margin unchanged 8058 comments.push(scanner.getTokenText()); 8059 indent = 0; 8060 break; 8061 case SyntaxKind.AtToken: 8062 if (state === JSDocState.SavingBackticks || !previousWhitespace && state === JSDocState.SavingComments) { 8063 // @ doesn't start a new tag inside ``, and inside a comment, only after whitespace 8064 comments.push(scanner.getTokenText()); 8065 break; 8066 } 8067 scanner.setTextPos(scanner.getTextPos() - 1); 8068 // falls through 8069 case SyntaxKind.EndOfFileToken: 8070 // Done 8071 break loop; 8072 case SyntaxKind.WhitespaceTrivia: 8073 if (state === JSDocState.SavingComments || state === JSDocState.SavingBackticks) { 8074 pushComment(scanner.getTokenText()); 8075 } 8076 else { 8077 const whitespace = scanner.getTokenText(); 8078 // if the whitespace crosses the margin, take only the whitespace that passes the margin 8079 if (margin !== undefined && indent + whitespace.length > margin) { 8080 comments.push(whitespace.slice(margin - indent)); 8081 } 8082 indent += whitespace.length; 8083 } 8084 break; 8085 case SyntaxKind.OpenBraceToken: 8086 state = JSDocState.SavingComments; 8087 if (lookAhead(() => nextTokenJSDoc() === SyntaxKind.AtToken && tokenIsIdentifierOrKeyword(nextTokenJSDoc()) && scanner.getTokenText() === "link")) { 8088 pushComment(scanner.getTokenText()); 8089 nextTokenJSDoc(); 8090 pushComment(scanner.getTokenText()); 8091 nextTokenJSDoc(); 8092 } 8093 pushComment(scanner.getTokenText()); 8094 break; 8095 case SyntaxKind.BacktickToken: 8096 if (state === JSDocState.SavingBackticks) { 8097 state = JSDocState.SavingComments; 8098 } 8099 else { 8100 state = JSDocState.SavingBackticks; 8101 } 8102 pushComment(scanner.getTokenText()); 8103 break; 8104 case SyntaxKind.AsteriskToken: 8105 if (state === JSDocState.BeginningOfLine) { 8106 // leading asterisks start recording on the *next* (non-whitespace) token 8107 state = JSDocState.SawAsterisk; 8108 indent += 1; 8109 break; 8110 } 8111 // record the * as a comment 8112 // falls through 8113 default: 8114 if (state !== JSDocState.SavingBackticks) { 8115 state = JSDocState.SavingComments; // leading identifiers start recording as well 8116 } 8117 pushComment(scanner.getTokenText()); 8118 break; 8119 } 8120 previousWhitespace = token() === SyntaxKind.WhitespaceTrivia; 8121 tok = nextTokenJSDoc(); 8122 } 8123 8124 removeLeadingNewlines(comments); 8125 removeTrailingWhitespace(comments); 8126 return comments.length === 0 ? undefined : comments.join(""); 8127 } 8128 8129 function parseUnknownTag(start: number, tagName: Identifier, indent: number, indentText: string) { 8130 const end = getNodePos(); 8131 return finishNode(factory.createJSDocUnknownTag(tagName, parseTrailingTagComments(start, end, indent, indentText)), start, end); 8132 } 8133 8134 function addTag(tag: JSDocTag | undefined): void { 8135 if (!tag) { 8136 return; 8137 } 8138 if (!tags) { 8139 tags = [tag]; 8140 tagsPos = tag.pos; 8141 } 8142 else { 8143 tags.push(tag); 8144 } 8145 tagsEnd = tag.end; 8146 } 8147 8148 function tryParseTypeExpression(): JSDocTypeExpression | undefined { 8149 skipWhitespaceOrAsterisk(); 8150 return token() === SyntaxKind.OpenBraceToken ? parseJSDocTypeExpression() : undefined; 8151 } 8152 8153 function parseBracketNameInPropertyAndParamTag(): { name: EntityName, isBracketed: boolean } { 8154 // Looking for something like '[foo]', 'foo', '[foo.bar]' or 'foo.bar' 8155 const isBracketed = parseOptionalJsdoc(SyntaxKind.OpenBracketToken); 8156 if (isBracketed) { 8157 skipWhitespace(); 8158 } 8159 // a markdown-quoted name: `arg` is not legal jsdoc, but occurs in the wild 8160 const isBackquoted = parseOptionalJsdoc(SyntaxKind.BacktickToken); 8161 const name = parseJSDocEntityName(); 8162 if (isBackquoted) { 8163 parseExpectedTokenJSDoc(SyntaxKind.BacktickToken); 8164 } 8165 if (isBracketed) { 8166 skipWhitespace(); 8167 // May have an optional default, e.g. '[foo = 42]' 8168 if (parseOptionalToken(SyntaxKind.EqualsToken)) { 8169 parseExpression(); 8170 } 8171 8172 parseExpected(SyntaxKind.CloseBracketToken); 8173 } 8174 8175 return { name, isBracketed }; 8176 } 8177 8178 function isObjectOrObjectArrayTypeReference(node: TypeNode): boolean { 8179 switch (node.kind) { 8180 case SyntaxKind.ObjectKeyword: 8181 return true; 8182 case SyntaxKind.ArrayType: 8183 return isObjectOrObjectArrayTypeReference((node as ArrayTypeNode).elementType); 8184 default: 8185 return isTypeReferenceNode(node) && ts.isIdentifier(node.typeName) && node.typeName.escapedText === "Object" && !node.typeArguments; 8186 } 8187 } 8188 8189 function parseParameterOrPropertyTag(start: number, tagName: Identifier, target: PropertyLikeParse, indent: number): JSDocParameterTag | JSDocPropertyTag { 8190 let typeExpression = tryParseTypeExpression(); 8191 let isNameFirst = !typeExpression; 8192 skipWhitespaceOrAsterisk(); 8193 8194 const { name, isBracketed } = parseBracketNameInPropertyAndParamTag(); 8195 const indentText = skipWhitespaceOrAsterisk(); 8196 8197 if (isNameFirst) { 8198 typeExpression = tryParseTypeExpression(); 8199 } 8200 8201 const comment = parseTrailingTagComments(start, getNodePos(), indent, indentText); 8202 8203 const nestedTypeLiteral = target !== PropertyLikeParse.CallbackParameter && parseNestedTypeLiteral(typeExpression, name, target, indent); 8204 if (nestedTypeLiteral) { 8205 typeExpression = nestedTypeLiteral; 8206 isNameFirst = true; 8207 } 8208 const result = target === PropertyLikeParse.Property 8209 ? factory.createJSDocPropertyTag(tagName, name, isBracketed, typeExpression, isNameFirst, comment) 8210 : factory.createJSDocParameterTag(tagName, name, isBracketed, typeExpression, isNameFirst, comment); 8211 return finishNode(result, start); 8212 } 8213 8214 function parseNestedTypeLiteral(typeExpression: JSDocTypeExpression | undefined, name: EntityName, target: PropertyLikeParse, indent: number) { 8215 if (typeExpression && isObjectOrObjectArrayTypeReference(typeExpression.type)) { 8216 const pos = getNodePos(); 8217 let child: JSDocPropertyLikeTag | JSDocTypeTag | false; 8218 let children: JSDocPropertyLikeTag[] | undefined; 8219 while (child = tryParse(() => parseChildParameterOrPropertyTag(target, indent, name))) { 8220 if (child.kind === SyntaxKind.JSDocParameterTag || child.kind === SyntaxKind.JSDocPropertyTag) { 8221 children = append(children, child); 8222 } 8223 } 8224 if (children) { 8225 const literal = finishNode(factory.createJSDocTypeLiteral(children, typeExpression.type.kind === SyntaxKind.ArrayType), pos); 8226 return finishNode(factory.createJSDocTypeExpression(literal), pos); 8227 } 8228 } 8229 } 8230 8231 function parseReturnTag(start: number, tagName: Identifier, indent: number, indentText: string): JSDocReturnTag { 8232 if (some(tags, isJSDocReturnTag)) { 8233 parseErrorAt(tagName.pos, scanner.getTokenPos(), Diagnostics._0_tag_already_specified, tagName.escapedText); 8234 } 8235 8236 const typeExpression = tryParseTypeExpression(); 8237 const end = getNodePos(); 8238 return finishNode(factory.createJSDocReturnTag(tagName, typeExpression, parseTrailingTagComments(start, end, indent, indentText)), start, end); 8239 } 8240 8241 function parseTypeTag(start: number, tagName: Identifier, indent?: number, indentText?: string): JSDocTypeTag { 8242 if (some(tags, isJSDocTypeTag)) { 8243 parseErrorAt(tagName.pos, scanner.getTokenPos(), Diagnostics._0_tag_already_specified, tagName.escapedText); 8244 } 8245 8246 const typeExpression = parseJSDocTypeExpression(/*mayOmitBraces*/ true); 8247 const end = getNodePos(); 8248 const comments = indent !== undefined && indentText !== undefined ? parseTrailingTagComments(start, end, indent, indentText) : undefined; 8249 return finishNode(factory.createJSDocTypeTag(tagName, typeExpression, comments), start, end); 8250 } 8251 8252 function parseSeeTag(start: number, tagName: Identifier, indent?: number, indentText?: string): JSDocSeeTag { 8253 const nameExpression = parseJSDocNameReference(); 8254 const end = getNodePos(); 8255 const comments = indent !== undefined && indentText !== undefined ? parseTrailingTagComments(start, end, indent, indentText) : undefined; 8256 return finishNode(factory.createJSDocSeeTag(tagName, nameExpression, comments), start, end); 8257 } 8258 8259 function parseAuthorTag(start: number, tagName: Identifier, indent: number, indentText: string): JSDocAuthorTag { 8260 const comments = parseAuthorNameAndEmail() + (parseTrailingTagComments(start, end, indent, indentText) || ""); 8261 return finishNode(factory.createJSDocAuthorTag(tagName, comments || undefined), start); 8262 } 8263 8264 function parseAuthorNameAndEmail(): string { 8265 const comments: string[] = []; 8266 let inEmail = false; 8267 let token = scanner.getToken(); 8268 while (token !== SyntaxKind.EndOfFileToken && token !== SyntaxKind.NewLineTrivia) { 8269 if (token === SyntaxKind.LessThanToken) { 8270 inEmail = true; 8271 } 8272 else if (token === SyntaxKind.AtToken && !inEmail) { 8273 break; 8274 } 8275 else if (token === SyntaxKind.GreaterThanToken && inEmail) { 8276 comments.push(scanner.getTokenText()); 8277 scanner.setTextPos(scanner.getTokenPos() + 1); 8278 break; 8279 } 8280 comments.push(scanner.getTokenText()); 8281 token = nextTokenJSDoc(); 8282 } 8283 8284 return comments.join(""); 8285 } 8286 8287 function parseImplementsTag(start: number, tagName: Identifier, margin: number, indentText: string): JSDocImplementsTag { 8288 const className = parseExpressionWithTypeArgumentsForAugments(); 8289 const end = getNodePos(); 8290 return finishNode(factory.createJSDocImplementsTag(tagName, className, parseTrailingTagComments(start, end, margin, indentText)), start, end); 8291 } 8292 8293 function parseAugmentsTag(start: number, tagName: Identifier, margin: number, indentText: string): JSDocAugmentsTag { 8294 const className = parseExpressionWithTypeArgumentsForAugments(); 8295 const end = getNodePos(); 8296 return finishNode(factory.createJSDocAugmentsTag(tagName, className, parseTrailingTagComments(start, end, margin, indentText)), start, end); 8297 } 8298 8299 function parseExpressionWithTypeArgumentsForAugments(): ExpressionWithTypeArguments & { expression: Identifier | PropertyAccessEntityNameExpression } { 8300 const usedBrace = parseOptional(SyntaxKind.OpenBraceToken); 8301 const pos = getNodePos(); 8302 const expression = parsePropertyAccessEntityNameExpression(); 8303 const typeArguments = tryParseTypeArguments(); 8304 const node = factory.createExpressionWithTypeArguments(expression, typeArguments) as ExpressionWithTypeArguments & { expression: Identifier | PropertyAccessEntityNameExpression }; 8305 const res = finishNode(node, pos); 8306 if (usedBrace) { 8307 parseExpected(SyntaxKind.CloseBraceToken); 8308 } 8309 return res; 8310 } 8311 8312 function parsePropertyAccessEntityNameExpression() { 8313 const pos = getNodePos(); 8314 let node: Identifier | PropertyAccessEntityNameExpression = parseJSDocIdentifierName(); 8315 while (parseOptional(SyntaxKind.DotToken)) { 8316 const name = parseJSDocIdentifierName(); 8317 node = finishNode(factory.createPropertyAccessExpression(node, name), pos) as PropertyAccessEntityNameExpression; 8318 } 8319 return node; 8320 } 8321 8322 function parseSimpleTag(start: number, createTag: (tagName: Identifier | undefined, comment?: string) => JSDocTag, tagName: Identifier, margin: number, indentText: string): JSDocTag { 8323 const end = getNodePos(); 8324 return finishNode(createTag(tagName, parseTrailingTagComments(start, end, margin, indentText)), start, end); 8325 } 8326 8327 function parseThisTag(start: number, tagName: Identifier, margin: number, indentText: string): JSDocThisTag { 8328 const typeExpression = parseJSDocTypeExpression(/*mayOmitBraces*/ true); 8329 skipWhitespace(); 8330 const end = getNodePos(); 8331 return finishNode(factory.createJSDocThisTag(tagName, typeExpression, parseTrailingTagComments(start, end, margin, indentText)), start, end); 8332 } 8333 8334 function parseEnumTag(start: number, tagName: Identifier, margin: number, indentText: string): JSDocEnumTag { 8335 const typeExpression = parseJSDocTypeExpression(/*mayOmitBraces*/ true); 8336 skipWhitespace(); 8337 const end = getNodePos(); 8338 return finishNode(factory.createJSDocEnumTag(tagName, typeExpression, parseTrailingTagComments(start, end, margin, indentText)), start, end); 8339 } 8340 8341 function parseTypedefTag(start: number, tagName: Identifier, indent: number, indentText: string): JSDocTypedefTag { 8342 let typeExpression: JSDocTypeExpression | JSDocTypeLiteral | undefined = tryParseTypeExpression(); 8343 skipWhitespaceOrAsterisk(); 8344 8345 const fullName = parseJSDocTypeNameWithNamespace(); 8346 skipWhitespace(); 8347 let comment = parseTagComments(indent); 8348 8349 let end: number | undefined; 8350 if (!typeExpression || isObjectOrObjectArrayTypeReference(typeExpression.type)) { 8351 let child: JSDocTypeTag | JSDocPropertyTag | false; 8352 let childTypeTag: JSDocTypeTag | undefined; 8353 let jsDocPropertyTags: JSDocPropertyTag[] | undefined; 8354 let hasChildren = false; 8355 while (child = tryParse(() => parseChildPropertyTag(indent))) { 8356 hasChildren = true; 8357 if (child.kind === SyntaxKind.JSDocTypeTag) { 8358 if (childTypeTag) { 8359 parseErrorAtCurrentToken(Diagnostics.A_JSDoc_typedef_comment_may_not_contain_multiple_type_tags); 8360 const lastError = lastOrUndefined(parseDiagnostics); 8361 if (lastError) { 8362 addRelatedInfo( 8363 lastError, 8364 createDetachedDiagnostic(fileName, 0, 0, Diagnostics.The_tag_was_first_specified_here) 8365 ); 8366 } 8367 break; 8368 } 8369 else { 8370 childTypeTag = child; 8371 } 8372 } 8373 else { 8374 jsDocPropertyTags = append(jsDocPropertyTags, child); 8375 } 8376 } 8377 if (hasChildren) { 8378 const isArrayType = typeExpression && typeExpression.type.kind === SyntaxKind.ArrayType; 8379 const jsdocTypeLiteral = factory.createJSDocTypeLiteral(jsDocPropertyTags, isArrayType); 8380 typeExpression = childTypeTag && childTypeTag.typeExpression && !isObjectOrObjectArrayTypeReference(childTypeTag.typeExpression.type) ? 8381 childTypeTag.typeExpression : 8382 finishNode(jsdocTypeLiteral, start); 8383 end = typeExpression.end; 8384 } 8385 } 8386 8387 // Only include the characters between the name end and the next token if a comment was actually parsed out - otherwise it's just whitespace 8388 end = end || comment !== undefined ? 8389 getNodePos() : 8390 (fullName ?? typeExpression ?? tagName).end; 8391 8392 if (!comment) { 8393 comment = parseTrailingTagComments(start, end, indent, indentText); 8394 } 8395 8396 const typedefTag = factory.createJSDocTypedefTag(tagName, typeExpression, fullName, comment); 8397 return finishNode(typedefTag, start, end); 8398 } 8399 8400 function parseJSDocTypeNameWithNamespace(nested?: boolean) { 8401 const pos = scanner.getTokenPos(); 8402 if (!tokenIsIdentifierOrKeyword(token())) { 8403 return undefined; 8404 } 8405 const typeNameOrNamespaceName = parseJSDocIdentifierName(); 8406 if (parseOptional(SyntaxKind.DotToken)) { 8407 const body = parseJSDocTypeNameWithNamespace(/*nested*/ true); 8408 const jsDocNamespaceNode = factory.createModuleDeclaration( 8409 /*decorators*/ undefined, 8410 /*modifiers*/ undefined, 8411 typeNameOrNamespaceName, 8412 body, 8413 nested ? NodeFlags.NestedNamespace : undefined 8414 ) as JSDocNamespaceDeclaration; 8415 return finishNode(jsDocNamespaceNode, pos); 8416 } 8417 8418 if (nested) { 8419 typeNameOrNamespaceName.isInJSDocNamespace = true; 8420 } 8421 return typeNameOrNamespaceName; 8422 } 8423 8424 8425 function parseCallbackTagParameters(indent: number) { 8426 const pos = getNodePos(); 8427 let child: JSDocParameterTag | false; 8428 let parameters; 8429 while (child = tryParse(() => parseChildParameterOrPropertyTag(PropertyLikeParse.CallbackParameter, indent) as JSDocParameterTag)) { 8430 parameters = append(parameters, child); 8431 } 8432 return createNodeArray(parameters || [], pos); 8433 } 8434 8435 function parseCallbackTag(start: number, tagName: Identifier, indent: number, indentText: string): JSDocCallbackTag { 8436 const fullName = parseJSDocTypeNameWithNamespace(); 8437 skipWhitespace(); 8438 let comment = parseTagComments(indent); 8439 const parameters = parseCallbackTagParameters(indent); 8440 const returnTag = tryParse(() => { 8441 if (parseOptionalJsdoc(SyntaxKind.AtToken)) { 8442 const tag = parseTag(indent); 8443 if (tag && tag.kind === SyntaxKind.JSDocReturnTag) { 8444 return tag as JSDocReturnTag; 8445 } 8446 } 8447 }); 8448 const typeExpression = finishNode(factory.createJSDocSignature(/*typeParameters*/ undefined, parameters, returnTag), start); 8449 const end = getNodePos(); 8450 if (!comment) { 8451 comment = parseTrailingTagComments(start, end, indent, indentText); 8452 } 8453 return finishNode(factory.createJSDocCallbackTag(tagName, typeExpression, fullName, comment), start, end); 8454 } 8455 8456 function escapedTextsEqual(a: EntityName, b: EntityName): boolean { 8457 while (!ts.isIdentifier(a) || !ts.isIdentifier(b)) { 8458 if (!ts.isIdentifier(a) && !ts.isIdentifier(b) && a.right.escapedText === b.right.escapedText) { 8459 a = a.left; 8460 b = b.left; 8461 } 8462 else { 8463 return false; 8464 } 8465 } 8466 return a.escapedText === b.escapedText; 8467 } 8468 8469 function parseChildPropertyTag(indent: number) { 8470 return parseChildParameterOrPropertyTag(PropertyLikeParse.Property, indent) as JSDocTypeTag | JSDocPropertyTag | false; 8471 } 8472 8473 function parseChildParameterOrPropertyTag(target: PropertyLikeParse, indent: number, name?: EntityName): JSDocTypeTag | JSDocPropertyTag | JSDocParameterTag | false { 8474 let canParseTag = true; 8475 let seenAsterisk = false; 8476 while (true) { 8477 switch (nextTokenJSDoc()) { 8478 case SyntaxKind.AtToken: 8479 if (canParseTag) { 8480 const child = tryParseChildTag(target, indent); 8481 if (child && (child.kind === SyntaxKind.JSDocParameterTag || child.kind === SyntaxKind.JSDocPropertyTag) && 8482 target !== PropertyLikeParse.CallbackParameter && 8483 name && (ts.isIdentifier(child.name) || !escapedTextsEqual(name, child.name.left))) { 8484 return false; 8485 } 8486 return child; 8487 } 8488 seenAsterisk = false; 8489 break; 8490 case SyntaxKind.NewLineTrivia: 8491 canParseTag = true; 8492 seenAsterisk = false; 8493 break; 8494 case SyntaxKind.AsteriskToken: 8495 if (seenAsterisk) { 8496 canParseTag = false; 8497 } 8498 seenAsterisk = true; 8499 break; 8500 case SyntaxKind.Identifier: 8501 canParseTag = false; 8502 break; 8503 case SyntaxKind.EndOfFileToken: 8504 return false; 8505 } 8506 } 8507 } 8508 8509 function tryParseChildTag(target: PropertyLikeParse, indent: number): JSDocTypeTag | JSDocPropertyTag | JSDocParameterTag | false { 8510 Debug.assert(token() === SyntaxKind.AtToken); 8511 const start = scanner.getStartPos(); 8512 nextTokenJSDoc(); 8513 8514 const tagName = parseJSDocIdentifierName(); 8515 skipWhitespace(); 8516 let t: PropertyLikeParse; 8517 switch (tagName.escapedText) { 8518 case "type": 8519 return target === PropertyLikeParse.Property && parseTypeTag(start, tagName); 8520 case "prop": 8521 case "property": 8522 t = PropertyLikeParse.Property; 8523 break; 8524 case "arg": 8525 case "argument": 8526 case "param": 8527 t = PropertyLikeParse.Parameter | PropertyLikeParse.CallbackParameter; 8528 break; 8529 default: 8530 return false; 8531 } 8532 if (!(target & t)) { 8533 return false; 8534 } 8535 return parseParameterOrPropertyTag(start, tagName, target, indent); 8536 } 8537 8538 function parseTemplateTagTypeParameter() { 8539 const typeParameterPos = getNodePos(); 8540 const name = parseJSDocIdentifierName(Diagnostics.Unexpected_token_A_type_parameter_name_was_expected_without_curly_braces); 8541 return finishNode(factory.createTypeParameterDeclaration(name, /*constraint*/ undefined, /*defaultType*/ undefined), typeParameterPos); 8542 } 8543 8544 function parseTemplateTagTypeParameters() { 8545 const pos = getNodePos(); 8546 const typeParameters = []; 8547 do { 8548 skipWhitespace(); 8549 typeParameters.push(parseTemplateTagTypeParameter()); 8550 skipWhitespaceOrAsterisk(); 8551 } while (parseOptionalJsdoc(SyntaxKind.CommaToken)); 8552 return createNodeArray(typeParameters, pos); 8553 } 8554 8555 function parseTemplateTag(start: number, tagName: Identifier, indent: number, indentText: string): JSDocTemplateTag { 8556 // The template tag looks like one of the following: 8557 // @template T,U,V 8558 // @template {Constraint} T 8559 // 8560 // According to the [closure docs](https://github.com/google/closure-compiler/wiki/Generic-Types#multiple-bounded-template-types): 8561 // > Multiple bounded generics cannot be declared on the same line. For the sake of clarity, if multiple templates share the same 8562 // > type bound they must be declared on separate lines. 8563 // 8564 // TODO: Determine whether we should enforce this in the checker. 8565 // TODO: Consider moving the `constraint` to the first type parameter as we could then remove `getEffectiveConstraintOfTypeParameter`. 8566 // TODO: Consider only parsing a single type parameter if there is a constraint. 8567 const constraint = token() === SyntaxKind.OpenBraceToken ? parseJSDocTypeExpression() : undefined; 8568 const typeParameters = parseTemplateTagTypeParameters(); 8569 const end = getNodePos(); 8570 return finishNode(factory.createJSDocTemplateTag(tagName, constraint, typeParameters, parseTrailingTagComments(start, end, indent, indentText)), start, end); 8571 } 8572 8573 function parseOptionalJsdoc(t: JSDocSyntaxKind): boolean { 8574 if (token() === t) { 8575 nextTokenJSDoc(); 8576 return true; 8577 } 8578 return false; 8579 } 8580 8581 function parseJSDocEntityName(): EntityName { 8582 let entity: EntityName = parseJSDocIdentifierName(); 8583 if (parseOptional(SyntaxKind.OpenBracketToken)) { 8584 parseExpected(SyntaxKind.CloseBracketToken); 8585 // Note that y[] is accepted as an entity name, but the postfix brackets are not saved for checking. 8586 // Technically usejsdoc.org requires them for specifying a property of a type equivalent to Array<{ x: ...}> 8587 // but it's not worth it to enforce that restriction. 8588 } 8589 while (parseOptional(SyntaxKind.DotToken)) { 8590 const name = parseJSDocIdentifierName(); 8591 if (parseOptional(SyntaxKind.OpenBracketToken)) { 8592 parseExpected(SyntaxKind.CloseBracketToken); 8593 } 8594 entity = createQualifiedName(entity, name); 8595 } 8596 return entity; 8597 } 8598 8599 function parseJSDocIdentifierName(message?: DiagnosticMessage): Identifier { 8600 if (!tokenIsIdentifierOrKeyword(token())) { 8601 return createMissingNode<Identifier>(SyntaxKind.Identifier, /*reportAtCurrentPosition*/ !message, message || Diagnostics.Identifier_expected); 8602 } 8603 8604 identifierCount++; 8605 const pos = scanner.getTokenPos(); 8606 const end = scanner.getTextPos(); 8607 const originalKeywordKind = token(); 8608 const text = internIdentifier(scanner.getTokenValue()); 8609 const result = finishNode(factory.createIdentifier(text, /*typeArguments*/ undefined, originalKeywordKind), pos, end); 8610 nextTokenJSDoc(); 8611 return result; 8612 } 8613 } 8614 } 8615 } 8616 8617 namespace IncrementalParser { 8618 export function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks: boolean): SourceFile { 8619 aggressiveChecks = aggressiveChecks || Debug.shouldAssert(AssertionLevel.Aggressive); 8620 8621 checkChangeRange(sourceFile, newText, textChangeRange, aggressiveChecks); 8622 if (textChangeRangeIsUnchanged(textChangeRange)) { 8623 // if the text didn't change, then we can just return our current source file as-is. 8624 return sourceFile; 8625 } 8626 8627 if (sourceFile.statements.length === 0) { 8628 // If we don't have any statements in the current source file, then there's no real 8629 // way to incrementally parse. So just do a full parse instead. 8630 return Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, /*syntaxCursor*/ undefined, /*setParentNodes*/ true, sourceFile.scriptKind); 8631 } 8632 8633 // Make sure we're not trying to incrementally update a source file more than once. Once 8634 // we do an update the original source file is considered unusable from that point onwards. 8635 // 8636 // This is because we do incremental parsing in-place. i.e. we take nodes from the old 8637 // tree and give them new positions and parents. From that point on, trusting the old 8638 // tree at all is not possible as far too much of it may violate invariants. 8639 const incrementalSourceFile = <IncrementalNode><Node>sourceFile; 8640 Debug.assert(!incrementalSourceFile.hasBeenIncrementallyParsed); 8641 incrementalSourceFile.hasBeenIncrementallyParsed = true; 8642 Parser.fixupParentReferences(incrementalSourceFile); 8643 const oldText = sourceFile.text; 8644 const syntaxCursor = createSyntaxCursor(sourceFile); 8645 8646 // Make the actual change larger so that we know to reparse anything whose lookahead 8647 // might have intersected the change. 8648 const changeRange = extendToAffectedRange(sourceFile, textChangeRange); 8649 checkChangeRange(sourceFile, newText, changeRange, aggressiveChecks); 8650 8651 // Ensure that extending the affected range only moved the start of the change range 8652 // earlier in the file. 8653 Debug.assert(changeRange.span.start <= textChangeRange.span.start); 8654 Debug.assert(textSpanEnd(changeRange.span) === textSpanEnd(textChangeRange.span)); 8655 Debug.assert(textSpanEnd(textChangeRangeNewSpan(changeRange)) === textSpanEnd(textChangeRangeNewSpan(textChangeRange))); 8656 8657 // The is the amount the nodes after the edit range need to be adjusted. It can be 8658 // positive (if the edit added characters), negative (if the edit deleted characters) 8659 // or zero (if this was a pure overwrite with nothing added/removed). 8660 const delta = textChangeRangeNewSpan(changeRange).length - changeRange.span.length; 8661 8662 // If we added or removed characters during the edit, then we need to go and adjust all 8663 // the nodes after the edit. Those nodes may move forward (if we inserted chars) or they 8664 // may move backward (if we deleted chars). 8665 // 8666 // Doing this helps us out in two ways. First, it means that any nodes/tokens we want 8667 // to reuse are already at the appropriate position in the new text. That way when we 8668 // reuse them, we don't have to figure out if they need to be adjusted. Second, it makes 8669 // it very easy to determine if we can reuse a node. If the node's position is at where 8670 // we are in the text, then we can reuse it. Otherwise we can't. If the node's position 8671 // is ahead of us, then we'll need to rescan tokens. If the node's position is behind 8672 // us, then we'll need to skip it or crumble it as appropriate 8673 // 8674 // We will also adjust the positions of nodes that intersect the change range as well. 8675 // By doing this, we ensure that all the positions in the old tree are consistent, not 8676 // just the positions of nodes entirely before/after the change range. By being 8677 // consistent, we can then easily map from positions to nodes in the old tree easily. 8678 // 8679 // Also, mark any syntax elements that intersect the changed span. We know, up front, 8680 // that we cannot reuse these elements. 8681 updateTokenPositionsAndMarkElements(incrementalSourceFile, 8682 changeRange.span.start, textSpanEnd(changeRange.span), textSpanEnd(textChangeRangeNewSpan(changeRange)), delta, oldText, newText, aggressiveChecks); 8683 8684 // Now that we've set up our internal incremental state just proceed and parse the 8685 // source file in the normal fashion. When possible the parser will retrieve and 8686 // reuse nodes from the old tree. 8687 // 8688 // Note: passing in 'true' for setNodeParents is very important. When incrementally 8689 // parsing, we will be reusing nodes from the old tree, and placing it into new 8690 // parents. If we don't set the parents now, we'll end up with an observably 8691 // inconsistent tree. Setting the parents on the new tree should be very fast. We 8692 // will immediately bail out of walking any subtrees when we can see that their parents 8693 // are already correct. 8694 const result = Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, syntaxCursor, /*setParentNodes*/ true, sourceFile.scriptKind); 8695 result.commentDirectives = getNewCommentDirectives( 8696 sourceFile.commentDirectives, 8697 result.commentDirectives, 8698 changeRange.span.start, 8699 textSpanEnd(changeRange.span), 8700 delta, 8701 oldText, 8702 newText, 8703 aggressiveChecks 8704 ); 8705 return result; 8706 } 8707 8708 function getNewCommentDirectives( 8709 oldDirectives: CommentDirective[] | undefined, 8710 newDirectives: CommentDirective[] | undefined, 8711 changeStart: number, 8712 changeRangeOldEnd: number, 8713 delta: number, 8714 oldText: string, 8715 newText: string, 8716 aggressiveChecks: boolean 8717 ): CommentDirective[] | undefined { 8718 if (!oldDirectives) return newDirectives; 8719 let commentDirectives: CommentDirective[] | undefined; 8720 let addedNewlyScannedDirectives = false; 8721 for (const directive of oldDirectives) { 8722 const { range, type } = directive; 8723 // Range before the change 8724 if (range.end < changeStart) { 8725 commentDirectives = append(commentDirectives, directive); 8726 } 8727 else if (range.pos > changeRangeOldEnd) { 8728 addNewlyScannedDirectives(); 8729 // Node is entirely past the change range. We need to move both its pos and 8730 // end, forward or backward appropriately. 8731 const updatedDirective: CommentDirective = { 8732 range: { pos: range.pos + delta, end: range.end + delta }, 8733 type 8734 }; 8735 commentDirectives = append(commentDirectives, updatedDirective); 8736 if (aggressiveChecks) { 8737 Debug.assert(oldText.substring(range.pos, range.end) === newText.substring(updatedDirective.range.pos, updatedDirective.range.end)); 8738 } 8739 } 8740 // Ignore ranges that fall in change range 8741 } 8742 addNewlyScannedDirectives(); 8743 return commentDirectives; 8744 8745 function addNewlyScannedDirectives() { 8746 if (addedNewlyScannedDirectives) return; 8747 addedNewlyScannedDirectives = true; 8748 if (!commentDirectives) { 8749 commentDirectives = newDirectives; 8750 } 8751 else if (newDirectives) { 8752 commentDirectives.push(...newDirectives); 8753 } 8754 } 8755 } 8756 8757 function moveElementEntirelyPastChangeRange(element: IncrementalElement, isArray: boolean, delta: number, oldText: string, newText: string, aggressiveChecks: boolean) { 8758 if (isArray) { 8759 visitArray(<IncrementalNodeArray>element); 8760 } 8761 else { 8762 visitNode(<IncrementalNode>element); 8763 } 8764 return; 8765 8766 function visitNode(node: IncrementalNode) { 8767 let text = ""; 8768 if (aggressiveChecks && shouldCheckNode(node)) { 8769 text = oldText.substring(node.pos, node.end); 8770 } 8771 8772 // Ditch any existing LS children we may have created. This way we can avoid 8773 // moving them forward. 8774 if (node._children) { 8775 node._children = undefined; 8776 } 8777 8778 setTextRangePosEnd(node, node.pos + delta, node.end + delta); 8779 8780 if (aggressiveChecks && shouldCheckNode(node)) { 8781 Debug.assert(text === newText.substring(node.pos, node.end)); 8782 } 8783 8784 forEachChild(node, visitNode, visitArray); 8785 if (hasJSDocNodes(node)) { 8786 for (const jsDocComment of node.jsDoc!) { 8787 visitNode(<IncrementalNode><Node>jsDocComment); 8788 } 8789 } 8790 checkNodePositions(node, aggressiveChecks); 8791 } 8792 8793 function visitArray(array: IncrementalNodeArray) { 8794 array._children = undefined; 8795 setTextRangePosEnd(array, array.pos + delta, array.end + delta); 8796 8797 for (const node of array) { 8798 visitNode(node); 8799 } 8800 } 8801 } 8802 8803 function shouldCheckNode(node: Node) { 8804 switch (node.kind) { 8805 case SyntaxKind.StringLiteral: 8806 case SyntaxKind.NumericLiteral: 8807 case SyntaxKind.Identifier: 8808 return true; 8809 } 8810 8811 return false; 8812 } 8813 8814 function adjustIntersectingElement(element: IncrementalElement, changeStart: number, changeRangeOldEnd: number, changeRangeNewEnd: number, delta: number) { 8815 Debug.assert(element.end >= changeStart, "Adjusting an element that was entirely before the change range"); 8816 Debug.assert(element.pos <= changeRangeOldEnd, "Adjusting an element that was entirely after the change range"); 8817 Debug.assert(element.pos <= element.end); 8818 8819 // We have an element that intersects the change range in some way. It may have its 8820 // start, or its end (or both) in the changed range. We want to adjust any part 8821 // that intersects such that the final tree is in a consistent state. i.e. all 8822 // children have spans within the span of their parent, and all siblings are ordered 8823 // properly. 8824 8825 // We may need to update both the 'pos' and the 'end' of the element. 8826 8827 // If the 'pos' is before the start of the change, then we don't need to touch it. 8828 // If it isn't, then the 'pos' must be inside the change. How we update it will 8829 // depend if delta is positive or negative. If delta is positive then we have 8830 // something like: 8831 // 8832 // -------------------AAA----------------- 8833 // -------------------BBBCCCCCCC----------------- 8834 // 8835 // In this case, we consider any node that started in the change range to still be 8836 // starting at the same position. 8837 // 8838 // however, if the delta is negative, then we instead have something like this: 8839 // 8840 // -------------------XXXYYYYYYY----------------- 8841 // -------------------ZZZ----------------- 8842 // 8843 // In this case, any element that started in the 'X' range will keep its position. 8844 // However any element that started after that will have their pos adjusted to be 8845 // at the end of the new range. i.e. any node that started in the 'Y' range will 8846 // be adjusted to have their start at the end of the 'Z' range. 8847 // 8848 // The element will keep its position if possible. Or Move backward to the new-end 8849 // if it's in the 'Y' range. 8850 const pos = Math.min(element.pos, changeRangeNewEnd); 8851 8852 // If the 'end' is after the change range, then we always adjust it by the delta 8853 // amount. However, if the end is in the change range, then how we adjust it 8854 // will depend on if delta is positive or negative. If delta is positive then we 8855 // have something like: 8856 // 8857 // -------------------AAA----------------- 8858 // -------------------BBBCCCCCCC----------------- 8859 // 8860 // In this case, we consider any node that ended inside the change range to keep its 8861 // end position. 8862 // 8863 // however, if the delta is negative, then we instead have something like this: 8864 // 8865 // -------------------XXXYYYYYYY----------------- 8866 // -------------------ZZZ----------------- 8867 // 8868 // In this case, any element that ended in the 'X' range will keep its position. 8869 // However any element that ended after that will have their pos adjusted to be 8870 // at the end of the new range. i.e. any node that ended in the 'Y' range will 8871 // be adjusted to have their end at the end of the 'Z' range. 8872 const end = element.end >= changeRangeOldEnd ? 8873 // Element ends after the change range. Always adjust the end pos. 8874 element.end + delta : 8875 // Element ends in the change range. The element will keep its position if 8876 // possible. Or Move backward to the new-end if it's in the 'Y' range. 8877 Math.min(element.end, changeRangeNewEnd); 8878 8879 Debug.assert(pos <= end); 8880 if (element.parent) { 8881 Debug.assertGreaterThanOrEqual(pos, element.parent.pos); 8882 Debug.assertLessThanOrEqual(end, element.parent.end); 8883 } 8884 8885 setTextRangePosEnd(element, pos, end); 8886 } 8887 8888 function checkNodePositions(node: Node, aggressiveChecks: boolean) { 8889 if (aggressiveChecks) { 8890 let pos = node.pos; 8891 const visitNode = (child: Node) => { 8892 Debug.assert(child.pos >= pos); 8893 pos = child.end; 8894 }; 8895 if (hasJSDocNodes(node)) { 8896 for (const jsDocComment of node.jsDoc!) { 8897 visitNode(jsDocComment); 8898 } 8899 } 8900 forEachChild(node, visitNode); 8901 Debug.assert(pos <= node.end); 8902 } 8903 } 8904 8905 function updateTokenPositionsAndMarkElements( 8906 sourceFile: IncrementalNode, 8907 changeStart: number, 8908 changeRangeOldEnd: number, 8909 changeRangeNewEnd: number, 8910 delta: number, 8911 oldText: string, 8912 newText: string, 8913 aggressiveChecks: boolean): void { 8914 8915 visitNode(sourceFile); 8916 return; 8917 8918 function visitNode(child: IncrementalNode) { 8919 Debug.assert(child.pos <= child.end); 8920 if (child.pos > changeRangeOldEnd) { 8921 // Node is entirely past the change range. We need to move both its pos and 8922 // end, forward or backward appropriately. 8923 moveElementEntirelyPastChangeRange(child, /*isArray*/ false, delta, oldText, newText, aggressiveChecks); 8924 return; 8925 } 8926 8927 // Check if the element intersects the change range. If it does, then it is not 8928 // reusable. Also, we'll need to recurse to see what constituent portions we may 8929 // be able to use. 8930 const fullEnd = child.end; 8931 if (fullEnd >= changeStart) { 8932 child.intersectsChange = true; 8933 child._children = undefined; 8934 8935 // Adjust the pos or end (or both) of the intersecting element accordingly. 8936 adjustIntersectingElement(child, changeStart, changeRangeOldEnd, changeRangeNewEnd, delta); 8937 forEachChild(child, visitNode, visitArray); 8938 if (hasJSDocNodes(child)) { 8939 for (const jsDocComment of child.jsDoc!) { 8940 visitNode(<IncrementalNode><Node>jsDocComment); 8941 } 8942 } 8943 checkNodePositions(child, aggressiveChecks); 8944 return; 8945 } 8946 8947 // Otherwise, the node is entirely before the change range. No need to do anything with it. 8948 Debug.assert(fullEnd < changeStart); 8949 } 8950 8951 function visitArray(array: IncrementalNodeArray) { 8952 Debug.assert(array.pos <= array.end); 8953 if (array.pos > changeRangeOldEnd) { 8954 // Array is entirely after the change range. We need to move it, and move any of 8955 // its children. 8956 moveElementEntirelyPastChangeRange(array, /*isArray*/ true, delta, oldText, newText, aggressiveChecks); 8957 return; 8958 } 8959 8960 // Check if the element intersects the change range. If it does, then it is not 8961 // reusable. Also, we'll need to recurse to see what constituent portions we may 8962 // be able to use. 8963 const fullEnd = array.end; 8964 if (fullEnd >= changeStart) { 8965 array.intersectsChange = true; 8966 array._children = undefined; 8967 8968 // Adjust the pos or end (or both) of the intersecting array accordingly. 8969 adjustIntersectingElement(array, changeStart, changeRangeOldEnd, changeRangeNewEnd, delta); 8970 for (const node of array) { 8971 if (!node.virtual) { 8972 visitNode(node); 8973 } 8974 } 8975 return; 8976 } 8977 8978 // Otherwise, the array is entirely before the change range. No need to do anything with it. 8979 Debug.assert(fullEnd < changeStart); 8980 } 8981 } 8982 8983 function extendToAffectedRange(sourceFile: SourceFile, changeRange: TextChangeRange): TextChangeRange { 8984 // Consider the following code: 8985 // void foo() { /; } 8986 // 8987 // If the text changes with an insertion of / just before the semicolon then we end up with: 8988 // void foo() { //; } 8989 // 8990 // If we were to just use the changeRange a is, then we would not rescan the { token 8991 // (as it does not intersect the actual original change range). Because an edit may 8992 // change the token touching it, we actually need to look back *at least* one token so 8993 // that the prior token sees that change. 8994 const maxLookahead = 1; 8995 8996 let start = changeRange.span.start; 8997 8998 // the first iteration aligns us with the change start. subsequent iteration move us to 8999 // the left by maxLookahead tokens. We only need to do this as long as we're not at the 9000 // start of the tree. 9001 for (let i = 0; start > 0 && i <= maxLookahead; i++) { 9002 const nearestNode = findNearestNodeStartingBeforeOrAtPosition(sourceFile, start); 9003 Debug.assert(nearestNode.pos <= start); 9004 const position = nearestNode.pos; 9005 9006 start = Math.max(0, position - 1); 9007 } 9008 9009 const finalSpan = createTextSpanFromBounds(start, textSpanEnd(changeRange.span)); 9010 const finalLength = changeRange.newLength + (changeRange.span.start - start); 9011 9012 return createTextChangeRange(finalSpan, finalLength); 9013 } 9014 9015 function findNearestNodeStartingBeforeOrAtPosition(sourceFile: SourceFile, position: number): Node { 9016 let bestResult: Node = sourceFile; 9017 let lastNodeEntirelyBeforePosition: Node | undefined; 9018 9019 forEachChild(sourceFile, visit); 9020 9021 if (lastNodeEntirelyBeforePosition) { 9022 const lastChildOfLastEntireNodeBeforePosition = getLastDescendant(lastNodeEntirelyBeforePosition); 9023 if (lastChildOfLastEntireNodeBeforePosition.pos > bestResult.pos) { 9024 bestResult = lastChildOfLastEntireNodeBeforePosition; 9025 } 9026 } 9027 9028 return bestResult; 9029 9030 function getLastDescendant(node: Node): Node { 9031 while (true) { 9032 const lastChild = getLastChild(node); 9033 if (lastChild) { 9034 node = lastChild; 9035 } 9036 else { 9037 return node; 9038 } 9039 } 9040 } 9041 9042 function visit(child: Node) { 9043 if (nodeIsMissing(child)) { 9044 // Missing nodes are effectively invisible to us. We never even consider them 9045 // When trying to find the nearest node before us. 9046 return; 9047 } 9048 9049 // If the child intersects this position, then this node is currently the nearest 9050 // node that starts before the position. 9051 if (child.pos <= position) { 9052 if (child.pos >= bestResult.pos) { 9053 // This node starts before the position, and is closer to the position than 9054 // the previous best node we found. It is now the new best node. 9055 bestResult = child; 9056 } 9057 9058 // Now, the node may overlap the position, or it may end entirely before the 9059 // position. If it overlaps with the position, then either it, or one of its 9060 // children must be the nearest node before the position. So we can just 9061 // recurse into this child to see if we can find something better. 9062 if (position < child.end) { 9063 // The nearest node is either this child, or one of the children inside 9064 // of it. We've already marked this child as the best so far. Recurse 9065 // in case one of the children is better. 9066 forEachChild(child, visit); 9067 9068 // Once we look at the children of this node, then there's no need to 9069 // continue any further. 9070 return true; 9071 } 9072 else { 9073 Debug.assert(child.end <= position); 9074 // The child ends entirely before this position. Say you have the following 9075 // (where $ is the position) 9076 // 9077 // <complex expr 1> ? <complex expr 2> $ : <...> <...> 9078 // 9079 // We would want to find the nearest preceding node in "complex expr 2". 9080 // To support that, we keep track of this node, and once we're done searching 9081 // for a best node, we recurse down this node to see if we can find a good 9082 // result in it. 9083 // 9084 // This approach allows us to quickly skip over nodes that are entirely 9085 // before the position, while still allowing us to find any nodes in the 9086 // last one that might be what we want. 9087 lastNodeEntirelyBeforePosition = child; 9088 } 9089 } 9090 else { 9091 Debug.assert(child.pos > position); 9092 // We're now at a node that is entirely past the position we're searching for. 9093 // This node (and all following nodes) could never contribute to the result, 9094 // so just skip them by returning 'true' here. 9095 return true; 9096 } 9097 } 9098 } 9099 9100 function checkChangeRange(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks: boolean) { 9101 const oldText = sourceFile.text; 9102 if (textChangeRange) { 9103 Debug.assert((oldText.length - textChangeRange.span.length + textChangeRange.newLength) === newText.length); 9104 9105 if (aggressiveChecks || Debug.shouldAssert(AssertionLevel.VeryAggressive)) { 9106 const oldTextPrefix = oldText.substr(0, textChangeRange.span.start); 9107 const newTextPrefix = newText.substr(0, textChangeRange.span.start); 9108 Debug.assert(oldTextPrefix === newTextPrefix); 9109 9110 const oldTextSuffix = oldText.substring(textSpanEnd(textChangeRange.span), oldText.length); 9111 const newTextSuffix = newText.substring(textSpanEnd(textChangeRangeNewSpan(textChangeRange)), newText.length); 9112 Debug.assert(oldTextSuffix === newTextSuffix); 9113 } 9114 } 9115 } 9116 9117 interface IncrementalElement extends ReadonlyTextRange { 9118 readonly parent: Node; 9119 intersectsChange: boolean; 9120 length?: number; 9121 _children: Node[] | undefined; 9122 } 9123 9124 export interface IncrementalNode extends Node, IncrementalElement { 9125 hasBeenIncrementallyParsed: boolean; 9126 } 9127 9128 interface IncrementalNodeArray extends NodeArray<IncrementalNode>, IncrementalElement { 9129 length: number; 9130 } 9131 9132 // Allows finding nodes in the source file at a certain position in an efficient manner. 9133 // The implementation takes advantage of the calling pattern it knows the parser will 9134 // make in order to optimize finding nodes as quickly as possible. 9135 export interface SyntaxCursor { 9136 currentNode(position: number): IncrementalNode; 9137 } 9138 9139 export function createSyntaxCursor(sourceFile: SourceFile): SyntaxCursor { 9140 let currentArray: NodeArray<Node> = sourceFile.statements; 9141 let currentArrayIndex = 0; 9142 9143 Debug.assert(currentArrayIndex < currentArray.length); 9144 let current = currentArray[currentArrayIndex]; 9145 let lastQueriedPosition = InvalidPosition.Value; 9146 9147 return { 9148 currentNode(position: number) { 9149 // Only compute the current node if the position is different than the last time 9150 // we were asked. The parser commonly asks for the node at the same position 9151 // twice. Once to know if can read an appropriate list element at a certain point, 9152 // and then to actually read and consume the node. 9153 if (position !== lastQueriedPosition) { 9154 // Much of the time the parser will need the very next node in the array that 9155 // we just returned a node from.So just simply check for that case and move 9156 // forward in the array instead of searching for the node again. 9157 if (current && current.end === position && currentArrayIndex < (currentArray.length - 1)) { 9158 currentArrayIndex++; 9159 current = currentArray[currentArrayIndex]; 9160 } 9161 9162 // If we don't have a node, or the node we have isn't in the right position, 9163 // then try to find a viable node at the position requested. 9164 if (!current || current.pos !== position) { 9165 findHighestListElementThatStartsAtPosition(position); 9166 } 9167 } 9168 9169 // Cache this query so that we don't do any extra work if the parser calls back 9170 // into us. Note: this is very common as the parser will make pairs of calls like 9171 // 'isListElement -> parseListElement'. If we were unable to find a node when 9172 // called with 'isListElement', we don't want to redo the work when parseListElement 9173 // is called immediately after. 9174 lastQueriedPosition = position; 9175 9176 // Either we don'd have a node, or we have a node at the position being asked for. 9177 Debug.assert(!current || current.pos === position); 9178 return <IncrementalNode>current; 9179 } 9180 }; 9181 9182 // Finds the highest element in the tree we can find that starts at the provided position. 9183 // The element must be a direct child of some node list in the tree. This way after we 9184 // return it, we can easily return its next sibling in the list. 9185 function findHighestListElementThatStartsAtPosition(position: number) { 9186 // Clear out any cached state about the last node we found. 9187 currentArray = undefined!; 9188 currentArrayIndex = InvalidPosition.Value; 9189 current = undefined!; 9190 9191 // Recurse into the source file to find the highest node at this position. 9192 forEachChild(sourceFile, visitNode, visitArray); 9193 return; 9194 9195 function visitNode(node: Node) { 9196 if (position >= node.pos && position < node.end) { 9197 // Position was within this node. Keep searching deeper to find the node. 9198 forEachChild(node, visitNode, visitArray); 9199 9200 // don't proceed any further in the search. 9201 return true; 9202 } 9203 9204 // position wasn't in this node, have to keep searching. 9205 return false; 9206 } 9207 9208 function visitArray(array: NodeArray<Node>) { 9209 if (position >= array.pos && position < array.end) { 9210 // position was in this array. Search through this array to see if we find a 9211 // viable element. 9212 for (let i = 0; i < array.length; i++) { 9213 const child = array[i]; 9214 if (child) { 9215 if (child.pos === position) { 9216 // Found the right node. We're done. 9217 currentArray = array; 9218 currentArrayIndex = i; 9219 current = child; 9220 return true; 9221 } 9222 else { 9223 if (child.pos < position && position < child.end) { 9224 // Position in somewhere within this child. Search in it and 9225 // stop searching in this array. 9226 forEachChild(child, visitNode, visitArray); 9227 return true; 9228 } 9229 } 9230 } 9231 } 9232 } 9233 9234 // position wasn't in this array, have to keep searching. 9235 return false; 9236 } 9237 } 9238 } 9239 9240 const enum InvalidPosition { 9241 Value = -1 9242 } 9243 } 9244 9245 /** @internal */ 9246 export function isDeclarationFileName(fileName: string): boolean { 9247 return fileExtensionIs(fileName, Extension.Dts) || fileExtensionIs(fileName, Extension.Dets); 9248 } 9249 9250 /*@internal*/ 9251 export interface PragmaContext { 9252 languageVersion: ScriptTarget; 9253 pragmas?: PragmaMap; 9254 checkJsDirective?: CheckJsDirective; 9255 referencedFiles: FileReference[]; 9256 typeReferenceDirectives: FileReference[]; 9257 libReferenceDirectives: FileReference[]; 9258 amdDependencies: AmdDependency[]; 9259 hasNoDefaultLib?: boolean; 9260 moduleName?: string; 9261 } 9262 9263 /*@internal*/ 9264 export function processCommentPragmas(context: PragmaContext, sourceText: string): void { 9265 const pragmas: PragmaPseudoMapEntry[] = []; 9266 9267 for (const range of getLeadingCommentRanges(sourceText, 0) || emptyArray) { 9268 const comment = sourceText.substring(range.pos, range.end); 9269 extractPragmas(pragmas, range, comment); 9270 } 9271 9272 context.pragmas = new Map() as PragmaMap; 9273 for (const pragma of pragmas) { 9274 if (context.pragmas.has(pragma.name)) { 9275 const currentValue = context.pragmas.get(pragma.name); 9276 if (currentValue instanceof Array) { 9277 currentValue.push(pragma.args); 9278 } 9279 else { 9280 context.pragmas.set(pragma.name, [currentValue, pragma.args]); 9281 } 9282 continue; 9283 } 9284 context.pragmas.set(pragma.name, pragma.args); 9285 } 9286 } 9287 9288 /*@internal*/ 9289 type PragmaDiagnosticReporter = (pos: number, length: number, message: DiagnosticMessage) => void; 9290 9291 /*@internal*/ 9292 export function processPragmasIntoFields(context: PragmaContext, reportDiagnostic: PragmaDiagnosticReporter): void { 9293 context.checkJsDirective = undefined; 9294 context.referencedFiles = []; 9295 context.typeReferenceDirectives = []; 9296 context.libReferenceDirectives = []; 9297 context.amdDependencies = []; 9298 context.hasNoDefaultLib = false; 9299 context.pragmas!.forEach((entryOrList, key) => { // TODO: GH#18217 9300 // TODO: The below should be strongly type-guarded and not need casts/explicit annotations, since entryOrList is related to 9301 // key and key is constrained to a union; but it's not (see GH#21483 for at least partial fix) :( 9302 switch (key) { 9303 case "reference": { 9304 const referencedFiles = context.referencedFiles; 9305 const typeReferenceDirectives = context.typeReferenceDirectives; 9306 const libReferenceDirectives = context.libReferenceDirectives; 9307 forEach(toArray(entryOrList) as PragmaPseudoMap["reference"][], arg => { 9308 const { types, lib, path } = arg.arguments; 9309 if (arg.arguments["no-default-lib"]) { 9310 context.hasNoDefaultLib = true; 9311 } 9312 else if (types) { 9313 typeReferenceDirectives.push({ pos: types.pos, end: types.end, fileName: types.value }); 9314 } 9315 else if (lib) { 9316 libReferenceDirectives.push({ pos: lib.pos, end: lib.end, fileName: lib.value }); 9317 } 9318 else if (path) { 9319 referencedFiles.push({ pos: path.pos, end: path.end, fileName: path.value }); 9320 } 9321 else { 9322 reportDiagnostic(arg.range.pos, arg.range.end - arg.range.pos, Diagnostics.Invalid_reference_directive_syntax); 9323 } 9324 }); 9325 break; 9326 } 9327 case "amd-dependency": { 9328 context.amdDependencies = map( 9329 toArray(entryOrList) as PragmaPseudoMap["amd-dependency"][], 9330 x => ({ name: x.arguments.name, path: x.arguments.path })); 9331 break; 9332 } 9333 case "amd-module": { 9334 if (entryOrList instanceof Array) { 9335 for (const entry of entryOrList) { 9336 if (context.moduleName) { 9337 // TODO: It's probably fine to issue this diagnostic on all instances of the pragma 9338 reportDiagnostic(entry.range.pos, entry.range.end - entry.range.pos, Diagnostics.An_AMD_module_cannot_have_multiple_name_assignments); 9339 } 9340 context.moduleName = (entry as PragmaPseudoMap["amd-module"]).arguments.name; 9341 } 9342 } 9343 else { 9344 context.moduleName = (entryOrList as PragmaPseudoMap["amd-module"]).arguments.name; 9345 } 9346 break; 9347 } 9348 case "ts-nocheck": 9349 case "ts-check": { 9350 // _last_ of either nocheck or check in a file is the "winner" 9351 forEach(toArray(entryOrList), entry => { 9352 if (!context.checkJsDirective || entry.range.pos > context.checkJsDirective.pos) { 9353 context.checkJsDirective = { 9354 enabled: key === "ts-check", 9355 end: entry.range.end, 9356 pos: entry.range.pos 9357 }; 9358 } 9359 }); 9360 break; 9361 } 9362 case "jsx": 9363 case "jsxfrag": 9364 case "jsximportsource": 9365 case "jsxruntime": 9366 return; // Accessed directly 9367 default: Debug.fail("Unhandled pragma kind"); // Can this be made into an assertNever in the future? 9368 } 9369 }); 9370 } 9371 9372 const namedArgRegExCache = new Map<string, RegExp>(); 9373 function getNamedArgRegEx(name: string): RegExp { 9374 if (namedArgRegExCache.has(name)) { 9375 return namedArgRegExCache.get(name)!; 9376 } 9377 const result = new RegExp(`(\\s${name}\\s*=\\s*)('|")(.+?)\\2`, "im"); 9378 namedArgRegExCache.set(name, result); 9379 return result; 9380 } 9381 9382 const tripleSlashXMLCommentStartRegEx = /^\/\/\/\s*<(\S+)\s.*?\/>/im; 9383 const singleLinePragmaRegEx = /^\/\/\/?\s*@(\S+)\s*(.*)\s*$/im; 9384 function extractPragmas(pragmas: PragmaPseudoMapEntry[], range: CommentRange, text: string) { 9385 const tripleSlash = range.kind === SyntaxKind.SingleLineCommentTrivia && tripleSlashXMLCommentStartRegEx.exec(text); 9386 if (tripleSlash) { 9387 const name = tripleSlash[1].toLowerCase() as keyof PragmaPseudoMap; // Technically unsafe cast, but we do it so the below check to make it safe typechecks 9388 const pragma = commentPragmas[name] as PragmaDefinition; 9389 if (!pragma || !(pragma.kind! & PragmaKindFlags.TripleSlashXML)) { 9390 return; 9391 } 9392 if (pragma.args) { 9393 const argument: {[index: string]: string | {value: string, pos: number, end: number}} = {}; 9394 for (const arg of pragma.args) { 9395 const matcher = getNamedArgRegEx(arg.name); 9396 const matchResult = matcher.exec(text); 9397 if (!matchResult && !arg.optional) { 9398 return; // Missing required argument, don't parse 9399 } 9400 else if (matchResult) { 9401 if (arg.captureSpan) { 9402 const startPos = range.pos + matchResult.index + matchResult[1].length + matchResult[2].length; 9403 argument[arg.name] = { 9404 value: matchResult[3], 9405 pos: startPos, 9406 end: startPos + matchResult[3].length 9407 }; 9408 } 9409 else { 9410 argument[arg.name] = matchResult[3]; 9411 } 9412 } 9413 } 9414 pragmas.push({ name, args: { arguments: argument, range } } as PragmaPseudoMapEntry); 9415 } 9416 else { 9417 pragmas.push({ name, args: { arguments: {}, range } } as PragmaPseudoMapEntry); 9418 } 9419 return; 9420 } 9421 9422 const singleLine = range.kind === SyntaxKind.SingleLineCommentTrivia && singleLinePragmaRegEx.exec(text); 9423 if (singleLine) { 9424 return addPragmaForMatch(pragmas, range, PragmaKindFlags.SingleLine, singleLine); 9425 } 9426 9427 if (range.kind === SyntaxKind.MultiLineCommentTrivia) { 9428 const multiLinePragmaRegEx = /\s*@(\S+)\s*(.*)\s*$/gim; // Defined inline since it uses the "g" flag, which keeps a persistent index (for iterating) 9429 let multiLineMatch: RegExpExecArray | null; 9430 while (multiLineMatch = multiLinePragmaRegEx.exec(text)) { 9431 addPragmaForMatch(pragmas, range, PragmaKindFlags.MultiLine, multiLineMatch); 9432 } 9433 } 9434 } 9435 9436 function addPragmaForMatch(pragmas: PragmaPseudoMapEntry[], range: CommentRange, kind: PragmaKindFlags, match: RegExpExecArray) { 9437 if (!match) return; 9438 const name = match[1].toLowerCase() as keyof PragmaPseudoMap; // Technically unsafe cast, but we do it so they below check to make it safe typechecks 9439 const pragma = commentPragmas[name] as PragmaDefinition; 9440 if (!pragma || !(pragma.kind! & kind)) { 9441 return; 9442 } 9443 const args = match[2]; // Split on spaces and match up positionally with definition 9444 const argument = getNamedPragmaArguments(pragma, args); 9445 if (argument === "fail") return; // Missing required argument, fail to parse it 9446 pragmas.push({ name, args: { arguments: argument, range } } as PragmaPseudoMapEntry); 9447 return; 9448 } 9449 9450 function getNamedPragmaArguments(pragma: PragmaDefinition, text: string | undefined): {[index: string]: string} | "fail" { 9451 if (!text) return {}; 9452 if (!pragma.args) return {}; 9453 const args = text.split(/\s+/); 9454 const argMap: {[index: string]: string} = {}; 9455 for (let i = 0; i < pragma.args.length; i++) { 9456 const argument = pragma.args[i]; 9457 if (!args[i] && !argument.optional) { 9458 return "fail"; 9459 } 9460 if (argument.captureSpan) { 9461 return Debug.fail("Capture spans not yet implemented for non-xml pragmas"); 9462 } 9463 argMap[argument.name] = args[i]; 9464 } 9465 return argMap; 9466 } 9467 9468 /** @internal */ 9469 export function tagNamesAreEquivalent(lhs: JsxTagNameExpression, rhs: JsxTagNameExpression): boolean { 9470 if (lhs.kind !== rhs.kind) { 9471 return false; 9472 } 9473 9474 if (lhs.kind === SyntaxKind.Identifier) { 9475 return lhs.escapedText === (<Identifier>rhs).escapedText; 9476 } 9477 9478 if (lhs.kind === SyntaxKind.ThisKeyword) { 9479 return true; 9480 } 9481 9482 // If we are at this statement then we must have PropertyAccessExpression and because tag name in Jsx element can only 9483 // take forms of JsxTagNameExpression which includes an identifier, "this" expression, or another propertyAccessExpression 9484 // it is safe to case the expression property as such. See parseJsxElementName for how we parse tag name in Jsx element 9485 return (<PropertyAccessExpression>lhs).name.escapedText === (<PropertyAccessExpression>rhs).name.escapedText && 9486 tagNamesAreEquivalent((<PropertyAccessExpression>lhs).expression as JsxTagNameExpression, (<PropertyAccessExpression>rhs).expression as JsxTagNameExpression); 9487 } 9488} 9489