diff --git a/crates/oxc_linter/src/ast_util.rs b/crates/oxc_linter/src/ast_util.rs index d2616d8294f1b..c10289a56cca3 100644 --- a/crates/oxc_linter/src/ast_util.rs +++ b/crates/oxc_linter/src/ast_util.rs @@ -1,5 +1,6 @@ use std::hash::{Hash, Hasher}; +use oxc_ast::ast::BindingIdentifier; use oxc_ast::AstKind; use oxc_semantic::{AstNode, SymbolId}; use oxc_span::{GetSpan, Span}; @@ -239,6 +240,18 @@ pub fn outermost_paren_parent<'a, 'b>( .find(|parent| !matches!(parent.kind(), AstKind::ParenthesizedExpression(_))) } +pub fn nth_outermost_paren_parent<'a, 'b>( + node: &'b AstNode<'a>, + ctx: &'b LintContext<'a>, + n: usize, +) -> Option<&'b AstNode<'a>> { + ctx.nodes() + .iter_parents(node.id()) + .skip(1) + .filter(|parent| !matches!(parent.kind(), AstKind::ParenthesizedExpression(_))) + .nth(n) +} + pub fn get_declaration_of_variable<'a, 'b>( ident: &IdentifierReference, ctx: &'b LintContext<'a>, @@ -393,3 +406,17 @@ pub fn is_function_node(node: &AstNode) -> bool { _ => false, } } + +pub fn get_function_like_declaration<'b>( + node: &AstNode<'b>, + ctx: &LintContext<'b>, +) -> Option<&'b BindingIdentifier<'b>> { + let parent = outermost_paren_parent(node, ctx)?; + + if let AstKind::VariableDeclarator(decl) = parent.kind() { + let ident = decl.id.get_binding_identifier()?; + Some(ident) + } else { + None + } +} diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs index ebed8e21138bc..fcc3ef9c07052 100644 --- a/crates/oxc_linter/src/rules.rs +++ b/crates/oxc_linter/src/rules.rs @@ -254,6 +254,7 @@ mod react_perf { mod unicorn { pub mod catch_error_name; + pub mod consistent_function_scoping; pub mod empty_brace_spaces; pub mod error_message; pub mod escape_case; @@ -643,6 +644,7 @@ oxc_macros::declare_all_lint_rules! { jest::valid_expect, jest::valid_title, unicorn::catch_error_name, + unicorn::consistent_function_scoping, unicorn::empty_brace_spaces, unicorn::error_message, unicorn::escape_case, diff --git a/crates/oxc_linter/src/rules/oxc/only_used_in_recursion.rs b/crates/oxc_linter/src/rules/oxc/only_used_in_recursion.rs index 4d6a9b2e58d4f..57d68c31966d7 100644 --- a/crates/oxc_linter/src/rules/oxc/only_used_in_recursion.rs +++ b/crates/oxc_linter/src/rules/oxc/only_used_in_recursion.rs @@ -7,7 +7,7 @@ use oxc_macros::declare_oxc_lint; use oxc_span::{GetSpan, Span}; use crate::{ - ast_util::outermost_paren_parent, context::LintContext, fixer::Fix, rule::Rule, AstNode, + ast_util::get_function_like_declaration, context::LintContext, fixer::Fix, rule::Rule, AstNode, }; fn only_used_in_recursion_diagnostic(span0: Span, x1: &str) -> OxcDiagnostic { @@ -162,20 +162,6 @@ impl Rule for OnlyUsedInRecursion { } } -fn get_function_like_declaration<'b>( - node: &AstNode<'b>, - ctx: &LintContext<'b>, -) -> Option<&'b BindingIdentifier<'b>> { - let parent = outermost_paren_parent(node, ctx)?; - - if let AstKind::VariableDeclarator(decl) = parent.kind() { - let ident = decl.id.get_binding_identifier()?; - Some(ident) - } else { - None - } -} - fn is_argument_only_used_in_recursion<'a>( function_id: &'a BindingIdentifier, arg: &'a BindingIdentifier, diff --git a/crates/oxc_linter/src/rules/unicorn/consistent_function_scoping.rs b/crates/oxc_linter/src/rules/unicorn/consistent_function_scoping.rs new file mode 100644 index 0000000000000..6b26cc25051f2 --- /dev/null +++ b/crates/oxc_linter/src/rules/unicorn/consistent_function_scoping.rs @@ -0,0 +1,1205 @@ +use oxc_ast::{AstKind, Visit}; +use oxc_diagnostics::OxcDiagnostic; +use oxc_macros::declare_oxc_lint; +use oxc_semantic::ReferenceId; +use oxc_span::{GetSpan, Span}; +use rustc_hash::FxHashSet; + +use crate::{ + ast_util::{get_function_like_declaration, nth_outermost_paren_parent, outermost_paren_parent}, + context::LintContext, + rule::Rule, + utils::is_react_hook, + AstNode, +}; + +fn consistent_function_scoping(x0: Span) -> OxcDiagnostic { + OxcDiagnostic::warn("Function does not capture any variables from the outer scope.") + .with_help("Move this function to the outer scope.") + .with_label(x0) +} + +#[derive(Debug, Default, Clone)] +pub struct ConsistentFunctionScoping(Box); + +#[derive(Debug, Clone)] +pub struct ConsistentFunctionScopingConfig { + check_arrow_functions: bool, +} + +impl Default for ConsistentFunctionScopingConfig { + fn default() -> Self { + Self { check_arrow_functions: true } + } +} + +impl std::ops::Deref for ConsistentFunctionScoping { + type Target = ConsistentFunctionScopingConfig; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +declare_oxc_lint!( + /// ### What it does + /// + /// Disallow functions that are declared in a scope which does not capture any variables from the outer scope. + /// + /// ### Why is this bad? + /// + /// Moving function declarations to the highest possible scope improves readability, directly improves performance and allows JavaScript engines to better optimize your performance. + /// + /// + /// ### Examples + /// + /// Examples of **incorrect** code for this rule: + /// ```js + /// export function doFoo(foo) { + /// // Does not capture anything from the scope, can be moved to the outer scope + /// function doBar(bar) { + /// return bar === 'bar'; + /// } + /// return doBar; + /// } + /// function doFoo(foo) { + /// const doBar = bar => { + /// return bar === 'bar'; + /// }; + /// } + /// ``` + /// + /// Examples of **correct** code for this rule: + /// ```js + /// function doBar(bar) { + /// return bar === 'bar'; + /// } + /// + /// export function doFoo(foo) { + /// return doBar; + /// } + /// + /// export function doFoo(foo) { + /// function doBar(bar) { + /// return bar === 'bar' && foo.doBar(bar); + /// } + /// + /// return doBar; + /// } + /// ``` + ConsistentFunctionScoping, + correctness, + pending +); + +impl Rule for ConsistentFunctionScoping { + fn from_configuration(value: serde_json::Value) -> Self { + let mut configuration = ConsistentFunctionScopingConfig::default(); + + if let Some(config) = value.get(0) { + if let Some(val) = + config.get("checkArrowFunctions").and_then(serde_json::Value::as_bool) + { + configuration.check_arrow_functions = val; + } + } + + Self(Box::new(configuration)) + } + fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { + let (function_declaration_symbol_id, function_body, reporter_span) = match node.kind() { + AstKind::Function(function) => { + if function.is_typescript_syntax() { + return; + } + + let Some(function_body) = &function.body else { return }; + + if let Some(binding_ident) = get_function_like_declaration(node, ctx) { + ( + binding_ident.symbol_id.get().unwrap(), + function_body, + Span::new(function.span.start, function.span.start + 8), + ) + } else if let Some(function_id) = &function.id { + (function_id.symbol_id.get().unwrap(), function_body, function_id.span()) + } else { + return; + } + } + AstKind::ArrowFunctionExpression(arrow_function) => { + if !self.check_arrow_functions { + return; + } + if let Some(binding_ident) = get_function_like_declaration(node, ctx) { + ( + binding_ident.symbol_id.get().unwrap(), + &arrow_function.body, + binding_ident.span(), + ) + } else { + return; + } + } + _ => return, + }; + // if the function is declared at the root scope, we don't need to check anything + if ctx.symbols().get_scope_id(function_declaration_symbol_id) + == ctx.scopes().root_scope_id() + { + return; + } + + if let Some(AstKind::ReturnStatement(_)) = + outermost_paren_parent(node, ctx).map(oxc_semantic::AstNode::kind) + { + return; + } + + if is_parent_scope_iife(node, ctx) || is_in_react_hook(node, ctx) { + return; + } + + // get all references in the function body + let (function_body_var_references, is_parent_this_referenced) = { + let mut rf = ReferencesFinder::default(); + rf.visit_function_body(function_body); + (rf.references, rf.is_parent_this_referenced) + }; + + if is_parent_this_referenced && matches!(node.kind(), AstKind::ArrowFunctionExpression(_)) { + return; + } + + let parent_scope_ids = { + let mut current_scope_id = ctx.symbols().get_scope_id(function_declaration_symbol_id); + let mut parent_scope_ids = FxHashSet::default(); + parent_scope_ids.insert(current_scope_id); + while let Some(parent_scope_id) = ctx.scopes().get_parent_id(current_scope_id) { + parent_scope_ids.insert(parent_scope_id); + current_scope_id = parent_scope_id; + } + parent_scope_ids + }; + + for reference_id in function_body_var_references { + let reference = ctx.symbols().get_reference(reference_id); + let Some(symbol_id) = reference.symbol_id() else { continue }; + let scope_id = ctx.symbols().get_scope_id(symbol_id); + if parent_scope_ids.contains(&scope_id) && symbol_id != function_declaration_symbol_id { + return; + } + } + + ctx.diagnostic(consistent_function_scoping(reporter_span)); + } +} + +#[derive(Default)] +struct ReferencesFinder { + is_parent_this_referenced: bool, + references: Vec, + in_function: usize, +} + +impl<'a> Visit<'a> for ReferencesFinder { + fn visit_identifier_reference(&mut self, it: &oxc_ast::ast::IdentifierReference<'a>) { + self.references.push(it.reference_id().unwrap()); + } + + fn visit_this_expression(&mut self, _: &oxc_ast::ast::ThisExpression) { + if self.in_function == 0 { + self.is_parent_this_referenced = true; + } + } + + fn enter_node(&mut self, kind: AstKind<'a>) { + if let AstKind::Function(_) = kind { + self.in_function += 1; + } + } + fn leave_node(&mut self, kind: AstKind<'a>) { + if let AstKind::Function(_) = kind { + self.in_function -= 1; + } + } +} + +fn is_parent_scope_iife<'a>(node: &AstNode<'a>, ctx: &LintContext<'a>) -> bool { + if let Some(parent_node) = outermost_paren_parent(node, ctx) { + if let Some(parent_node) = outermost_paren_parent(parent_node, ctx) { + if matches!( + parent_node.kind(), + AstKind::Function(_) | AstKind::ArrowFunctionExpression(_) + ) { + if let Some(parent_node) = outermost_paren_parent(parent_node, ctx) { + return matches!(parent_node.kind(), AstKind::CallExpression(_)); + } + } + } + } + + false +} + +fn is_in_react_hook<'a>(node: &AstNode<'a>, ctx: &LintContext<'a>) -> bool { + // we want the 3rd outermost parent + // parents are: function body -> function -> argument -> call expression + if let Some(parent) = nth_outermost_paren_parent(node, ctx, 3) { + if let AstKind::CallExpression(call_expr) = parent.kind() { + return is_react_hook(&call_expr.callee); + } + } + false +} + +#[test] +fn test() { + use crate::tester::Tester; + + let pass = vec![ + ( + " + function doFoo(foo) { + return foo; + } + ", + None, + ), + ( + " + function doFoo(foo) { + return bar; + } + ", + None, + ), + ( + " + const doFoo = function() {}; + ", + None, + ), + ( + " + const doFoo = foo => foo; + ", + None, + ), + ( + " + foo => foo; + ", + None, + ), + ( + " + function doFoo(foo) { + function doBar(bar) { + return foo + bar; + } + return foo; + } + ", + None, + ), + ( + " + const doFoo = function(foo) { + function doBar(bar) { + return foo + bar; + } + return foo; + }; + ", + None, + ), + ( + " + const doFoo = function(foo) { + const doBar = function(bar) { + return foo + bar; + }; + return foo; + }; + ", + None, + ), + ( + " + function doFoo(foo) { + const doBar = function(bar) { + return foo + bar; + }; + return foo; + } + ", + None, + ), + ( + " + function doFoo(foo) { + function doBar(bar) { + return foo + bar; + } + } + ", + None, + ), + ( + " + function doFoo(foo = 'foo') { + function doBar(bar) { + return foo + bar; + } + } + ", + None, + ), + ( + " + function doFoo() { + const foo = 'foo'; + function doBar(bar) { + return foo + bar; + } + return foo; + } + ", + None, + ), + ( + " + function doFoo(foo) { + function doBar(bar) { + function doZaz(zaz) { + return foo + bar + zaz; + } + return bar; + } + return foo; + } + ", + None, + ), + ( + " + for (let foo = 0; foo < 1; foo++) { + function doBar(bar) { + return bar + foo; + } + } + ", + None, + ), + ( + " + let foo = 0; + function doFoo() { + foo = 1; + function doBar(bar) { + return foo + bar; + } + return foo; + } + ", + None, + ), + ( + " + const doFoo = foo => { + return foo; + } + ", + None, + ), + ( + " + const doFoo = + foo => + bar => + foo + bar; + ", + None, + ), + ( + " + const doFoo = () => { + return bar => bar; + } + ", + None, + ), + ( + " + function doFoo() { + return bar => bar; + } + ", + None, + ), + ( + " + const doFoo = foo => { + const doBar = bar => { + return foo + bar; + } + return foo; + } + ", + None, + ), + ( + " + function doFoo() { + { + const foo = 'foo'; + function doBar(bar) { + return bar + foo; + } + } + } + ", + None, + ), + ( + " + function doFoo(foo) { + function doBar(bar) { + foo.bar = bar; + } + function doZaz(zaz) { + doBar(zaz); + } + + doZaz('zaz'); + }; + ", + None, + ), + ( + " + function doFoo() { + return function doBar() {}; + } + ", + None, + ), + ( + " + function doFoo(Foo) { + function doBar() { + return new Foo(); + } + return doBar; + }; + ", + None, + ), + ( + " + function doFoo(FooComponent) { + return ; + } + ", + None, + ), + ( + " + const foo = ; + ", + None, + ), + ( + " + function foo() { + function bar() { + return ; + } + } + ", + None, + ), + ( + " + function doFoo(Foo) { + const doBar = () => this; + return doBar(); + }; + ", + None, + ), + ( + " + function doFoo(Foo) { + const doBar = () => () => this; + return doBar(); + }; + ", + None, + ), + ( + " + function doFoo(Foo) { + const doBar = () => () => () => this; + return doBar(); + }; + ", + None, + ), + ( + " + useEffect(() => { + function foo() {} + }, []) + ", + None, + ), + ( + " + React.useEffect(() => { + function foo() {} + }, []) + ", + None, + ), + ( + " + (function() { + function bar() {} + })(); + ", + None, + ), + ( + " + (function() { + function bar() {} + }()); + ", + None, + ), + ( + " + !function() { + function bar() {} + }(); + ", + None, + ), + ( + " + (() => { + function bar() {} + })(); + ", + None, + ), + ( + " + (async function() { + function bar() {} + })(); + ", + None, + ), + ( + " + (async function * () { + function bar() {} + })(); + ", + None, + ), + ( + " + function doFoo() { + const doBar = (function(bar) { + return bar; + })(); + } + ", + None, + ), + ( + " + const enrichErrors = (packageName, cliArgs, f) => async (...args) => { + try { + return await f(...args); + } catch (error) { + error.packageName = packageName; + error.cliArgs = cliArgs; + throw error; + } + }; + ", + None, + ), + ( + " + export const canStepForward = ([X, Y]) => ([x, y]) => direction => { + switch (direction) { + case 0: + return y !== 0 + case 1: + return x !== X - 1 + case 2: + return y !== Y - 1 + case 3: + return x !== 0 + default: + throw new Error('unknown direction') + } + } + ", + None, + ), + ( + " + 'use strict'; + + module.exports = function recordErrors(eventEmitter, stateArgument) { + const stateVariable = stateArgument; + function onError(error) { + stateVariable.inputError = error; + } + eventEmitter.once('error', onError); + }; + ", + None, + ), + ( + " + module.exports = function recordErrors(eventEmitter, stateArgument) { + function onError(error) { + stateArgument.inputError = error; + } + function onError2(error) { + onError(error); + } + + eventEmitter.once('error', onError2); + }; + ", + None, + ), + ( + " + function outer(stream) { + let content; + + function inner() { + process.stdout.write(content); + } + + inner(); + } + ", + None, + ), + ( + " + function outer () { + const inner = () => {} + } + ", + Some(serde_json::json!([{"checkArrowFunctions": false}])), + ), + ( + " + type Data = T extends 'error' ? Error : Record | unknown[] + + type Method = 'info' | 'error' + + export function createLogger(name: string) { + // Two lint errors are on the next line. + const log = (method: T) => (data: Data) => { + try { + // eslint-disable-next-line no-console + console[method](JSON.stringify({ name, data })) + } catch (error) { + console.error(error) + } + } + + return { + info: log('info'), + error: log('error'), + } + } + ", + None, + ), + ( + " + test('it works', async function(assert) { + function assertHeader(assertions) { + for (const [key, value] of Object.entries(assertions)) { + assert.strictEqual( + native[key], + value + ); + } + } + + // ... + }); + ", + None, + ), + ( + " + export function a(x: number) { + const b = (y: number) => (z: number): number => x + y + z; + return b(1)(2); + } + ", + None, + ), + ]; + + let fail = vec![ + // start of cases that eslint-plugin-unicorn passes, but we fail. + + // declared function is inside a block statement + ( + " + function doFoo(foo) { + { + function doBar(bar) { + return bar; + } + } + return foo; + } + ", + None, + ), + ( + " + function doFoo(FooComponent) { + function Bar() { + return ; + } + return Bar; + }; + ", + None, + ), + ( + " + function Foo() { + function Bar () { + return
+ } + return
{ Bar() }
+ } + ", + None, + ), + ( + " + function foo() { + function bar() { + return ; + } + } + ", + None, + ), + ( + " + function doFoo(Foo) { + const doBar = () => arguments; + return doBar(); + }; + ", + None, + ), + // end of cases that eslint-plugin-unicorn passes, but we fail. + ( + " + function doFoo(foo) { + function doBar(bar) { + return bar; + } + return foo; + } + ", + None, + ), + ( + " + function doFoo() { + const foo = 'foo'; + function doBar(bar) { + return bar; + } + return foo; + } + ", + None, + ), + ( + " + function doFoo() { + function doBar(bar) { + return bar; + } + } + ", + None, + ), + ( + " + const doFoo = function() { + function doBar(bar) { + return bar; + } + }; + ", + None, + ), + ( + " + const doFoo = function() { + const doBar = function(bar) { + return bar; + }; + }; + ", + None, + ), + ( + " + function doFoo() { + const doBar = function(bar) { + return bar; + }; + } + ", + None, + ), + ( + " + function doFoo() { + const doBar = function(bar) { + return bar; + }; + doBar(); + } + ", + None, + ), + ( + " + const doFoo = () => { + const doBar = bar => { + return bar; + } + } + ", + None, + ), + ( + " + function doFoo(Foo) { + function doBar() { + return this; + } + return doBar(); + }; + ", + None, + ), + ( + " + function doFoo(Foo) { + const doBar = () => (function() {return this})(); + return doBar(); + }; + ", + None, + ), + ( + " + function doFoo(Foo) { + const doBar = () => (function() {return () => this})(); + return doBar(); + }; + ", + None, + ), + ( + " + function doFoo(Foo) { + function doBar() { + return arguments; + } + return doBar(); + }; + ", + None, + ), + ( + " + function doFoo(Foo) { + const doBar = () => (function() {return arguments})(); + return doBar(); + }; + ", + None, + ), + ( + " + function doFoo(foo) { + function doBar(bar) { + return doBar(bar); + } + return foo; + } + ", + None, + ), + ( + " + function doFoo(foo) { + function doBar(bar) { + return bar; + } + return doBar; + } + ", + None, + ), + ( + " + function doFoo() { + function doBar() {} + } + ", + None, + ), + ( + " + function doFoo(foo) { + { + { + function doBar(bar) { + return bar; + } + } + } + return foo; + } + ", + None, + ), + ( + " + { + { + function doBar(bar) { + return bar; + } + } + } + ", + None, + ), + ( + " + for (let foo = 0; foo < 1; foo++) { + function doBar(bar) { + return bar; + } + } + ", + None, + ), + ("function foo() { function bar() {} }", None), + ("function foo() { async function bar() {} }", None), + ("function foo() { function* bar() {} }", None), + ("function foo() { async function* bar() {} }", None), + ("function foo() { const bar = () => {} }", None), + // ("const doFoo = () => bar => bar;", None), + ("function foo() { const bar = async () => {} }", None), + ("function foo() { async function* baz() {} }", None), + ( + " + useEffect(() => { + function foo() { + function bar() { + } + } + }, []) + ", + None, + ), + ( + " + (function() { + function foo() { + function bar() { + } + } + })(); + ", + None, + ), + ( + " + process.nextTick(() => { + function returnsZero() { + return true; + } + process.exitCode = returnsZero(); + }); + ", + None, + ), + ( + " + foo( + // This is not IIFE + function() { + function bar() { + } + }, + // This is IIFE + (function() { + function baz() { + } + })(), + ) + ", + None, + ), + ( + " + // This is IIFE + (function() { + function bar() { + } + })( + // This is not IIFE + function() { + function baz() { + } + }, + ) + ", + None, + ), + ( + " + function Foo() { + const Bar =
+ function doBaz() { + return 42 + } + return
{ doBaz() }
+ } + ", + None, + ), + ( + " + function Foo() { + function Bar () { + return
+ } + function doBaz() { + return 42 + } + return
{ doBaz() }
+ } + ", + None, + ), + ( + " + function fn1() { + function a() { + return ; + } + function b() {} + function c() {} + } + function fn2() { + function foo() {} + } + ", + None, + ), + ( + " + const outer = () => { + function inner() {} + } + ", + Some(serde_json::json!([{"checkArrowFunctions": false}])), + ), + ( + " + function foo() { + function bar() {} + } + ", + None, + ), + ( + " + function foo() { + async function bar() {} + } + ", + None, + ), + ( + " + function foo() { + function * bar() {} + } + ", + None, + ), + ( + " + function foo() { + async function * bar() {} + } + ", + None, + ), + ( + " + function foo() { + const bar = () => {} + } + ", + None, + ), + // ("const doFoo = () => bar => bar;", None), + ( + " + function foo() { + const bar = async () => {} + } + ", + None, + ), + ( + " + function doFoo() { + const doBar = function(bar) { + return bar; + }; + } + ", + None, + ), + ]; + + Tester::new(ConsistentFunctionScoping::NAME, pass, fail).test_and_snapshot(); +} diff --git a/crates/oxc_linter/src/snapshots/consistent_function_scoping.snap b/crates/oxc_linter/src/snapshots/consistent_function_scoping.snap new file mode 100644 index 0000000000000..b61d9a06c64d7 --- /dev/null +++ b/crates/oxc_linter/src/snapshots/consistent_function_scoping.snap @@ -0,0 +1,438 @@ +--- +source: crates/oxc_linter/src/tester.rs +--- + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:4:26] + 3 │ { + 4 │ function doBar(bar) { + · ───── + 5 │ return bar; + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:3:23] + 2 │ function doFoo(FooComponent) { + 3 │ function Bar() { + · ─── + 4 │ return ; + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:3:23] + 2 │ function Foo() { + 3 │ function Bar () { + · ─── + 4 │ return
+ ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:3:23] + 2 │ function foo() { + 3 │ function bar() { + · ─── + 4 │ return ; + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:3:20] + 2 │ function doFoo(Foo) { + 3 │ const doBar = () => arguments; + · ───── + 4 │ return doBar(); + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:3:18] + 2 │ function doFoo(foo) { + 3 │ function doBar(bar) { + · ───── + 4 │ return bar; + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:4:18] + 3 │ const foo = 'foo'; + 4 │ function doBar(bar) { + · ───── + 5 │ return bar; + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:3:18] + 2 │ function doFoo() { + 3 │ function doBar(bar) { + · ───── + 4 │ return bar; + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:3:18] + 2 │ const doFoo = function() { + 3 │ function doBar(bar) { + · ───── + 4 │ return bar; + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:3:23] + 2 │ const doFoo = function() { + 3 │ const doBar = function(bar) { + · ──────── + 4 │ return bar; + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:3:23] + 2 │ function doFoo() { + 3 │ const doBar = function(bar) { + · ──────── + 4 │ return bar; + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:3:23] + 2 │ function doFoo() { + 3 │ const doBar = function(bar) { + · ──────── + 4 │ return bar; + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:3:15] + 2 │ const doFoo = () => { + 3 │ const doBar = bar => { + · ───── + 4 │ return bar; + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:3:18] + 2 │ function doFoo(Foo) { + 3 │ function doBar() { + · ───── + 4 │ return this; + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:3:15] + 2 │ function doFoo(Foo) { + 3 │ const doBar = () => (function() {return this})(); + · ───── + 4 │ return doBar(); + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:3:15] + 2 │ function doFoo(Foo) { + 3 │ const doBar = () => (function() {return () => this})(); + · ───── + 4 │ return doBar(); + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:3:18] + 2 │ function doFoo(Foo) { + 3 │ function doBar() { + · ───── + 4 │ return arguments; + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:3:15] + 2 │ function doFoo(Foo) { + 3 │ const doBar = () => (function() {return arguments})(); + · ───── + 4 │ return doBar(); + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:3:24] + 2 │ function doFoo(foo) { + 3 │ function doBar(bar) { + · ───── + 4 │ return doBar(bar); + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:3:18] + 2 │ function doFoo(foo) { + 3 │ function doBar(bar) { + · ───── + 4 │ return bar; + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:3:18] + 2 │ function doFoo() { + 3 │ function doBar() {} + · ───── + 4 │ } + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:5:20] + 4 │ { + 5 │ function doBar(bar) { + · ───── + 6 │ return bar; + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:4:19] + 3 │ { + 4 │ function doBar(bar) { + · ───── + 5 │ return bar; + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:3:18] + 2 │ for (let foo = 0; foo < 1; foo++) { + 3 │ function doBar(bar) { + · ───── + 4 │ return bar; + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:1:27] + 1 │ function foo() { function bar() {} } + · ─── + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:1:33] + 1 │ function foo() { async function bar() {} } + · ─── + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:1:28] + 1 │ function foo() { function* bar() {} } + · ─── + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:1:34] + 1 │ function foo() { async function* bar() {} } + · ─── + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:1:24] + 1 │ function foo() { const bar = () => {} } + · ─── + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:1:24] + 1 │ function foo() { const bar = async () => {} } + · ─── + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:1:34] + 1 │ function foo() { async function* baz() {} } + · ─── + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:4:19] + 3 │ function foo() { + 4 │ function bar() { + · ─── + 5 │ } + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:4:19] + 3 │ function foo() { + 4 │ function bar() { + · ─── + 5 │ } + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:3:18] + 2 │ process.nextTick(() => { + 3 │ function returnsZero() { + · ─────────── + 4 │ return true; + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:5:19] + 4 │ function() { + 5 │ function bar() { + · ─── + 6 │ } + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:9:19] + 8 │ function() { + 9 │ function baz() { + · ─── + 10 │ } + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:4:18] + 3 │ const Bar =
+ 4 │ function doBaz() { + · ───── + 5 │ return 42 + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:3:18] + 2 │ function Foo() { + 3 │ function Bar () { + · ─── + 4 │ return
+ ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:6:18] + 5 │ } + 6 │ function doBaz() { + · ───── + 7 │ return 42 + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:6:18] + 5 │ } + 6 │ function b() {} + · ─ + 7 │ function c() {} + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:7:18] + 6 │ function b() {} + 7 │ function c() {} + · ─ + 8 │ } + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:10:18] + 9 │ function fn2() { + 10 │ function foo() {} + · ─── + 11 │ } + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:3:18] + 2 │ const outer = () => { + 3 │ function inner() {} + · ───── + 4 │ } + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:3:17] + 2 │ function foo() { + 3 │ function bar() {} + · ─── + 4 │ } + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:3:23] + 2 │ function foo() { + 3 │ async function bar() {} + · ─── + 4 │ } + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:3:19] + 2 │ function foo() { + 3 │ function * bar() {} + · ─── + 4 │ } + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:3:25] + 2 │ function foo() { + 3 │ async function * bar() {} + · ─── + 4 │ } + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:3:14] + 2 │ function foo() { + 3 │ const bar = () => {} + · ─── + 4 │ } + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:3:14] + 2 │ function foo() { + 3 │ const bar = async () => {} + · ─── + 4 │ } + ╰──── + help: Move this function to the outer scope. + + ⚠ eslint-plugin-unicorn(consistent-function-scoping): Function does not capture any variables from the outer scope. + ╭─[consistent_function_scoping.tsx:3:22] + 2 │ function doFoo() { + 3 │ const doBar = function(bar) { + · ──────── + 4 │ return bar; + ╰──── + help: Move this function to the outer scope.