diff --git a/crates/oxc_ast/src/trivia.rs b/crates/oxc_ast/src/trivia.rs index 230680c629fae3..52e4090d2c44f9 100644 --- a/crates/oxc_ast/src/trivia.rs +++ b/crates/oxc_ast/src/trivia.rs @@ -64,4 +64,11 @@ impl Trivias { let comment = Comment::new(span.end, CommentKind::MultiLine); self.comments.insert(span.start, comment); } + + pub fn comments_spans(&self) -> Vec<(&Comment, Span)> { + self.comments() + .into_iter() + .map(|(start, comment)| (comment, Span::new(*start, comment.end))) + .collect() + } } diff --git a/crates/oxc_linter/src/rules/eslint/no_fallthrough.rs b/crates/oxc_linter/src/rules/eslint/no_fallthrough.rs index 11c1aea912d872..141f1315f969f6 100644 --- a/crates/oxc_linter/src/rules/eslint/no_fallthrough.rs +++ b/crates/oxc_linter/src/rules/eslint/no_fallthrough.rs @@ -1,9 +1,13 @@ +// Ported from https://github.com/eslint/eslint/blob/main/lib/rules/no-fallthrough.js +use lazy_static::lazy_static; +use oxc_ast::AstKind; use oxc_diagnostics::{ miette::{self, Diagnostic}, thiserror::Error, }; use oxc_macros::declare_oxc_lint; use oxc_span::Span; +use regex::Regex; use crate::{context::LintContext, rule::Rule, AstNode}; @@ -30,7 +34,21 @@ declare_oxc_lint!( ); impl Rule for NoFallthrough { - fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {} + fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { + lazy_static! { + static ref DEFAULT_FALLTHROUGH_COMMENT_PATTERN: Regex = + Regex::new(r"(?iu)falls?\s?through").unwrap(); + } + if let AstKind::SwitchStatement(expr) = node.kind() { + let haha = ctx.semantic().trivias().comments_spans(); + for (c, span) in ctx.semantic().trivias().comments_spans() { + let s = span.source_text(ctx.source_text()).trim(); + let res = DEFAULT_FALLTHROUGH_COMMENT_PATTERN.is_match(s); + dbg!(s); + dbg!(res); + } + } + } } #[test] @@ -39,154 +57,154 @@ fn test() { let pass = vec![ ("switch(foo) { case 0: a(); /* falls through */ case 1: b(); }", None), - ("switch(foo) { case 0: a()\n /* falls through */ case 1: b(); }", None), - ("switch(foo) { case 0: a(); /* fall through */ case 1: b(); }", None), - ("switch(foo) { case 0: a(); /* fallthrough */ case 1: b(); }", None), - ("switch(foo) { case 0: a(); /* FALLS THROUGH */ case 1: b(); }", None), - ("switch(foo) { case 0: { a(); /* falls through */ } case 1: b(); }", None), - ("switch(foo) { case 0: { a()\n /* falls through */ } case 1: b(); }", None), - ("switch(foo) { case 0: { a(); /* fall through */ } case 1: b(); }", None), - ("switch(foo) { case 0: { a(); /* fallthrough */ } case 1: b(); }", None), - ("switch(foo) { case 0: { a(); /* FALLS THROUGH */ } case 1: b(); }", None), - ("switch(foo) { case 0: { a(); } /* falls through */ case 1: b(); }", None), - ("switch(foo) { case 0: { a(); /* falls through */ } /* comment */ case 1: b(); }", None), - ("switch(foo) { case 0: { /* falls through */ } case 1: b(); }", None), - ("function foo() { switch(foo) { case 0: a(); return; case 1: b(); }; }", None), - ("switch(foo) { case 0: a(); throw 'foo'; case 1: b(); }", None), - ("while (a) { switch(foo) { case 0: a(); continue; case 1: b(); } }", None), - ("switch(foo) { case 0: a(); break; case 1: b(); }", None), - ("switch(foo) { case 0: case 1: a(); break; case 2: b(); }", None), - ("switch(foo) { case 0: case 1: break; case 2: b(); }", None), - ("switch(foo) { case 0: case 1: break; default: b(); }", None), - ("switch(foo) { case 0: case 1: a(); }", None), - ("switch(foo) { case 0: case 1: a(); break; }", None), - ("switch(foo) { case 0: case 1: break; }", None), - ("switch(foo) { case 0:\n case 1: break; }", None), - ("switch(foo) { case 0: // comment\n case 1: break; }", None), - ("function foo() { switch(foo) { case 0: case 1: return; } }", None), - ("function foo() { switch(foo) { case 0: {return;}\n case 1: {return;} } }", None), - ("switch(foo) { case 0: case 1: {break;} }", None), - ("switch(foo) { }", None), - ( - "switch(foo) { case 0: switch(bar) { case 2: break; } /* falls through */ case 1: break; }", - None, - ), - ("function foo() { switch(foo) { case 1: return a; a++; }}", None), - ("switch (foo) { case 0: a(); /* falls through */ default: b(); /* comment */ }", None), - ("switch (foo) { case 0: a(); /* falls through */ default: /* comment */ b(); }", None), - ("switch (foo) { case 0: if (a) { break; } else { throw 0; } default: b(); }", None), - ("switch (foo) { case 0: try { break; } finally {} default: b(); }", None), - ("switch (foo) { case 0: try {} finally { break; } default: b(); }", None), - ("switch (foo) { case 0: try { throw 0; } catch (err) { break; } default: b(); }", None), - ("switch (foo) { case 0: do { throw 0; } while(a); default: b(); }", None), - ( - "switch (foo) { case 0: a(); \n// eslint-disable-next-line no-fallthrough\n case 1: }", - None, - ), - ( - "switch(foo) { case 0: a(); /* no break */ case 1: b(); }", - Some(serde_json::json!([{ - "commentPattern": "no break" - }])), - ), - ( - "switch(foo) { case 0: a(); /* no break: need to execute b() */ case 1: b(); }", - Some(serde_json::json!([{ - "commentPattern": "no break:\\s?\\w+" - }])), - ), - ( - "switch(foo) { case 0: a();\n// need to execute b(), so\n// falling through\n case 1: b(); }", - Some(serde_json::json!([{ - "commentPattern": "falling through" - }])), - ), - ( - "switch(foo) { case 0: a(); /* break omitted */ default: b(); /* comment */ }", - Some(serde_json::json!([{ - "commentPattern": "break omitted" - }])), - ), - ( - "switch(foo) { case 0: a(); /* caution: break is omitted intentionally */ case 1: b(); /* break omitted */ default: c(); }", - Some(serde_json::json!([{ - "commentPattern": "break[\\s\\w]+omitted" - }])), - ), - ( - "switch(foo) { case 0: \n\n\n case 1: b(); }", - Some(serde_json::json!([{ "allowEmptyCase": true }])), - ), - ( - "switch(foo) { case 0: \n /* with comments */ \n case 1: b(); }", - Some(serde_json::json!([{ "allowEmptyCase": true }])), - ), - ( - "switch (a) {\n case 1: ; break; \n case 3: }", - Some(serde_json::json!([{ "allowEmptyCase": true }])), - ), - ( - "switch (a) {\n case 1: ; break; \n case 3: }", - Some(serde_json::json!([{ "allowEmptyCase": false }])), - ), + // ("switch(foo) { case 0: a()\n /* falls through */ case 1: b(); }", None), + // ("switch(foo) { case 0: a(); /* fall through */ case 1: b(); }", None), + // ("switch(foo) { case 0: a(); /* fallthrough */ case 1: b(); }", None), + // ("switch(foo) { case 0: a(); /* FALLS THROUGH */ case 1: b(); }", None), + // ("switch(foo) { case 0: { a(); /* falls through */ } case 1: b(); }", None), + // ("switch(foo) { case 0: { a()\n /* falls through */ } case 1: b(); }", None), + // ("switch(foo) { case 0: { a(); /* fall through */ } case 1: b(); }", None), + // ("switch(foo) { case 0: { a(); /* fallthrough */ } case 1: b(); }", None), + // ("switch(foo) { case 0: { a(); /* FALLS THROUGH */ } case 1: b(); }", None), + // ("switch(foo) { case 0: { a(); } /* falls through */ case 1: b(); }", None), + // ("switch(foo) { case 0: { a(); /* falls through */ } /* comment */ case 1: b(); }", None), + // ("switch(foo) { case 0: { /* falls through */ } case 1: b(); }", None), + // ("function foo() { switch(foo) { case 0: a(); return; case 1: b(); }; }", None), + // ("switch(foo) { case 0: a(); throw 'foo'; case 1: b(); }", None), + // ("while (a) { switch(foo) { case 0: a(); continue; case 1: b(); } }", None), + // ("switch(foo) { case 0: a(); break; case 1: b(); }", None), + // ("switch(foo) { case 0: case 1: a(); break; case 2: b(); }", None), + // ("switch(foo) { case 0: case 1: break; case 2: b(); }", None), + // ("switch(foo) { case 0: case 1: break; default: b(); }", None), + // ("switch(foo) { case 0: case 1: a(); }", None), + // ("switch(foo) { case 0: case 1: a(); break; }", None), + // ("switch(foo) { case 0: case 1: break; }", None), + // ("switch(foo) { case 0:\n case 1: break; }", None), + // ("switch(foo) { case 0: // comment\n case 1: break; }", None), + // ("function foo() { switch(foo) { case 0: case 1: return; } }", None), + // ("function foo() { switch(foo) { case 0: {return;}\n case 1: {return;} } }", None), + // ("switch(foo) { case 0: case 1: {break;} }", None), + // ("switch(foo) { }", None), + // ( + // "switch(foo) { case 0: switch(bar) { case 2: break; } /* falls through */ case 1: break; }", + // None, + // ), + // ("function foo() { switch(foo) { case 1: return a; a++; }}", None), + // ("switch (foo) { case 0: a(); /* falls through */ default: b(); /* comment */ }", None), + // ("switch (foo) { case 0: a(); /* falls through */ default: /* comment */ b(); }", None), + // ("switch (foo) { case 0: if (a) { break; } else { throw 0; } default: b(); }", None), + // ("switch (foo) { case 0: try { break; } finally {} default: b(); }", None), + // ("switch (foo) { case 0: try {} finally { break; } default: b(); }", None), + // ("switch (foo) { case 0: try { throw 0; } catch (err) { break; } default: b(); }", None), + // ("switch (foo) { case 0: do { throw 0; } while(a); default: b(); }", None), + // ( + // "switch (foo) { case 0: a(); \n// eslint-disable-next-line no-fallthrough\n case 1: }", + // None, + // ), + // ( + // "switch(foo) { case 0: a(); /* no break */ case 1: b(); }", + // Some(serde_json::json!([{ + // "commentPattern": "no break" + // }])), + // ), + // ( + // "switch(foo) { case 0: a(); /* no break: need to execute b() */ case 1: b(); }", + // Some(serde_json::json!([{ + // "commentPattern": "no break:\\s?\\w+" + // }])), + // ), + // ( + // "switch(foo) { case 0: a();\n// need to execute b(), so\n// falling through\n case 1: b(); }", + // Some(serde_json::json!([{ + // "commentPattern": "falling through" + // }])), + // ), + // ( + // "switch(foo) { case 0: a(); /* break omitted */ default: b(); /* comment */ }", + // Some(serde_json::json!([{ + // "commentPattern": "break omitted" + // }])), + // ), + // ( + // "switch(foo) { case 0: a(); /* caution: break is omitted intentionally */ case 1: b(); /* break omitted */ default: c(); }", + // Some(serde_json::json!([{ + // "commentPattern": "break[\\s\\w]+omitted" + // }])), + // ), + // ( + // "switch(foo) { case 0: \n\n\n case 1: b(); }", + // Some(serde_json::json!([{ "allowEmptyCase": true }])), + // ), + // ( + // "switch(foo) { case 0: \n /* with comments */ \n case 1: b(); }", + // Some(serde_json::json!([{ "allowEmptyCase": true }])), + // ), + // ( + // "switch (a) {\n case 1: ; break; \n case 3: }", + // Some(serde_json::json!([{ "allowEmptyCase": true }])), + // ), + // ( + // "switch (a) {\n case 1: ; break; \n case 3: }", + // Some(serde_json::json!([{ "allowEmptyCase": false }])), + // ), ]; let fail = vec![ ("switch(foo) { case 0: a();\ncase 1: b() }", None), - ("switch(foo) { case 0: a();\ndefault: b() }", None), - ("switch(foo) { case 0: a(); default: b() }", None), - ("switch(foo) { case 0: if (a) { break; } default: b() }", None), - ("switch(foo) { case 0: try { throw 0; } catch (err) {} default: b() }", None), - ("switch(foo) { case 0: while (a) { break; } default: b() }", None), - ("switch(foo) { case 0: do { break; } while (a); default: b() }", None), - ("switch(foo) { case 0:\n\n default: b() }", None), - ("switch(foo) { case 0: {} default: b() }", None), - ("switch(foo) { case 0: a(); { /* falls through */ } default: b() }", None), - ("switch(foo) { case 0: { /* falls through */ } a(); default: b() }", None), - ("switch(foo) { case 0: if (a) { /* falls through */ } default: b() }", None), - ("switch(foo) { case 0: { { /* falls through */ } } default: b() }", None), - ("switch(foo) { case 0: { /* comment */ } default: b() }", None), - ("switch(foo) { case 0:\n // comment\n default: b() }", None), - ("switch(foo) { case 0: a(); /* falling through */ default: b() }", None), - ( - "switch(foo) { case 0: a();\n/* no break */\ncase 1: b(); }", - Some(serde_json::json!([{ - "commentPattern": "break omitted" - }])), - ), - ( - "switch(foo) { case 0: a();\n/* no break */\n/* todo: fix readability */\ndefault: b() }", - Some(serde_json::json!([{ - "commentPattern": "no break" - }])), - ), - ( - "switch(foo) { case 0: { a();\n/* no break */\n/* todo: fix readability */ }\ndefault: b() }", - Some(serde_json::json!([{ - "commentPattern": "no break" - }])), - ), - ("switch(foo) { case 0: \n /* with comments */ \ncase 1: b(); }", None), - ( - "switch(foo) { case 0:\n\ncase 1: b(); }", - Some(serde_json::json!([{ - "allowEmptyCase": false - }])), - ), - ("switch(foo) { case 0:\n\ncase 1: b(); }", Some(serde_json::json!([{}]))), - ( - "switch (a) { case 1: \n ; case 2: }", - Some(serde_json::json!([{ "allowEmptyCase": false }])), - ), - ( - "switch (a) { case 1: ; case 2: ; case 3: }", - Some(serde_json::json!([{ "allowEmptyCase": true }])), - ), - ( - "switch (foo) { case 0: a(); \n// eslint-enable no-fallthrough\n case 1: }", - Some(serde_json::json!([{}])), - ), + // ("switch(foo) { case 0: a();\ndefault: b() }", None), + // ("switch(foo) { case 0: a(); default: b() }", None), + // ("switch(foo) { case 0: if (a) { break; } default: b() }", None), + // ("switch(foo) { case 0: try { throw 0; } catch (err) {} default: b() }", None), + // ("switch(foo) { case 0: while (a) { break; } default: b() }", None), + // ("switch(foo) { case 0: do { break; } while (a); default: b() }", None), + // ("switch(foo) { case 0:\n\n default: b() }", None), + // ("switch(foo) { case 0: {} default: b() }", None), + // ("switch(foo) { case 0: a(); { /* falls through */ } default: b() }", None), + // ("switch(foo) { case 0: { /* falls through */ } a(); default: b() }", None), + // ("switch(foo) { case 0: if (a) { /* falls through */ } default: b() }", None), + // ("switch(foo) { case 0: { { /* falls through */ } } default: b() }", None), + // ("switch(foo) { case 0: { /* comment */ } default: b() }", None), + // ("switch(foo) { case 0:\n // comment\n default: b() }", None), + // ("switch(foo) { case 0: a(); /* falling through */ default: b() }", None), + // ( + // "switch(foo) { case 0: a();\n/* no break */\ncase 1: b(); }", + // Some(serde_json::json!([{ + // "commentPattern": "break omitted" + // }])), + // ), + // ( + // "switch(foo) { case 0: a();\n/* no break */\n/* todo: fix readability */\ndefault: b() }", + // Some(serde_json::json!([{ + // "commentPattern": "no break" + // }])), + // ), + // ( + // "switch(foo) { case 0: { a();\n/* no break */\n/* todo: fix readability */ }\ndefault: b() }", + // Some(serde_json::json!([{ + // "commentPattern": "no break" + // }])), + // ), + // ("switch(foo) { case 0: \n /* with comments */ \ncase 1: b(); }", None), + // ( + // "switch(foo) { case 0:\n\ncase 1: b(); }", + // Some(serde_json::json!([{ + // "allowEmptyCase": false + // }])), + // ), + // ("switch(foo) { case 0:\n\ncase 1: b(); }", Some(serde_json::json!([{}]))), + // ( + // "switch (a) { case 1: \n ; case 2: }", + // Some(serde_json::json!([{ "allowEmptyCase": false }])), + // ), + // ( + // "switch (a) { case 1: ; case 2: ; case 3: }", + // Some(serde_json::json!([{ "allowEmptyCase": true }])), + // ), + // ( + // "switch (foo) { case 0: a(); \n// eslint-enable no-fallthrough\n case 1: }", + // Some(serde_json::json!([{}])), + // ), ]; - Tester::new(NoFallthrough::NAME, pass, fail).test_and_snapshot(); + Tester::new(NoFallthrough::NAME, pass, fail).test(); }