Skip to content

Commit

Permalink
feat(lint/noEmptyBlockStatements): add rule (#521)
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewangelle authored Oct 16, 2023
1 parent 556df66 commit 7b6520b
Show file tree
Hide file tree
Showing 22 changed files with 1,709 additions and 58 deletions.
1 change: 1 addition & 0 deletions crates/biome_diagnostics_categories/src/categories.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ define_categories! {
"lint/nursery/noApproximativeNumericConstant": "https://biomejs.dev/lint/rules/no-approximative-numeric-constant",
"lint/nursery/noConfusingVoidType": "https://biomejs.dev/linter/rules/no-confusing-void-type",
"lint/nursery/noDuplicateJsonKeys": "https://biomejs.dev/linter/rules/no-duplicate-json-keys",
"lint/nursery/noEmptyBlockStatements": "https://biomejs.dev/lint/rules/no-empty-block-statements",
"lint/nursery/noEmptyCharacterClassInRegex": "https://biomejs.dev/lint/rules/no-empty-character-class-in-regex",
"lint/nursery/noExcessiveComplexity": "https://biomejs.dev/linter/rules/no-excessive-complexity",
"lint/nursery/noFallthroughSwitchClause": "https://biomejs.dev/linter/rules/no-fallthrough-switch-clause",
Expand Down
2 changes: 2 additions & 0 deletions crates/biome_js_analyze/src/analyzers/nursery.rs

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

Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
use biome_analyze::{context::RuleContext, declare_rule, Ast, Rule, RuleDiagnostic};
use biome_console::markup;
use biome_js_syntax::{
JsBlockStatement, JsFunctionBody, JsStaticInitializationBlockClassMember, JsSwitchStatement,
};
use biome_rowan::{declare_node_union, AstNode, AstNodeList};

declare_rule! {
/// Disallow empty block statements and static blocks.
///
/// Empty static blocks and block statements, while not technically errors, usually occur due to refactoring that wasn’t completed. They can cause confusion when reading code.
///
/// This rule disallows empty block statements and static blocks.
/// This rule ignores block statements or static blocks which contain a comment (for example, in an empty catch or finally block of a try statement to indicate that execution should continue regardless of errors).
///
/// Source: https://eslint.org/docs/latest/rules/no-empty-static-block/
/// Source: https://eslint.org/docs/latest/rules/no-empty/
///
/// ## Examples
///
/// ### Invalid
///
/// ```js,expect_diagnostic
/// function emptyFunctionBody () {}
/// ```
///
/// ```js,expect_diagnostic
/// try {
/// doSomething();
/// } catch(ex) {
///
/// }
/// ```
///
/// ```js,expect_diagnostic
/// class Foo {
/// static {}
/// }
/// ```
///
/// ## Valid
///
/// ```js
/// function foo () {
/// doSomething();
/// }
/// ```
///
/// ```js
/// try {
/// doSomething();
/// } catch (ex) {
/// // continue regardless of error
/// }
/// ```
///
pub(crate) NoEmptyBlockStatements {
version: "next",
name: "noEmptyBlockStatements",
recommended: false,
}
}

declare_node_union! {
pub(crate) Query = JsBlockStatement | JsFunctionBody | JsStaticInitializationBlockClassMember | JsSwitchStatement
}

impl Rule for NoEmptyBlockStatements {
type Query = Ast<Query>;
type State = ();
type Signals = Option<Self::State>;
type Options = ();

fn run(ctx: &RuleContext<Self>) -> Self::Signals {
let query = ctx.query();
let is_empty = is_empty(query);
let has_comments = query.syntax().has_comments_descendants();

(is_empty && !has_comments).then_some(())
}

fn diagnostic(ctx: &RuleContext<Self>, _: &Self::State) -> Option<RuleDiagnostic> {
let query = ctx.query();
Some(
RuleDiagnostic::new(
rule_category!(),
query.range(),
markup! {
"Unexpected empty block."
},
)
.note(markup! {
"Empty blocks are usually the result of an incomplete refactoring. Remove the empty block or add a comment inside it if it is intentional."
}),
)
}
}

fn is_empty(query: &Query) -> bool {
use Query::*;
match query {
JsFunctionBody(body) => body.directives().len() == 0 && body.statements().len() == 0,
JsBlockStatement(block) => block.statements().len() == 0,
JsStaticInitializationBlockClassMember(block) => block.statements().len() == 0,
JsSwitchStatement(statement) => statement.cases().len() == 0,
}
}
1 change: 0 additions & 1 deletion crates/biome_js_analyze/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,6 @@ impl PossibleOptions {
options.visit_map(key.syntax(), value.syntax(), diagnostics)?;
*self = PossibleOptions::RestrictedGlobals(options);
}

_ => (),
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
function foo() {}

const bar = () => {};

function fooWithNestedEmptyFnBlock() {
let a = 1;

function shouldFail(){}

return a
}


const barWithNestedEmptyFnBlock = () => {
let a = 1;

const shouldFail = () => {}

return a
}

let someVar;
if (someVar) {
}

while (someVar) {
}

switch(someVar) {
}

try {
doSomething();
} catch(ex) {

} finally {

}

class Foo {
static {}
}

for(let i; i>0; i++){}

const ob = {}
for (key in ob) {}

const ar = []
for (val of ar) {}

function fooWithInternalEmptyBlocks(){
let someVar;
if (someVar) {}

while (someVar) {
}

switch(someVar) {
}

try {
doSomething();
} catch(ex) {

} finally {

}
}
Loading

0 comments on commit 7b6520b

Please sign in to comment.