refactor(linter): all ast_util functions take Semantic (#6753)

Needed in `oxc/no-map-spread` rule.
This commit is contained in:
DonIsaac 2024-10-21 22:39:39 +00:00
parent 744aa74e81
commit b8845773f7
4 changed files with 192 additions and 181 deletions

View file

@ -1,26 +1,24 @@
use oxc_ast::{ast::BindingIdentifier, AstKind};
use oxc_ecmascript::ToBoolean;
use oxc_semantic::{AstNode, IsGlobalReference, NodeId, SymbolId};
use oxc_semantic::{AstNode, IsGlobalReference, NodeId, Semantic, SymbolId};
use oxc_span::{GetSpan, Span};
use oxc_syntax::operator::{AssignmentOperator, BinaryOperator, LogicalOperator, UnaryOperator};
#[allow(clippy::wildcard_imports)]
use oxc_ast::ast::*;
use crate::context::LintContext;
/// Test if an AST node is a boolean value that never changes. Specifically we
/// test for:
/// 1. Literal booleans (`true` or `false`)
/// 2. Unary `!` expressions with a constant value
/// 3. Constant booleans created via the `Boolean` global function
pub fn is_static_boolean<'a>(expr: &Expression<'a>, ctx: &LintContext<'a>) -> bool {
pub fn is_static_boolean<'a>(expr: &Expression<'a>, semantic: &Semantic<'a>) -> bool {
match expr {
Expression::BooleanLiteral(_) => true,
Expression::CallExpression(call_expr) => call_expr.is_constant(true, ctx),
Expression::CallExpression(call_expr) => call_expr.is_constant(true, semantic),
Expression::UnaryExpression(unary_expr) => {
unary_expr.operator == UnaryOperator::LogicalNot
&& unary_expr.argument.is_constant(true, ctx)
&& unary_expr.argument.is_constant(true, semantic)
}
_ => false,
}
@ -64,11 +62,11 @@ fn is_logical_identity(op: LogicalOperator, expr: &Expression) -> bool {
/// When `false`, checks if -- for both string and number --
/// if coerced to that type, the value will be constant.
pub trait IsConstant<'a, 'b> {
fn is_constant(&self, in_boolean_position: bool, ctx: &LintContext<'a>) -> bool;
fn is_constant(&self, in_boolean_position: bool, semantic: &Semantic<'a>) -> bool;
}
impl<'a, 'b> IsConstant<'a, 'b> for Expression<'a> {
fn is_constant(&self, in_boolean_position: bool, ctx: &LintContext<'a>) -> bool {
fn is_constant(&self, in_boolean_position: bool, semantic: &Semantic<'a>) -> bool {
match self {
Self::ArrowFunctionExpression(_)
| Self::FunctionExpression(_)
@ -80,29 +78,29 @@ impl<'a, 'b> IsConstant<'a, 'b> for Expression<'a> {
quasi.value.cooked.as_ref().map_or(false, |cooked| !cooked.is_empty())
});
let test_expressions =
template.expressions.iter().all(|expr| expr.is_constant(false, ctx));
template.expressions.iter().all(|expr| expr.is_constant(false, semantic));
test_quasis || test_expressions
}
Self::ArrayExpression(expr) => {
if in_boolean_position {
return true;
}
expr.elements.iter().all(|element| element.is_constant(false, ctx))
expr.elements.iter().all(|element| element.is_constant(false, semantic))
}
Self::UnaryExpression(expr) => match expr.operator {
UnaryOperator::Void => true,
UnaryOperator::Typeof if in_boolean_position => true,
UnaryOperator::LogicalNot => expr.argument.is_constant(true, ctx),
_ => expr.argument.is_constant(false, ctx),
UnaryOperator::LogicalNot => expr.argument.is_constant(true, semantic),
_ => expr.argument.is_constant(false, semantic),
},
Self::BinaryExpression(expr) => {
expr.operator != BinaryOperator::In
&& expr.left.is_constant(false, ctx)
&& expr.right.is_constant(false, ctx)
&& expr.left.is_constant(false, semantic)
&& expr.right.is_constant(false, semantic)
}
Self::LogicalExpression(expr) => {
let is_left_constant = expr.left.is_constant(in_boolean_position, ctx);
let is_right_constant = expr.right.is_constant(in_boolean_position, ctx);
let is_left_constant = expr.left.is_constant(in_boolean_position, semantic);
let is_right_constant = expr.right.is_constant(in_boolean_position, semantic);
let is_left_short_circuit =
is_left_constant && is_logical_identity(expr.operator, &expr.left);
let is_right_short_circuit = in_boolean_position
@ -114,7 +112,7 @@ impl<'a, 'b> IsConstant<'a, 'b> for Expression<'a> {
}
Self::NewExpression(_) => in_boolean_position,
Self::AssignmentExpression(expr) => match expr.operator {
AssignmentOperator::Assign => expr.right.is_constant(in_boolean_position, ctx),
AssignmentOperator::Assign => expr.right.is_constant(in_boolean_position, semantic),
AssignmentOperator::LogicalAnd if in_boolean_position => {
is_logical_identity(LogicalOperator::And, &expr.right)
}
@ -127,13 +125,13 @@ impl<'a, 'b> IsConstant<'a, 'b> for Expression<'a> {
.expressions
.iter()
.last()
.map_or(false, |last| last.is_constant(in_boolean_position, ctx)),
Self::CallExpression(call_expr) => call_expr.is_constant(in_boolean_position, ctx),
.map_or(false, |last| last.is_constant(in_boolean_position, semantic)),
Self::CallExpression(call_expr) => call_expr.is_constant(in_boolean_position, semantic),
Self::ParenthesizedExpression(paren_expr) => {
paren_expr.expression.is_constant(in_boolean_position, ctx)
paren_expr.expression.is_constant(in_boolean_position, semantic)
}
Self::Identifier(ident) => {
ident.name == "undefined" && ctx.semantic().is_reference_to_global_variable(ident)
ident.name == "undefined" && semantic.is_reference_to_global_variable(ident)
}
_ if self.is_literal() => true,
_ => false,
@ -142,12 +140,16 @@ impl<'a, 'b> IsConstant<'a, 'b> for Expression<'a> {
}
impl<'a, 'b> IsConstant<'a, 'b> for CallExpression<'a> {
fn is_constant(&self, _in_boolean_position: bool, ctx: &LintContext<'a>) -> bool {
fn is_constant(&self, _in_boolean_position: bool, semantic: &Semantic<'a>) -> bool {
if let Expression::Identifier(ident) = &self.callee {
if ident.name == "Boolean"
&& self.arguments.iter().next().map_or(true, |first| first.is_constant(true, ctx))
&& self
.arguments
.iter()
.next()
.map_or(true, |first| first.is_constant(true, semantic))
{
return ctx.semantic().is_reference_to_global_variable(ident);
return semantic.is_reference_to_global_variable(ident);
}
}
false
@ -155,27 +157,31 @@ impl<'a, 'b> IsConstant<'a, 'b> for CallExpression<'a> {
}
impl<'a, 'b> IsConstant<'a, 'b> for Argument<'a> {
fn is_constant(&self, in_boolean_position: bool, ctx: &LintContext<'a>) -> bool {
fn is_constant(&self, in_boolean_position: bool, semantic: &Semantic<'a>) -> bool {
match self {
Self::SpreadElement(element) => element.is_constant(in_boolean_position, ctx),
match_expression!(Self) => self.to_expression().is_constant(in_boolean_position, ctx),
Self::SpreadElement(element) => element.is_constant(in_boolean_position, semantic),
match_expression!(Self) => {
self.to_expression().is_constant(in_boolean_position, semantic)
}
}
}
}
impl<'a, 'b> IsConstant<'a, 'b> for ArrayExpressionElement<'a> {
fn is_constant(&self, in_boolean_position: bool, ctx: &LintContext<'a>) -> bool {
fn is_constant(&self, in_boolean_position: bool, semantic: &Semantic<'a>) -> bool {
match self {
Self::SpreadElement(element) => element.is_constant(in_boolean_position, ctx),
match_expression!(Self) => self.to_expression().is_constant(in_boolean_position, ctx),
Self::SpreadElement(element) => element.is_constant(in_boolean_position, semantic),
match_expression!(Self) => {
self.to_expression().is_constant(in_boolean_position, semantic)
}
Self::Elision(_) => true,
}
}
}
impl<'a, 'b> IsConstant<'a, 'b> for SpreadElement<'a> {
fn is_constant(&self, in_boolean_position: bool, ctx: &LintContext<'a>) -> bool {
self.argument.is_constant(in_boolean_position, ctx)
fn is_constant(&self, in_boolean_position: bool, semantic: &Semantic<'a>) -> bool {
self.argument.is_constant(in_boolean_position, semantic)
}
}
@ -183,7 +189,7 @@ impl<'a, 'b> IsConstant<'a, 'b> for SpreadElement<'a> {
/// enclosing the specified node
pub fn get_enclosing_function<'a, 'b>(
node: &'b AstNode<'a>,
ctx: &'b LintContext<'a>,
semantic: &'b Semantic<'a>,
) -> Option<&'b AstNode<'a>> {
let mut current_node = node;
loop {
@ -194,7 +200,7 @@ pub fn get_enclosing_function<'a, 'b>(
{
return Some(current_node);
}
current_node = ctx.nodes().parent_node(current_node.id())?;
current_node = semantic.nodes().parent_node(current_node.id())?;
}
}
@ -205,11 +211,14 @@ pub fn is_nth_argument<'a>(call: &CallExpression<'a>, arg: &Argument<'a>, n: usi
}
/// Jump to the outer most of chained parentheses if any
pub fn outermost_paren<'a, 'b>(node: &'b AstNode<'a>, ctx: &'b LintContext<'a>) -> &'b AstNode<'a> {
pub fn outermost_paren<'a, 'b>(
node: &'b AstNode<'a>,
semantic: &'b Semantic<'a>,
) -> &'b AstNode<'a> {
let mut node = node;
loop {
if let Some(parent) = ctx.nodes().parent_node(node.id()) {
if let Some(parent) = semantic.nodes().parent_node(node.id()) {
if let AstKind::ParenthesizedExpression(_) = parent.kind() {
node = parent;
continue;
@ -224,9 +233,10 @@ pub fn outermost_paren<'a, 'b>(node: &'b AstNode<'a>, ctx: &'b LintContext<'a>)
pub fn outermost_paren_parent<'a, 'b>(
node: &'b AstNode<'a>,
ctx: &'b LintContext<'a>,
semantic: &'b Semantic<'a>,
) -> Option<&'b AstNode<'a>> {
ctx.nodes()
semantic
.nodes()
.iter_parents(node.id())
.skip(1)
.find(|parent| !matches!(parent.kind(), AstKind::ParenthesizedExpression(_)))
@ -234,10 +244,11 @@ pub fn outermost_paren_parent<'a, 'b>(
pub fn nth_outermost_paren_parent<'a, 'b>(
node: &'b AstNode<'a>,
ctx: &'b LintContext<'a>,
semantic: &'b Semantic<'a>,
n: usize,
) -> Option<&'b AstNode<'a>> {
ctx.nodes()
semantic
.nodes()
.iter_parents(node.id())
.skip(1)
.filter(|parent| !matches!(parent.kind(), AstKind::ParenthesizedExpression(_)))
@ -245,11 +256,11 @@ pub fn nth_outermost_paren_parent<'a, 'b>(
}
/// Iterate over parents of `node`, skipping nodes that are also ignored by
/// [`Expression::get_inner_expression`].
pub fn iter_outer_expressions<'a, 'ctx>(
ctx: &'ctx LintContext<'a>,
pub fn iter_outer_expressions<'a, 's>(
semantic: &'s Semantic<'a>,
node_id: NodeId,
) -> impl Iterator<Item = &'ctx AstNode<'a>> + 'ctx {
ctx.nodes().iter_parents(node_id).skip(1).filter(|parent| {
) -> impl Iterator<Item = &'s AstNode<'a>> + 's {
semantic.nodes().iter_parents(node_id).skip(1).filter(|parent| {
!matches!(
parent.kind(),
AstKind::ParenthesizedExpression(_)
@ -263,19 +274,19 @@ pub fn iter_outer_expressions<'a, 'ctx>(
}
pub fn get_declaration_of_variable<'a, 'b>(
ident: &IdentifierReference,
ctx: &'b LintContext<'a>,
ident: &IdentifierReference<'a>,
semantic: &'b Semantic<'a>,
) -> Option<&'b AstNode<'a>> {
let symbol_id = get_symbol_id_of_variable(ident, ctx)?;
let symbol_table = ctx.semantic().symbols();
Some(ctx.nodes().get_node(symbol_table.get_declaration(symbol_id)))
let symbol_id = get_symbol_id_of_variable(ident, semantic)?;
let symbol_table = semantic.symbols();
Some(semantic.nodes().get_node(symbol_table.get_declaration(symbol_id)))
}
pub fn get_symbol_id_of_variable(
ident: &IdentifierReference,
ctx: &LintContext<'_>,
semantic: &Semantic<'_>,
) -> Option<SymbolId> {
let symbol_table = ctx.semantic().symbols();
let symbol_table = semantic.symbols();
let reference_id = ident.reference_id.get()?;
let reference = symbol_table.get_reference(reference_id);
reference.symbol_id()
@ -389,7 +400,7 @@ pub fn get_new_expr_ident_name<'a>(new_expr: &'a NewExpression<'a>) -> Option<&'
Some(ident.name.as_str())
}
pub fn is_global_require_call(call_expr: &CallExpression, ctx: &LintContext) -> bool {
pub fn is_global_require_call(call_expr: &CallExpression, ctx: &Semantic) -> bool {
if call_expr.arguments.len() != 1 {
return false;
}
@ -407,7 +418,7 @@ pub fn is_function_node(node: &AstNode) -> bool {
pub fn get_function_like_declaration<'b>(
node: &AstNode<'b>,
ctx: &LintContext<'b>,
ctx: &Semantic<'b>,
) -> Option<&'b BindingIdentifier<'b>> {
let parent = outermost_paren_parent(node, ctx)?;
let decl = parent.kind().as_variable_declarator()?;

View file

@ -29,24 +29,24 @@ use crate::{
},
};
pub trait ListenerMap {
fn report_effects(&self, _options: &NodeListenerOptions) {}
fn report_effects_when_assigned(&self, _options: &NodeListenerOptions) {}
fn report_effects_when_called(&self, _options: &NodeListenerOptions) {}
fn report_effects_when_mutated(&self, _options: &NodeListenerOptions) {}
fn get_value_and_report_effects(&self, _options: &NodeListenerOptions) -> Value {
pub trait ListenerMap<'a> {
fn report_effects(&self, _options: &NodeListenerOptions<'a, '_>) {}
fn report_effects_when_assigned(&self, _options: &NodeListenerOptions<'a, '_>) {}
fn report_effects_when_called(&self, _options: &NodeListenerOptions<'a, '_>) {}
fn report_effects_when_mutated(&self, _options: &NodeListenerOptions<'a, '_>) {}
fn get_value_and_report_effects(&self, _options: &NodeListenerOptions<'a, '_>) -> Value {
Value::Unknown
}
}
impl<'a> ListenerMap for Program<'a> {
fn report_effects(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for Program<'a> {
fn report_effects(&self, options: &NodeListenerOptions<'a, '_>) {
self.body.iter().for_each(|stmt| stmt.report_effects(options));
}
}
impl<'a> ListenerMap for Statement<'a> {
fn report_effects(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for Statement<'a> {
fn report_effects(&self, options: &NodeListenerOptions<'a, '_>) {
match self {
Self::ExpressionStatement(expr_stmt) => {
expr_stmt.expression.report_effects(options);
@ -179,8 +179,8 @@ impl<'a> ListenerMap for Statement<'a> {
}
}
impl<'a> ListenerMap for ForStatementInit<'a> {
fn report_effects(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for ForStatementInit<'a> {
fn report_effects(&self, options: &NodeListenerOptions<'a, '_>) {
match self {
match_expression!(Self) => self.to_expression().report_effects(options),
Self::VariableDeclaration(decl) => {
@ -190,8 +190,8 @@ impl<'a> ListenerMap for ForStatementInit<'a> {
}
}
impl<'a> ListenerMap for ExportSpecifier<'a> {
fn report_effects(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for ExportSpecifier<'a> {
fn report_effects(&self, options: &NodeListenerOptions<'a, '_>) {
let ctx = options.ctx;
let symbol_table = ctx.symbols();
if has_comment_about_side_effect_check(self.exported.span(), ctx) {
@ -220,8 +220,8 @@ impl<'a> ListenerMap for ExportSpecifier<'a> {
// we don't need implement all AstNode
// it's same as `reportSideEffectsInDefinitionWhenCalled` in eslint-plugin-tree-shaking
// <https://github.com/lukastaegert/eslint-plugin-tree-shaking/blob/463fa1f0bef7caa2b231a38b9c3557051f506c92/src/rules/no-side-effects-in-initialization.ts#L1070-L1080>
impl<'a> ListenerMap for AstNode<'a> {
fn report_effects_when_called(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for AstNode<'a> {
fn report_effects_when_called(&self, options: &NodeListenerOptions<'a, '_>) {
match self.kind() {
AstKind::VariableDeclarator(decl) => {
if let Some(init) = &decl.init {
@ -273,7 +273,7 @@ impl<'a> ListenerMap for AstNode<'a> {
}
}
fn report_effects_when_mutated(&self, options: &NodeListenerOptions) {
fn report_effects_when_mutated(&self, options: &NodeListenerOptions<'a, '_>) {
match self.kind() {
AstKind::VariableDeclarator(decl) => {
if let Some(init) = &decl.init {
@ -315,8 +315,8 @@ fn report_on_imported_call(span: Span, name: &str, node_id: NodeId, options: &No
options.ctx.diagnostic(super::call_import(span));
}
impl<'a> ListenerMap for Declaration<'a> {
fn report_effects(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for Declaration<'a> {
fn report_effects(&self, options: &NodeListenerOptions<'a, '_>) {
match self {
Self::VariableDeclaration(decl) => {
decl.declarations.iter().for_each(|decl| decl.report_effects(options));
@ -336,15 +336,15 @@ impl<'a> ListenerMap for Declaration<'a> {
}
}
impl<'a> ListenerMap for Class<'a> {
fn report_effects(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for Class<'a> {
fn report_effects(&self, options: &NodeListenerOptions<'a, '_>) {
if let Some(super_class) = &self.super_class {
super_class.report_effects(options);
}
self.body.report_effects(options);
}
fn report_effects_when_called(&self, options: &NodeListenerOptions) {
fn report_effects_when_called(&self, options: &NodeListenerOptions<'a, '_>) {
if let Some(super_class) = &self.super_class {
super_class.report_effects_when_called(options);
}
@ -352,14 +352,14 @@ impl<'a> ListenerMap for Class<'a> {
}
}
impl<'a> ListenerMap for ClassBody<'a> {
fn report_effects(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for ClassBody<'a> {
fn report_effects(&self, options: &NodeListenerOptions<'a, '_>) {
self.body.iter().for_each(|class_element| {
class_element.report_effects(options);
});
}
fn report_effects_when_called(&self, options: &NodeListenerOptions) {
fn report_effects_when_called(&self, options: &NodeListenerOptions<'a, '_>) {
let constructor = self.body.iter().find(|class_element| {
if let ClassElement::MethodDefinition(definition) = class_element {
return definition.kind.is_constructor();
@ -383,8 +383,8 @@ impl<'a> ListenerMap for ClassBody<'a> {
}
}
impl<'a> ListenerMap for ClassElement<'a> {
fn report_effects(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for ClassElement<'a> {
fn report_effects(&self, options: &NodeListenerOptions<'a, '_>) {
match self {
Self::MethodDefinition(method) => {
method.key.report_effects(options);
@ -396,7 +396,7 @@ impl<'a> ListenerMap for ClassElement<'a> {
}
}
fn report_effects_when_called(&self, options: &NodeListenerOptions) {
fn report_effects_when_called(&self, options: &NodeListenerOptions<'a, '_>) {
match self {
Self::MethodDefinition(method) => {
method.value.report_effects_when_called(options);
@ -411,8 +411,8 @@ impl<'a> ListenerMap for ClassElement<'a> {
}
}
impl<'a> ListenerMap for PropertyKey<'a> {
fn report_effects(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for PropertyKey<'a> {
fn report_effects(&self, options: &NodeListenerOptions<'a, '_>) {
match self.as_expression() {
Some(expr) => expr.report_effects(options),
None => no_effects(),
@ -420,8 +420,8 @@ impl<'a> ListenerMap for PropertyKey<'a> {
}
}
impl<'a> ListenerMap for VariableDeclarator<'a> {
fn report_effects(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for VariableDeclarator<'a> {
fn report_effects(&self, options: &NodeListenerOptions<'a, '_>) {
self.id.report_effects(options);
if has_comment_about_side_effect_check(self.id.span(), options.ctx) {
self.id.report_effects_when_called(options);
@ -433,8 +433,8 @@ impl<'a> ListenerMap for VariableDeclarator<'a> {
}
}
impl<'a> ListenerMap for BindingPattern<'a> {
fn report_effects(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for BindingPattern<'a> {
fn report_effects(&self, options: &NodeListenerOptions<'a, '_>) {
match &self.kind {
BindingPatternKind::BindingIdentifier(_) => {}
BindingPatternKind::ArrayPattern(array) => {
@ -457,19 +457,19 @@ impl<'a> ListenerMap for BindingPattern<'a> {
}
}
fn report_effects_when_called(&self, options: &NodeListenerOptions) {
fn report_effects_when_called(&self, options: &NodeListenerOptions<'a, '_>) {
if let BindingPatternKind::BindingIdentifier(ident) = &self.kind {
ident.report_effects_when_called(options);
}
}
}
impl<'a> ListenerMap for BindingIdentifier<'a> {
fn report_effects(&self, _options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for BindingIdentifier<'a> {
fn report_effects(&self, _options: &NodeListenerOptions<'a, '_>) {
no_effects();
}
fn report_effects_when_called(&self, options: &NodeListenerOptions) {
fn report_effects_when_called(&self, options: &NodeListenerOptions<'a, '_>) {
let ctx = options.ctx;
if let Some(symbol_id) = self.symbol_id.get() {
let symbol_table = ctx.semantic().symbols();
@ -487,8 +487,8 @@ impl<'a> ListenerMap for BindingIdentifier<'a> {
}
}
impl<'a> ListenerMap for Expression<'a> {
fn report_effects(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for Expression<'a> {
fn report_effects(&self, options: &NodeListenerOptions<'a, '_>) {
match self {
Self::ArrayExpression(array_expr) => {
array_expr.elements.iter().for_each(|el| el.report_effects(options));
@ -569,7 +569,7 @@ impl<'a> ListenerMap for Expression<'a> {
}
}
fn report_effects_when_mutated(&self, options: &NodeListenerOptions) {
fn report_effects_when_mutated(&self, options: &NodeListenerOptions<'a, '_>) {
match self {
Self::Identifier(ident) => {
ident.report_effects_when_mutated(options);
@ -591,7 +591,7 @@ impl<'a> ListenerMap for Expression<'a> {
}
}
fn report_effects_when_called(&self, options: &NodeListenerOptions) {
fn report_effects_when_called(&self, options: &NodeListenerOptions<'a, '_>) {
match self {
Self::CallExpression(expr) => {
expr.report_effects_when_called(options);
@ -631,7 +631,7 @@ impl<'a> ListenerMap for Expression<'a> {
}
}
fn get_value_and_report_effects(&self, options: &NodeListenerOptions) -> Value {
fn get_value_and_report_effects(&self, options: &NodeListenerOptions<'a, '_>) -> Value {
match self {
Self::BooleanLiteral(_)
| Self::StringLiteral(_)
@ -665,8 +665,8 @@ fn defined_custom_report_effects_when_called(expr: &Expression) -> bool {
)
}
impl<'a> ListenerMap for SwitchCase<'a> {
fn report_effects(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for SwitchCase<'a> {
fn report_effects(&self, options: &NodeListenerOptions<'a, '_>) {
if let Some(test) = &self.test {
test.report_effects(options);
}
@ -676,8 +676,8 @@ impl<'a> ListenerMap for SwitchCase<'a> {
}
}
impl<'a> ListenerMap for SequenceExpression<'a> {
fn get_value_and_report_effects(&self, options: &NodeListenerOptions) -> Value {
impl<'a> ListenerMap<'a> for SequenceExpression<'a> {
fn get_value_and_report_effects(&self, options: &NodeListenerOptions<'a, '_>) -> Value {
let mut val = Value::Unknown;
for expr in &self.expressions {
val = expr.get_value_and_report_effects(options);
@ -686,8 +686,8 @@ impl<'a> ListenerMap for SequenceExpression<'a> {
}
}
impl<'a> ListenerMap for UnaryExpression<'a> {
fn get_value_and_report_effects(&self, options: &NodeListenerOptions) -> Value {
impl<'a> ListenerMap<'a> for UnaryExpression<'a> {
fn get_value_and_report_effects(&self, options: &NodeListenerOptions<'a, '_>) -> Value {
if self.operator == UnaryOperator::Delete {
match &self.argument {
Expression::StaticMemberExpression(expr) => {
@ -709,8 +709,8 @@ impl<'a> ListenerMap for UnaryExpression<'a> {
}
}
impl<'a> ListenerMap for LogicalExpression<'a> {
fn get_value_and_report_effects(&self, options: &NodeListenerOptions) -> Value {
impl<'a> ListenerMap<'a> for LogicalExpression<'a> {
fn get_value_and_report_effects(&self, options: &NodeListenerOptions<'a, '_>) -> Value {
let left = self.left.get_value_and_report_effects(options);
// `false && foo`
if self.operator == LogicalOperator::And
@ -729,8 +729,8 @@ impl<'a> ListenerMap for LogicalExpression<'a> {
}
}
impl<'a> ListenerMap for ObjectExpression<'a> {
fn report_effects(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for ObjectExpression<'a> {
fn report_effects(&self, options: &NodeListenerOptions<'a, '_>) {
self.properties.iter().for_each(|property| match property {
ObjectPropertyKind::ObjectProperty(p) => {
p.key.report_effects(options);
@ -743,8 +743,8 @@ impl<'a> ListenerMap for ObjectExpression<'a> {
}
}
impl<'a> ListenerMap for JSXElement<'a> {
fn report_effects(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for JSXElement<'a> {
fn report_effects(&self, options: &NodeListenerOptions<'a, '_>) {
self.opening_element.report_effects(options);
self.children.iter().for_each(|child| {
child.report_effects(options);
@ -752,8 +752,8 @@ impl<'a> ListenerMap for JSXElement<'a> {
}
}
impl<'a> ListenerMap for JSXChild<'a> {
fn report_effects(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for JSXChild<'a> {
fn report_effects(&self, options: &NodeListenerOptions<'a, '_>) {
match self {
JSXChild::Element(element) => {
element.report_effects(options);
@ -774,15 +774,15 @@ impl<'a> ListenerMap for JSXChild<'a> {
}
}
impl<'a> ListenerMap for JSXOpeningElement<'a> {
fn report_effects(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for JSXOpeningElement<'a> {
fn report_effects(&self, options: &NodeListenerOptions<'a, '_>) {
self.name.report_effects_when_called(options);
self.attributes.iter().for_each(|attr| attr.report_effects(options));
}
}
impl<'a> ListenerMap for JSXElementName<'a> {
fn report_effects_when_called(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for JSXElementName<'a> {
fn report_effects_when_called(&self, options: &NodeListenerOptions<'a, '_>) {
match self {
Self::Identifier(_) | Self::NamespacedName(_) => {}
Self::IdentifierReference(ident) => ident.report_effects_when_called(options),
@ -792,14 +792,14 @@ impl<'a> ListenerMap for JSXElementName<'a> {
}
}
impl<'a> ListenerMap for JSXMemberExpression<'a> {
fn report_effects_when_called(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for JSXMemberExpression<'a> {
fn report_effects_when_called(&self, options: &NodeListenerOptions<'a, '_>) {
options.ctx.diagnostic(super::call_member(self.property.span()));
}
}
impl<'a> ListenerMap for JSXAttributeItem<'a> {
fn report_effects(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for JSXAttributeItem<'a> {
fn report_effects(&self, options: &NodeListenerOptions<'a, '_>) {
match self {
Self::Attribute(attribute) => {
attribute.report_effects(options);
@ -811,8 +811,8 @@ impl<'a> ListenerMap for JSXAttributeItem<'a> {
}
}
impl<'a> ListenerMap for JSXAttribute<'a> {
fn report_effects(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for JSXAttribute<'a> {
fn report_effects(&self, options: &NodeListenerOptions<'a, '_>) {
if let Some(value) = &self.value {
match value {
JSXAttributeValue::ExpressionContainer(container) => {
@ -832,14 +832,14 @@ impl<'a> ListenerMap for JSXAttribute<'a> {
}
}
impl<'a> ListenerMap for JSXExpressionContainer<'a> {
fn report_effects(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for JSXExpressionContainer<'a> {
fn report_effects(&self, options: &NodeListenerOptions<'a, '_>) {
self.expression.report_effects(options);
}
}
impl<'a> ListenerMap for JSXExpression<'a> {
fn report_effects(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for JSXExpression<'a> {
fn report_effects(&self, options: &NodeListenerOptions<'a, '_>) {
match self {
Self::ArrayExpression(array_expr) => {
array_expr.elements.iter().for_each(|el| el.report_effects(options));
@ -902,14 +902,14 @@ impl<'a> ListenerMap for JSXExpression<'a> {
}
}
impl<'a> ListenerMap for JSXFragment<'a> {
fn report_effects(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for JSXFragment<'a> {
fn report_effects(&self, options: &NodeListenerOptions<'a, '_>) {
self.children.iter().for_each(|child| child.report_effects(options));
}
}
impl<'a> ListenerMap for ConditionalExpression<'a> {
fn get_value_and_report_effects(&self, options: &NodeListenerOptions) -> Value {
impl<'a> ListenerMap<'a> for ConditionalExpression<'a> {
fn get_value_and_report_effects(&self, options: &NodeListenerOptions<'a, '_>) -> Value {
let test_result = self.test.get_value_and_report_effects(options);
if let Some(is_falsy) = test_result.get_falsy_value() {
@ -925,7 +925,7 @@ impl<'a> ListenerMap for ConditionalExpression<'a> {
}
}
fn report_effects_when_called(&self, options: &NodeListenerOptions) {
fn report_effects_when_called(&self, options: &NodeListenerOptions<'a, '_>) {
let test_result = self.test.get_value_and_report_effects(options);
if let Some(falsy) = test_result.get_falsy_value() {
@ -941,24 +941,24 @@ impl<'a> ListenerMap for ConditionalExpression<'a> {
}
}
impl<'a> ListenerMap for BinaryExpression<'a> {
fn get_value_and_report_effects(&self, options: &NodeListenerOptions) -> Value {
impl<'a> ListenerMap<'a> for BinaryExpression<'a> {
fn get_value_and_report_effects(&self, options: &NodeListenerOptions<'a, '_>) -> Value {
let left = self.left.get_value_and_report_effects(options);
let right = self.right.get_value_and_report_effects(options);
calculate_binary_operation(self.operator, left, right)
}
}
impl ListenerMap for ThisExpression {
fn report_effects_when_mutated(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for ThisExpression {
fn report_effects_when_mutated(&self, options: &NodeListenerOptions<'a, '_>) {
if !options.has_valid_this.get() {
options.ctx.diagnostic(super::mutate_of_this(self.span));
}
}
}
impl<'a> ListenerMap for NewExpression<'a> {
fn report_effects(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for NewExpression<'a> {
fn report_effects(&self, options: &NodeListenerOptions<'a, '_>) {
if has_pure_notation(self.span, options.ctx) {
return;
}
@ -970,37 +970,37 @@ impl<'a> ListenerMap for NewExpression<'a> {
}
}
impl<'a> ListenerMap for ParenthesizedExpression<'a> {
fn report_effects(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for ParenthesizedExpression<'a> {
fn report_effects(&self, options: &NodeListenerOptions<'a, '_>) {
self.expression.report_effects(options);
}
fn report_effects_when_assigned(&self, options: &NodeListenerOptions) {
fn report_effects_when_assigned(&self, options: &NodeListenerOptions<'a, '_>) {
self.expression.report_effects_when_assigned(options);
}
fn report_effects_when_called(&self, options: &NodeListenerOptions) {
fn report_effects_when_called(&self, options: &NodeListenerOptions<'a, '_>) {
self.expression.report_effects_when_called(options);
}
fn report_effects_when_mutated(&self, options: &NodeListenerOptions) {
fn report_effects_when_mutated(&self, options: &NodeListenerOptions<'a, '_>) {
self.expression.report_effects_when_mutated(options);
}
fn get_value_and_report_effects(&self, options: &NodeListenerOptions) -> Value {
fn get_value_and_report_effects(&self, options: &NodeListenerOptions<'a, '_>) -> Value {
self.expression.get_value_and_report_effects(options)
}
}
impl<'a> ListenerMap for ArrowFunctionExpression<'a> {
fn report_effects_when_called(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for ArrowFunctionExpression<'a> {
fn report_effects_when_called(&self, options: &NodeListenerOptions<'a, '_>) {
self.params.items.iter().for_each(|param| param.report_effects(options));
self.body.statements.iter().for_each(|stmt| stmt.report_effects(options));
}
}
impl<'a> ListenerMap for Function<'a> {
fn report_effects_when_called(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for Function<'a> {
fn report_effects_when_called(&self, options: &NodeListenerOptions<'a, '_>) {
self.params.items.iter().for_each(|param| param.report_effects(options));
if let Some(body) = &self.body {
@ -1009,14 +1009,14 @@ impl<'a> ListenerMap for Function<'a> {
}
}
impl<'a> ListenerMap for FormalParameter<'a> {
fn report_effects(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for FormalParameter<'a> {
fn report_effects(&self, options: &NodeListenerOptions<'a, '_>) {
self.pattern.report_effects(options);
}
}
impl<'a> ListenerMap for CallExpression<'a> {
fn report_effects(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for CallExpression<'a> {
fn report_effects(&self, options: &NodeListenerOptions<'a, '_>) {
self.arguments.iter().for_each(|arg| arg.report_effects(options));
if defined_custom_report_effects_when_called(&self.callee) {
let old_value = options.called_with_new.get();
@ -1028,7 +1028,7 @@ impl<'a> ListenerMap for CallExpression<'a> {
}
}
fn report_effects_when_called(&self, options: &NodeListenerOptions) {
fn report_effects_when_called(&self, options: &NodeListenerOptions<'a, '_>) {
let ctx = options.ctx;
if let Expression::Identifier(ident) = &self.callee {
if let Some(node) = get_declaration_of_variable(ident, ctx) {
@ -1042,13 +1042,13 @@ impl<'a> ListenerMap for CallExpression<'a> {
}
}
fn report_effects_when_mutated(&self, options: &NodeListenerOptions) {
fn report_effects_when_mutated(&self, options: &NodeListenerOptions<'a, '_>) {
options.ctx.diagnostic(super::mutate_function_return_value(self.span));
}
}
impl<'a> ListenerMap for Argument<'a> {
fn report_effects(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for Argument<'a> {
fn report_effects(&self, options: &NodeListenerOptions<'a, '_>) {
match self {
match_expression!(Self) => self.to_expression().report_effects(options),
Self::SpreadElement(spread) => {
@ -1058,8 +1058,8 @@ impl<'a> ListenerMap for Argument<'a> {
}
}
impl<'a> ListenerMap for AssignmentTarget<'a> {
fn report_effects_when_assigned(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for AssignmentTarget<'a> {
fn report_effects_when_assigned(&self, options: &NodeListenerOptions<'a, '_>) {
match self {
match_simple_assignment_target!(Self) => {
self.to_simple_assignment_target().report_effects_when_assigned(options);
@ -1069,8 +1069,8 @@ impl<'a> ListenerMap for AssignmentTarget<'a> {
}
}
impl<'a> ListenerMap for SimpleAssignmentTarget<'a> {
fn report_effects_when_assigned(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for SimpleAssignmentTarget<'a> {
fn report_effects_when_assigned(&self, options: &NodeListenerOptions<'a, '_>) {
match self {
Self::AssignmentTargetIdentifier(ident) => {
ident.report_effects_when_assigned(options);
@ -1088,14 +1088,14 @@ impl<'a> ListenerMap for SimpleAssignmentTarget<'a> {
}
}
impl<'a> ListenerMap for IdentifierReference<'a> {
fn report_effects_when_assigned(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for IdentifierReference<'a> {
fn report_effects_when_assigned(&self, options: &NodeListenerOptions<'a, '_>) {
if get_symbol_id_of_variable(self, options.ctx).is_none() {
options.ctx.diagnostic(super::assignment(self.name.as_str(), self.span));
}
}
fn report_effects_when_called(&self, options: &NodeListenerOptions) {
fn report_effects_when_called(&self, options: &NodeListenerOptions<'a, '_>) {
if is_pure_function(&FunctionName::Identifier(self), options) {
return;
}
@ -1150,7 +1150,7 @@ impl<'a> ListenerMap for IdentifierReference<'a> {
}
}
fn report_effects_when_mutated(&self, options: &NodeListenerOptions) {
fn report_effects_when_mutated(&self, options: &NodeListenerOptions<'a, '_>) {
let ctx = options.ctx;
if let Some(symbol_id) = get_symbol_id_of_variable(self, ctx) {
if options.insert_mutated_node(symbol_id) {
@ -1173,8 +1173,8 @@ impl<'a> ListenerMap for IdentifierReference<'a> {
}
}
impl<'a> ListenerMap for MemberExpression<'a> {
fn report_effects(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for MemberExpression<'a> {
fn report_effects(&self, options: &NodeListenerOptions<'a, '_>) {
match self {
Self::ComputedMemberExpression(expr) => {
expr.report_effects(options);
@ -1188,7 +1188,7 @@ impl<'a> ListenerMap for MemberExpression<'a> {
}
}
fn report_effects_when_assigned(&self, options: &NodeListenerOptions) {
fn report_effects_when_assigned(&self, options: &NodeListenerOptions<'a, '_>) {
match self {
Self::ComputedMemberExpression(expr) => {
expr.report_effects_when_assigned(options);
@ -1202,7 +1202,7 @@ impl<'a> ListenerMap for MemberExpression<'a> {
}
}
fn report_effects_when_called(&self, options: &NodeListenerOptions) {
fn report_effects_when_called(&self, options: &NodeListenerOptions<'a, '_>) {
match self {
Self::ComputedMemberExpression(expr) => {
expr.report_effects_when_called(options);
@ -1217,13 +1217,13 @@ impl<'a> ListenerMap for MemberExpression<'a> {
}
}
impl<'a> ListenerMap for ComputedMemberExpression<'a> {
fn report_effects(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for ComputedMemberExpression<'a> {
fn report_effects(&self, options: &NodeListenerOptions<'a, '_>) {
self.expression.report_effects(options);
self.object.report_effects(options);
}
fn report_effects_when_called(&self, options: &NodeListenerOptions) {
fn report_effects_when_called(&self, options: &NodeListenerOptions<'a, '_>) {
self.report_effects(options);
let mut node = &self.object;
@ -1247,18 +1247,18 @@ impl<'a> ListenerMap for ComputedMemberExpression<'a> {
}
}
fn report_effects_when_assigned(&self, options: &NodeListenerOptions) {
fn report_effects_when_assigned(&self, options: &NodeListenerOptions<'a, '_>) {
self.report_effects(options);
self.object.report_effects_when_mutated(options);
}
}
impl<'a> ListenerMap for StaticMemberExpression<'a> {
fn report_effects(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for StaticMemberExpression<'a> {
fn report_effects(&self, options: &NodeListenerOptions<'a, '_>) {
self.object.report_effects(options);
}
fn report_effects_when_called(&self, options: &NodeListenerOptions) {
fn report_effects_when_called(&self, options: &NodeListenerOptions<'a, '_>) {
self.report_effects(options);
let mut root_member_expr = &self.object;
@ -1300,18 +1300,18 @@ impl<'a> ListenerMap for StaticMemberExpression<'a> {
options.ctx.diagnostic(super::call_member(self.span));
}
fn report_effects_when_assigned(&self, options: &NodeListenerOptions) {
fn report_effects_when_assigned(&self, options: &NodeListenerOptions<'a, '_>) {
self.report_effects(options);
self.object.report_effects_when_mutated(options);
}
}
impl<'a> ListenerMap for PrivateFieldExpression<'a> {
fn report_effects(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for PrivateFieldExpression<'a> {
fn report_effects(&self, options: &NodeListenerOptions<'a, '_>) {
self.object.report_effects(options);
}
fn report_effects_when_called(&self, options: &NodeListenerOptions) {
fn report_effects_when_called(&self, options: &NodeListenerOptions<'a, '_>) {
self.report_effects(options);
let mut node = &self.object;
@ -1335,14 +1335,14 @@ impl<'a> ListenerMap for PrivateFieldExpression<'a> {
}
}
fn report_effects_when_assigned(&self, options: &NodeListenerOptions) {
fn report_effects_when_assigned(&self, options: &NodeListenerOptions<'a, '_>) {
self.report_effects(options);
self.object.report_effects_when_mutated(options);
}
}
impl<'a> ListenerMap for ArrayExpressionElement<'a> {
fn report_effects(&self, options: &NodeListenerOptions) {
impl<'a> ListenerMap<'a> for ArrayExpressionElement<'a> {
fn report_effects(&self, options: &NodeListenerOptions<'a, '_>) {
match self {
match_expression!(Self) => self.to_expression().report_effects(options),
Self::SpreadElement(spreed) => {

View file

@ -85,7 +85,7 @@ impl Rule for NoTypeofUndefined {
}
}
fn is_global_variable(ident: &Expression, ctx: &LintContext) -> bool {
fn is_global_variable<'a>(ident: &Expression<'a>, ctx: &LintContext<'a>) -> bool {
let Expression::Identifier(ident) = ident else {
return false;
};

View file

@ -91,7 +91,7 @@ impl Rule for PreferSetSize {
}
}
fn is_set(maybe_set: &Expression, ctx: &LintContext) -> bool {
fn is_set<'a>(maybe_set: &Expression<'a>, ctx: &LintContext<'a>) -> bool {
if let Expression::NewExpression(new_expr) = maybe_set {
if let Expression::Identifier(identifier) = &new_expr.callee {
return identifier.name == "Set";