Skip to content
Open
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
71 changes: 58 additions & 13 deletions crates/oxc_macros/src/declare_oxc_lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ use syn::{
parse::{Parse, ParseStream},
};

/// Documentation source for a lint rule
#[cfg(feature = "ruledocs")]
pub enum DocumentationSource {
/// Inline documentation from doc comments
Inline(String),
/// Reference to a shared documentation constant
Path(syn::Path),
}

pub struct LintRuleMeta {
name: Ident,
// Whether this rule should be exposed to tsgolint integration
Expand All @@ -16,7 +25,7 @@ pub struct LintRuleMeta {
/// Describes what auto-fixing capabilities the rule has
fix: Option<Ident>,
#[cfg(feature = "ruledocs")]
documentation: String,
documentation: DocumentationSource,
pub used_in_test: bool,
/// Rule configuration
/// This is the name of a struct/enum/whatever implementing
Expand All @@ -27,7 +36,9 @@ pub struct LintRuleMeta {
impl Parse for LintRuleMeta {
fn parse(input: ParseStream<'_>) -> Result<Self> {
#[cfg(feature = "ruledocs")]
let mut documentation = String::new();
let mut documentation = None;
#[cfg(feature = "ruledocs")]
let mut doc_comments = String::new();

#[cfg(feature = "ruledocs")]
let mut backtick_fences_count: usize = 0;
Expand All @@ -39,9 +50,8 @@ impl Parse for LintRuleMeta {
{
let value = lit.value();
let line = value.strip_prefix(' ').unwrap_or(&value);

documentation.push_str(line);
documentation.push('\n');
doc_comments.push_str(line);
doc_comments.push('\n');

// Count occurrences of "```" to ensure the markdown code blocks are closed properly.
backtick_fences_count += line.matches("```").count();
Expand All @@ -57,6 +67,13 @@ impl Parse for LintRuleMeta {
}
}

#[cfg(feature = "ruledocs")]
{
if !doc_comments.is_empty() {
documentation.replace(DocumentationSource::Inline(doc_comments));
}
}

let struct_name: Ident = input.parse()?;
// Optional marker `(tsgolint)` directly after the rule struct name
let mut is_tsgolint_rule = false;
Expand Down Expand Up @@ -111,13 +128,27 @@ impl Parse for LintRuleMeta {
input.parse::<Token!(=)>()?;
config.replace(input.parse()?);
}
// docs = path::to::SHARED_DOCUMENTATION_CONSTANT
"docs" => {
#[cfg(feature = "ruledocs")]
{
if documentation.is_some() {
return Err(Error::new_spanned(
key,
"documentation source already specified inlined via doc comments",
));
}
input.parse::<Token!(=)>()?;

documentation.replace(DocumentationSource::Path(input.parse()?));
}
}
_ => {
if input.peek(Token!(=)) || fix.is_some() {
panic!("invalid key: {key}");
} else {
// fix kind shorthand, e.g. `dangerous-suggestion``
fix.replace(key);
return Err(Error::new_spanned(key, "unexpected key in rule declaration"));
}
// fix kind shorthand, e.g. `dangerous-suggestion``
fix.replace(key);
}
}
}
Expand All @@ -139,6 +170,13 @@ impl Parse for LintRuleMeta {
"unclosed markdown code block in documentation, please close all ``` fences",
));
}
#[cfg(feature = "ruledocs")]
let Some(documentation) = documentation else {
return Err(Error::new(
struct_name.span(),
"rule documentation must be specified either via doc comments or the `docs` attribute",
));
};

Ok(Self {
name: struct_name,
Expand Down Expand Up @@ -204,10 +242,17 @@ pub fn declare_oxc_lint(metadata: LintRuleMeta) -> TokenStream {
let docs: Option<proc_macro2::TokenStream> = None;

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

let has_config = if config.is_some() {
Expand Down
Loading