Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion crates/oxc_linter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ repository.workspace = true
rust-version.workspace = true
description.workspace = true

[features]
ruledocs = ["oxc_macros/ruledocs"] # Enables the `ruledocs` feature for conditional compilation

[lints]
workspace = true

Expand All @@ -28,7 +31,7 @@ oxc_codegen = { workspace = true }
oxc_diagnostics = { workspace = true }
oxc_ecmascript = { workspace = true }
oxc_index = { workspace = true, features = ["serde"] }
oxc_macros = { workspace = true }
oxc_macros = { workspace = true, features = ["ruledocs"] }
oxc_parser = { workspace = true }
oxc_regular_expression = { workspace = true }
oxc_resolver = { workspace = true }
Expand Down
7 changes: 4 additions & 3 deletions crates/oxc_linter/src/rule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,13 +306,14 @@ impl RuleWithSeverity {

#[cfg(test)]
mod test {
use markdown::{Options, to_html_with_options};

use super::RuleCategory;
use crate::rules::RULES;

#[test]
#[cfg(feature = "ruledocs")]
fn ensure_documentation() {
use crate::rules::RULES;
use markdown::{Options, to_html_with_options};

assert!(!RULES.is_empty());
let options = Options::gfm();

Expand Down
2 changes: 2 additions & 0 deletions crates/oxc_linter/src/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub struct RuleTableRow {
pub name: &'static str,
pub plugin: String,
pub category: RuleCategory,
#[cfg(feature = "ruledocs")]
pub documentation: Option<&'static str>,
pub turned_on_by_default: bool,
pub autofix: RuleFixMeta,
Expand All @@ -46,6 +47,7 @@ impl RuleTable {
let name = rule.name();
RuleTableRow {
name,
#[cfg(feature = "ruledocs")]
documentation: rule.documentation(),
plugin: rule.plugin_name().to_string(),
category: rule.category(),
Expand Down
4 changes: 3 additions & 1 deletion crates/oxc_linter/tests/integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ declare_oxc_lint_test!(
correctness
);

#[expect(dead_code)]
struct TestRule2 {
#[expect(dead_code)]
dummy_field: u8,
}

Expand All @@ -26,9 +26,11 @@ declare_oxc_lint_test!(
#[test]
fn test_declare_oxc_lint() {
// Simple, multiline documentation
#[cfg(feature = "ruledocs")]
assert_eq!(TestRule::documentation().unwrap(), "Dummy description\n# which is multiline\n");

// Ensure structs with fields can be passed to the macro
#[cfg(feature = "ruledocs")]
assert_eq!(TestRule2::documentation().unwrap(), "Dummy description2\n");

// Auto-generated kebab-case name
Expand Down
3 changes: 3 additions & 0 deletions crates/oxc_macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ repository.workspace = true
rust-version.workspace = true
description.workspace = true

[features]
ruledocs = [] # Enables the `ruledocs` feature for conditional compilation

[lints]
workspace = true

Expand Down
50 changes: 40 additions & 10 deletions crates/oxc_macros/src/declare_oxc_lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,27 @@ pub struct LintRuleMeta {
category: Ident,
/// Describes what auto-fixing capabilities the rule has
fix: Option<Ident>,
#[cfg(feature = "ruledocs")]
documentation: String,
pub used_in_test: bool,
}

impl Parse for LintRuleMeta {
fn parse(input: ParseStream<'_>) -> Result<Self> {
#[cfg(feature = "ruledocs")]
let mut documentation = String::new();

for attr in input.call(Attribute::parse_outer)? {
match parse_attr(["doc"], &attr) {
Some(lit) => {
let value = lit.value();
let line = value.strip_prefix(' ').unwrap_or(&value);

documentation.push_str(line);
documentation.push('\n');
#[cfg(feature = "ruledocs")]
{
let value = lit.value();
let line = value.strip_prefix(' ').unwrap_or(&value);

documentation.push_str(line);
documentation.push('\n');
}
}
_ => {
return Err(Error::new_spanned(attr, "unexpected attribute"));
Expand All @@ -54,7 +60,15 @@ impl Parse for LintRuleMeta {
// Ignore the rest
input.parse::<proc_macro2::TokenStream>()?;

Ok(Self { name: struct_name, plugin, category, fix, documentation, used_in_test: false })
Ok(Self {
name: struct_name,
plugin,
category,
fix,
#[cfg(feature = "ruledocs")]
documentation,
used_in_test: false,
})
}
}

Expand All @@ -63,7 +77,15 @@ pub fn rule_name_converter() -> Converter {
}

pub fn declare_oxc_lint(metadata: LintRuleMeta) -> TokenStream {
let LintRuleMeta { name, plugin, category, fix, documentation, used_in_test } = metadata;
let LintRuleMeta {
name,
plugin,
category,
fix,
#[cfg(feature = "ruledocs")]
documentation,
used_in_test,
} = metadata;

let canonical_name = rule_name_converter().convert(name.to_string());
let plugin = plugin.to_string(); // ToDo: validate plugin name
Expand Down Expand Up @@ -91,6 +113,16 @@ pub fn declare_oxc_lint(metadata: LintRuleMeta) -> TokenStream {
Some(quote! { use crate::{rule::{RuleCategory, RuleMeta, RuleFixMeta}, fixer::FixKind}; })
};

#[cfg(not(feature = "ruledocs"))]
let docs: Option<proc_macro2::TokenStream> = None;

#[cfg(feature = "ruledocs")]
let docs = Some(quote! {
fn documentation() -> Option<&'static str> {
Some(#documentation)
}
});

let output = quote! {
#import_statement

Expand All @@ -103,9 +135,7 @@ pub fn declare_oxc_lint(metadata: LintRuleMeta) -> TokenStream {

#fix

fn documentation() -> Option<&'static str> {
Some(#documentation)
}
#docs
}
};

Expand Down
2 changes: 1 addition & 1 deletion tasks/website/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ doctest = false
[dependencies]
bpaf = { workspace = true, features = ["docgen"] }
handlebars = { workspace = true }
oxc_linter = { workspace = true }
oxc_linter = { workspace = true, features = ["ruledocs"] }
oxlint = { path = "../../apps/oxlint" }
pico-args = { workspace = true }
project-root = { workspace = true }
Expand Down