Skip to content

Commit 80b61b3

Browse files
committed
fix(lint): don't flag missing-jsdoc when @deprecated carries docs
A JSDoc comment whose first block is @deprecated absorbs any following description into the tag (per JSDoc semantics, the description must precede all tags), leaving the symbol with no top-level doc field. The missing-jsdoc lint then fired even though the symbol was clearly documented. Treat a symbol as documented when it has a @deprecated tag carrying a non-empty explanation.
1 parent 64a834e commit 80b61b3

2 files changed

Lines changed: 137 additions & 0 deletions

File tree

src/diagnostics.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
use crate::Location;
44
use crate::js_doc::JsDoc;
5+
use crate::js_doc::JsDocTag;
56
use crate::node::DeclarationDef;
67
use crate::node::DeclarationKind;
78
use crate::node::NamespaceDef;
@@ -181,6 +182,24 @@ impl Diagnostic for DocDiagnostic {
181182
}
182183
}
183184

185+
/// Whether a JSDoc block documents its symbol through a tag, even though it has
186+
/// no top-level description.
187+
///
188+
/// A `@deprecated` notice that carries an explanation counts as documentation.
189+
/// When `@deprecated` is the first block of a comment, any description that
190+
/// follows it is absorbed into the tag (per JSDoc semantics, the description
191+
/// must precede all tags), so the symbol ends up with no `doc` field even
192+
/// though it is clearly documented. Treating such a symbol as missing JSDoc
193+
/// would be a false positive.
194+
fn has_documenting_tag(js_doc: &JsDoc) -> bool {
195+
js_doc.tags.iter().any(|tag| {
196+
matches!(
197+
tag,
198+
JsDocTag::Deprecated { doc: Some(doc) } if !doc.trim().is_empty()
199+
)
200+
})
201+
}
202+
184203
pub struct DiagnosticsCollector<'a> {
185204
root_symbol: Rc<RootSymbol<'a>>,
186205
seen_private_types_in_public: HashSet<(UniqueSymbolId, UniqueSymbolId)>,
@@ -278,6 +297,7 @@ impl<'a> DiagnosticsCollector<'a> {
278297

279298
fn check_missing_js_doc(&mut self, js_doc: &JsDoc, location: &Location) {
280299
if js_doc.doc.is_none()
300+
&& !has_documenting_tag(js_doc)
281301
&& !has_ignorable_js_doc_tag(js_doc)
282302
&& self.seen_jsdoc_missing.insert(location.clone())
283303
&& let Some(text_info) = self.maybe_get_text_info(location)
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
# mod.ts
2+
/**
3+
* @deprecated Use {@link bar} instead.
4+
*
5+
* Does a thing with the given value and returns it unchanged.
6+
*/
7+
export function foo(value: string): string {
8+
return value;
9+
}
10+
11+
/** @deprecated */
12+
export function baz(): void {}
13+
14+
# diagnostics
15+
error[missing-jsdoc]: exported symbol is missing JSDoc documentation
16+
--> /mod.ts:11:1
17+
|
18+
11 | export function baz(): void {}
19+
| ^
20+
21+
# output.txt
22+
Defined in file:///mod.ts:10:1
23+
24+
function baz(): void
25+
26+
@deprecated
27+
28+
Defined in file:///mod.ts:5:1
29+
30+
function foo(value: string): string
31+
32+
@deprecated
33+
Use {@link bar} instead.
34+
35+
Does a thing with the given value and returns it unchanged.
36+
37+
38+
39+
# output.json
40+
{
41+
"symbols": [
42+
{
43+
"name": "foo",
44+
"declarations": [
45+
{
46+
"location": {
47+
"filename": "file:///mod.ts",
48+
"line": 5,
49+
"col": 0,
50+
"byteIndex": 114
51+
},
52+
"declarationKind": "export",
53+
"jsDoc": {
54+
"tags": [
55+
{
56+
"kind": "deprecated",
57+
"doc": "Use {@link bar} instead.\n\nDoes a thing with the given value and returns it unchanged."
58+
}
59+
]
60+
},
61+
"kind": "function",
62+
"def": {
63+
"params": [
64+
{
65+
"kind": "identifier",
66+
"name": "value",
67+
"optional": false,
68+
"tsType": {
69+
"repr": "string",
70+
"kind": "keyword",
71+
"value": "string"
72+
}
73+
}
74+
],
75+
"returnType": {
76+
"repr": "string",
77+
"kind": "keyword",
78+
"value": "string"
79+
},
80+
"hasBody": true
81+
}
82+
}
83+
]
84+
},
85+
{
86+
"name": "baz",
87+
"declarations": [
88+
{
89+
"location": {
90+
"filename": "file:///mod.ts",
91+
"line": 10,
92+
"col": 0,
93+
"byteIndex": 197
94+
},
95+
"declarationKind": "export",
96+
"jsDoc": {
97+
"tags": [
98+
{
99+
"kind": "deprecated"
100+
}
101+
]
102+
},
103+
"kind": "function",
104+
"def": {
105+
"params": [],
106+
"returnType": {
107+
"repr": "void",
108+
"kind": "keyword",
109+
"value": "void"
110+
},
111+
"hasBody": true
112+
}
113+
}
114+
]
115+
}
116+
]
117+
}

0 commit comments

Comments
 (0)