use ruff_python_ast::token::TokenKind;
use ruff_python_ast::{self as ast, CmpOp, Expr, ExprContext, Number};
use ruff_text_size::{Ranged, TextRange};
use crate::error::RelaxedDecoratorError;
pub(super) fn set_expr_ctx(expr: &mut Expr, new_ctx: ExprContext) {
match expr {
Expr::Name(ast::ExprName { ctx, .. })
| Expr::Attribute(ast::ExprAttribute { ctx, .. })
| Expr::Subscript(ast::ExprSubscript { ctx, .. }) => *ctx = new_ctx,
Expr::Starred(ast::ExprStarred { value, ctx, .. }) => {
*ctx = new_ctx;
set_expr_ctx(value, new_ctx);
}
Expr::UnaryOp(ast::ExprUnaryOp { operand, .. }) => {
set_expr_ctx(operand, new_ctx);
}
Expr::List(ast::ExprList { elts, ctx, .. })
| Expr::Tuple(ast::ExprTuple { elts, ctx, .. }) => {
*ctx = new_ctx;
for element in elts.iter_mut() {
set_expr_ctx(element, new_ctx);
}
}
_ => {}
}
}
pub(super) const fn token_kind_to_cmp_op(
current: TokenKind,
next: Option<TokenKind>,
) -> Option<CmpOp> {
Some(match (current, next) {
(TokenKind::Is, Some(TokenKind::Not)) => CmpOp::IsNot,
(TokenKind::Is, _) => CmpOp::Is,
(TokenKind::Not, Some(TokenKind::In)) => CmpOp::NotIn,
(TokenKind::In, _) => CmpOp::In,
(TokenKind::EqEqual, _) => CmpOp::Eq,
(TokenKind::NotEqual, _) => CmpOp::NotEq,
(TokenKind::Less, _) => CmpOp::Lt,
(TokenKind::LessEqual, _) => CmpOp::LtE,
(TokenKind::Greater, _) => CmpOp::Gt,
(TokenKind::GreaterEqual, _) => CmpOp::GtE,
_ => return None,
})
}
pub(super) fn detect_invalid_pre_py39_decorator_node(
expr: &Expr,
) -> Option<(RelaxedDecoratorError, TextRange)> {
let description = match expr {
Expr::Name(_) => return None,
Expr::Attribute(attribute) => {
return detect_invalid_pre_py39_decorator_node(&attribute.value);
}
Expr::Call(_) => return Some((RelaxedDecoratorError::CallExpression, expr.range())),
Expr::NumberLiteral(number) => match &number.value {
Number::Int(_) => "an int literal",
Number::Float(_) => "a float literal",
Number::Complex { .. } => "a complex literal",
},
Expr::BoolOp(_) => "boolean expression",
Expr::BinOp(_) => "binary-operation expression",
Expr::UnaryOp(_) => "unary-operation expression",
Expr::Await(_) => "`await` expression",
Expr::Lambda(_) => "lambda expression",
Expr::If(_) => "conditional expression",
Expr::Dict(_) => "a dict literal",
Expr::Set(_) => "a set literal",
Expr::List(_) => "a list literal",
Expr::Tuple(_) => "a tuple literal",
Expr::Starred(_) => "starred expression",
Expr::Slice(_) => "slice expression",
Expr::BytesLiteral(_) => "a bytes literal",
Expr::StringLiteral(_) => "a string literal",
Expr::EllipsisLiteral(_) => "an ellipsis literal",
Expr::NoneLiteral(_) => "a `None` literal",
Expr::BooleanLiteral(_) => "a boolean literal",
Expr::ListComp(_) => "a list comprehension",
Expr::SetComp(_) => "a set comprehension",
Expr::DictComp(_) => "a dict comprehension",
Expr::Generator(_) => "generator expression",
Expr::Yield(_) => "`yield` expression",
Expr::YieldFrom(_) => "`yield from` expression",
Expr::Compare(_) => "comparison expression",
Expr::FString(_) => "f-string",
Expr::TString(_) => "t-string",
Expr::Named(_) => "assignment expression",
Expr::Subscript(_) => "subscript expression",
Expr::IpyEscapeCommand(_) => "IPython escape command",
};
Some((RelaxedDecoratorError::Other(description), expr.range()))
}