1use std::borrow::Cow;
2use std::path::Path;
3
4use rustc_hash::FxHashMap;
5
6use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer, indentation_at_offset};
7use ruff_source_file::LineRanges;
8use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
9
10use crate::name::{Name, QualifiedName, QualifiedNameBuilder};
11use crate::statement_visitor::StatementVisitor;
12use crate::token::Tokens;
13use crate::token::parenthesized_range;
14use crate::visitor::Visitor;
15use crate::{
16 self as ast, Arguments, AtomicNodeIndex, CmpOp, DictItem, ExceptHandler, Expr, ExprNoneLiteral,
17 InterpolatedStringElement, MatchCase, Operator, Pattern, Stmt, TypeParam,
18};
19use crate::{AnyNodeRef, ExprContext};
20
21pub const fn is_compound_statement(stmt: &Stmt) -> bool {
23 matches!(
24 stmt,
25 Stmt::FunctionDef(_)
26 | Stmt::ClassDef(_)
27 | Stmt::While(_)
28 | Stmt::For(_)
29 | Stmt::Match(_)
30 | Stmt::With(_)
31 | Stmt::If(_)
32 | Stmt::Try(_)
33 )
34}
35
36fn is_iterable_initializer<F>(id: &str, is_builtin: F) -> bool
37where
38 F: Fn(&str) -> bool,
39{
40 matches!(id, "list" | "tuple" | "set" | "dict" | "frozenset") && is_builtin(id)
41}
42
43pub fn contains_effect<F>(expr: &Expr, is_builtin: F) -> bool
48where
49 F: Fn(&str) -> bool,
50{
51 any_over_expr(expr, &|expr| {
52 if let Expr::Call(ast::ExprCall {
54 func,
55 arguments,
56 range: _,
57 node_index: _,
58 }) = expr
59 {
60 if arguments.is_empty() {
62 if let Expr::Name(ast::ExprName { id, .. }) = func.as_ref() {
63 if !is_iterable_initializer(id.as_str(), |id| is_builtin(id)) {
64 return true;
65 }
66 return false;
67 }
68 }
69 }
70
71 if let Expr::BinOp(ast::ExprBinOp { left, right, .. }) = expr {
73 if !matches!(
74 left.as_ref(),
75 Expr::StringLiteral(_)
76 | Expr::BytesLiteral(_)
77 | Expr::NumberLiteral(_)
78 | Expr::BooleanLiteral(_)
79 | Expr::NoneLiteral(_)
80 | Expr::EllipsisLiteral(_)
81 | Expr::FString(_)
82 | Expr::List(_)
83 | Expr::Tuple(_)
84 | Expr::Set(_)
85 | Expr::Dict(_)
86 | Expr::ListComp(_)
87 | Expr::SetComp(_)
88 | Expr::DictComp(_)
89 ) {
90 return true;
91 }
92 if !matches!(
93 right.as_ref(),
94 Expr::StringLiteral(_)
95 | Expr::BytesLiteral(_)
96 | Expr::NumberLiteral(_)
97 | Expr::BooleanLiteral(_)
98 | Expr::NoneLiteral(_)
99 | Expr::EllipsisLiteral(_)
100 | Expr::FString(_)
101 | Expr::List(_)
102 | Expr::Tuple(_)
103 | Expr::Set(_)
104 | Expr::Dict(_)
105 | Expr::ListComp(_)
106 | Expr::SetComp(_)
107 | Expr::DictComp(_)
108 ) {
109 return true;
110 }
111 return false;
112 }
113
114 matches!(
116 expr,
117 Expr::Await(_)
118 | Expr::Call(_)
119 | Expr::DictComp(_)
120 | Expr::Generator(_)
121 | Expr::ListComp(_)
122 | Expr::SetComp(_)
123 | Expr::Subscript(_)
124 | Expr::Yield(_)
125 | Expr::YieldFrom(_)
126 | Expr::IpyEscapeCommand(_)
127 )
128 })
129}
130
131pub fn any_over_expr(expr: &Expr, func: &dyn Fn(&Expr) -> bool) -> bool {
134 if func(expr) {
135 return true;
136 }
137 match expr {
138 Expr::BoolOp(ast::ExprBoolOp { values, .. }) => {
139 values.iter().any(|expr| any_over_expr(expr, func))
140 }
141 Expr::FString(ast::ExprFString { value, .. }) => value
142 .elements()
143 .any(|expr| any_over_interpolated_string_element(expr, func)),
144 Expr::TString(ast::ExprTString { value, .. }) => value
145 .elements()
146 .any(|expr| any_over_interpolated_string_element(expr, func)),
147 Expr::Named(ast::ExprNamed {
148 target,
149 value,
150 range: _,
151 node_index: _,
152 }) => any_over_expr(target, func) || any_over_expr(value, func),
153 Expr::BinOp(ast::ExprBinOp { left, right, .. }) => {
154 any_over_expr(left, func) || any_over_expr(right, func)
155 }
156 Expr::UnaryOp(ast::ExprUnaryOp { operand, .. }) => any_over_expr(operand, func),
157 Expr::Lambda(ast::ExprLambda { body, .. }) => any_over_expr(body, func),
158 Expr::If(ast::ExprIf {
159 test,
160 body,
161 orelse,
162 range: _,
163 node_index: _,
164 }) => any_over_expr(test, func) || any_over_expr(body, func) || any_over_expr(orelse, func),
165 Expr::Dict(ast::ExprDict {
166 items,
167 range: _,
168 node_index: _,
169 }) => items.iter().any(|ast::DictItem { key, value }| {
170 any_over_expr(value, func) || key.as_ref().is_some_and(|key| any_over_expr(key, func))
171 }),
172 Expr::Set(ast::ExprSet {
173 elts,
174 range: _,
175 node_index: _,
176 })
177 | Expr::List(ast::ExprList {
178 elts,
179 range: _,
180 node_index: _,
181 ..
182 })
183 | Expr::Tuple(ast::ExprTuple {
184 elts,
185 range: _,
186 node_index: _,
187 ..
188 }) => elts.iter().any(|expr| any_over_expr(expr, func)),
189 Expr::ListComp(ast::ExprListComp {
190 elt,
191 generators,
192 range: _,
193 node_index: _,
194 })
195 | Expr::SetComp(ast::ExprSetComp {
196 elt,
197 generators,
198 range: _,
199 node_index: _,
200 })
201 | Expr::Generator(ast::ExprGenerator {
202 elt,
203 generators,
204 range: _,
205 node_index: _,
206 parenthesized: _,
207 }) => {
208 any_over_expr(elt, func)
209 || generators.iter().any(|generator| {
210 any_over_expr(&generator.target, func)
211 || any_over_expr(&generator.iter, func)
212 || generator.ifs.iter().any(|expr| any_over_expr(expr, func))
213 })
214 }
215 Expr::DictComp(ast::ExprDictComp {
216 key,
217 value,
218 generators,
219 range: _,
220 node_index: _,
221 }) => {
222 any_over_expr(key, func)
223 || any_over_expr(value, func)
224 || generators.iter().any(|generator| {
225 any_over_expr(&generator.target, func)
226 || any_over_expr(&generator.iter, func)
227 || generator.ifs.iter().any(|expr| any_over_expr(expr, func))
228 })
229 }
230 Expr::Await(ast::ExprAwait {
231 value,
232 range: _,
233 node_index: _,
234 })
235 | Expr::YieldFrom(ast::ExprYieldFrom {
236 value,
237 range: _,
238 node_index: _,
239 })
240 | Expr::Attribute(ast::ExprAttribute {
241 value,
242 range: _,
243 node_index: _,
244 ..
245 })
246 | Expr::Starred(ast::ExprStarred {
247 value,
248 range: _,
249 node_index: _,
250 ..
251 }) => any_over_expr(value, func),
252 Expr::Yield(ast::ExprYield {
253 value,
254 range: _,
255 node_index: _,
256 }) => value
257 .as_ref()
258 .is_some_and(|value| any_over_expr(value, func)),
259 Expr::Compare(ast::ExprCompare {
260 left, comparators, ..
261 }) => any_over_expr(left, func) || comparators.iter().any(|expr| any_over_expr(expr, func)),
262 Expr::Call(ast::ExprCall {
263 func: call_func,
264 arguments,
265 range: _,
266 node_index: _,
267 }) => {
268 any_over_expr(call_func, func)
269 || arguments.args.iter().any(|expr| any_over_expr(expr, func))
272 || arguments.keywords
273 .iter()
274 .any(|keyword| any_over_expr(&keyword.value, func))
275 }
276 Expr::Subscript(ast::ExprSubscript { value, slice, .. }) => {
277 any_over_expr(value, func) || any_over_expr(slice, func)
278 }
279 Expr::Slice(ast::ExprSlice {
280 lower,
281 upper,
282 step,
283 range: _,
284 node_index: _,
285 }) => {
286 lower
287 .as_ref()
288 .is_some_and(|value| any_over_expr(value, func))
289 || upper
290 .as_ref()
291 .is_some_and(|value| any_over_expr(value, func))
292 || step
293 .as_ref()
294 .is_some_and(|value| any_over_expr(value, func))
295 }
296 Expr::Name(_)
297 | Expr::StringLiteral(_)
298 | Expr::BytesLiteral(_)
299 | Expr::NumberLiteral(_)
300 | Expr::BooleanLiteral(_)
301 | Expr::NoneLiteral(_)
302 | Expr::EllipsisLiteral(_)
303 | Expr::IpyEscapeCommand(_) => false,
304 }
305}
306
307pub fn any_over_type_param(type_param: &TypeParam, func: &dyn Fn(&Expr) -> bool) -> bool {
308 match type_param {
309 TypeParam::TypeVar(ast::TypeParamTypeVar { bound, default, .. }) => {
310 bound
311 .as_ref()
312 .is_some_and(|value| any_over_expr(value, func))
313 || default
314 .as_ref()
315 .is_some_and(|value| any_over_expr(value, func))
316 }
317 TypeParam::TypeVarTuple(ast::TypeParamTypeVarTuple { default, .. }) => default
318 .as_ref()
319 .is_some_and(|value| any_over_expr(value, func)),
320 TypeParam::ParamSpec(ast::TypeParamParamSpec { default, .. }) => default
321 .as_ref()
322 .is_some_and(|value| any_over_expr(value, func)),
323 }
324}
325
326pub fn any_over_pattern(pattern: &Pattern, func: &dyn Fn(&Expr) -> bool) -> bool {
327 match pattern {
328 Pattern::MatchValue(ast::PatternMatchValue {
329 value,
330 range: _,
331 node_index: _,
332 }) => any_over_expr(value, func),
333 Pattern::MatchSingleton(_) => false,
334 Pattern::MatchSequence(ast::PatternMatchSequence {
335 patterns,
336 range: _,
337 node_index: _,
338 }) => patterns
339 .iter()
340 .any(|pattern| any_over_pattern(pattern, func)),
341 Pattern::MatchMapping(ast::PatternMatchMapping { keys, patterns, .. }) => {
342 keys.iter().any(|key| any_over_expr(key, func))
343 || patterns
344 .iter()
345 .any(|pattern| any_over_pattern(pattern, func))
346 }
347 Pattern::MatchClass(ast::PatternMatchClass { cls, arguments, .. }) => {
348 any_over_expr(cls, func)
349 || arguments
350 .patterns
351 .iter()
352 .any(|pattern| any_over_pattern(pattern, func))
353 || arguments
354 .keywords
355 .iter()
356 .any(|keyword| any_over_pattern(&keyword.pattern, func))
357 }
358 Pattern::MatchStar(_) => false,
359 Pattern::MatchAs(ast::PatternMatchAs { pattern, .. }) => pattern
360 .as_ref()
361 .is_some_and(|pattern| any_over_pattern(pattern, func)),
362 Pattern::MatchOr(ast::PatternMatchOr {
363 patterns,
364 range: _,
365 node_index: _,
366 }) => patterns
367 .iter()
368 .any(|pattern| any_over_pattern(pattern, func)),
369 }
370}
371
372pub fn any_over_interpolated_string_element(
373 element: &ast::InterpolatedStringElement,
374 func: &dyn Fn(&Expr) -> bool,
375) -> bool {
376 match element {
377 ast::InterpolatedStringElement::Literal(_) => false,
378 ast::InterpolatedStringElement::Interpolation(ast::InterpolatedElement {
379 expression,
380 format_spec,
381 ..
382 }) => {
383 any_over_expr(expression, func)
384 || format_spec.as_ref().is_some_and(|spec| {
385 spec.elements.iter().any(|spec_element| {
386 any_over_interpolated_string_element(spec_element, func)
387 })
388 })
389 }
390 }
391}
392
393pub fn any_over_stmt(stmt: &Stmt, func: &dyn Fn(&Expr) -> bool) -> bool {
394 match stmt {
395 Stmt::FunctionDef(ast::StmtFunctionDef {
396 parameters,
397 type_params,
398 body,
399 decorator_list,
400 returns,
401 ..
402 }) => {
403 parameters.iter().any(|param| {
404 param
405 .default()
406 .is_some_and(|default| any_over_expr(default, func))
407 || param
408 .annotation()
409 .is_some_and(|annotation| any_over_expr(annotation, func))
410 }) || type_params.as_ref().is_some_and(|type_params| {
411 type_params
412 .iter()
413 .any(|type_param| any_over_type_param(type_param, func))
414 }) || body.iter().any(|stmt| any_over_stmt(stmt, func))
415 || decorator_list
416 .iter()
417 .any(|decorator| any_over_expr(&decorator.expression, func))
418 || returns
419 .as_ref()
420 .is_some_and(|value| any_over_expr(value, func))
421 }
422 Stmt::ClassDef(ast::StmtClassDef {
423 arguments,
424 type_params,
425 body,
426 decorator_list,
427 ..
428 }) => {
429 arguments
432 .as_deref()
433 .is_some_and(|Arguments { args, keywords, .. }| {
434 args.iter().any(|expr| any_over_expr(expr, func))
435 || keywords
436 .iter()
437 .any(|keyword| any_over_expr(&keyword.value, func))
438 })
439 || type_params.as_ref().is_some_and(|type_params| {
440 type_params
441 .iter()
442 .any(|type_param| any_over_type_param(type_param, func))
443 })
444 || body.iter().any(|stmt| any_over_stmt(stmt, func))
445 || decorator_list
446 .iter()
447 .any(|decorator| any_over_expr(&decorator.expression, func))
448 }
449 Stmt::Return(ast::StmtReturn {
450 value,
451 range: _,
452 node_index: _,
453 }) => value
454 .as_ref()
455 .is_some_and(|value| any_over_expr(value, func)),
456 Stmt::Delete(ast::StmtDelete {
457 targets,
458 range: _,
459 node_index: _,
460 }) => targets.iter().any(|expr| any_over_expr(expr, func)),
461 Stmt::TypeAlias(ast::StmtTypeAlias {
462 name,
463 type_params,
464 value,
465 ..
466 }) => {
467 any_over_expr(name, func)
468 || type_params.as_ref().is_some_and(|type_params| {
469 type_params
470 .iter()
471 .any(|type_param| any_over_type_param(type_param, func))
472 })
473 || any_over_expr(value, func)
474 }
475 Stmt::Assign(ast::StmtAssign { targets, value, .. }) => {
476 targets.iter().any(|expr| any_over_expr(expr, func)) || any_over_expr(value, func)
477 }
478 Stmt::AugAssign(ast::StmtAugAssign { target, value, .. }) => {
479 any_over_expr(target, func) || any_over_expr(value, func)
480 }
481 Stmt::AnnAssign(ast::StmtAnnAssign {
482 target,
483 annotation,
484 value,
485 ..
486 }) => {
487 any_over_expr(target, func)
488 || any_over_expr(annotation, func)
489 || value
490 .as_ref()
491 .is_some_and(|value| any_over_expr(value, func))
492 }
493 Stmt::For(ast::StmtFor {
494 target,
495 iter,
496 body,
497 orelse,
498 ..
499 }) => {
500 any_over_expr(target, func)
501 || any_over_expr(iter, func)
502 || any_over_body(body, func)
503 || any_over_body(orelse, func)
504 }
505 Stmt::While(ast::StmtWhile {
506 test,
507 body,
508 orelse,
509 range: _,
510 node_index: _,
511 }) => any_over_expr(test, func) || any_over_body(body, func) || any_over_body(orelse, func),
512 Stmt::If(ast::StmtIf {
513 test,
514 body,
515 elif_else_clauses,
516 range: _,
517 node_index: _,
518 }) => {
519 any_over_expr(test, func)
520 || any_over_body(body, func)
521 || elif_else_clauses.iter().any(|clause| {
522 clause
523 .test
524 .as_ref()
525 .is_some_and(|test| any_over_expr(test, func))
526 || any_over_body(&clause.body, func)
527 })
528 }
529 Stmt::With(ast::StmtWith { items, body, .. }) => {
530 items.iter().any(|with_item| {
531 any_over_expr(&with_item.context_expr, func)
532 || with_item
533 .optional_vars
534 .as_ref()
535 .is_some_and(|expr| any_over_expr(expr, func))
536 }) || any_over_body(body, func)
537 }
538 Stmt::Raise(ast::StmtRaise {
539 exc,
540 cause,
541 range: _,
542 node_index: _,
543 }) => {
544 exc.as_ref().is_some_and(|value| any_over_expr(value, func))
545 || cause
546 .as_ref()
547 .is_some_and(|value| any_over_expr(value, func))
548 }
549 Stmt::Try(ast::StmtTry {
550 body,
551 handlers,
552 orelse,
553 finalbody,
554 is_star: _,
555 range: _,
556 node_index: _,
557 }) => {
558 any_over_body(body, func)
559 || handlers.iter().any(|handler| {
560 let ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler {
561 type_,
562 body,
563 ..
564 }) = handler;
565 type_.as_ref().is_some_and(|expr| any_over_expr(expr, func))
566 || any_over_body(body, func)
567 })
568 || any_over_body(orelse, func)
569 || any_over_body(finalbody, func)
570 }
571 Stmt::Assert(ast::StmtAssert {
572 test,
573 msg,
574 range: _,
575 node_index: _,
576 }) => {
577 any_over_expr(test, func)
578 || msg.as_ref().is_some_and(|value| any_over_expr(value, func))
579 }
580 Stmt::Match(ast::StmtMatch {
581 subject,
582 cases,
583 range: _,
584 node_index: _,
585 }) => {
586 any_over_expr(subject, func)
587 || cases.iter().any(|case| {
588 let MatchCase {
589 pattern,
590 guard,
591 body,
592 range: _,
593 node_index: _,
594 } = case;
595 any_over_pattern(pattern, func)
596 || guard.as_ref().is_some_and(|expr| any_over_expr(expr, func))
597 || any_over_body(body, func)
598 })
599 }
600 Stmt::Import(_) => false,
601 Stmt::ImportFrom(_) => false,
602 Stmt::Global(_) => false,
603 Stmt::Nonlocal(_) => false,
604 Stmt::Expr(ast::StmtExpr {
605 value,
606 range: _,
607 node_index: _,
608 }) => any_over_expr(value, func),
609 Stmt::Pass(_) | Stmt::Break(_) | Stmt::Continue(_) => false,
610 Stmt::IpyEscapeCommand(_) => false,
611 }
612}
613
614pub fn any_over_body(body: &[Stmt], func: &dyn Fn(&Expr) -> bool) -> bool {
615 body.iter().any(|stmt| any_over_stmt(stmt, func))
616}
617
618pub fn is_dunder(id: &str) -> bool {
619 id.starts_with("__") && id.ends_with("__")
620}
621
622pub fn is_sunder(id: &str) -> bool {
626 id.starts_with('_') && id.ends_with('_') && !id.starts_with("__") && !id.ends_with("__")
627}
628
629pub fn is_assignment_to_a_dunder(stmt: &Stmt) -> bool {
631 match stmt {
634 Stmt::Assign(ast::StmtAssign { targets, .. }) => {
635 if let [Expr::Name(ast::ExprName { id, .. })] = targets.as_slice() {
636 is_dunder(id)
637 } else {
638 false
639 }
640 }
641 Stmt::AnnAssign(ast::StmtAnnAssign { target, .. }) => {
642 if let Expr::Name(ast::ExprName { id, .. }) = target.as_ref() {
643 is_dunder(id)
644 } else {
645 false
646 }
647 }
648 _ => false,
649 }
650}
651
652pub const fn is_singleton(expr: &Expr) -> bool {
655 matches!(
656 expr,
657 Expr::NoneLiteral(_) | Expr::BooleanLiteral(_) | Expr::EllipsisLiteral(_)
658 )
659}
660
661pub fn is_constant(expr: &Expr) -> bool {
663 if let Expr::Tuple(tuple) = expr {
664 tuple.iter().all(is_constant)
665 } else {
666 expr.is_literal_expr()
667 }
668}
669
670pub fn is_constant_non_singleton(expr: &Expr) -> bool {
672 is_constant(expr) && !is_singleton(expr)
673}
674
675pub const fn is_const_true(expr: &Expr) -> bool {
677 matches!(
678 expr,
679 Expr::BooleanLiteral(ast::ExprBooleanLiteral { value: true, .. }),
680 )
681}
682
683pub const fn is_const_false(expr: &Expr) -> bool {
685 matches!(
686 expr,
687 Expr::BooleanLiteral(ast::ExprBooleanLiteral { value: false, .. }),
688 )
689}
690
691pub const fn is_mutable_iterable_initializer(expr: &Expr) -> bool {
693 matches!(
694 expr,
695 Expr::Set(_)
696 | Expr::SetComp(_)
697 | Expr::List(_)
698 | Expr::ListComp(_)
699 | Expr::Dict(_)
700 | Expr::DictComp(_)
701 )
702}
703
704pub fn extract_handled_exceptions(handlers: &[ExceptHandler]) -> Vec<&Expr> {
706 let mut handled_exceptions = Vec::new();
707 for handler in handlers {
708 match handler {
709 ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler { type_, .. }) => {
710 if let Some(type_) = type_ {
711 if let Expr::Tuple(tuple) = &**type_ {
712 for type_ in tuple {
713 handled_exceptions.push(type_);
714 }
715 } else {
716 handled_exceptions.push(type_);
717 }
718 }
719 }
720 }
721 }
722 handled_exceptions
723}
724
725pub fn map_callable(decorator: &Expr) -> &Expr {
729 if let Expr::Call(ast::ExprCall { func, .. }) = decorator {
730 func
732 } else {
733 decorator
735 }
736}
737
738pub fn map_subscript(expr: &Expr) -> &Expr {
741 if let Expr::Subscript(ast::ExprSubscript { value, .. }) = expr {
742 value
744 } else {
745 expr
747 }
748}
749
750pub fn map_starred(expr: &Expr) -> &Expr {
752 if let Expr::Starred(ast::ExprStarred { value, .. }) = expr {
753 value
755 } else {
756 expr
758 }
759}
760
761pub fn uses_magic_variable_access<F>(body: &[Stmt], is_builtin: F) -> bool
765where
766 F: Fn(&str) -> bool,
767{
768 any_over_body(body, &|expr| {
769 if let Expr::Call(ast::ExprCall { func, .. }) = expr {
770 if let Expr::Name(ast::ExprName { id, .. }) = func.as_ref() {
771 if matches!(id.as_str(), "locals" | "globals" | "vars" | "exec" | "eval") {
772 if is_builtin(id.as_str()) {
773 return true;
774 }
775 }
776 }
777 }
778 false
779 })
780}
781
782pub fn format_import_from(level: u32, module: Option<&str>) -> Cow<'_, str> {
794 match (level, module) {
795 (0, Some(module)) => Cow::Borrowed(module),
796 (level, module) => {
797 let mut module_name =
798 String::with_capacity((level as usize) + module.map_or(0, str::len));
799 for _ in 0..level {
800 module_name.push('.');
801 }
802 if let Some(module) = module {
803 module_name.push_str(module);
804 }
805 Cow::Owned(module_name)
806 }
807 }
808}
809
810pub fn format_import_from_member(level: u32, module: Option<&str>, member: &str) -> String {
822 let mut qualified_name =
823 String::with_capacity((level as usize) + module.map_or(0, str::len) + 1 + member.len());
824 if level > 0 {
825 for _ in 0..level {
826 qualified_name.push('.');
827 }
828 }
829 if let Some(module) = module {
830 qualified_name.push_str(module);
831 qualified_name.push('.');
832 }
833 qualified_name.push_str(member);
834 qualified_name
835}
836
837pub fn to_module_path(package: &Path, path: &Path) -> Option<Vec<String>> {
842 path.strip_prefix(package.parent()?)
843 .ok()?
844 .iter()
845 .map(Path::new)
846 .map(Path::file_stem)
847 .map(|path| path.and_then(|path| path.to_os_string().into_string().ok()))
848 .collect::<Option<Vec<String>>>()
849}
850
851pub fn collect_import_from_member<'a>(
863 level: u32,
864 module: Option<&'a str>,
865 member: &'a str,
866) -> QualifiedName<'a> {
867 let mut qualified_name_builder = QualifiedNameBuilder::with_capacity(
868 level as usize
869 + module
870 .map(|module| module.split('.').count())
871 .unwrap_or_default()
872 + 1,
873 );
874
875 if level > 0 {
877 for _ in 0..level {
878 qualified_name_builder.push(".");
879 }
880 }
881
882 if let Some(module) = module {
884 qualified_name_builder.extend(module.split('.'));
885 }
886
887 qualified_name_builder.push(member);
889
890 qualified_name_builder.build()
891}
892
893pub fn from_relative_import<'a>(
896 module: &'a [String],
898 import: &[&'a str],
900 tail: &[&'a str],
902) -> Option<QualifiedName<'a>> {
903 let mut qualified_name_builder =
904 QualifiedNameBuilder::with_capacity(module.len() + import.len() + tail.len());
905
906 qualified_name_builder.extend(module.iter().map(String::as_str));
908
909 for segment in import {
911 if *segment == "." {
912 if qualified_name_builder.is_empty() {
913 return None;
914 }
915 qualified_name_builder.pop();
916 } else {
917 qualified_name_builder.push(segment);
918 }
919 }
920
921 qualified_name_builder.extend_from_slice(tail);
923
924 Some(qualified_name_builder.build())
925}
926
927pub fn resolve_imported_module_path<'a>(
930 level: u32,
931 module: Option<&'a str>,
932 module_path: Option<&[String]>,
933) -> Option<Cow<'a, str>> {
934 if level == 0 {
935 return Some(Cow::Borrowed(module.unwrap_or("")));
936 }
937
938 let module_path = module_path?;
939
940 if level as usize >= module_path.len() {
941 return None;
942 }
943
944 let mut qualified_path = module_path[..module_path.len() - level as usize].join(".");
945 if let Some(module) = module {
946 if !qualified_path.is_empty() {
947 qualified_path.push('.');
948 }
949 qualified_path.push_str(module);
950 }
951 Some(Cow::Owned(qualified_path))
952}
953
954#[derive(Debug, Default)]
956pub struct NameFinder<'a> {
957 pub names: FxHashMap<&'a str, &'a ast::ExprName>,
959}
960
961impl<'a> Visitor<'a> for NameFinder<'a> {
962 fn visit_expr(&mut self, expr: &'a Expr) {
963 if let Expr::Name(name) = expr {
964 self.names.insert(&name.id, name);
965 }
966 crate::visitor::walk_expr(self, expr);
967 }
968}
969
970#[derive(Debug, Default)]
972pub struct StoredNameFinder<'a> {
973 pub names: FxHashMap<&'a str, &'a ast::ExprName>,
975}
976
977impl<'a> Visitor<'a> for StoredNameFinder<'a> {
978 fn visit_expr(&mut self, expr: &'a Expr) {
979 if let Expr::Name(name) = expr {
980 if name.ctx.is_store() {
981 self.names.insert(&name.id, name);
982 }
983 }
984 crate::visitor::walk_expr(self, expr);
985 }
986}
987
988#[derive(Default)]
990pub struct ReturnStatementVisitor<'a> {
991 pub returns: Vec<&'a ast::StmtReturn>,
992 pub is_generator: bool,
993}
994
995impl<'a> Visitor<'a> for ReturnStatementVisitor<'a> {
996 fn visit_stmt(&mut self, stmt: &'a Stmt) {
997 match stmt {
998 Stmt::FunctionDef(_) | Stmt::ClassDef(_) => {
999 }
1001 Stmt::Return(stmt) => self.returns.push(stmt),
1002 _ => crate::visitor::walk_stmt(self, stmt),
1003 }
1004 }
1005
1006 fn visit_expr(&mut self, expr: &'a Expr) {
1007 if let Expr::Yield(_) | Expr::YieldFrom(_) = expr {
1008 self.is_generator = true;
1009 } else {
1010 crate::visitor::walk_expr(self, expr);
1011 }
1012 }
1013}
1014
1015#[derive(Default)]
1017pub struct RaiseStatementVisitor<'a> {
1018 pub raises: Vec<(TextRange, Option<&'a Expr>, Option<&'a Expr>)>,
1019}
1020
1021impl<'a> StatementVisitor<'a> for RaiseStatementVisitor<'a> {
1022 fn visit_stmt(&mut self, stmt: &'a Stmt) {
1023 match stmt {
1024 Stmt::Raise(ast::StmtRaise {
1025 exc,
1026 cause,
1027 range: _,
1028 node_index: _,
1029 }) => {
1030 self.raises
1031 .push((stmt.range(), exc.as_deref(), cause.as_deref()));
1032 }
1033 Stmt::ClassDef(_) | Stmt::FunctionDef(_) | Stmt::Try(_) => {}
1034 Stmt::If(ast::StmtIf {
1035 body,
1036 elif_else_clauses,
1037 ..
1038 }) => {
1039 crate::statement_visitor::walk_body(self, body);
1040 for clause in elif_else_clauses {
1041 self.visit_elif_else_clause(clause);
1042 }
1043 }
1044 Stmt::While(ast::StmtWhile { body, .. })
1045 | Stmt::With(ast::StmtWith { body, .. })
1046 | Stmt::For(ast::StmtFor { body, .. }) => {
1047 crate::statement_visitor::walk_body(self, body);
1048 }
1049 Stmt::Match(ast::StmtMatch { cases, .. }) => {
1050 for case in cases {
1051 crate::statement_visitor::walk_body(self, &case.body);
1052 }
1053 }
1054 _ => {}
1055 }
1056 }
1057}
1058
1059#[derive(Debug, Default)]
1061pub struct AwaitVisitor {
1062 pub seen_await: bool,
1063}
1064
1065impl Visitor<'_> for AwaitVisitor {
1066 fn visit_stmt(&mut self, stmt: &Stmt) {
1067 match stmt {
1068 Stmt::FunctionDef(_) | Stmt::ClassDef(_) => (),
1069 Stmt::With(ast::StmtWith { is_async: true, .. }) => {
1070 self.seen_await = true;
1071 }
1072 Stmt::For(ast::StmtFor { is_async: true, .. }) => {
1073 self.seen_await = true;
1074 }
1075 _ => crate::visitor::walk_stmt(self, stmt),
1076 }
1077 }
1078
1079 fn visit_expr(&mut self, expr: &Expr) {
1080 if let Expr::Await(ast::ExprAwait { .. }) = expr {
1081 self.seen_await = true;
1082 } else {
1083 crate::visitor::walk_expr(self, expr);
1084 }
1085 }
1086
1087 fn visit_comprehension(&mut self, comprehension: &'_ crate::Comprehension) {
1088 if comprehension.is_async {
1089 self.seen_await = true;
1090 } else {
1091 crate::visitor::walk_comprehension(self, comprehension);
1092 }
1093 }
1094}
1095
1096pub fn is_docstring_stmt(stmt: &Stmt) -> bool {
1098 if let Stmt::Expr(ast::StmtExpr {
1099 value,
1100 range: _,
1101 node_index: _,
1102 }) = stmt
1103 {
1104 value.is_string_literal_expr()
1105 } else {
1106 false
1107 }
1108}
1109
1110pub fn is_stub_body(body: &[Stmt]) -> bool {
1114 !body.is_empty()
1115 && body.iter().all(|stmt| match stmt {
1116 Stmt::Pass(_) => true,
1117 Stmt::Expr(ast::StmtExpr { value, .. }) => value.is_ellipsis_literal_expr(),
1118 _ => false,
1119 })
1120}
1121
1122pub fn on_conditional_branch<'a>(parents: &mut impl Iterator<Item = &'a Stmt>) -> bool {
1124 parents.any(|parent| {
1125 if matches!(parent, Stmt::If(_) | Stmt::While(_) | Stmt::Match(_)) {
1126 return true;
1127 }
1128 if let Stmt::Expr(ast::StmtExpr {
1129 value,
1130 range: _,
1131 node_index: _,
1132 }) = parent
1133 {
1134 if value.is_if_expr() {
1135 return true;
1136 }
1137 }
1138 false
1139 })
1140}
1141
1142pub fn in_nested_block<'a>(mut parents: impl Iterator<Item = &'a Stmt>) -> bool {
1144 parents.any(|parent| {
1145 matches!(
1146 parent,
1147 Stmt::Try(_) | Stmt::If(_) | Stmt::With(_) | Stmt::Match(_)
1148 )
1149 })
1150}
1151
1152pub fn is_unpacking_assignment(parent: &Stmt, child: &Expr) -> bool {
1154 match parent {
1155 Stmt::With(ast::StmtWith { items, .. }) => items.iter().any(|item| {
1156 if let Some(optional_vars) = &item.optional_vars {
1157 if optional_vars.is_tuple_expr() {
1158 if any_over_expr(optional_vars, &|expr| expr == child) {
1159 return true;
1160 }
1161 }
1162 }
1163 false
1164 }),
1165 Stmt::Assign(ast::StmtAssign { targets, value, .. }) => {
1166 let value_is_tuple = matches!(
1168 value.as_ref(),
1169 Expr::Set(_) | Expr::List(_) | Expr::Tuple(_)
1170 );
1171 let targets_are_tuples = targets
1175 .iter()
1176 .all(|item| matches!(item, Expr::Set(_) | Expr::List(_) | Expr::Tuple(_)));
1177 let child_in_tuple = targets_are_tuples
1180 || targets.iter().any(|item| {
1181 matches!(item, Expr::Set(_) | Expr::List(_) | Expr::Tuple(_))
1182 && any_over_expr(item, &|expr| expr == child)
1183 });
1184
1185 if child_in_tuple && !value_is_tuple {
1188 return true;
1189 }
1190
1191 if !child_in_tuple && value_is_tuple {
1194 return false;
1195 }
1196
1197 if child_in_tuple && value_is_tuple {
1203 return !targets_are_tuples;
1204 }
1205
1206 false
1207 }
1208 _ => false,
1209 }
1210}
1211
1212#[derive(Copy, Clone, Debug, PartialEq, is_macro::Is)]
1213pub enum Truthiness {
1214 True,
1216 False,
1218 Falsey,
1220 Truthy,
1222 None,
1224 Unknown,
1226}
1227
1228impl Truthiness {
1229 pub fn from_expr<F>(expr: &Expr, is_builtin: F) -> Self
1231 where
1232 F: Fn(&str) -> bool,
1233 {
1234 match expr {
1235 Expr::Lambda(_) => Self::Truthy,
1236 Expr::Generator(_) => Self::Truthy,
1237 Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => {
1238 if value.is_empty() {
1239 Self::Falsey
1240 } else {
1241 Self::Truthy
1242 }
1243 }
1244 Expr::BytesLiteral(ast::ExprBytesLiteral { value, .. }) => {
1245 if value.is_empty() {
1246 Self::Falsey
1247 } else {
1248 Self::Truthy
1249 }
1250 }
1251 Expr::NumberLiteral(ast::ExprNumberLiteral { value, .. }) => match value {
1252 ast::Number::Int(int) => {
1253 if *int == 0 {
1254 Self::Falsey
1255 } else {
1256 Self::Truthy
1257 }
1258 }
1259 ast::Number::Float(float) => {
1260 if *float == 0.0 {
1261 Self::Falsey
1262 } else {
1263 Self::Truthy
1264 }
1265 }
1266 ast::Number::Complex { real, imag, .. } => {
1267 if *real == 0.0 && *imag == 0.0 {
1268 Self::Falsey
1269 } else {
1270 Self::Truthy
1271 }
1272 }
1273 },
1274 Expr::BooleanLiteral(ast::ExprBooleanLiteral { value, .. }) => {
1275 if *value {
1276 Self::True
1277 } else {
1278 Self::False
1279 }
1280 }
1281 Expr::NoneLiteral(_) => Self::None,
1282 Expr::EllipsisLiteral(_) => Self::Truthy,
1283 Expr::FString(f_string) => {
1284 if is_empty_f_string(f_string) {
1285 Self::Falsey
1286 } else if is_non_empty_f_string(f_string) {
1287 Self::Truthy
1288 } else {
1289 Self::Unknown
1290 }
1291 }
1292 Expr::TString(_) => Self::Truthy,
1293 Expr::List(ast::ExprList { elts, .. })
1294 | Expr::Set(ast::ExprSet { elts, .. })
1295 | Expr::Tuple(ast::ExprTuple { elts, .. }) => {
1296 if elts.is_empty() {
1297 return Self::Falsey;
1298 }
1299
1300 if elts.iter().all(Expr::is_starred_expr) {
1301 Self::Unknown
1303 } else {
1304 Self::Truthy
1305 }
1306 }
1307 Expr::Dict(dict) => {
1308 if dict.is_empty() {
1309 return Self::Falsey;
1310 }
1311
1312 if dict
1316 .items
1317 .iter()
1318 .all(|item| matches!(item, DictItem { key: None, .. }))
1319 {
1320 Self::Unknown
1322 } else {
1323 Self::Truthy
1324 }
1325 }
1326 Expr::Call(ast::ExprCall {
1327 func, arguments, ..
1328 }) => {
1329 if let Expr::Name(ast::ExprName { id, .. }) = func.as_ref() {
1330 if is_iterable_initializer(id.as_str(), |id| is_builtin(id)) {
1331 if arguments.is_empty() {
1332 Self::Falsey
1334 } else if let [argument] = &*arguments.args
1335 && arguments.keywords.is_empty()
1336 {
1337 match argument {
1339 Expr::NumberLiteral(_)
1344 | Expr::BooleanLiteral(_)
1345 | Expr::NoneLiteral(_)
1346 | Expr::EllipsisLiteral(_)
1347 | Expr::TString(_)
1348 | Expr::Lambda(_)
1349 | Expr::Generator(_) => Self::Unknown,
1350 _ => Self::from_expr(argument, is_builtin),
1354 }
1355 } else {
1356 Self::Unknown
1357 }
1358 } else {
1359 Self::Unknown
1360 }
1361 } else {
1362 Self::Unknown
1363 }
1364 }
1365 _ => Self::Unknown,
1366 }
1367 }
1368
1369 pub fn into_bool(self) -> Option<bool> {
1370 match self {
1371 Self::True | Self::Truthy => Some(true),
1372 Self::False | Self::Falsey => Some(false),
1373 Self::None => Some(false),
1374 Self::Unknown => None,
1375 }
1376 }
1377}
1378
1379fn is_non_empty_f_string(expr: &ast::ExprFString) -> bool {
1382 fn inner(expr: &Expr) -> bool {
1383 match expr {
1384 Expr::Lambda(_) => true,
1386 Expr::Dict(_) => true,
1387 Expr::Set(_) => true,
1388 Expr::ListComp(_) => true,
1389 Expr::SetComp(_) => true,
1390 Expr::DictComp(_) => true,
1391 Expr::Compare(_) => true,
1392 Expr::NumberLiteral(_) => true,
1393 Expr::BooleanLiteral(_) => true,
1394 Expr::NoneLiteral(_) => true,
1395 Expr::EllipsisLiteral(_) => true,
1396 Expr::List(_) => true,
1397 Expr::Tuple(_) => true,
1398 Expr::TString(_) => true,
1399
1400 Expr::If(ast::ExprIf { body, orelse, .. }) => inner(body) && inner(orelse),
1402 Expr::Named(ast::ExprNamed { value, .. }) => inner(value),
1403
1404 Expr::BoolOp(ast::ExprBoolOp { .. }) => false,
1406 Expr::BinOp(ast::ExprBinOp { .. }) => false,
1407 Expr::UnaryOp(ast::ExprUnaryOp { .. }) => false,
1408 Expr::Generator(_) => false,
1409 Expr::Await(_) => false,
1410 Expr::Yield(_) => false,
1411 Expr::YieldFrom(_) => false,
1412 Expr::Call(_) => false,
1413 Expr::Attribute(_) => false,
1414 Expr::Subscript(_) => false,
1415 Expr::Starred(_) => false,
1416 Expr::Name(_) => false,
1417 Expr::Slice(_) => false,
1418 Expr::IpyEscapeCommand(_) => false,
1419
1420 Expr::FString(f_string) => is_non_empty_f_string(f_string),
1422 Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => !value.is_empty(),
1424 Expr::BytesLiteral(_) => true,
1427 }
1428 }
1429
1430 expr.value.iter().any(|part| match part {
1431 ast::FStringPart::Literal(string_literal) => !string_literal.is_empty(),
1432 ast::FStringPart::FString(f_string) => {
1433 f_string.elements.iter().all(|element| match element {
1434 InterpolatedStringElement::Literal(string_literal) => !string_literal.is_empty(),
1435 InterpolatedStringElement::Interpolation(f_string) => {
1436 f_string.debug_text.is_some() || inner(&f_string.expression)
1437 }
1438 })
1439 }
1440 })
1441}
1442
1443pub fn is_empty_f_string(expr: &ast::ExprFString) -> bool {
1446 fn inner(expr: &Expr) -> bool {
1447 match expr {
1448 Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => value.is_empty(),
1449 Expr::BytesLiteral(_) => false,
1453 Expr::FString(ast::ExprFString { value, .. }) => {
1454 is_empty_interpolated_elements(value.elements())
1455 }
1456 _ => false,
1457 }
1458 }
1459
1460 fn is_empty_interpolated_elements<'a>(
1461 mut elements: impl Iterator<Item = &'a InterpolatedStringElement>,
1462 ) -> bool {
1463 elements.all(|element| match element {
1464 InterpolatedStringElement::Literal(ast::InterpolatedStringLiteralElement {
1465 value,
1466 ..
1467 }) => value.is_empty(),
1468 InterpolatedStringElement::Interpolation(f_string) => {
1469 f_string.debug_text.is_none()
1470 && f_string.conversion.is_none()
1471 && f_string.format_spec.is_none()
1472 && inner(&f_string.expression)
1473 }
1474 })
1475 }
1476
1477 expr.value.iter().all(|part| match part {
1478 ast::FStringPart::Literal(string_literal) => string_literal.is_empty(),
1479 ast::FStringPart::FString(f_string) => {
1480 is_empty_interpolated_elements(f_string.elements.iter())
1481 }
1482 })
1483}
1484
1485pub fn generate_comparison(
1486 left: &Expr,
1487 ops: &[CmpOp],
1488 comparators: &[Expr],
1489 parent: AnyNodeRef,
1490 tokens: &Tokens,
1491 source: &str,
1492) -> String {
1493 let start = left.start();
1494 let end = comparators.last().map_or_else(|| left.end(), Ranged::end);
1495 let mut contents = String::with_capacity(usize::from(end - start));
1496
1497 contents.push_str(
1499 &source[parenthesized_range(left.into(), parent, tokens).unwrap_or(left.range())],
1500 );
1501
1502 for (op, comparator) in ops.iter().zip(comparators) {
1503 contents.push_str(match op {
1505 CmpOp::Eq => " == ",
1506 CmpOp::NotEq => " != ",
1507 CmpOp::Lt => " < ",
1508 CmpOp::LtE => " <= ",
1509 CmpOp::Gt => " > ",
1510 CmpOp::GtE => " >= ",
1511 CmpOp::In => " in ",
1512 CmpOp::NotIn => " not in ",
1513 CmpOp::Is => " is ",
1514 CmpOp::IsNot => " is not ",
1515 });
1516
1517 contents.push_str(
1519 &source[parenthesized_range(comparator.into(), parent, tokens)
1520 .unwrap_or(comparator.range())],
1521 );
1522 }
1523
1524 contents
1525}
1526
1527pub fn pep_604_optional(expr: &Expr) -> Expr {
1529 ast::ExprBinOp {
1530 left: Box::new(expr.clone()),
1531 op: Operator::BitOr,
1532 right: Box::new(Expr::NoneLiteral(ExprNoneLiteral::default())),
1533 range: TextRange::default(),
1534 node_index: AtomicNodeIndex::NONE,
1535 }
1536 .into()
1537}
1538
1539pub fn pep_604_union(elts: &[Expr]) -> Expr {
1541 match elts {
1542 [] => Expr::Tuple(ast::ExprTuple {
1543 elts: vec![],
1544 ctx: ExprContext::Load,
1545 range: TextRange::default(),
1546 node_index: AtomicNodeIndex::NONE,
1547 parenthesized: true,
1548 }),
1549 [Expr::Tuple(ast::ExprTuple { elts, .. })] => pep_604_union(elts),
1550 [elt] => elt.clone(),
1551 [rest @ .., elt] => Expr::BinOp(ast::ExprBinOp {
1552 left: Box::new(pep_604_union(rest)),
1553 op: Operator::BitOr,
1554 right: Box::new(pep_604_union(std::slice::from_ref(elt))),
1555 range: TextRange::default(),
1556 node_index: AtomicNodeIndex::NONE,
1557 }),
1558 }
1559}
1560
1561pub fn typing_optional(elt: Expr, binding: Name) -> Expr {
1563 Expr::Subscript(ast::ExprSubscript {
1564 value: Box::new(Expr::Name(ast::ExprName {
1565 id: binding,
1566 range: TextRange::default(),
1567 node_index: AtomicNodeIndex::NONE,
1568 ctx: ExprContext::Load,
1569 })),
1570 slice: Box::new(elt),
1571 ctx: ExprContext::Load,
1572 range: TextRange::default(),
1573 node_index: AtomicNodeIndex::NONE,
1574 })
1575}
1576
1577pub fn typing_union(elts: &[Expr], binding: Name) -> Expr {
1582 Expr::Subscript(ast::ExprSubscript {
1583 value: Box::new(Expr::Name(ast::ExprName {
1584 id: binding,
1585 range: TextRange::default(),
1586 node_index: AtomicNodeIndex::NONE,
1587 ctx: ExprContext::Load,
1588 })),
1589 slice: Box::new(Expr::Tuple(ast::ExprTuple {
1590 range: TextRange::default(),
1591 node_index: AtomicNodeIndex::NONE,
1592 elts: elts.to_vec(),
1593 ctx: ExprContext::Load,
1594 parenthesized: false,
1595 })),
1596 ctx: ExprContext::Load,
1597 range: TextRange::default(),
1598 node_index: AtomicNodeIndex::NONE,
1599 })
1600}
1601
1602pub fn comment_indentation_after(
1655 preceding: AnyNodeRef,
1656 comment_range: TextRange,
1657 source: &str,
1658) -> TextSize {
1659 let tokenizer = SimpleTokenizer::new(
1660 source,
1661 TextRange::new(source.full_line_end(preceding.end()), comment_range.end()),
1662 );
1663
1664 tokenizer
1665 .filter_map(|token| {
1666 if token.kind() == SimpleTokenKind::Comment {
1667 indentation_at_offset(token.start(), source).map(TextLen::text_len)
1668 } else {
1669 None
1670 }
1671 })
1672 .min()
1673 .unwrap_or_default()
1674}
1675
1676#[cfg(test)]
1677mod tests {
1678 use std::borrow::Cow;
1679 use std::cell::RefCell;
1680 use std::vec;
1681
1682 use ruff_text_size::TextRange;
1683
1684 use crate::helpers::{any_over_stmt, any_over_type_param, resolve_imported_module_path};
1685 use crate::{
1686 AtomicNodeIndex, Expr, ExprContext, ExprName, ExprNumberLiteral, Identifier, Int, Number,
1687 Stmt, StmtTypeAlias, TypeParam, TypeParamParamSpec, TypeParamTypeVar,
1688 TypeParamTypeVarTuple, TypeParams,
1689 };
1690
1691 #[test]
1692 fn resolve_import() {
1693 assert_eq!(
1695 resolve_imported_module_path(0, Some("foo"), None),
1696 Some(Cow::Borrowed("foo"))
1697 );
1698
1699 assert_eq!(
1701 resolve_imported_module_path(
1702 1,
1703 Some("foo"),
1704 Some(&["bar".to_string(), "baz".to_string()])
1705 ),
1706 Some(Cow::Owned("bar.foo".to_string()))
1707 );
1708
1709 assert_eq!(resolve_imported_module_path(1, Some("foo"), None), None);
1712
1713 assert_eq!(
1716 resolve_imported_module_path(1, Some("foo"), Some(&["bar".to_string()])),
1717 None,
1718 );
1719 assert_eq!(
1720 resolve_imported_module_path(2, Some("foo"), Some(&["bar".to_string()])),
1721 None
1722 );
1723 }
1724
1725 #[test]
1726 fn any_over_stmt_type_alias() {
1727 let seen = RefCell::new(Vec::new());
1728 let name = Expr::Name(ExprName {
1729 id: "x".into(),
1730 range: TextRange::default(),
1731 node_index: AtomicNodeIndex::NONE,
1732 ctx: ExprContext::Load,
1733 });
1734 let constant_one = Expr::NumberLiteral(ExprNumberLiteral {
1735 value: Number::Int(Int::from(1u8)),
1736 range: TextRange::default(),
1737 node_index: AtomicNodeIndex::NONE,
1738 });
1739 let constant_two = Expr::NumberLiteral(ExprNumberLiteral {
1740 value: Number::Int(Int::from(2u8)),
1741 range: TextRange::default(),
1742 node_index: AtomicNodeIndex::NONE,
1743 });
1744 let constant_three = Expr::NumberLiteral(ExprNumberLiteral {
1745 value: Number::Int(Int::from(3u8)),
1746 range: TextRange::default(),
1747 node_index: AtomicNodeIndex::NONE,
1748 });
1749 let type_var_one = TypeParam::TypeVar(TypeParamTypeVar {
1750 range: TextRange::default(),
1751 node_index: AtomicNodeIndex::NONE,
1752 bound: Some(Box::new(constant_one.clone())),
1753 default: None,
1754 name: Identifier::new("x", TextRange::default()),
1755 });
1756 let type_var_two = TypeParam::TypeVar(TypeParamTypeVar {
1757 range: TextRange::default(),
1758 node_index: AtomicNodeIndex::NONE,
1759 bound: None,
1760 default: Some(Box::new(constant_two.clone())),
1761 name: Identifier::new("x", TextRange::default()),
1762 });
1763 let type_alias = Stmt::TypeAlias(StmtTypeAlias {
1764 name: Box::new(name.clone()),
1765 type_params: Some(Box::new(TypeParams {
1766 type_params: vec![type_var_one, type_var_two],
1767 range: TextRange::default(),
1768 node_index: AtomicNodeIndex::NONE,
1769 })),
1770 value: Box::new(constant_three.clone()),
1771 range: TextRange::default(),
1772 node_index: AtomicNodeIndex::NONE,
1773 });
1774 assert!(!any_over_stmt(&type_alias, &|expr| {
1775 seen.borrow_mut().push(expr.clone());
1776 false
1777 }));
1778 assert_eq!(
1779 seen.take(),
1780 vec![name, constant_one, constant_two, constant_three]
1781 );
1782 }
1783
1784 #[test]
1785 fn any_over_type_param_type_var() {
1786 let type_var_no_bound = TypeParam::TypeVar(TypeParamTypeVar {
1787 range: TextRange::default(),
1788 node_index: AtomicNodeIndex::NONE,
1789 bound: None,
1790 default: None,
1791 name: Identifier::new("x", TextRange::default()),
1792 });
1793 assert!(!any_over_type_param(&type_var_no_bound, &|_expr| true));
1794
1795 let constant = Expr::NumberLiteral(ExprNumberLiteral {
1796 value: Number::Int(Int::ONE),
1797 range: TextRange::default(),
1798 node_index: AtomicNodeIndex::NONE,
1799 });
1800
1801 let type_var_with_bound = TypeParam::TypeVar(TypeParamTypeVar {
1802 range: TextRange::default(),
1803 node_index: AtomicNodeIndex::NONE,
1804 bound: Some(Box::new(constant.clone())),
1805 default: None,
1806 name: Identifier::new("x", TextRange::default()),
1807 });
1808 assert!(
1809 any_over_type_param(&type_var_with_bound, &|expr| {
1810 assert_eq!(
1811 *expr, constant,
1812 "the received expression should be the unwrapped bound"
1813 );
1814 true
1815 }),
1816 "if true is returned from `func` it should be respected"
1817 );
1818
1819 let type_var_with_default = TypeParam::TypeVar(TypeParamTypeVar {
1820 range: TextRange::default(),
1821 node_index: AtomicNodeIndex::NONE,
1822 default: Some(Box::new(constant.clone())),
1823 bound: None,
1824 name: Identifier::new("x", TextRange::default()),
1825 });
1826 assert!(
1827 any_over_type_param(&type_var_with_default, &|expr| {
1828 assert_eq!(
1829 *expr, constant,
1830 "the received expression should be the unwrapped default"
1831 );
1832 true
1833 }),
1834 "if true is returned from `func` it should be respected"
1835 );
1836 }
1837
1838 #[test]
1839 fn any_over_type_param_type_var_tuple() {
1840 let type_var_tuple = TypeParam::TypeVarTuple(TypeParamTypeVarTuple {
1841 range: TextRange::default(),
1842 node_index: AtomicNodeIndex::NONE,
1843 name: Identifier::new("x", TextRange::default()),
1844 default: None,
1845 });
1846 assert!(
1847 !any_over_type_param(&type_var_tuple, &|_expr| true),
1848 "this TypeVarTuple has no expressions to visit"
1849 );
1850
1851 let constant = Expr::NumberLiteral(ExprNumberLiteral {
1852 value: Number::Int(Int::ONE),
1853 range: TextRange::default(),
1854 node_index: AtomicNodeIndex::NONE,
1855 });
1856
1857 let type_var_tuple_with_default = TypeParam::TypeVarTuple(TypeParamTypeVarTuple {
1858 range: TextRange::default(),
1859 node_index: AtomicNodeIndex::NONE,
1860 default: Some(Box::new(constant.clone())),
1861 name: Identifier::new("x", TextRange::default()),
1862 });
1863 assert!(
1864 any_over_type_param(&type_var_tuple_with_default, &|expr| {
1865 assert_eq!(
1866 *expr, constant,
1867 "the received expression should be the unwrapped default"
1868 );
1869 true
1870 }),
1871 "if true is returned from `func` it should be respected"
1872 );
1873 }
1874
1875 #[test]
1876 fn any_over_type_param_param_spec() {
1877 let type_param_spec = TypeParam::ParamSpec(TypeParamParamSpec {
1878 range: TextRange::default(),
1879 node_index: AtomicNodeIndex::NONE,
1880 name: Identifier::new("x", TextRange::default()),
1881 default: None,
1882 });
1883 assert!(
1884 !any_over_type_param(&type_param_spec, &|_expr| true),
1885 "this ParamSpec has no expressions to visit"
1886 );
1887
1888 let constant = Expr::NumberLiteral(ExprNumberLiteral {
1889 value: Number::Int(Int::ONE),
1890 range: TextRange::default(),
1891 node_index: AtomicNodeIndex::NONE,
1892 });
1893
1894 let param_spec_with_default = TypeParam::TypeVarTuple(TypeParamTypeVarTuple {
1895 range: TextRange::default(),
1896 node_index: AtomicNodeIndex::NONE,
1897 default: Some(Box::new(constant.clone())),
1898 name: Identifier::new("x", TextRange::default()),
1899 });
1900 assert!(
1901 any_over_type_param(¶m_spec_with_default, &|expr| {
1902 assert_eq!(
1903 *expr, constant,
1904 "the received expression should be the unwrapped default"
1905 );
1906 true
1907 }),
1908 "if true is returned from `func` it should be respected"
1909 );
1910 }
1911}