From 366a608fd8260ac85e4dc0aa4cfb10a401f44598 Mon Sep 17 00:00:00 2001 From: Matthew Nitschke Date: Fri, 26 Sep 2025 13:51:33 -0600 Subject: [PATCH 1/6] implemented @deprecated diagnostics reporting --- snapshots/input/diagnostics/index.ts | 10 +++++++++ snapshots/input/diagnostics/package.json | 4 ++++ snapshots/input/diagnostics/tsconfig.json | 1 + snapshots/output/diagnostics/index.ts | 26 +++++++++++++++++++++++ src/FileIndexer.ts | 22 +++++++++++++++++++ src/SnapshotTesting.ts | 12 +++++++++++ 6 files changed, 75 insertions(+) create mode 100644 snapshots/input/diagnostics/index.ts create mode 100644 snapshots/input/diagnostics/package.json create mode 100644 snapshots/input/diagnostics/tsconfig.json create mode 100644 snapshots/output/diagnostics/index.ts diff --git a/snapshots/input/diagnostics/index.ts b/snapshots/input/diagnostics/index.ts new file mode 100644 index 00000000..08f2d579 --- /dev/null +++ b/snapshots/input/diagnostics/index.ts @@ -0,0 +1,10 @@ +/** @deprecated This class is deprecated */ +class Foo {} + +/** @deprecated This function is deprecated */ +function bar() {} + +function main() { + new Foo() + bar() +} diff --git a/snapshots/input/diagnostics/package.json b/snapshots/input/diagnostics/package.json new file mode 100644 index 00000000..5dee8dc4 --- /dev/null +++ b/snapshots/input/diagnostics/package.json @@ -0,0 +1,4 @@ +{ + "name": "diagnostics", + "version": "0.0.1" +} \ No newline at end of file diff --git a/snapshots/input/diagnostics/tsconfig.json b/snapshots/input/diagnostics/tsconfig.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/snapshots/input/diagnostics/tsconfig.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/snapshots/output/diagnostics/index.ts b/snapshots/output/diagnostics/index.ts new file mode 100644 index 00000000..51d49ef9 --- /dev/null +++ b/snapshots/output/diagnostics/index.ts @@ -0,0 +1,26 @@ +// < definition diagnostics 0.0.1 `index.ts`/ + +/** @deprecated This class is deprecated */ +class Foo {} +// ^^^ definition diagnostics 0.0.1 `index.ts`/Foo# +// diagnostic Information: +// > This class is deprecated + +/** @deprecated This function is deprecated */ +function bar() {} +// ^^^ definition diagnostics 0.0.1 `index.ts`/bar(). +// diagnostic Information: +// > This function is deprecated + +function main() { +// ^^^^ definition diagnostics 0.0.1 `index.ts`/main(). + new Foo() +// ^^^ reference diagnostics 0.0.1 `index.ts`/Foo# +// diagnostic Information: +// > This class is deprecated + bar() +//^^^ reference diagnostics 0.0.1 `index.ts`/bar(). +//diagnostic Information: +//> This function is deprecated +} + diff --git a/src/FileIndexer.ts b/src/FileIndexer.ts index d1960b89..d02a5b04 100644 --- a/src/FileIndexer.ts +++ b/src/FileIndexer.ts @@ -242,6 +242,7 @@ export class FileIndexer { range, symbol: scipSymbol.value, symbol_roles: role, + diagnostics: this.diagnosticsForSymbol(sym), }) ) if (isDefinitionNode) { @@ -718,6 +719,27 @@ export class FileIndexer { } loop(node) } + + // Returns the scip diagnostics for a given typescript symbol + private diagnosticsForSymbol( + sym: ts.Symbol + ): scip.scip.Diagnostic[] | undefined { + const jsDocTags = sym.getJsDocTags() + + const deprecatedTag = jsDocTags.find(tag => tag.name === 'deprecated') + if (deprecatedTag) { + return [ + new scip.scip.Diagnostic({ + severity: scip.scip.Severity.Information, + code: 'DEPRECATED', + message: deprecatedTag.text?.map(part => part.text).join(''), + tags: [scip.scip.DiagnosticTag.Deprecated], + }), + ] + } + + return undefined + } } function isAnonymousContainerOfSymbols(node: ts.Node): boolean { diff --git a/src/SnapshotTesting.ts b/src/SnapshotTesting.ts index 8b91023c..0168ddcb 100644 --- a/src/SnapshotTesting.ts +++ b/src/SnapshotTesting.ts @@ -295,6 +295,18 @@ export function formatSnapshot( out.push(symbol.replace('\n', '|')) pushDoc(range, occurrence.symbol, isDefinition, isStartOfLine) + + if (occurrence.diagnostics && occurrence.diagnostics.length > 0) { + for (let diagnostic of occurrence.diagnostics) { + let indent = ' '.repeat(range.start.character - 2) + out.push(commentSyntax + indent) + out.push(`diagnostic ${scip.Severity[diagnostic.severity]}:\n`) + if (diagnostic.message) { + out.push(commentSyntax + indent) + out.push(`> ${diagnostic.message}\n`) + } + } + } } // Check if any enclosing ranges end on this line From 2c3f3c52ff16141e1f126af1b8c9cc7e48e99a7c Mon Sep 17 00:00:00 2001 From: Matthew Nitschke Date: Mon, 29 Sep 2025 17:54:48 -0600 Subject: [PATCH 2/6] addressed PR comments --- snapshots/input/diagnostics/index.ts | 7 +++++++ snapshots/output/diagnostics/index.ts | 12 ++++++++---- src/FileIndexer.ts | 9 ++++++++- src/SnapshotTesting.ts | 5 +++-- 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/snapshots/input/diagnostics/index.ts b/snapshots/input/diagnostics/index.ts index 08f2d579..f20b52c9 100644 --- a/snapshots/input/diagnostics/index.ts +++ b/snapshots/input/diagnostics/index.ts @@ -4,6 +4,13 @@ class Foo {} /** @deprecated This function is deprecated */ function bar() {} +/** + * @deprecated This is a function that has + * multiple lines and is also deprecated. Make + * sure to reference {@link bar} for some reason + */ +function car() {} + function main() { new Foo() bar() diff --git a/snapshots/output/diagnostics/index.ts b/snapshots/output/diagnostics/index.ts index 51d49ef9..f0328e9d 100644 --- a/snapshots/output/diagnostics/index.ts +++ b/snapshots/output/diagnostics/index.ts @@ -3,14 +3,18 @@ /** @deprecated This class is deprecated */ class Foo {} // ^^^ definition diagnostics 0.0.1 `index.ts`/Foo# -// diagnostic Information: -// > This class is deprecated /** @deprecated This function is deprecated */ function bar() {} // ^^^ definition diagnostics 0.0.1 `index.ts`/bar(). -// diagnostic Information: -// > This function is deprecated + +/** + * @deprecated This is a function that has + * multiple lines and is also deprecated. Make + * sure to reference {@link bar} for some reason + */ +function car() {} +// ^^^ definition diagnostics 0.0.1 `index.ts`/car(). function main() { // ^^^^ definition diagnostics 0.0.1 `index.ts`/main(). diff --git a/src/FileIndexer.ts b/src/FileIndexer.ts index d02a5b04..219943ab 100644 --- a/src/FileIndexer.ts +++ b/src/FileIndexer.ts @@ -242,7 +242,12 @@ export class FileIndexer { range, symbol: scipSymbol.value, symbol_roles: role, - diagnostics: this.diagnosticsForSymbol(sym), + + // Diagnostics should only be added for references to the symbol, not + // the definition + diagnostics: !isDefinitionNode + ? this.diagnosticsForSymbol(sym) + : undefined, }) ) if (isDefinitionNode) { @@ -732,6 +737,8 @@ export class FileIndexer { new scip.scip.Diagnostic({ severity: scip.scip.Severity.Information, code: 'DEPRECATED', + // jsDocInfo.text is a tokenized representation of the tag text. + // Concatenate the elements to get the full message message: deprecatedTag.text?.map(part => part.text).join(''), tags: [scip.scip.DiagnosticTag.Deprecated], }), diff --git a/src/SnapshotTesting.ts b/src/SnapshotTesting.ts index 0168ddcb..92327bb7 100644 --- a/src/SnapshotTesting.ts +++ b/src/SnapshotTesting.ts @@ -302,8 +302,9 @@ export function formatSnapshot( out.push(commentSyntax + indent) out.push(`diagnostic ${scip.Severity[diagnostic.severity]}:\n`) if (diagnostic.message) { - out.push(commentSyntax + indent) - out.push(`> ${diagnostic.message}\n`) + for (let messageLine of diagnostic.message.split('\n')) { + out.push(`${commentSyntax + indent}> ${messageLine}\n`) + } } } } From 62e1fcc660c175bcf174fddf4dd2fb9fe140fa09 Mon Sep 17 00:00:00 2001 From: Matthew Nitschke Date: Tue, 30 Sep 2025 08:36:05 -0600 Subject: [PATCH 3/6] added multiline call diagnostic --- snapshots/input/diagnostics/index.ts | 1 + snapshots/output/diagnostics/index.ts | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/snapshots/input/diagnostics/index.ts b/snapshots/input/diagnostics/index.ts index f20b52c9..4a2c7b8c 100644 --- a/snapshots/input/diagnostics/index.ts +++ b/snapshots/input/diagnostics/index.ts @@ -14,4 +14,5 @@ function car() {} function main() { new Foo() bar() + car() } diff --git a/snapshots/output/diagnostics/index.ts b/snapshots/output/diagnostics/index.ts index f0328e9d..a13960dd 100644 --- a/snapshots/output/diagnostics/index.ts +++ b/snapshots/output/diagnostics/index.ts @@ -26,5 +26,11 @@ function main() { //^^^ reference diagnostics 0.0.1 `index.ts`/bar(). //diagnostic Information: //> This function is deprecated + car() +//^^^ reference diagnostics 0.0.1 `index.ts`/car(). +//diagnostic Information: +//> This is a function that has +//> multiple lines and is also deprecated. Make +//> sure to reference {@link bar } for some reason } From b7dceb36054803e6541fa3cca6a6bf72dfd66fd8 Mon Sep 17 00:00:00 2001 From: Matthew Nitschke Date: Wed, 1 Oct 2025 08:55:27 -0600 Subject: [PATCH 4/6] fmt --- snapshots/input/diagnostics/package.json | 6 +++--- snapshots/input/diagnostics/tsconfig.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/snapshots/input/diagnostics/package.json b/snapshots/input/diagnostics/package.json index 5dee8dc4..a780d977 100644 --- a/snapshots/input/diagnostics/package.json +++ b/snapshots/input/diagnostics/package.json @@ -1,4 +1,4 @@ { - "name": "diagnostics", - "version": "0.0.1" -} \ No newline at end of file + "name": "diagnostics", + "version": "0.0.1" +} diff --git a/snapshots/input/diagnostics/tsconfig.json b/snapshots/input/diagnostics/tsconfig.json index 9e26dfee..0967ef42 100644 --- a/snapshots/input/diagnostics/tsconfig.json +++ b/snapshots/input/diagnostics/tsconfig.json @@ -1 +1 @@ -{} \ No newline at end of file +{} From 02a1ddfbd6e30b3b0353eaafbfbbf6658322b71b Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Thu, 2 Oct 2025 12:27:11 +0800 Subject: [PATCH 5/6] Fix eslint errors Amp-Thread-ID: https://ampcode.com/threads/T-0319390d-8526-40e6-b85d-aba74c0e2522 Co-authored-by: Amp --- src/FileIndexer.ts | 8 ++++---- src/SnapshotTesting.ts | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/FileIndexer.ts b/src/FileIndexer.ts index 219943ab..a5171179 100644 --- a/src/FileIndexer.ts +++ b/src/FileIndexer.ts @@ -245,9 +245,9 @@ export class FileIndexer { // Diagnostics should only be added for references to the symbol, not // the definition - diagnostics: !isDefinitionNode - ? this.diagnosticsForSymbol(sym) - : undefined, + diagnostics: isDefinitionNode + ? undefined + : FileIndexer.diagnosticsForSymbol(sym), }) ) if (isDefinitionNode) { @@ -726,7 +726,7 @@ export class FileIndexer { } // Returns the scip diagnostics for a given typescript symbol - private diagnosticsForSymbol( + private static diagnosticsForSymbol( sym: ts.Symbol ): scip.scip.Diagnostic[] | undefined { const jsDocTags = sym.getJsDocTags() diff --git a/src/SnapshotTesting.ts b/src/SnapshotTesting.ts index 92327bb7..feeef308 100644 --- a/src/SnapshotTesting.ts +++ b/src/SnapshotTesting.ts @@ -297,12 +297,12 @@ export function formatSnapshot( pushDoc(range, occurrence.symbol, isDefinition, isStartOfLine) if (occurrence.diagnostics && occurrence.diagnostics.length > 0) { - for (let diagnostic of occurrence.diagnostics) { - let indent = ' '.repeat(range.start.character - 2) + for (const diagnostic of occurrence.diagnostics) { + const indent = ' '.repeat(range.start.character - 2) out.push(commentSyntax + indent) out.push(`diagnostic ${scip.Severity[diagnostic.severity]}:\n`) if (diagnostic.message) { - for (let messageLine of diagnostic.message.split('\n')) { + for (const messageLine of diagnostic.message.split('\n')) { out.push(`${commentSyntax + indent}> ${messageLine}\n`) } } From 8ab1b4d42b381768bd1a4641e22215f6cc17f157 Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Thu, 2 Oct 2025 13:53:44 +0800 Subject: [PATCH 6/6] Push boolean down to be adjacent to deprecated diagnostic logic --- src/FileIndexer.ts | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/FileIndexer.ts b/src/FileIndexer.ts index a5171179..991ce9dc 100644 --- a/src/FileIndexer.ts +++ b/src/FileIndexer.ts @@ -243,11 +243,7 @@ export class FileIndexer { symbol: scipSymbol.value, symbol_roles: role, - // Diagnostics should only be added for references to the symbol, not - // the definition - diagnostics: isDefinitionNode - ? undefined - : FileIndexer.diagnosticsForSymbol(sym), + diagnostics: FileIndexer.diagnosticsFor(sym, isDefinitionNode), }) ) if (isDefinitionNode) { @@ -725,10 +721,23 @@ export class FileIndexer { loop(node) } - // Returns the scip diagnostics for a given typescript symbol - private static diagnosticsForSymbol( - sym: ts.Symbol + /** + * Returns the scip diagnostics for a given typescript symbol. + * @param sym - The TypeScript symbol to get diagnostics for + * @param isDefinition - Whether this occurrence is a definition of the symbol + */ + private static diagnosticsFor( + sym: ts.Symbol, + isDefinition: boolean ): scip.scip.Diagnostic[] | undefined { + // Currently, the logic below only supports deprecation + // diagnostics. Since linters typically only emit such + // diagnostics at reference sites, skip the check if we're + // not at a definition. + if (isDefinition) { + return undefined + } + const jsDocTags = sym.getJsDocTags() const deprecatedTag = jsDocTags.find(tag => tag.name === 'deprecated')