diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 6815b2c352..f835afff31 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -697,10 +697,11 @@ namespace ts { bindJSDocTypeAlias(node as JSDocTypedefTag | JSDocCallbackTag); break; // In source files and blocks, bind functions first to match hoisting that occurs at runtime - case SyntaxKind.SourceFile: + case SyntaxKind.SourceFile: { bindEachFunctionsFirst((node as SourceFile).statements); bind((node as SourceFile).endOfFileToken); break; + } case SyntaxKind.Block: case SyntaxKind.ModuleBlock: bindEachFunctionsFirst((node as Block).statements); @@ -1975,7 +1976,10 @@ namespace ts { } else if (!skipTransformFlagAggregation && (node.transformFlags & TransformFlags.HasComputedFlags) === 0) { subtreeTransformFlags |= computeTransformFlagsForNode(node, 0); + const saveParent = parent; + if (node.kind === SyntaxKind.EndOfFileToken) parent = node; bindJSDoc(node); + parent = saveParent; } inStrictMode = saveInStrictMode; } diff --git a/src/services/breakpoints.ts b/src/services/breakpoints.ts index 812a8a45dd..404e75db14 100644 --- a/src/services/breakpoints.ts +++ b/src/services/breakpoints.ts @@ -9,7 +9,7 @@ namespace ts.BreakpointResolver { return undefined; } - let tokenAtLocation = getTokenAtPosition(sourceFile, position, /*includeJsDocComment*/ false); + let tokenAtLocation = getTokenAtPosition(sourceFile, position); const lineOfPosition = sourceFile.getLineAndCharacterOfPosition(position).line; if (sourceFile.getLineAndCharacterOfPosition(tokenAtLocation.getStart(sourceFile)).line > lineOfPosition) { // Get previous token if the token is returned starts on new line diff --git a/src/services/codefixes/addMissingInvocationForDecorator.ts b/src/services/codefixes/addMissingInvocationForDecorator.ts index 02b97eec37..2510087062 100644 --- a/src/services/codefixes/addMissingInvocationForDecorator.ts +++ b/src/services/codefixes/addMissingInvocationForDecorator.ts @@ -13,7 +13,7 @@ namespace ts.codefix { }); function makeChange(changeTracker: textChanges.ChangeTracker, sourceFile: SourceFile, pos: number) { - const token = getTokenAtPosition(sourceFile, pos, /*includeJsDocComment*/ false); + const token = getTokenAtPosition(sourceFile, pos); const decorator = findAncestor(token, isDecorator)!; Debug.assert(!!decorator, "Expected position to be owned by a decorator."); const replacement = createCall(decorator.expression, /*typeArguments*/ undefined, /*argumentsArray*/ undefined); diff --git a/src/services/codefixes/annotateWithTypeFromJSDoc.ts b/src/services/codefixes/annotateWithTypeFromJSDoc.ts index 36664ead4a..c314392110 100644 --- a/src/services/codefixes/annotateWithTypeFromJSDoc.ts +++ b/src/services/codefixes/annotateWithTypeFromJSDoc.ts @@ -18,7 +18,7 @@ namespace ts.codefix { }); function getDeclaration(file: SourceFile, pos: number): DeclarationWithType | undefined { - const name = getTokenAtPosition(file, pos, /*includeJsDocComment*/ false); + const name = getTokenAtPosition(file, pos); // For an arrow function with no name, 'name' lands on the first parameter. return tryCast(isParameter(name.parent) ? name.parent.parent : name.parent, parameterShouldGetTypeFromJSDoc); } diff --git a/src/services/codefixes/convertFunctionToEs6Class.ts b/src/services/codefixes/convertFunctionToEs6Class.ts index 4c47d3dca7..b98f2abb70 100644 --- a/src/services/codefixes/convertFunctionToEs6Class.ts +++ b/src/services/codefixes/convertFunctionToEs6Class.ts @@ -14,7 +14,7 @@ namespace ts.codefix { function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, position: number, checker: TypeChecker): void { const deletedNodes: { node: Node, inList: boolean }[] = []; - const ctorSymbol = checker.getSymbolAtLocation(getTokenAtPosition(sourceFile, position, /*includeJsDocComment*/ false))!; + const ctorSymbol = checker.getSymbolAtLocation(getTokenAtPosition(sourceFile, position))!; if (!ctorSymbol || !(ctorSymbol.flags & (SymbolFlags.Function | SymbolFlags.Variable))) { // Bad input diff --git a/src/services/codefixes/convertToMappedObjectType.ts b/src/services/codefixes/convertToMappedObjectType.ts index ff86a9872b..0d79d6363c 100644 --- a/src/services/codefixes/convertToMappedObjectType.ts +++ b/src/services/codefixes/convertToMappedObjectType.ts @@ -25,7 +25,7 @@ namespace ts.codefix { interface Info { readonly indexSignature: IndexSignatureDeclaration; readonly container: FixableDeclaration; } function getInfo(sourceFile: SourceFile, pos: number): Info | undefined { - const token = getTokenAtPosition(sourceFile, pos, /*includeJsDocComment*/ false); + const token = getTokenAtPosition(sourceFile, pos); const indexSignature = cast(token.parent.parent, isIndexSignatureDeclaration); if (isClassDeclaration(indexSignature.parent)) return undefined; const container = isInterfaceDeclaration(indexSignature.parent) ? indexSignature.parent : cast(indexSignature.parent.parent, isTypeAliasDeclaration); diff --git a/src/services/codefixes/correctQualifiedNameToIndexedAccessType.ts b/src/services/codefixes/correctQualifiedNameToIndexedAccessType.ts index 54fe182386..02ccbfd1d6 100644 --- a/src/services/codefixes/correctQualifiedNameToIndexedAccessType.ts +++ b/src/services/codefixes/correctQualifiedNameToIndexedAccessType.ts @@ -21,7 +21,7 @@ namespace ts.codefix { }); function getQualifiedName(sourceFile: SourceFile, pos: number): QualifiedName & { left: Identifier } | undefined { - const qualifiedName = findAncestor(getTokenAtPosition(sourceFile, pos, /*includeJsDocComment*/ true), isQualifiedName)!; + const qualifiedName = findAncestor(getTokenAtPosition(sourceFile, pos), isQualifiedName)!; Debug.assert(!!qualifiedName, "Expected position to be owned by a qualified name."); return isIdentifier(qualifiedName.left) ? qualifiedName as QualifiedName & { left: Identifier } : undefined; } diff --git a/src/services/codefixes/fixAddMissingMember.ts b/src/services/codefixes/fixAddMissingMember.ts index 7dbb43b94c..38f4324af6 100644 --- a/src/services/codefixes/fixAddMissingMember.ts +++ b/src/services/codefixes/fixAddMissingMember.ts @@ -52,7 +52,7 @@ namespace ts.codefix { // The identifier of the missing property. eg: // this.missing = 1; // ^^^^^^^ - const token = getTokenAtPosition(tokenSourceFile, tokenPos, /*includeJsDocComment*/ false); + const token = getTokenAtPosition(tokenSourceFile, tokenPos); if (!isIdentifier(token)) { return undefined; } diff --git a/src/services/codefixes/fixAddModuleReferTypeMissingTypeof.ts b/src/services/codefixes/fixAddModuleReferTypeMissingTypeof.ts index ac79c29fde..b558a270de 100644 --- a/src/services/codefixes/fixAddModuleReferTypeMissingTypeof.ts +++ b/src/services/codefixes/fixAddModuleReferTypeMissingTypeof.ts @@ -17,7 +17,7 @@ namespace ts.codefix { }); function getImportTypeNode(sourceFile: SourceFile, pos: number): ImportTypeNode { - const token = getTokenAtPosition(sourceFile, pos, /*includeJsDocComment*/ false); + const token = getTokenAtPosition(sourceFile, pos); Debug.assert(token.kind === SyntaxKind.ImportKeyword); Debug.assert(token.parent.kind === SyntaxKind.ImportType); return token.parent; diff --git a/src/services/codefixes/fixAwaitInSyncFunction.ts b/src/services/codefixes/fixAwaitInSyncFunction.ts index fd990aebcd..e5b3a24dbe 100644 --- a/src/services/codefixes/fixAwaitInSyncFunction.ts +++ b/src/services/codefixes/fixAwaitInSyncFunction.ts @@ -34,7 +34,7 @@ namespace ts.codefix { } function getNodes(sourceFile: SourceFile, start: number): { insertBefore: Node, returnType: TypeNode | undefined } | undefined { - const token = getTokenAtPosition(sourceFile, start, /*includeJsDocComment*/ false); + const token = getTokenAtPosition(sourceFile, start); const containingFunction = getContainingFunction(token); if (!containingFunction) { return; diff --git a/src/services/codefixes/fixCannotFindModule.ts b/src/services/codefixes/fixCannotFindModule.ts index 78532f1527..68fa3a5c03 100644 --- a/src/services/codefixes/fixCannotFindModule.ts +++ b/src/services/codefixes/fixCannotFindModule.ts @@ -28,7 +28,7 @@ namespace ts.codefix { } function getTypesPackageNameToInstall(host: LanguageServiceHost, sourceFile: SourceFile, pos: number, diagCode: number): string | undefined { - const moduleName = cast(getTokenAtPosition(sourceFile, pos, /*includeJsDocComment*/ false), isStringLiteral).text; + const moduleName = cast(getTokenAtPosition(sourceFile, pos), isStringLiteral).text; const { packageName } = getPackageName(moduleName); return diagCode === errorCodeCannotFindModule ? (JsTyping.nodeCoreModules.has(packageName) ? "@types/node" : undefined) diff --git a/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts b/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts index c0624c5529..b292e6d1cd 100644 --- a/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts +++ b/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts @@ -28,7 +28,7 @@ namespace ts.codefix { function getClass(sourceFile: SourceFile, pos: number): ClassLikeDeclaration { // Token is the identifier in the case of a class declaration // or the class keyword token in the case of a class expression. - const token = getTokenAtPosition(sourceFile, pos, /*includeJsDocComment*/ false); + const token = getTokenAtPosition(sourceFile, pos); return cast(token.parent, isClassLike); } diff --git a/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts b/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts index ad093de632..04830511c2 100644 --- a/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts +++ b/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts @@ -29,7 +29,7 @@ namespace ts.codefix { }); function getClass(sourceFile: SourceFile, pos: number): ClassLikeDeclaration { - return Debug.assertDefined(getContainingClass(getTokenAtPosition(sourceFile, pos, /*includeJsDocComment*/ false))); + return Debug.assertDefined(getContainingClass(getTokenAtPosition(sourceFile, pos))); } function symbolPointsToNonPrivateMember (symbol: Symbol) { diff --git a/src/services/codefixes/fixClassSuperMustPrecedeThisAccess.ts b/src/services/codefixes/fixClassSuperMustPrecedeThisAccess.ts index 6a68b9b600..8ee9b60051 100644 --- a/src/services/codefixes/fixClassSuperMustPrecedeThisAccess.ts +++ b/src/services/codefixes/fixClassSuperMustPrecedeThisAccess.ts @@ -33,7 +33,7 @@ namespace ts.codefix { } function getNodes(sourceFile: SourceFile, pos: number): { readonly constructor: ConstructorDeclaration, readonly superCall: ExpressionStatement } | undefined { - const token = getTokenAtPosition(sourceFile, pos, /*includeJsDocComment*/ false); + const token = getTokenAtPosition(sourceFile, pos); if (token.kind !== SyntaxKind.ThisKeyword) return undefined; const constructor = getContainingFunction(token) as ConstructorDeclaration; const superCall = findSuperCall(constructor.body!); diff --git a/src/services/codefixes/fixConstructorForDerivedNeedSuperCall.ts b/src/services/codefixes/fixConstructorForDerivedNeedSuperCall.ts index 605bb660f0..7d021d5e26 100644 --- a/src/services/codefixes/fixConstructorForDerivedNeedSuperCall.ts +++ b/src/services/codefixes/fixConstructorForDerivedNeedSuperCall.ts @@ -16,7 +16,7 @@ namespace ts.codefix { }); function getNode(sourceFile: SourceFile, pos: number): ConstructorDeclaration { - const token = getTokenAtPosition(sourceFile, pos, /*includeJsDocComment*/ false); + const token = getTokenAtPosition(sourceFile, pos); Debug.assert(token.kind === SyntaxKind.ConstructorKeyword); return token.parent as ConstructorDeclaration; } diff --git a/src/services/codefixes/fixExtendsInterfaceBecomesImplements.ts b/src/services/codefixes/fixExtendsInterfaceBecomesImplements.ts index c2c040b42a..78dc6b1ae1 100644 --- a/src/services/codefixes/fixExtendsInterfaceBecomesImplements.ts +++ b/src/services/codefixes/fixExtendsInterfaceBecomesImplements.ts @@ -20,7 +20,7 @@ namespace ts.codefix { }); function getNodes(sourceFile: SourceFile, pos: number) { - const token = getTokenAtPosition(sourceFile, pos, /*includeJsDocComment*/ false); + const token = getTokenAtPosition(sourceFile, pos); const heritageClauses = getContainingClass(token)!.heritageClauses!; const extendsToken = heritageClauses[0].getFirstToken()!; return extendsToken.kind === SyntaxKind.ExtendsKeyword ? { extendsToken, heritageClauses } : undefined; diff --git a/src/services/codefixes/fixForgottenThisPropertyAccess.ts b/src/services/codefixes/fixForgottenThisPropertyAccess.ts index 82954673d4..e5f58885e1 100644 --- a/src/services/codefixes/fixForgottenThisPropertyAccess.ts +++ b/src/services/codefixes/fixForgottenThisPropertyAccess.ts @@ -26,7 +26,7 @@ namespace ts.codefix { interface Info { readonly node: Identifier; readonly className: string | undefined; } function getInfo(sourceFile: SourceFile, pos: number, diagCode: number): Info | undefined { - const node = getTokenAtPosition(sourceFile, pos, /*includeJsDocComment*/ false); + const node = getTokenAtPosition(sourceFile, pos); if (!isIdentifier(node)) return undefined; return { node, className: diagCode === didYouMeanStaticMemberCode ? getContainingClass(node)!.name!.text : undefined }; } diff --git a/src/services/codefixes/fixInvalidImportSyntax.ts b/src/services/codefixes/fixInvalidImportSyntax.ts index 906c6f13cd..ab8014bd0b 100644 --- a/src/services/codefixes/fixInvalidImportSyntax.ts +++ b/src/services/codefixes/fixInvalidImportSyntax.ts @@ -40,7 +40,7 @@ namespace ts.codefix { function getActionsForUsageOfInvalidImport(context: CodeFixContext): CodeFixAction[] | undefined { const sourceFile = context.sourceFile; const targetKind = Diagnostics.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures.code === context.errorCode ? SyntaxKind.CallExpression : SyntaxKind.NewExpression; - const node = findAncestor(getTokenAtPosition(sourceFile, context.span.start, /*includeJsDocComment*/ false), a => a.kind === targetKind && a.getStart() === context.span.start && a.getEnd() === (context.span.start + context.span.length)) as CallExpression | NewExpression; + const node = findAncestor(getTokenAtPosition(sourceFile, context.span.start), a => a.kind === targetKind && a.getStart() === context.span.start && a.getEnd() === (context.span.start + context.span.length)) as CallExpression | NewExpression; if (!node) { return []; } @@ -69,7 +69,7 @@ namespace ts.codefix { function getActionsForInvalidImportLocation(context: CodeFixContext): CodeFixAction[] | undefined { const sourceFile = context.sourceFile; - const node = findAncestor(getTokenAtPosition(sourceFile, context.span.start, /*includeJsDocComment*/ false), a => a.getStart() === context.span.start && a.getEnd() === (context.span.start + context.span.length)); + const node = findAncestor(getTokenAtPosition(sourceFile, context.span.start), a => a.getStart() === context.span.start && a.getEnd() === (context.span.start + context.span.length)); if (!node) { return []; } diff --git a/src/services/codefixes/fixJSDocTypes.ts b/src/services/codefixes/fixJSDocTypes.ts index 043ed018aa..8769b0339e 100644 --- a/src/services/codefixes/fixJSDocTypes.ts +++ b/src/services/codefixes/fixJSDocTypes.ts @@ -44,7 +44,7 @@ namespace ts.codefix { } function getInfo(sourceFile: SourceFile, pos: number, checker: TypeChecker): { readonly typeNode: TypeNode, readonly type: Type } | undefined { - const decl = findAncestor(getTokenAtPosition(sourceFile, pos, /*includeJsDocComment*/ false), isTypeContainer); + const decl = findAncestor(getTokenAtPosition(sourceFile, pos), isTypeContainer); const typeNode = decl && decl.type; return typeNode && { typeNode, type: checker.getTypeFromTypeNode(typeNode) }; } diff --git a/src/services/codefixes/fixSpelling.ts b/src/services/codefixes/fixSpelling.ts index 6986e161f4..fbe3953c1b 100644 --- a/src/services/codefixes/fixSpelling.ts +++ b/src/services/codefixes/fixSpelling.ts @@ -29,7 +29,7 @@ namespace ts.codefix { // This is the identifier of the misspelled word. eg: // this.speling = 1; // ^^^^^^^ - const node = getTokenAtPosition(sourceFile, pos, /*includeJsDocComment*/ false); // TODO: GH#15852 + const node = getTokenAtPosition(sourceFile, pos); const checker = context.program.getTypeChecker(); let suggestion: string | undefined; diff --git a/src/services/codefixes/fixStrictClassInitialization.ts b/src/services/codefixes/fixStrictClassInitialization.ts index f52ebadc85..0647a64c36 100644 --- a/src/services/codefixes/fixStrictClassInitialization.ts +++ b/src/services/codefixes/fixStrictClassInitialization.ts @@ -48,7 +48,7 @@ namespace ts.codefix { }); function getPropertyDeclaration (sourceFile: SourceFile, pos: number): PropertyDeclaration | undefined { - const token = getTokenAtPosition(sourceFile, pos, /*includeJsDocComment*/ false); + const token = getTokenAtPosition(sourceFile, pos); return isIdentifier(token) ? cast(token.parent, isPropertyDeclaration) : undefined; } diff --git a/src/services/codefixes/fixUnreachableCode.ts b/src/services/codefixes/fixUnreachableCode.ts index aed55f96f2..6c279d75ab 100644 --- a/src/services/codefixes/fixUnreachableCode.ts +++ b/src/services/codefixes/fixUnreachableCode.ts @@ -13,7 +13,7 @@ namespace ts.codefix { }); function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, start: number): void { - const token = getTokenAtPosition(sourceFile, start, /*includeJsDocComment*/ false); + const token = getTokenAtPosition(sourceFile, start); const statement = findAncestor(token, isStatement)!; Debug.assert(statement.getStart(sourceFile) === token.getStart(sourceFile)); diff --git a/src/services/codefixes/fixUnusedIdentifier.ts b/src/services/codefixes/fixUnusedIdentifier.ts index 505a09813c..4df28b85a3 100644 --- a/src/services/codefixes/fixUnusedIdentifier.ts +++ b/src/services/codefixes/fixUnusedIdentifier.ts @@ -16,7 +16,7 @@ namespace ts.codefix { getCodeActions(context) { const { errorCode, sourceFile, program } = context; const checker = program.getTypeChecker(); - const startToken = getTokenAtPosition(sourceFile, context.span.start, /*includeJsDocComment*/ false); + const startToken = getTokenAtPosition(sourceFile, context.span.start); const importDecl = tryGetFullImport(startToken); if (importDecl) { @@ -54,7 +54,7 @@ namespace ts.codefix { const { sourceFile, program } = context; const checker = program.getTypeChecker(); return codeFixAll(context, errorCodes, (changes, diag) => { - const startToken = getTokenAtPosition(sourceFile, diag.start, /*includeJsDocComment*/ false); + const startToken = getTokenAtPosition(sourceFile, diag.start); const token = findPrecedingToken(textSpanEnd(diag), diag.file)!; switch (context.fixId) { case fixIdPrefix: @@ -173,8 +173,8 @@ namespace ts.codefix { const typeParameters = getEffectiveTypeParameterDeclarations(parent.parent); if (typeParameters.length === 1) { const { pos, end } = cast(typeParameters, isNodeArray); - const previousToken = getTokenAtPosition(sourceFile, pos - 1, /*includeJsDocComment*/ true); - const nextToken = getTokenAtPosition(sourceFile, end, /*includeJsDocComment*/ true); + const previousToken = getTokenAtPosition(sourceFile, pos - 1); + const nextToken = getTokenAtPosition(sourceFile, end); Debug.assert(previousToken.kind === SyntaxKind.LessThanToken); Debug.assert(nextToken.kind === SyntaxKind.GreaterThanToken); @@ -251,7 +251,7 @@ namespace ts.codefix { else { // import |d,| * as ns from './file' const start = importClause.name!.getStart(sourceFile); - const nextToken = getTokenAtPosition(sourceFile, importClause.name!.end, /*includeJsDocComment*/ false); + const nextToken = getTokenAtPosition(sourceFile, importClause.name!.end); if (nextToken && nextToken.kind === SyntaxKind.CommaToken) { // shift first non-whitespace position after comma to the start position of the node const end = skipTrivia(sourceFile.text, nextToken.end, /*stopAfterLineBreaks*/ false, /*stopAtComments*/ true); @@ -285,7 +285,7 @@ namespace ts.codefix { // Delete named imports while preserving the default import // import d|, * as ns| from './file' // import d|, { a }| from './file' - const previousToken = getTokenAtPosition(sourceFile, namedBindings.pos - 1, /*includeJsDocComment*/ false); + const previousToken = getTokenAtPosition(sourceFile, namedBindings.pos - 1); if (previousToken && previousToken.kind === SyntaxKind.CommaToken) { changes.deleteRange(sourceFile, { pos: previousToken.getStart(), end: namedBindings.end }); } diff --git a/src/services/codefixes/fixUnusedLabel.ts b/src/services/codefixes/fixUnusedLabel.ts index b99f72d683..49f334720e 100644 --- a/src/services/codefixes/fixUnusedLabel.ts +++ b/src/services/codefixes/fixUnusedLabel.ts @@ -13,7 +13,7 @@ namespace ts.codefix { }); function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, start: number): void { - const token = getTokenAtPosition(sourceFile, start, /*includeJsDocComment*/ false); + const token = getTokenAtPosition(sourceFile, start); const labeledStatement = cast(token.parent, isLabeledStatement); const pos = token.getStart(sourceFile); const statementPos = labeledStatement.statement.getStart(sourceFile); diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index 86f64a79cf..d2f1095ef4 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -326,7 +326,7 @@ namespace ts.codefix { } function getActionsForUMDImport(context: CodeFixContext): CodeFixAction[] | undefined { - const token = getTokenAtPosition(context.sourceFile, context.span.start, /*includeJsDocComment*/ false); + const token = getTokenAtPosition(context.sourceFile, context.span.start); const checker = context.program.getTypeChecker(); let umdSymbol: Symbol | undefined; @@ -385,7 +385,7 @@ namespace ts.codefix { // This will always be an Identifier, since the diagnostics we fix only fail on identifiers. const { sourceFile, span, program, cancellationToken } = context; const checker = program.getTypeChecker(); - const symbolToken = getTokenAtPosition(sourceFile, span.start, /*includeJsDocComment*/ false); + const symbolToken = getTokenAtPosition(sourceFile, span.start); // If we're at ``, we must check if `Foo` is already in scope, and if so, get an import for `React` instead. const symbolName = isJsxOpeningLikeElement(symbolToken.parent) && symbolToken.parent.tagName === symbolToken diff --git a/src/services/codefixes/inferFromUsage.ts b/src/services/codefixes/inferFromUsage.ts index 3e26e4cdd3..cadb6d232d 100644 --- a/src/services/codefixes/inferFromUsage.ts +++ b/src/services/codefixes/inferFromUsage.ts @@ -30,7 +30,7 @@ namespace ts.codefix { return undefined; // TODO: GH#20113 } - const token = getTokenAtPosition(sourceFile, start, /*includeJsDocComment*/ false); + const token = getTokenAtPosition(sourceFile, start); let declaration!: Declaration | undefined; const changes = textChanges.ChangeTracker.with(context, changes => { declaration = doChange(changes, sourceFile, token, errorCode, program, cancellationToken, /*markSeenseen*/ returnTrue); }); const name = getNameOfDeclaration(declaration!); @@ -42,7 +42,7 @@ namespace ts.codefix { const { sourceFile, program, cancellationToken } = context; const markSeen = nodeSeenTracker(); return codeFixAll(context, errorCodes, (changes, err) => { - doChange(changes, sourceFile, getTokenAtPosition(err.file, err.start, /*includeJsDocComment*/ false), err.code, program, cancellationToken, markSeen); + doChange(changes, sourceFile, getTokenAtPosition(err.file, err.start), err.code, program, cancellationToken, markSeen); }); }, }); diff --git a/src/services/codefixes/requireInTs.ts b/src/services/codefixes/requireInTs.ts index 7efd9bd7fa..ee2a898147 100644 --- a/src/services/codefixes/requireInTs.ts +++ b/src/services/codefixes/requireInTs.ts @@ -21,7 +21,7 @@ namespace ts.codefix { interface Info { readonly statement: VariableStatement; readonly name: Identifier; readonly required: StringLiteralLike; } function getInfo(sourceFile: SourceFile, pos: number): Info { - const { parent } = getTokenAtPosition(sourceFile, pos, /*includeJsDocComment*/ false); + const { parent } = getTokenAtPosition(sourceFile, pos); if (!isRequireCall(parent, /*checkArgumentIsStringLiteralLike*/ true)) throw Debug.failBadSyntaxKind(parent); const decl = cast(parent.parent, isVariableDeclaration); return { statement: cast(decl.parent.parent, isVariableStatement), name: cast(decl.name, isIdentifier), required: parent.arguments[0] }; diff --git a/src/services/codefixes/useDefaultImport.ts b/src/services/codefixes/useDefaultImport.ts index 36aa0bb169..75d4d15bd7 100644 --- a/src/services/codefixes/useDefaultImport.ts +++ b/src/services/codefixes/useDefaultImport.ts @@ -24,7 +24,7 @@ namespace ts.codefix { readonly moduleSpecifier: Expression; } function getInfo(sourceFile: SourceFile, pos: number): Info | undefined { - const name = getTokenAtPosition(sourceFile, pos, /*includeJsDocComment*/ false); + const name = getTokenAtPosition(sourceFile, pos); if (!isIdentifier(name)) return undefined; // bad input const { parent } = name; if (isImportEqualsDeclaration(parent) && isExternalModuleReference(parent.moduleReference)) { diff --git a/src/services/completions.ts b/src/services/completions.ts index 59c280ae4f..372819464b 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -797,13 +797,12 @@ namespace ts.Completions { const typeChecker = program.getTypeChecker(); let start = timestamp(); - let currentToken = getTokenAtPosition(sourceFile, position, /*includeJsDocComment*/ false); // TODO: GH#15853 + let currentToken = getTokenAtPosition(sourceFile, position); // TODO: GH#15853 // We will check for jsdoc comments with insideComment and getJsDocTagAtPosition. (TODO: that seems rather inefficient to check the same thing so many times.) log("getCompletionData: Get current token: " + (timestamp() - start)); start = timestamp(); - // Completion not allowed inside comments, bail out if this is the case const insideComment = isInComment(sourceFile, position, currentToken); log("getCompletionData: Is inside comment: " + (timestamp() - start)); @@ -849,7 +848,7 @@ namespace ts.Completions { return { kind: CompletionDataKind.JsDocTagName }; } if (isTagWithTypeExpression(tag) && tag.typeExpression && tag.typeExpression.kind === SyntaxKind.JSDocTypeExpression) { - currentToken = getTokenAtPosition(sourceFile, position, /*includeJsDocComment*/ true); + currentToken = getTokenAtPosition(sourceFile, position); if (!currentToken || (!isDeclarationName(currentToken) && (currentToken.parent.kind !== SyntaxKind.JSDocPropertyTag || @@ -2156,32 +2155,8 @@ namespace ts.Completions { /** Get the corresponding JSDocTag node if the position is in a jsDoc comment */ function getJsDocTagAtPosition(node: Node, position: number): JSDocTag | undefined { - const { jsDoc } = getJsDocHavingNode(node) as JSDocContainer; - if (!jsDoc) return undefined; - - for (const { pos, end, tags } of jsDoc) { - if (!tags || position < pos || position > end) continue; - for (let i = tags.length - 1; i >= 0; i--) { - const tag = tags[i]; - if (position >= tag.pos) { - return tag; - } - } - } - } - - function getJsDocHavingNode(node: Node): Node { - if (!isToken(node)) return node; - - switch (node.kind) { - case SyntaxKind.VarKeyword: - case SyntaxKind.LetKeyword: - case SyntaxKind.ConstKeyword: - // if the current token is var, let or const, skip the VariableDeclarationList - return node.parent.parent; - default: - return node.parent; - } + const jsdoc = findAncestor(node, isJSDoc); + return jsdoc && jsdoc.tags && (rangeContainsPosition(jsdoc, position) ? findLast(jsdoc.tags, tag => tag.pos < position) : undefined); } /** diff --git a/src/services/formatting/formatting.ts b/src/services/formatting/formatting.ts index f37fb1bc5d..0e37672f66 100644 --- a/src/services/formatting/formatting.ts +++ b/src/services/formatting/formatting.ts @@ -1148,8 +1148,10 @@ namespace ts.formatting { position: number, onlyMultiLine: boolean, precedingToken?: Node | null, // tslint:disable-line:no-null-keyword - tokenAtPosition = getTokenAtPosition(sourceFile, position, /*includeJsDocComment*/ false), + tokenAtPosition = getTokenAtPosition(sourceFile, position), predicate?: (c: CommentRange) => boolean): CommentRange | undefined { + const jsdoc = findAncestor(tokenAtPosition, isJSDoc); + if (jsdoc) tokenAtPosition = jsdoc.parent; const tokenStart = tokenAtPosition.getStart(sourceFile); if (tokenStart <= position && position < tokenAtPosition.getEnd()) { return undefined; diff --git a/src/services/jsDoc.ts b/src/services/jsDoc.ts index 136ac7b09e..1b6f21ac64 100644 --- a/src/services/jsDoc.ts +++ b/src/services/jsDoc.ts @@ -242,7 +242,7 @@ namespace ts.JsDoc { return undefined; } - const tokenAtPos = getTokenAtPosition(sourceFile, position, /*includeJsDocComment*/ false); + const tokenAtPos = getTokenAtPosition(sourceFile, position); const tokenStart = tokenAtPos.getStart(sourceFile); if (!tokenAtPos || tokenStart < position) { return undefined; diff --git a/src/services/pathCompletions.ts b/src/services/pathCompletions.ts index eb329ea3eb..8ab7cc315a 100644 --- a/src/services/pathCompletions.ts +++ b/src/services/pathCompletions.ts @@ -304,7 +304,7 @@ namespace ts.Completions.PathCompletions { } export function getTripleSlashReferenceCompletion(sourceFile: SourceFile, position: number, compilerOptions: CompilerOptions, host: LanguageServiceHost): ReadonlyArray | undefined { - const token = getTokenAtPosition(sourceFile, position, /*includeJsDocComment*/ false); + const token = getTokenAtPosition(sourceFile, position); const commentRanges = getLeadingCommentRanges(sourceFile.text, token.pos); const range = commentRanges && find(commentRanges, commentRange => position >= commentRange.pos && position <= commentRange.end); if (!range) { diff --git a/src/services/refactors/addOrRemoveBracesToArrowFunction.ts b/src/services/refactors/addOrRemoveBracesToArrowFunction.ts index e982412ed7..57e7dbd97a 100644 --- a/src/services/refactors/addOrRemoveBracesToArrowFunction.ts +++ b/src/services/refactors/addOrRemoveBracesToArrowFunction.ts @@ -69,7 +69,7 @@ namespace ts.refactor.addOrRemoveBracesToArrowFunction { } function getConvertibleArrowFunctionAtPosition(file: SourceFile, startPosition: number): Info | undefined { - const node = getTokenAtPosition(file, startPosition, /*includeJsDocComment*/ false); + const node = getTokenAtPosition(file, startPosition); const func = getContainingFunction(node); if (!func || !isArrowFunction(func) || (!rangeContainsRange(func, node) || rangeContainsRange(func.body, node))) return undefined; diff --git a/src/services/refactors/convertExport.ts b/src/services/refactors/convertExport.ts index 72c72e719b..9ce9fcf264 100644 --- a/src/services/refactors/convertExport.ts +++ b/src/services/refactors/convertExport.ts @@ -30,7 +30,7 @@ namespace ts.refactor { function getInfo(context: RefactorContext): Info | undefined { const { file } = context; const span = getRefactorContextSpan(context); - const token = getTokenAtPosition(file, span.start, /*includeJsDocComment*/ false); + const token = getTokenAtPosition(file, span.start); const exportNode = getParentNodeInSpan(token, file, span); if (!exportNode || (!isSourceFile(exportNode.parent) && !(isModuleBlock(exportNode.parent) && isAmbientModule(exportNode.parent.parent)))) { return undefined; diff --git a/src/services/refactors/convertImport.ts b/src/services/refactors/convertImport.ts index cfdd9baffd..c91c06cb1f 100644 --- a/src/services/refactors/convertImport.ts +++ b/src/services/refactors/convertImport.ts @@ -22,7 +22,7 @@ namespace ts.refactor { function getImportToConvert(context: RefactorContext): NamedImportBindings | undefined { const { file } = context; const span = getRefactorContextSpan(context); - const token = getTokenAtPosition(file, span.start, /*includeJsDocComment*/ false); + const token = getTokenAtPosition(file, span.start); const importDecl = getParentNodeInSpan(token, file, span); if (!importDecl || !isImportDeclaration(importDecl)) return undefined; const { importClause } = importDecl; diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index 3f2794e439..ade704e364 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -194,7 +194,7 @@ namespace ts.refactor.extractSymbol { // Walk up starting from the the start position until we find a non-SourceFile node that subsumes the selected span. // This may fail (e.g. you select two statements in the root of a source file) - const start = getParentNodeInSpan(getTokenAtPosition(sourceFile, span.start, /*includeJsDocComment*/ false), sourceFile, span); + const start = getParentNodeInSpan(getTokenAtPosition(sourceFile, span.start), sourceFile, span); // Do the same for the ending position const end = getParentNodeInSpan(findTokenOnLeftOfPosition(sourceFile, textSpanEnd(span)), sourceFile, span); diff --git a/src/services/refactors/generateGetAccessorAndSetAccessor.ts b/src/services/refactors/generateGetAccessorAndSetAccessor.ts index b7240f2e98..e39eb20f8d 100644 --- a/src/services/refactors/generateGetAccessorAndSetAccessor.ts +++ b/src/services/refactors/generateGetAccessorAndSetAccessor.ts @@ -119,7 +119,7 @@ namespace ts.refactor.generateGetAccessorAndSetAccessor { function getConvertibleFieldAtPosition(context: RefactorContext): Info | undefined { const { file, startPosition, endPosition } = context; - const node = getTokenAtPosition(file, startPosition, /*includeJsDocComment*/ false); + const node = getTokenAtPosition(file, startPosition); const declaration = findAncestor(node.parent, isAcceptedDeclaration); // make sure declaration have AccessibilityModifier or Static Modifier or Readonly Modifier const meaning = ModifierFlags.AccessibilityModifier | ModifierFlags.Static | ModifierFlags.Readonly; diff --git a/src/services/services.ts b/src/services/services.ts index 29b3a4988b..d07f703470 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -261,7 +261,7 @@ namespace ts { } public getChildren(): Node[] { - return emptyArray; + return this.kind === SyntaxKind.EndOfFileToken ? (this as EndOfFileToken).jsDoc || emptyArray : emptyArray; } public getFirstToken(): Node | undefined { diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index 3001f5cf44..f71434fcb5 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -576,7 +576,7 @@ namespace ts.textChanges { if (index !== containingList.length - 1) { // any element except the last one // use next sibling as an anchor - const nextToken = getTokenAtPosition(sourceFile, after.end, /*includeJsDocComment*/ false); + const nextToken = getTokenAtPosition(sourceFile, after.end); if (nextToken && isSeparator(after, nextToken)) { // for list // a, b, c diff --git a/src/services/utilities.ts b/src/services/utilities.ts index eb710b28dc..ff44076b8a 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -665,19 +665,14 @@ namespace ts { } /** Returns a token if position is in [start-of-leading-trivia, end) */ - export function getTokenAtPosition(sourceFile: SourceFile, position: number, includeJsDocComment: boolean, includeEndPosition = false): Node { - return getTokenAtPositionWorker(sourceFile, position, /*allowPositionInLeadingTrivia*/ true, /*includePrecedingTokenAtEndPosition*/ undefined, includeEndPosition, includeJsDocComment); + export function getTokenAtPosition(sourceFile: SourceFile, position: number): Node { + return getTokenAtPositionWorker(sourceFile, position, /*allowPositionInLeadingTrivia*/ true, /*includePrecedingTokenAtEndPosition*/ undefined, /*includeEndPosition*/ false, /*includeJsDocComment*/ true); } /** Get the token whose text contains the position */ function getTokenAtPositionWorker(sourceFile: SourceFile, position: number, allowPositionInLeadingTrivia: boolean, includePrecedingTokenAtEndPosition: ((n: Node) => boolean) | undefined, includeEndPosition: boolean, includeJsDocComment: boolean): Node { let current: Node = sourceFile; outer: while (true) { - if (isToken(current)) { - // exit early - return current; - } - // find the child that contains 'position' for (const child of current.getChildren()) { if (!includeJsDocComment && isJSDocNode(child)) { @@ -718,7 +713,7 @@ namespace ts { export function findTokenOnLeftOfPosition(file: SourceFile, position: number): Node | undefined { // Ideally, getTokenAtPosition should return a token. However, it is currently // broken, so we do a check to make sure the result was indeed a token. - const tokenAtPosition = getTokenAtPosition(file, position, /*includeJsDocComment*/ false); + const tokenAtPosition = getTokenAtPosition(file, position); if (isToken(tokenAtPosition) && position > tokenAtPosition.getStart(file) && position < tokenAtPosition.getEnd()) { return tokenAtPosition; } @@ -861,7 +856,7 @@ namespace ts { * returns true if the position is in between the open and close elements of an JSX expression. */ export function isInsideJsxElementOrAttribute(sourceFile: SourceFile, position: number) { - const token = getTokenAtPosition(sourceFile, position, /*includeJsDocComment*/ false); + const token = getTokenAtPosition(sourceFile, position); if (!token) { return false; @@ -901,7 +896,7 @@ namespace ts { } export function isInTemplateString(sourceFile: SourceFile, position: number) { - const token = getTokenAtPosition(sourceFile, position, /*includeJsDocComment*/ false); + const token = getTokenAtPosition(sourceFile, position); return isTemplateLiteralKind(token.kind) && position > token.getStart(sourceFile); } @@ -1037,18 +1032,9 @@ namespace ts { return !!formatting.getRangeOfEnclosingComment(sourceFile, position, /*onlyMultiLine*/ false, /*precedingToken*/ undefined, tokenAtPosition, predicate); } - export function hasDocComment(sourceFile: SourceFile, position: number) { - const token = getTokenAtPosition(sourceFile, position, /*includeJsDocComment*/ false); - - // First, we have to see if this position actually landed in a comment. - const commentRanges = getLeadingCommentRanges(sourceFile.text, token.pos); - - return forEach(commentRanges, jsDocPrefix); - - function jsDocPrefix(c: CommentRange): boolean { - const text = sourceFile.text; - return text.length >= c.pos + 3 && text[c.pos] === "/" && text[c.pos + 1] === "*" && text[c.pos + 2] === "*"; - } + export function hasDocComment(sourceFile: SourceFile, position: number): boolean { + const token = getTokenAtPosition(sourceFile, position); + return !!findAncestor(token, isJSDoc); } function nodeHasTokens(n: Node, sourceFile: SourceFileLike): boolean { diff --git a/src/testRunner/unittests/tsbuild.ts b/src/testRunner/unittests/tsbuild.ts index bd40c7297d..f9e030ba3a 100644 --- a/src/testRunner/unittests/tsbuild.ts +++ b/src/testRunner/unittests/tsbuild.ts @@ -261,20 +261,22 @@ namespace ts { }); describe("tsbuild - downstream prepend projects always get rebuilt", () => { - const fs = outFileFs.shadow(); - const host = new fakes.CompilerHost(fs); - const builder = createSolutionBuilder(host, buildHost, ["/src/third"], { dry: false, force: false, verbose: false }); - clearDiagnostics(); - builder.buildAllProjects(); - assertDiagnosticMessages(/*none*/); - assert.equal(fs.statSync("src/third/thirdjs/output/third-output.js").mtimeMs, time(), "First build timestamp is correct"); - tick(); - replaceText(fs, "src/first/first_PART1.ts", "Hello", "Hola"); - tick(); - builder.resetBuildContext(); - builder.buildAllProjects(); - assertDiagnosticMessages(/*none*/); - assert.equal(fs.statSync("src/third/thirdjs/output/third-output.js").mtimeMs, time(), "Second build timestamp is correct"); + it("", () => { + const fs = outFileFs.shadow(); + const host = new fakes.CompilerHost(fs); + const builder = createSolutionBuilder(host, buildHost, ["/src/third"], { dry: false, force: false, verbose: false }); + clearDiagnostics(); + builder.buildAllProjects(); + assertDiagnosticMessages(/*none*/); + assert.equal(fs.statSync("src/third/thirdjs/output/third-output.js").mtimeMs, time(), "First build timestamp is correct"); + tick(); + replaceText(fs, "src/first/first_PART1.ts", "Hello", "Hola"); + tick(); + builder.resetBuildContext(); + builder.buildAllProjects(); + assertDiagnosticMessages(/*none*/); + assert.equal(fs.statSync("src/third/thirdjs/output/third-output.js").mtimeMs, time(), "Second build timestamp is correct"); + }); }); } diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 89214bdb96..89bde088da 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -10743,7 +10743,7 @@ declare namespace ts { */ function getTouchingToken(sourceFile: SourceFile, position: number, includeJsDocComment: boolean, includePrecedingTokenAtEndPosition?: (n: Node) => boolean): Node; /** Returns a token if position is in [start-of-leading-trivia, end) */ - function getTokenAtPosition(sourceFile: SourceFile, position: number, includeJsDocComment: boolean, includeEndPosition?: boolean): Node; + function getTokenAtPosition(sourceFile: SourceFile, position: number): Node; /** * The token on the left of the position is the token that strictly includes the position * or sits to the left of the cursor if it is on a boundary. For example @@ -10778,7 +10778,7 @@ declare namespace ts { * @param predicate Additional predicate to test on the comment range. */ function isInComment(sourceFile: SourceFile, position: number, tokenAtPosition?: Node, predicate?: (c: CommentRange) => boolean): boolean; - function hasDocComment(sourceFile: SourceFile, position: number): boolean | undefined; + function hasDocComment(sourceFile: SourceFile, position: number): boolean; function getNodeModifiers(node: Node): string; function getTypeArgumentOrTypeParameterList(node: Node): NodeArray | undefined; function isComment(kind: SyntaxKind): boolean; diff --git a/tests/baselines/reference/callbackTag1.types b/tests/baselines/reference/callbackTag1.types index 2f1e64c8ed..8438da1a6a 100644 --- a/tests/baselines/reference/callbackTag1.types +++ b/tests/baselines/reference/callbackTag1.types @@ -19,7 +19,7 @@ var sid = s => s + "!"; /** @type {NoReturn} */ var noreturn = obj => void obj.title ->noreturn : (s: { e: number; m: number; title: string; }) => any +>noreturn : NoReturn >obj => void obj.title : (obj: { e: number; m: number; title: string; }) => any >obj : { e: number; m: number; title: string; } >void obj.title : undefined diff --git a/tests/baselines/reference/callbackTag2.types b/tests/baselines/reference/callbackTag2.types index 5a13438ebb..953d7d335a 100644 --- a/tests/baselines/reference/callbackTag2.types +++ b/tests/baselines/reference/callbackTag2.types @@ -42,7 +42,7 @@ var outside = n => n + 1; /** @type {Final<{ fantasy }, { heroes }>} */ var noreturn = (barts, tidus, noctis) => "cecil" ->noreturn : (barts: { fantasy: any; }, tidus: { heroes: any; }, noctis: { heroes: any; } & { fantasy: any; }) => "cecil" | "zidane" +>noreturn : Final<{ fantasy: any; }, { heroes: any; }> >(barts, tidus, noctis) => "cecil" : (barts: { fantasy: any; }, tidus: { heroes: any; }, noctis: { heroes: any; } & { fantasy: any; }) => "cecil" >barts : { fantasy: any; } >tidus : { heroes: any; }