diff --git a/snapshots/input/diagnostics/index.ts b/snapshots/input/diagnostics/index.ts new file mode 100644 index 00000000..4a2c7b8c --- /dev/null +++ b/snapshots/input/diagnostics/index.ts @@ -0,0 +1,18 @@ +/** @deprecated This class is deprecated */ +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() + car() +} diff --git a/snapshots/input/diagnostics/package.json b/snapshots/input/diagnostics/package.json new file mode 100644 index 00000000..a780d977 --- /dev/null +++ b/snapshots/input/diagnostics/package.json @@ -0,0 +1,4 @@ +{ + "name": "diagnostics", + "version": "0.0.1" +} diff --git a/snapshots/input/diagnostics/tsconfig.json b/snapshots/input/diagnostics/tsconfig.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/snapshots/input/diagnostics/tsconfig.json @@ -0,0 +1 @@ +{} diff --git a/snapshots/output/diagnostics/index.ts b/snapshots/output/diagnostics/index.ts new file mode 100644 index 00000000..a13960dd --- /dev/null +++ b/snapshots/output/diagnostics/index.ts @@ -0,0 +1,36 @@ +// < definition diagnostics 0.0.1 `index.ts`/ + +/** @deprecated This class is deprecated */ +class Foo {} +// ^^^ definition diagnostics 0.0.1 `index.ts`/Foo# + +/** @deprecated This function is deprecated */ +function bar() {} +// ^^^ definition diagnostics 0.0.1 `index.ts`/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() {} +// ^^^ definition diagnostics 0.0.1 `index.ts`/car(). + +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 + 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 +} + diff --git a/src/FileIndexer.ts b/src/FileIndexer.ts index d1960b89..991ce9dc 100644 --- a/src/FileIndexer.ts +++ b/src/FileIndexer.ts @@ -242,6 +242,8 @@ export class FileIndexer { range, symbol: scipSymbol.value, symbol_roles: role, + + diagnostics: FileIndexer.diagnosticsFor(sym, isDefinitionNode), }) ) if (isDefinitionNode) { @@ -718,6 +720,42 @@ export class FileIndexer { } loop(node) } + + /** + * 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') + if (deprecatedTag) { + return [ + 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], + }), + ] + } + + return undefined + } } function isAnonymousContainerOfSymbols(node: ts.Node): boolean { diff --git a/src/SnapshotTesting.ts b/src/SnapshotTesting.ts index 8b91023c..feeef308 100644 --- a/src/SnapshotTesting.ts +++ b/src/SnapshotTesting.ts @@ -295,6 +295,19 @@ export function formatSnapshot( out.push(symbol.replace('\n', '|')) pushDoc(range, occurrence.symbol, isDefinition, isStartOfLine) + + if (occurrence.diagnostics && occurrence.diagnostics.length > 0) { + 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 (const messageLine of diagnostic.message.split('\n')) { + out.push(`${commentSyntax + indent}> ${messageLine}\n`) + } + } + } + } } // Check if any enclosing ranges end on this line