Skip to content

Commit 747500a

Browse files
authored
feat(linter/jsdoc): Implement require-returns-type rule (#3458)
Part of #1170 > https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-returns-type.md
1 parent 1fb9d23 commit 747500a

File tree

3 files changed

+192
-0
lines changed

3 files changed

+192
-0
lines changed

crates/oxc_linter/src/rules.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,7 @@ mod jsdoc {
396396
pub mod require_property_type;
397397
pub mod require_returns;
398398
pub mod require_returns_description;
399+
pub mod require_returns_type;
399400
pub mod require_yields;
400401
}
401402

@@ -762,6 +763,7 @@ oxc_macros::declare_all_lint_rules! {
762763
jsdoc::require_property_description,
763764
jsdoc::require_returns,
764765
jsdoc::require_returns_description,
766+
jsdoc::require_returns_type,
765767
jsdoc::require_yields,
766768
tree_shaking::no_side_effects_in_initialization,
767769
}
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
use oxc_diagnostics::OxcDiagnostic;
2+
use oxc_macros::declare_oxc_lint;
3+
use oxc_span::Span;
4+
5+
use crate::{
6+
ast_util::is_function_node,
7+
context::LintContext,
8+
rule::Rule,
9+
utils::{get_function_nearest_jsdoc_node, should_ignore_as_internal, should_ignore_as_private},
10+
AstNode,
11+
};
12+
13+
fn missing_type_diagnostic(span0: Span) -> OxcDiagnostic {
14+
OxcDiagnostic::warn("eslint-plugin-jsdoc(require-returns-type): Missing JSDoc `@returns` type.")
15+
.with_help("Add {type} to `@returns` tag.")
16+
.with_labels([span0.into()])
17+
}
18+
19+
#[derive(Debug, Default, Clone)]
20+
pub struct RequireReturnsType;
21+
22+
declare_oxc_lint!(
23+
/// ### What it does
24+
/// Requires that `@returns` tag has a type value (in curly brackets).
25+
///
26+
/// ### Why is this bad?
27+
/// A `@returns` tag should have a type value.
28+
///
29+
/// ### Example
30+
/// ```javascript
31+
/// // Passing
32+
/// /** @returns {string} */
33+
/// function quux (foo) {}
34+
///
35+
/// // Failing
36+
/// /** @returns */
37+
/// function quux (foo) {}
38+
/// ```
39+
RequireReturnsType,
40+
pedantic,
41+
);
42+
43+
impl Rule for RequireReturnsType {
44+
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
45+
if !is_function_node(node) {
46+
return;
47+
}
48+
49+
// If no JSDoc is found, skip
50+
let Some(jsdocs) = get_function_nearest_jsdoc_node(node, ctx)
51+
.and_then(|node| ctx.jsdoc().get_all_by_node(node))
52+
else {
53+
return;
54+
};
55+
56+
let settings = &ctx.settings().jsdoc;
57+
let resolved_returns_tag_name = settings.resolve_tag_name("returns");
58+
for jsdoc in jsdocs
59+
.iter()
60+
.filter(|jsdoc| !should_ignore_as_internal(jsdoc, settings))
61+
.filter(|jsdoc| !should_ignore_as_private(jsdoc, settings))
62+
{
63+
for tag in jsdoc.tags() {
64+
if tag.kind.parsed() != resolved_returns_tag_name {
65+
continue;
66+
}
67+
68+
// If type exists, skip
69+
if let (Some(_), _) = tag.type_comment() {
70+
continue;
71+
};
72+
73+
ctx.diagnostic(missing_type_diagnostic(tag.kind.span));
74+
}
75+
}
76+
}
77+
}
78+
79+
#[test]
80+
fn test() {
81+
use crate::tester::Tester;
82+
83+
let pass = vec![
84+
(
85+
"
86+
/**
87+
* @returns {number}
88+
*/
89+
function quux () {
90+
91+
}
92+
",
93+
None,
94+
None,
95+
),
96+
(
97+
"
98+
/**
99+
* @function
100+
* @returns Foo.
101+
*/
102+
",
103+
None,
104+
None,
105+
),
106+
(
107+
"
108+
/**
109+
* @callback
110+
* @returns Foo.
111+
*/
112+
",
113+
None,
114+
None,
115+
),
116+
];
117+
118+
let fail = vec![
119+
(
120+
"
121+
/**
122+
* @returns
123+
*/
124+
function quux () {
125+
126+
}
127+
",
128+
None,
129+
None,
130+
),
131+
(
132+
"
133+
/**
134+
* @returns Foo.
135+
*/
136+
function quux () {
137+
138+
}
139+
",
140+
None,
141+
None,
142+
),
143+
(
144+
"
145+
/**
146+
* @return Foo.
147+
*/
148+
function quux () {
149+
150+
}
151+
",
152+
None,
153+
Some(serde_json::json!({ "settings": { "jsdoc": {
154+
"tagNamePreference": { "returns": "return", },
155+
}, } })),
156+
),
157+
];
158+
159+
Tester::new(RequireReturnsType::NAME, pass, fail).test_and_snapshot();
160+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
source: crates/oxc_linter/src/tester.rs
3+
expression: require_returns_type
4+
---
5+
eslint-plugin-jsdoc(require-returns-type): Missing JSDoc `@returns` type.
6+
╭─[require_returns_type.tsx:3:17]
7+
2/**
8+
3 │ * @returns
9+
· ────────
10+
4 │ */
11+
╰────
12+
help: Add {type} to `@returns` tag.
13+
14+
eslint-plugin-jsdoc(require-returns-type): Missing JSDoc `@returns` type.
15+
╭─[require_returns_type.tsx:3:17]
16+
2/**
17+
3 │ * @returns Foo.
18+
· ────────
19+
4 │ */
20+
╰────
21+
help: Add {type} to `@returns` tag.
22+
23+
eslint-plugin-jsdoc(require-returns-type): Missing JSDoc `@returns` type.
24+
╭─[require_returns_type.tsx:3:17]
25+
2/**
26+
3 │ * @return Foo.
27+
· ───────
28+
4 │ */
29+
╰────
30+
help: Add {type} to `@returns` tag.

0 commit comments

Comments
 (0)