Skip to content

Commit

Permalink
feat(linter/jsdoc): Implement require-param-type rule (#3601)
Browse files Browse the repository at this point in the history
  • Loading branch information
leaysgur authored Jun 10, 2024
1 parent f98f777 commit d6370f1
Show file tree
Hide file tree
Showing 5 changed files with 425 additions and 120 deletions.
2 changes: 2 additions & 0 deletions crates/oxc_linter/src/rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,7 @@ mod jsdoc {
pub mod implements_on_classes;
pub mod no_defaults;
pub mod require_param;
pub mod require_param_type;
pub mod require_property;
pub mod require_property_description;
pub mod require_property_name;
Expand Down Expand Up @@ -759,6 +760,7 @@ oxc_macros::declare_all_lint_rules! {
jsdoc::implements_on_classes,
jsdoc::no_defaults,
jsdoc::require_param,
jsdoc::require_param_type,
jsdoc::require_property,
jsdoc::require_property_type,
jsdoc::require_property_name,
Expand Down
122 changes: 3 additions & 119 deletions crates/oxc_linter/src/rules/jsdoc/require_param.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
use lazy_static::lazy_static;
use oxc_ast::{
ast::{BindingPattern, BindingPatternKind, Expression, FormalParameters, MethodDefinitionKind},
AstKind,
};
use oxc_ast::{ast::MethodDefinitionKind, AstKind};
use oxc_diagnostics::LabeledSpan;
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_semantic::{AstNode, JSDoc};
use oxc_span::Span;
use regex::Regex;
use rustc_hash::{FxHashMap, FxHashSet};
use serde::Deserialize;
Expand All @@ -17,8 +13,8 @@ use crate::{
context::LintContext,
rule::Rule,
utils::{
get_function_nearest_jsdoc_node, should_ignore_as_avoid, should_ignore_as_internal,
should_ignore_as_private,
collect_params, get_function_nearest_jsdoc_node, should_ignore_as_avoid,
should_ignore_as_internal, should_ignore_as_private, ParamKind,
},
};

Expand Down Expand Up @@ -258,118 +254,6 @@ impl Rule for RequireParam {
}
}

#[derive(Debug, Clone)]
struct Param {
span: Span,
name: String,
is_rest: bool,
}

#[derive(Debug, Clone)]
enum ParamKind {
Single(Param),
Nested(Vec<Param>),
}

fn collect_params(params: &FormalParameters) -> Vec<ParamKind> {
// NOTE: Property level `is_rest` is implemented.
// - fn(a, { b1, ...b2 })
// ^^^^^
// But Object|Array level `is_rest` is not implemented
// - fn(a, ...{ b })
// ^^^^ ^
// Tests are not covering these cases...
fn get_param_name(pattern: &BindingPattern, is_rest: bool) -> ParamKind {
match &pattern.kind {
BindingPatternKind::BindingIdentifier(ident) => {
ParamKind::Single(Param { span: ident.span, name: ident.name.to_string(), is_rest })
}
BindingPatternKind::ObjectPattern(obj_pat) => {
let mut collected = vec![];

for prop in &obj_pat.properties {
let Some(name) = prop.key.name() else { continue };

match get_param_name(&prop.value, false) {
ParamKind::Single(param) => {
collected.push(Param { name: format!("{name}"), ..param });
}
ParamKind::Nested(params) => {
collected.push(Param {
span: prop.span,
name: format!("{name}"),
is_rest: false,
});

for param in params {
collected.push(Param {
name: format!("{name}.{}", param.name),
..param
});
}
}
}
}

if let Some(rest) = &obj_pat.rest {
match get_param_name(&rest.argument, true) {
ParamKind::Single(param) => collected.push(param),
ParamKind::Nested(params) => collected.extend(params),
}
}

ParamKind::Nested(collected)
}
BindingPatternKind::ArrayPattern(arr_pat) => {
let mut collected = vec![];

for (idx, elm) in arr_pat.elements.iter().enumerate() {
let name = format!("\"{idx}\"");

if let Some(pat) = elm {
match get_param_name(pat, false) {
ParamKind::Single(param) => collected.push(Param { name, ..param }),
ParamKind::Nested(params) => collected.extend(params),
}
}
}

if let Some(rest) = &arr_pat.rest {
match get_param_name(&rest.argument, true) {
ParamKind::Single(param) => collected.push(param),
ParamKind::Nested(params) => collected.extend(params),
}
}

ParamKind::Nested(collected)
}
BindingPatternKind::AssignmentPattern(assign_pat) => match &assign_pat.right {
Expression::Identifier(_) => get_param_name(&assign_pat.left, false),
_ => {
// TODO: If `config.useDefaultObjectProperties` = true,
// collect default parameters from `assign_pat.right` like:
// { prop = { a: 1, b: 2 }} => [prop, prop.a, prop.b]
// get_param_name(&assign_pat.left, false)
// }
get_param_name(&assign_pat.left, false)
}
},
}
}

let mut collected =
params.items.iter().map(|param| get_param_name(&param.pattern, false)).collect::<Vec<_>>();

if let Some(rest) = &params.rest {
match get_param_name(&rest.argument, true) {
ParamKind::Single(param) => collected.push(ParamKind::Single(param)),
ParamKind::Nested(params) => collected.push(ParamKind::Nested(params)),
}
}

collected
}

fn collect_tags<'a>(
jsdocs: &[JSDoc<'a>],
resolved_param_tag_name: &str,
Expand Down
Loading

0 comments on commit d6370f1

Please sign in to comment.