Skip to content

Commit

Permalink
feat(analyzer): top level suppression (#4306)
Browse files Browse the repository at this point in the history
  • Loading branch information
ematipico committed Oct 31, 2024
1 parent 82269d7 commit 0a27ee8
Show file tree
Hide file tree
Showing 65 changed files with 2,367 additions and 569 deletions.
34 changes: 34 additions & 0 deletions .changeset/new_top_level_suppression_for_the_analyzer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
cli: minor
---

# New top-level suppression for the analyzer

The Biome analyzer now supports a new top-level suppression. These suppression have to be placed at the top of the file, and they must be followed by two newlines (`\n\n\`).

The analyzer rules specified inside the block comment will be suppressed for the whole file.

In the example, we suppress the rules `lint/style/useConst` and `lint/suspicious/noDebugger` for the whole file:

```js
// main.js
/**
* biome-ignore lint/style/useConst: i like let
* biome-ignore lint/suspicious/noDebugger: needed now
*/

let path = "/path";
let _tmp = undefined;
debugger
```

In this other example, we suppress `lint/suspicious/noEmptyBlock` for a whole CSS file:

```css
/**
/* biome-ignore lint/suspicious/noEmptyBlock: it's fine to have empty blocks
*/

a {}
span {}
```
13 changes: 13 additions & 0 deletions .changeset/remove_support_for_legacy_suppressions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
cli: major
---

# Remove support for legacy suppressions

Biome used to support "legacy suppressions" that looked like this:

```js
// biome-ignore lint(style/useWhile): reason
```

This format is no longer supported.
35 changes: 35 additions & 0 deletions .changeset/the_action_quickfixsuppressrule_is_removed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
cli: major
---

# Remove the code action `quickfix.suppressRule`

The code action `quickfix.suppressRule` was removed in favour of two new code actions:

- `quickfix.suppressRule.inline.biome`: a code action that adds a suppression comment for each violation.
- `quickfix.suppressRule.topLevel.biome`: a code action that adds a suppression comment at the top of the file which suppresses a rule for the whole file.


Given the following code
```js
let foo = "one";
debugger
```

The code action `quickfix.suppressRule.inline.biome` will result in the following code:
```js
// biome-ignore lint/style/useConst: <explanation>
let foo = "one";
// biome-ignore lint/suspicious/noDebugger: <explanation>
debugger
```

The code action `quickfix.suppressRule.topLevel.biome`, instead, will result in the following code:
```js
/** biome-ignore lint/suspicious/noDebugger: <explanation> */
/** biome-ignore lint/style/useConst: <explanation> */

let foo = "one";
debugger;
```

3 changes: 2 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 30 additions & 4 deletions crates/biome_analyze/src/categories.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ pub enum RuleCategory {
}

/// Actions that suppress rules should start with this string
pub const SUPPRESSION_ACTION_CATEGORY: &str = "quickfix.suppressRule";
pub const SUPPRESSION_INLINE_ACTION_CATEGORY: &str = "quickfix.suppressRule.inline";
pub const SUPPRESSION_TOP_LEVEL_ACTION_CATEGORY: &str = "quickfix.suppressRule.topLevel";

/// The category of a code action, this type maps directly to the
/// [CodeActionKind] type in the Language Server Protocol specification
Expand All @@ -48,7 +49,21 @@ pub enum ActionCategory {
Source(SourceActionKind),
/// This action is using a base kind not covered by any of the previous
/// variants
Other(Cow<'static, str>),
Other(OtherActionCategory),
}

#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize, schemars::JsonSchema)
)]
pub enum OtherActionCategory {
/// Base kind for inline suppressions actions: `quickfix.suppressRule.inline.biome`
InlineSuppression,
/// Base kind for inline suppressions actions: `quickfix.suppressRule.topLevel.biome`
ToplevelSuppression,
/// Generic action that can't be mapped
Generic(Cow<'static, str>),
}

impl ActionCategory {
Expand All @@ -57,7 +72,7 @@ impl ActionCategory {
/// ## Examples
///
/// ```
/// use biome_analyze::{ActionCategory, RefactorKind};
/// use biome_analyze::{ActionCategory, RefactorKind, OtherActionCategory};
///
/// assert!(ActionCategory::QuickFix.matches("quickfix"));
/// assert!(!ActionCategory::QuickFix.matches("refactor"));
Expand All @@ -67,6 +82,9 @@ impl ActionCategory {
///
/// assert!(ActionCategory::Refactor(RefactorKind::Extract).matches("refactor"));
/// assert!(ActionCategory::Refactor(RefactorKind::Extract).matches("refactor.extract"));
///
/// assert!(ActionCategory::Other(OtherActionCategory::InlineSuppression).matches("quickfix.suppressRule.inline.biome"));
/// assert!(ActionCategory::Other(OtherActionCategory::ToplevelSuppression).matches("quickfix.suppressRule.topLevel.biome"));
/// ```
pub fn matches(&self, filter: &str) -> bool {
self.to_str().starts_with(filter)
Expand Down Expand Up @@ -102,7 +120,15 @@ impl ActionCategory {
Cow::Owned(format!("source.{tag}.biome"))
}

ActionCategory::Other(tag) => Cow::Owned(format!("{tag}.biome")),
ActionCategory::Other(other_action) => match other_action {
OtherActionCategory::InlineSuppression => {
Cow::Borrowed("quickfix.suppressRule.inline.biome")
}
OtherActionCategory::ToplevelSuppression => {
Cow::Borrowed("quickfix.suppressRule.topLevel.biome")
}
OtherActionCategory::Generic(tag) => Cow::Owned(format!("{tag}.biome")),
},
}
}
}
Expand Down
33 changes: 27 additions & 6 deletions crates/biome_analyze/src/diagnostics.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use biome_console::MarkupBuf;
use biome_console::{markup, MarkupBuf};
use biome_diagnostics::{
advice::CodeSuggestionAdvice, category, Advices, Category, Diagnostic, DiagnosticExt,
DiagnosticTags, Error, Location, Severity, Visit,
DiagnosticTags, Error, Location, LogCategory, Severity, Visit,
};
use biome_rowan::TextRange;
use std::borrow::Cow;
Expand Down Expand Up @@ -141,7 +141,7 @@ impl AnalyzerDiagnostic {

#[derive(Debug, Diagnostic, Clone)]
#[diagnostic(severity = Warning)]
pub struct SuppressionDiagnostic {
pub struct AnalyzerSuppressionDiagnostic {
#[category]
category: &'static Category,
#[location(span)]
Expand All @@ -151,9 +151,12 @@ pub struct SuppressionDiagnostic {
message: String,
#[tags]
tags: DiagnosticTags,

#[advice]
advice: SuppressionAdvice,
}

impl SuppressionDiagnostic {
impl AnalyzerSuppressionDiagnostic {
pub(crate) fn new(
category: &'static Category,
range: TextRange,
Expand All @@ -164,15 +167,33 @@ impl SuppressionDiagnostic {
range,
message: message.to_string(),
tags: DiagnosticTags::empty(),
advice: SuppressionAdvice::default(),
}
}

pub(crate) fn with_tags(mut self, tags: DiagnosticTags) -> Self {
self.tags |= tags;
pub(crate) fn note(mut self, message: impl Into<String>, range: impl Into<TextRange>) -> Self {
self.advice.messages.push((message.into(), range.into()));
self
}
}

#[derive(Debug, Default, Clone)]
struct SuppressionAdvice {
messages: Vec<(String, TextRange)>,
}

impl Advices for SuppressionAdvice {
fn record(&self, visitor: &mut dyn Visit) -> std::io::Result<()> {
for (message, range) in &self.messages {
visitor.record_log(LogCategory::Info, &markup! {{message}})?;
let location = Location::builder().span(range);

visitor.record_frame(location.build())?
}
Ok(())
}
}

/// Series of errors encountered when running rules on a file
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
Expand Down
Loading

0 comments on commit 0a27ee8

Please sign in to comment.