From 380d740bcb102694f3604839bc23133288d492c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sun, 28 Jul 2024 10:20:09 +0200 Subject: [PATCH 1/2] Hide property access completions of `@ignore`d properties --- src/compiler/checker.ts | 5 ++++- src/compiler/utilitiesPublic.ts | 7 +++++-- .../fourslash/completionsIgnoreTagOnProperty1.ts | 16 ++++++++++++++++ 3 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 tests/cases/fourslash/completionsIgnoreTagOnProperty1.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f18a0b2a32a23..942d3fe908c97 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -303,6 +303,7 @@ import { getExternalModuleRequireArgument, getFirstConstructorWithBody, getFirstIdentifier, + getFirstJSDocTag, getFunctionFlags, getHostSignatureFromJSDoc, getIdentifierGeneratedImportReference, @@ -805,6 +806,7 @@ import { JSDocPublicTag, JSDocSatisfiesTag, JSDocSignature, + JSDocTag, JSDocTemplateTag, JSDocThisTag, JSDocTypeAssertion, @@ -34489,7 +34491,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { * @param property the accessed property's symbol. */ function isValidPropertyAccessForCompletions(node: PropertyAccessExpression | ImportTypeNode | QualifiedName, type: Type, property: Symbol): boolean { - return isPropertyAccessible(node, node.kind === SyntaxKind.PropertyAccessExpression && node.expression.kind === SyntaxKind.SuperKeyword, /*isWrite*/ false, type, property); + return isPropertyAccessible(node, node.kind === SyntaxKind.PropertyAccessExpression && node.expression.kind === SyntaxKind.SuperKeyword, /*isWrite*/ false, type, property) + && (!property.valueDeclaration || !getFirstJSDocTag(property.valueDeclaration, (t): t is JSDocTag => t.kind === SyntaxKind.JSDocTag && t.tagName.escapedText === "ignore")); // Previously we validated the 'this' type of methods but this adversely affected performance. See #31377 for more context. } diff --git a/src/compiler/utilitiesPublic.ts b/src/compiler/utilitiesPublic.ts index dfc3f2d069bde..b3f4c2eacf83e 100644 --- a/src/compiler/utilitiesPublic.ts +++ b/src/compiler/utilitiesPublic.ts @@ -1260,8 +1260,11 @@ export function getJSDocTags(node: Node): readonly JSDocTag[] { return getJSDocTagsWorker(node, /*noCache*/ false); } -/** Get the first JSDoc tag of a specified kind, or undefined if not present. */ -function getFirstJSDocTag(node: Node, predicate: (tag: JSDocTag) => tag is T, noCache?: boolean): T | undefined { +/** + * @internal + * Get the first JSDoc tag of a specified kind, or undefined if not present. + */ +export function getFirstJSDocTag(node: Node, predicate: (tag: JSDocTag) => tag is T, noCache?: boolean): T | undefined { return find(getJSDocTagsWorker(node, noCache), predicate); } diff --git a/tests/cases/fourslash/completionsIgnoreTagOnProperty1.ts b/tests/cases/fourslash/completionsIgnoreTagOnProperty1.ts new file mode 100644 index 0000000000000..a422d329e3f87 --- /dev/null +++ b/tests/cases/fourslash/completionsIgnoreTagOnProperty1.ts @@ -0,0 +1,16 @@ +/// + +//// type Foo = { +//// /** @ignore */ +//// a: string; +//// b: number; +//// }; +//// +//// declare const foo: Foo; +//// foo./**/ + +verify.completions({ + marker: "", + exact: "b", + isNewIdentifierLocation: false, +}); From 3696f9464fa0b07b3d9e9b1b37ce255900df2df4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sun, 28 Jul 2024 18:21:44 +0200 Subject: [PATCH 2/2] introduce `JSDocIgnoreTag` --- src/compiler/checker.ts | 5 +- src/compiler/emitter.ts | 1 + src/compiler/factory/nodeFactory.ts | 9 +++ src/compiler/factory/nodeTests.ts | 5 ++ src/compiler/parser.ts | 7 +- src/compiler/types.ts | 8 +++ src/compiler/utilitiesPublic.ts | 14 ++-- tests/baselines/reference/api/typescript.d.ts | 65 +++++++++++-------- 8 files changed, 77 insertions(+), 37 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 942d3fe908c97..ff89c409b15f5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -303,7 +303,6 @@ import { getExternalModuleRequireArgument, getFirstConstructorWithBody, getFirstIdentifier, - getFirstJSDocTag, getFunctionFlags, getHostSignatureFromJSDoc, getIdentifierGeneratedImportReference, @@ -317,6 +316,7 @@ import { getJSDocDeprecatedTag, getJSDocEnumTag, getJSDocHost, + getJSDocIgnoreTag, getJSDocOverloadTags, getJSDocParameterTags, getJSDocRoot, @@ -806,7 +806,6 @@ import { JSDocPublicTag, JSDocSatisfiesTag, JSDocSignature, - JSDocTag, JSDocTemplateTag, JSDocThisTag, JSDocTypeAssertion, @@ -34492,7 +34491,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { */ function isValidPropertyAccessForCompletions(node: PropertyAccessExpression | ImportTypeNode | QualifiedName, type: Type, property: Symbol): boolean { return isPropertyAccessible(node, node.kind === SyntaxKind.PropertyAccessExpression && node.expression.kind === SyntaxKind.SuperKeyword, /*isWrite*/ false, type, property) - && (!property.valueDeclaration || !getFirstJSDocTag(property.valueDeclaration, (t): t is JSDocTag => t.kind === SyntaxKind.JSDocTag && t.tagName.escapedText === "ignore")); + && (!property.declarations || some(property.declarations, decl => !getJSDocIgnoreTag(decl))); // Previously we validated the 'this' type of methods but this adversely affected performance. See #31377 for more context. } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 6f68329ba0a90..f365692f1f8cf 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1846,6 +1846,7 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri return emitJSDocHeritageTag(node as JSDocImplementsTag | JSDocAugmentsTag); case SyntaxKind.JSDocAuthorTag: case SyntaxKind.JSDocDeprecatedTag: + case SyntaxKind.JSDocIgnoreTag: return; // SyntaxKind.JSDocClassTag (see JSDocTag, above) case SyntaxKind.JSDocPublicTag: diff --git a/src/compiler/factory/nodeFactory.ts b/src/compiler/factory/nodeFactory.ts index c90e4c71f19f5..7ba84cd3a3c3a 100644 --- a/src/compiler/factory/nodeFactory.ts +++ b/src/compiler/factory/nodeFactory.ts @@ -225,6 +225,7 @@ import { JSDocDeprecatedTag, JSDocEnumTag, JSDocFunctionType, + JSDocIgnoreTag, JSDocImplementsTag, JSDocImportTag, JSDocLink, @@ -938,6 +939,12 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode get updateJSDocDeprecatedTag() { return getJSDocSimpleTagUpdateFunction(SyntaxKind.JSDocDeprecatedTag); }, + get createJSDocIgnoreTag() { + return getJSDocSimpleTagCreateFunction(SyntaxKind.JSDocIgnoreTag); + }, + get updateJSDocIgnoreTag() { + return getJSDocSimpleTagUpdateFunction(SyntaxKind.JSDocIgnoreTag); + }, get createJSDocThrowsTag() { return getJSDocTypeLikeTagCreateFunction(SyntaxKind.JSDocThrowsTag); }, @@ -5448,6 +5455,7 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode // createJSDocProtectedTag // createJSDocReadonlyTag // createJSDocDeprecatedTag + // createJSDocIgnoreTag function createJSDocSimpleTagWorker(kind: T["kind"], tagName: Identifier | undefined, comment?: string | NodeArray) { const node = createBaseJSDocTag(kind, tagName ?? createIdentifier(getDefaultTagNameForKind(kind)), comment); return node; @@ -5461,6 +5469,7 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode // updateJSDocProtectedTag // updateJSDocReadonlyTag // updateJSDocDeprecatedTag + // updateJSDocIgnoreTag function updateJSDocSimpleTagWorker(kind: T["kind"], node: T, tagName: Identifier = getDefaultTagName(node), comment: string | NodeArray | undefined) { return node.tagName !== tagName || node.comment !== comment diff --git a/src/compiler/factory/nodeTests.ts b/src/compiler/factory/nodeTests.ts index 8aa4bb02e83d2..f354ade7ee3fa 100644 --- a/src/compiler/factory/nodeTests.ts +++ b/src/compiler/factory/nodeTests.ts @@ -90,6 +90,7 @@ import { JSDocDeprecatedTag, JSDocEnumTag, JSDocFunctionType, + JSDocIgnoreTag, JSDocImplementsTag, JSDocImportTag, JSDocLink, @@ -1137,6 +1138,10 @@ export function isJSDocDeprecatedTag(node: Node): node is JSDocDeprecatedTag { return node.kind === SyntaxKind.JSDocDeprecatedTag; } +export function isJSDocIgnoreTag(node: Node): node is JSDocIgnoreTag { + return node.kind === SyntaxKind.JSDocIgnoreTag; +} + export function isJSDocSeeTag(node: Node): node is JSDocSeeTag { return node.kind === SyntaxKind.JSDocSeeTag; } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index b20d4b8b8b52e..3647054a2437c 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -176,6 +176,7 @@ import { JSDocDeprecatedTag, JSDocEnumTag, JSDocFunctionType, + JSDocIgnoreTag, JSDocImplementsTag, JSDocImportTag, JSDocLink, @@ -1129,6 +1130,7 @@ const forEachChildTable: ForEachChildTable = { [SyntaxKind.JSDocProtectedTag]: forEachChildInJSDocTag, [SyntaxKind.JSDocReadonlyTag]: forEachChildInJSDocTag, [SyntaxKind.JSDocDeprecatedTag]: forEachChildInJSDocTag, + [SyntaxKind.JSDocIgnoreTag]: forEachChildInJSDocTag, [SyntaxKind.JSDocOverrideTag]: forEachChildInJSDocTag, [SyntaxKind.JSDocImportTag]: forEachChildInJSDocImportTag, [SyntaxKind.PartiallyEmittedExpression]: forEachChildInPartiallyEmittedExpression, @@ -1215,7 +1217,7 @@ function forEachChildInJSDocLinkCodeOrPlain(node: JSDocLink | JSDocLinkCode | return visitNode(cbNode, node.name); } -function forEachChildInJSDocTag(node: JSDocUnknownTag | JSDocClassTag | JSDocPublicTag | JSDocPrivateTag | JSDocProtectedTag | JSDocReadonlyTag | JSDocDeprecatedTag | JSDocOverrideTag, cbNode: (node: Node) => T | undefined, cbNodes?: (nodes: NodeArray) => T | undefined): T | undefined { +function forEachChildInJSDocTag(node: JSDocUnknownTag | JSDocClassTag | JSDocPublicTag | JSDocPrivateTag | JSDocProtectedTag | JSDocReadonlyTag | JSDocDeprecatedTag | JSDocIgnoreTag | JSDocOverrideTag, cbNode: (node: Node) => T | undefined, cbNodes?: (nodes: NodeArray) => T | undefined): T | undefined { return visitNode(cbNode, node.tagName) || (typeof node.comment === "string" ? undefined : visitNodes(cbNode, cbNodes, node.comment)); } @@ -9080,6 +9082,9 @@ namespace Parser { hasDeprecatedTag = true; tag = parseSimpleTag(start, factory.createJSDocDeprecatedTag, tagName, margin, indentText); break; + case "ignore": + tag = parseSimpleTag(start, factory.createJSDocIgnoreTag, tagName, margin, indentText); + break; case "this": tag = parseThisTag(start, tagName, margin, indentText); break; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 564fa64e1294c..561774f1a9ccf 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -419,6 +419,7 @@ export const enum SyntaxKind { JSDocImplementsTag, JSDocAuthorTag, JSDocDeprecatedTag, + JSDocIgnoreTag, JSDocClassTag, JSDocPublicTag, JSDocPrivateTag, @@ -1046,6 +1047,7 @@ export type ForEachChildNodes = | JSDocProtectedTag | JSDocReadonlyTag | JSDocDeprecatedTag + | JSDocIgnoreTag | JSDocThrowsTag | JSDocOverrideTag | JSDocSatisfiesTag @@ -3986,6 +3988,10 @@ export interface JSDocDeprecatedTag extends JSDocTag { kind: SyntaxKind.JSDocDeprecatedTag; } +export interface JSDocIgnoreTag extends JSDocTag { + kind: SyntaxKind.JSDocIgnoreTag; +} + export interface JSDocClassTag extends JSDocTag { readonly kind: SyntaxKind.JSDocClassTag; } @@ -9042,6 +9048,8 @@ export interface NodeFactory { updateJSDocUnknownTag(node: JSDocUnknownTag, tagName: Identifier, comment: string | NodeArray | undefined): JSDocUnknownTag; createJSDocDeprecatedTag(tagName: Identifier | undefined, comment?: string | NodeArray): JSDocDeprecatedTag; updateJSDocDeprecatedTag(node: JSDocDeprecatedTag, tagName: Identifier | undefined, comment?: string | NodeArray): JSDocDeprecatedTag; + createJSDocIgnoreTag(tagName: Identifier | undefined, comment?: string | NodeArray): JSDocIgnoreTag; + updateJSDocIgnoreTag(node: JSDocIgnoreTag, tagName: Identifier | undefined, comment?: string | NodeArray): JSDocIgnoreTag; createJSDocOverrideTag(tagName: Identifier | undefined, comment?: string | NodeArray): JSDocOverrideTag; updateJSDocOverrideTag(node: JSDocOverrideTag, tagName: Identifier | undefined, comment?: string | NodeArray): JSDocOverrideTag; createJSDocThrowsTag(tagName: Identifier, typeExpression: JSDocTypeExpression | undefined, comment?: string | NodeArray): JSDocThrowsTag; diff --git a/src/compiler/utilitiesPublic.ts b/src/compiler/utilitiesPublic.ts index b3f4c2eacf83e..646e19edafe31 100644 --- a/src/compiler/utilitiesPublic.ts +++ b/src/compiler/utilitiesPublic.ts @@ -140,6 +140,7 @@ import { isJSDocDeprecatedTag, isJSDocEnumTag, isJSDocFunctionType, + isJSDocIgnoreTag, isJSDocImplementsTag, isJSDocOverloadTag, isJSDocOverrideTag, @@ -183,6 +184,7 @@ import { JSDocContainer, JSDocDeprecatedTag, JSDocEnumTag, + JSDocIgnoreTag, JSDocImplementsTag, JSDocLink, JSDocLinkCode, @@ -1161,6 +1163,11 @@ export function getJSDocDeprecatedTagNoCache(node: Node): JSDocDeprecatedTag | u return getFirstJSDocTag(node, isJSDocDeprecatedTag, /*noCache*/ true); } +/** Gets the JSDoc ignore tag for the node if present */ +export function getJSDocIgnoreTag(node: Node): JSDocIgnoreTag | undefined { + return getFirstJSDocTag(node, isJSDocIgnoreTag); +} + /** Gets the JSDoc enum tag for the node if present */ export function getJSDocEnumTag(node: Node): JSDocEnumTag | undefined { return getFirstJSDocTag(node, isJSDocEnumTag); @@ -1260,11 +1267,8 @@ export function getJSDocTags(node: Node): readonly JSDocTag[] { return getJSDocTagsWorker(node, /*noCache*/ false); } -/** - * @internal - * Get the first JSDoc tag of a specified kind, or undefined if not present. - */ -export function getFirstJSDocTag(node: Node, predicate: (tag: JSDocTag) => tag is T, noCache?: boolean): T | undefined { +/** Get the first JSDoc tag of a specified kind, or undefined if not present. */ +function getFirstJSDocTag(node: Node, predicate: (tag: JSDocTag) => tag is T, noCache?: boolean): T | undefined { return find(getJSDocTagsWorker(node, noCache), predicate); } diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 8275e4222820c..1dbc0110ae4f0 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3960,32 +3960,33 @@ declare namespace ts { JSDocImplementsTag = 329, JSDocAuthorTag = 330, JSDocDeprecatedTag = 331, - JSDocClassTag = 332, - JSDocPublicTag = 333, - JSDocPrivateTag = 334, - JSDocProtectedTag = 335, - JSDocReadonlyTag = 336, - JSDocOverrideTag = 337, - JSDocCallbackTag = 338, - JSDocOverloadTag = 339, - JSDocEnumTag = 340, - JSDocParameterTag = 341, - JSDocReturnTag = 342, - JSDocThisTag = 343, - JSDocTypeTag = 344, - JSDocTemplateTag = 345, - JSDocTypedefTag = 346, - JSDocSeeTag = 347, - JSDocPropertyTag = 348, - JSDocThrowsTag = 349, - JSDocSatisfiesTag = 350, - JSDocImportTag = 351, - SyntaxList = 352, - NotEmittedStatement = 353, - PartiallyEmittedExpression = 354, - CommaListExpression = 355, - SyntheticReferenceExpression = 356, - Count = 357, + JSDocIgnoreTag = 332, + JSDocClassTag = 333, + JSDocPublicTag = 334, + JSDocPrivateTag = 335, + JSDocProtectedTag = 336, + JSDocReadonlyTag = 337, + JSDocOverrideTag = 338, + JSDocCallbackTag = 339, + JSDocOverloadTag = 340, + JSDocEnumTag = 341, + JSDocParameterTag = 342, + JSDocReturnTag = 343, + JSDocThisTag = 344, + JSDocTypeTag = 345, + JSDocTemplateTag = 346, + JSDocTypedefTag = 347, + JSDocSeeTag = 348, + JSDocPropertyTag = 349, + JSDocThrowsTag = 350, + JSDocSatisfiesTag = 351, + JSDocImportTag = 352, + SyntaxList = 353, + NotEmittedStatement = 354, + PartiallyEmittedExpression = 355, + CommaListExpression = 356, + SyntheticReferenceExpression = 357, + Count = 358, FirstAssignment = 64, LastAssignment = 79, FirstCompoundAssignment = 65, @@ -4014,9 +4015,9 @@ declare namespace ts { LastStatement = 259, FirstNode = 166, FirstJSDocNode = 309, - LastJSDocNode = 351, + LastJSDocNode = 352, FirstJSDocTagNode = 327, - LastJSDocTagNode = 351, + LastJSDocTagNode = 352, } type TriviaSyntaxKind = SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia | SyntaxKind.NewLineTrivia | SyntaxKind.WhitespaceTrivia | SyntaxKind.ShebangTrivia | SyntaxKind.ConflictMarkerTrivia; type LiteralSyntaxKind = SyntaxKind.NumericLiteral | SyntaxKind.BigIntLiteral | SyntaxKind.StringLiteral | SyntaxKind.JsxText | SyntaxKind.JsxTextAllWhiteSpaces | SyntaxKind.RegularExpressionLiteral | SyntaxKind.NoSubstitutionTemplateLiteral; @@ -5721,6 +5722,9 @@ declare namespace ts { interface JSDocDeprecatedTag extends JSDocTag { kind: SyntaxKind.JSDocDeprecatedTag; } + interface JSDocIgnoreTag extends JSDocTag { + kind: SyntaxKind.JSDocIgnoreTag; + } interface JSDocClassTag extends JSDocTag { readonly kind: SyntaxKind.JSDocClassTag; } @@ -7735,6 +7739,8 @@ declare namespace ts { updateJSDocUnknownTag(node: JSDocUnknownTag, tagName: Identifier, comment: string | NodeArray | undefined): JSDocUnknownTag; createJSDocDeprecatedTag(tagName: Identifier | undefined, comment?: string | NodeArray): JSDocDeprecatedTag; updateJSDocDeprecatedTag(node: JSDocDeprecatedTag, tagName: Identifier | undefined, comment?: string | NodeArray): JSDocDeprecatedTag; + createJSDocIgnoreTag(tagName: Identifier | undefined, comment?: string | NodeArray): JSDocIgnoreTag; + updateJSDocIgnoreTag(node: JSDocIgnoreTag, tagName: Identifier | undefined, comment?: string | NodeArray): JSDocIgnoreTag; createJSDocOverrideTag(tagName: Identifier | undefined, comment?: string | NodeArray): JSDocOverrideTag; updateJSDocOverrideTag(node: JSDocOverrideTag, tagName: Identifier | undefined, comment?: string | NodeArray): JSDocOverrideTag; createJSDocThrowsTag(tagName: Identifier, typeExpression: JSDocTypeExpression | undefined, comment?: string | NodeArray): JSDocThrowsTag; @@ -8588,6 +8594,8 @@ declare namespace ts { function getJSDocOverrideTagNoCache(node: Node): JSDocOverrideTag | undefined; /** Gets the JSDoc deprecated tag for the node if present */ function getJSDocDeprecatedTag(node: Node): JSDocDeprecatedTag | undefined; + /** Gets the JSDoc ignore tag for the node if present */ + function getJSDocIgnoreTag(node: Node): JSDocIgnoreTag | undefined; /** Gets the JSDoc enum tag for the node if present */ function getJSDocEnumTag(node: Node): JSDocEnumTag | undefined; /** Gets the JSDoc this tag for the node if present */ @@ -9032,6 +9040,7 @@ declare namespace ts { function isJSDocOverrideTag(node: Node): node is JSDocOverrideTag; function isJSDocOverloadTag(node: Node): node is JSDocOverloadTag; function isJSDocDeprecatedTag(node: Node): node is JSDocDeprecatedTag; + function isJSDocIgnoreTag(node: Node): node is JSDocIgnoreTag; function isJSDocSeeTag(node: Node): node is JSDocSeeTag; function isJSDocEnumTag(node: Node): node is JSDocEnumTag; function isJSDocParameterTag(node: Node): node is JSDocParameterTag;