|
2 | 2 | //! consider variables ignored by name pattern, but by where they are declared. |
3 | 3 | use oxc_ast::{ast::*, AstKind}; |
4 | 4 | use oxc_semantic::{NodeId, Semantic}; |
5 | | -use oxc_span::GetSpan; |
6 | 5 |
|
7 | 6 | use super::{options::ArgsOption, NoUnusedVars, Symbol}; |
8 | 7 | use crate::rules::eslint::no_unused_vars::binding_pattern::{BindingContext, HasAnyUsedBinding}; |
9 | 8 |
|
10 | 9 | impl<'s, 'a> Symbol<'s, 'a> { |
11 | | - /// Returns `true` if this function is use. |
| 10 | + /// Check if the declaration of this [`Symbol`] is use. |
12 | 11 | /// |
13 | | - /// Checks for these cases |
14 | | - /// 1. passed as a callback to another [`CallExpression`] or [`NewExpression`] |
15 | | - /// 2. invoked as an IIFE |
16 | | - /// 3. Returned from another function |
17 | | - /// 4. Used as an attribute in a JSX element |
| 12 | + /// If it's an expression, then it's always passed in as an argument |
| 13 | + /// or assigned to a variable, so it's always used. |
| 14 | + /// |
| 15 | + /// ```js |
| 16 | + /// // True: |
| 17 | + /// const a = class Name{} |
| 18 | + /// export default (function Name() {}) |
| 19 | + /// console.log(function Name() {}) |
| 20 | + /// |
| 21 | + /// // False |
| 22 | + /// function foo() {} |
| 23 | + /// { |
| 24 | + /// class Foo {} |
| 25 | + /// } |
| 26 | + /// ``` |
18 | 27 | #[inline] |
19 | | - pub fn is_function_or_class_declaration_used(&self) -> bool { |
20 | | - #[cfg(debug_assertions)] |
21 | | - { |
22 | | - let kind = self.declaration().kind(); |
23 | | - assert!(kind.is_function_like() || matches!(kind, AstKind::Class(_))); |
24 | | - } |
25 | | - |
26 | | - for parent in self.iter_relevant_parents() { |
27 | | - match parent.kind() { |
28 | | - AstKind::MemberExpression(_) | AstKind::ParenthesizedExpression(_) |
29 | | - // e.g. `const x = [function foo() {}]` |
30 | | - // Only considered used if the array containing the symbol is used. |
31 | | - | AstKind::ArrayExpressionElement(_) |
32 | | - | AstKind::ArrayExpression(_) |
33 | | - // a ? b : function foo() {} |
34 | | - // Only considered used if the function is the test or the selected branch, |
35 | | - // but we can't determine that here. |
36 | | - | AstKind::ConditionalExpression(_) |
37 | | - => { |
38 | | - continue; |
39 | | - } |
40 | | - // Returned from another function. Definitely won't be the same |
41 | | - // function because we're walking up from its declaration |
42 | | - AstKind::ReturnStatement(_) |
43 | | - // <Component onClick={function onClick(e) { }} /> |
44 | | - | AstKind::JSXExpressionContainer(_) |
45 | | - // Function declaration is passed as an argument to another function. |
46 | | - | AstKind::CallExpression(_) | AstKind::Argument(_) |
47 | | - // e.g. `const x = { foo: function foo() {} }` |
48 | | - // Allowed off-the-bat since objects being the only child of an |
49 | | - // ExpressionStatement is rare, since you would need to wrap the |
50 | | - // object in parentheses to avoid creating a block statement. |
51 | | - | AstKind::ObjectProperty(_) |
52 | | - // e.g. var foo = function bar() { } |
53 | | - // we don't want to check for violations on `bar`, just `foo` |
54 | | - | AstKind::VariableDeclarator(_) |
55 | | - // new (class CustomRenderer{}) |
56 | | - // new (function() {}) |
57 | | - | AstKind::NewExpression(_) |
58 | | - => { |
59 | | - return true; |
60 | | - } |
61 | | - // !function() {}; is an IIFE |
62 | | - AstKind::UnaryExpression(expr) => return expr.operator.is_not(), |
63 | | - // function is used as a value for an assignment |
64 | | - // e.g. Array.prototype.sort ||= function sort(a, b) { } |
65 | | - AstKind::AssignmentExpression(assignment) if assignment.right.span().contains_inclusive(self.span()) => { |
66 | | - return self != &assignment.left; |
67 | | - } |
68 | | - AstKind::ExpressionStatement(_) => { |
69 | | - // implicit return in arrow function expression |
70 | | - let Some(AstKind::FunctionBody(body)) = self.nodes().parent_kind(parent.id()) else { |
71 | | - return false; |
72 | | - }; |
73 | | - return body.span.contains_inclusive(self.span()) && body.statements.len() == 1 && !self.get_snippet(body.span).starts_with('{') |
74 | | - } |
75 | | - _ => { |
76 | | - parent.kind().debug_name(); |
77 | | - return false; |
78 | | - } |
79 | | - } |
| 28 | + pub(crate) fn is_function_or_class_declaration_used(&self) -> bool { |
| 29 | + match self.declaration().kind() { |
| 30 | + AstKind::Class(class) => class.is_expression(), |
| 31 | + AstKind::Function(func) => func.is_expression(), |
| 32 | + _ => false, |
80 | 33 | } |
81 | | - |
82 | | - false |
83 | 34 | } |
84 | 35 |
|
85 | 36 | fn is_declared_in_for_of_loop(&self) -> bool { |
@@ -124,12 +75,6 @@ fn is_ambient_namespace(namespace: &TSModuleDeclaration) -> bool { |
124 | 75 | } |
125 | 76 |
|
126 | 77 | impl NoUnusedVars { |
127 | | - #[allow(clippy::unused_self)] |
128 | | - pub(super) fn is_allowed_class_or_function(&self, symbol: &Symbol<'_, '_>) -> bool { |
129 | | - symbol.is_function_or_class_declaration_used() |
130 | | - // || symbol.is_function_or_class_assigned_to_same_name_variable() |
131 | | - } |
132 | | - |
133 | 78 | #[allow(clippy::unused_self)] |
134 | 79 | pub(super) fn is_allowed_ts_namespace<'a>( |
135 | 80 | &self, |
|
0 commit comments