Skip to content

Commit 38784b7

Browse files
author
Andy Hanson
committed
Support for JSDoc in services
1 parent 1328cb8 commit 38784b7

32 files changed

+156
-81
lines changed

src/compiler/binder.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -603,7 +603,7 @@ namespace ts {
603603
// Binding of JsDocComment should be done before the current block scope container changes.
604604
// because the scope of JsDocComment should not be affected by whether the current node is a
605605
// container or not.
606-
if (isInJavaScriptFile(node) && node.jsDoc) {
606+
if (node.jsDoc) {
607607
forEach(node.jsDoc, bind);
608608
}
609609
if (checkUnreachable(node)) {

src/compiler/checker.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22213,6 +22213,11 @@ namespace ts {
2221322213
}
2221422214
}
2221522215

22216+
if (entityName.parent!.kind === SyntaxKind.JSDocParameterTag) {
22217+
const parameter = ts.getParameterFromJSDoc(entityName.parent as JSDocParameterTag);
22218+
return parameter && parameter.symbol;
22219+
}
22220+
2221622221
if (isPartOfExpression(entityName)) {
2221722222
if (nodeIsMissing(entityName)) {
2221822223
// Missing entity name.

src/compiler/parser.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ namespace ts {
5050
// stored in properties. If a 'cbNodes' callback is specified, it is invoked for embedded arrays; otherwise,
5151
// embedded arrays are flattened and the 'cbNode' callback is invoked for each element. If a callback returns
5252
// a truthy value, iteration stops and that value is returned. Otherwise, undefined is returned.
53-
export function forEachChild<T>(node: Node, cbNode: (node: Node) => T, cbNodeArray?: (nodes: Node[]) => T): T {
53+
export function forEachChild<T>(node: Node, cbNode: (node: Node) => T, cbNodeArray?: (nodes: NodeArray<Node>) => T): T {
5454
if (!node) {
5555
return;
5656
}

src/compiler/utilities.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1591,7 +1591,7 @@ namespace ts {
15911591

15921592
// Pull parameter comments from declaring function as well
15931593
if (node.kind === SyntaxKind.Parameter) {
1594-
cache = concatenate(cache, getJSDocParameterTags(node));
1594+
cache = concatenate(cache, getJSDocParameterTags(node as ParameterDeclaration));
15951595
}
15961596

15971597
if (isVariableLike(node) && node.initializer) {
@@ -1602,11 +1602,8 @@ namespace ts {
16021602
}
16031603
}
16041604

1605-
export function getJSDocParameterTags(param: Node): JSDocParameterTag[] {
1606-
if (!isParameter(param)) {
1607-
return undefined;
1608-
}
1609-
const func = param.parent as FunctionLikeDeclaration;
1605+
export function getJSDocParameterTags(param: ParameterDeclaration): JSDocParameterTag[] {
1606+
const func = param.parent;
16101607
const tags = getJSDocTags(func, SyntaxKind.JSDocParameterTag) as JSDocParameterTag[];
16111608
if (!param.name) {
16121609
// this is an anonymous jsdoc param from a `function(type1, type2): type3` specification
@@ -1627,10 +1624,22 @@ namespace ts {
16271624
}
16281625
}
16291626

1627+
/** Does the opposite of `getJSDocParameterTags`: given a JSDoc parameter, finds the parameter corresponding to it. */
1628+
export function getParameterFromJSDoc(node: JSDocParameterTag): ParameterDeclaration | undefined {
1629+
const name = node.parameterName.text;
1630+
const grandParent = node.parent!.parent!;
1631+
Debug.assert(node.parent!.kind === SyntaxKind.JSDocComment);
1632+
if (!isFunctionLike(grandParent)) {
1633+
return undefined;
1634+
}
1635+
return find(grandParent.parameters, p =>
1636+
p.name.kind === SyntaxKind.Identifier && p.name.text === name);
1637+
}
1638+
16301639
export function getJSDocType(node: Node): JSDocType {
16311640
let tag: JSDocTypeTag | JSDocParameterTag = getFirstJSDocTag(node, SyntaxKind.JSDocTypeTag) as JSDocTypeTag;
16321641
if (!tag && node.kind === SyntaxKind.Parameter) {
1633-
const paramTags = getJSDocParameterTags(node);
1642+
const paramTags = getJSDocParameterTags(node as ParameterDeclaration);
16341643
if (paramTags) {
16351644
tag = find(paramTags, tag => !!tag.typeExpression);
16361645
}

src/harness/fourslash.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -916,7 +916,7 @@ namespace FourSlash {
916916
}
917917

918918
private getNode(): ts.Node {
919-
return ts.getTouchingPropertyName(this.getSourceFile(), this.currentCaretPosition);
919+
return ts.getTouchingPropertyName(this.getSourceFile(), this.currentCaretPosition, /*includeJsDocComment*/ false);
920920
}
921921

922922
private goToAndGetNode(range: Range): ts.Node {
@@ -994,17 +994,15 @@ namespace FourSlash {
994994
}
995995

996996
public verifyReferenceGroups(startRanges: Range | Range[], parts: Array<{ definition: string, ranges: Range[] }>): void {
997-
interface ReferenceJson { definition: string; ranges: ts.ReferenceEntry[]; }
998-
type ReferencesJson = ReferenceJson[];
999-
const fullExpected = parts.map<ReferenceJson>(({ definition, ranges }) => ({ definition, ranges: ranges.map(rangeToReferenceEntry) }));
997+
const fullExpected = ts.map(parts, ({ definition, ranges }) => ({ definition, ranges: ranges.map(rangeToReferenceEntry) }));
1000998

1001999
for (const startRange of toArray(startRanges)) {
10021000
this.goToRangeStart(startRange);
1003-
const fullActual = ts.map<ts.ReferencedSymbol, ReferenceJson>(this.findReferencesAtCaret(), ({ definition, references }) => ({
1001+
const fullActual = ts.map(this.findReferencesAtCaret(), ({ definition, references }) => ({
10041002
definition: definition.displayParts.map(d => d.text).join(""),
10051003
ranges: references
10061004
}));
1007-
this.assertObjectsEqual<ReferencesJson>(fullActual, fullExpected);
1005+
this.assertObjectsEqual(fullActual, fullExpected);
10081006
}
10091007

10101008
function rangeToReferenceEntry(r: Range): ts.ReferenceEntry {
@@ -1047,6 +1045,10 @@ namespace FourSlash {
10471045
this.raiseError(`${msgPrefix}At ${path}: ${msg}`);
10481046
};
10491047

1048+
if ((actual === undefined) !== (expected === undefined)) {
1049+
fail(`Expected ${expected}, got ${actual}`);
1050+
}
1051+
10501052
for (const key in actual) if (ts.hasProperty(actual as any, key)) {
10511053
const ak = actual[key], ek = expected[key];
10521054
if (typeof ak === "object" && typeof ek === "object") {

src/harness/harness.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ namespace Utils {
172172
assert.isFalse(child.pos < currentPos, "child.pos < currentPos");
173173
currentPos = child.end;
174174
},
175-
(array: ts.NodeArray<ts.Node>) => {
175+
array => {
176176
assert.isFalse(array.pos < node.pos, "array.pos < node.pos");
177177
assert.isFalse(array.end > node.end, "array.end > node.end");
178178
assert.isFalse(array.pos < currentPos, "array.pos < currentPos");
@@ -383,7 +383,7 @@ namespace Utils {
383383

384384
assertStructuralEquals(child1, child2);
385385
},
386-
(array1: ts.NodeArray<ts.Node>) => {
386+
array1 => {
387387
const childName = findChildName(node1, array1);
388388
const array2: ts.NodeArray<ts.Node> = (<any>node2)[childName];
389389

src/services/breakpoints.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ namespace ts.BreakpointResolver {
1414
return undefined;
1515
}
1616

17-
let tokenAtLocation = getTokenAtPosition(sourceFile, position);
17+
let tokenAtLocation = getTokenAtPosition(sourceFile, position, /*includeJsDocComment*/ false);
1818
const lineOfPosition = sourceFile.getLineAndCharacterOfPosition(position).line;
1919
if (sourceFile.getLineAndCharacterOfPosition(tokenAtLocation.getStart(sourceFile)).line > lineOfPosition) {
2020
// Get previous token if the token is returned starts on new line

src/services/codefixes/disableJsDiagnostics.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ namespace ts.codefix {
2222
// We also want to check if the previous line holds a comment for a node on the next line
2323
// if so, we do not want to separate the node from its comment if we can.
2424
if (!isInComment(sourceFile, startPosition) && !isInString(sourceFile, startPosition) && !isInTemplateString(sourceFile, startPosition)) {
25-
const token = getTouchingToken(sourceFile, startPosition);
25+
const token = getTouchingToken(sourceFile, startPosition, /*includeJsDocComment*/ false);
2626
const tokenLeadingCommnets = getLeadingCommentRangesOfNode(token, sourceFile);
2727
if (!tokenLeadingCommnets || !tokenLeadingCommnets.length || tokenLeadingCommnets[0].pos >= startPosition) {
2828
return {

src/services/codefixes/fixAddMissingMember.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ namespace ts.codefix {
1313
// This is the identifier of the missing property. eg:
1414
// this.missing = 1;
1515
// ^^^^^^^
16-
const token = getTokenAtPosition(sourceFile, start);
16+
const token = getTokenAtPosition(sourceFile, start, /*includeJsDocComment*/ false);
1717

1818
if (token.kind !== SyntaxKind.Identifier) {
1919
return undefined;

src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ namespace ts.codefix {
1515
const start = context.span.start;
1616
// This is the identifier in the case of a class declaration
1717
// or the class keyword token in the case of a class expression.
18-
const token = getTokenAtPosition(sourceFile, start);
18+
const token = getTokenAtPosition(sourceFile, start, /*includeJsDocComment*/ false);
1919
const checker = context.program.getTypeChecker();
2020

2121
if (isClassLike(token.parent)) {

0 commit comments

Comments
 (0)