Move CallPath into its own module (#3847)

This commit is contained in:
Charlie Marsh 2023-04-01 11:25:04 -04:00 committed by GitHub
parent 2f90157ce2
commit d822e08111
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 129 additions and 102 deletions

View file

@ -0,0 +1,63 @@
use rustpython_parser::ast::{Expr, ExprKind};
use smallvec::smallvec;
/// A representation of a qualified name, like `typing.List`.
pub type CallPath<'a> = smallvec::SmallVec<[&'a str; 8]>;
fn collect_call_path_inner<'a>(expr: &'a Expr, parts: &mut CallPath<'a>) -> bool {
match &expr.node {
ExprKind::Attribute { value, attr, .. } => {
if collect_call_path_inner(value, parts) {
parts.push(attr);
true
} else {
false
}
}
ExprKind::Name { id, .. } => {
parts.push(id);
true
}
_ => false,
}
}
/// Convert an `Expr` to its [`CallPath`] segments (like `["typing", "List"]`).
pub fn collect_call_path(expr: &Expr) -> CallPath {
let mut segments = smallvec![];
collect_call_path_inner(expr, &mut segments);
segments
}
/// Convert an `Expr` to its call path (like `List`, or `typing.List`).
pub fn compose_call_path(expr: &Expr) -> Option<String> {
let call_path = collect_call_path(expr);
if call_path.is_empty() {
None
} else {
Some(format_call_path(&call_path))
}
}
/// Format a call path for display.
pub fn format_call_path(call_path: &[&str]) -> String {
if call_path
.first()
.expect("Unable to format empty call path")
.is_empty()
{
call_path[1..].join(".")
} else {
call_path.join(".")
}
}
/// Split a fully-qualified name (like `typing.List`) into (`typing`, `List`).
pub fn to_call_path(target: &str) -> CallPath {
if target.contains('.') {
target.split('.').collect()
} else {
// Special-case: for builtins, return `["", "int"]` instead of `["int"]`.
smallvec!["", target]
}
}

View file

@ -1,14 +1,13 @@
//! An equivalent object hierarchy to the [`Expr`] hierarchy, but with the
//! ability to compare expressions for equality (via [`Eq`] and [`Hash`]).
use num_bigint::BigInt;
use rustpython_parser::ast::{
Alias, Arg, Arguments, Boolop, Cmpop, Comprehension, Constant, Excepthandler,
ExcepthandlerKind, Expr, ExprContext, ExprKind, Keyword, MatchCase, Operator, Pattern,
PatternKind, Stmt, StmtKind, Unaryop, Withitem,
};
use num_bigint::BigInt;
#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
pub enum ComparableExprContext {
Load,

View file

@ -12,9 +12,10 @@ use crate::binding::{
Binding, BindingId, BindingKind, Bindings, Exceptions, ExecutionContext, FromImportation,
Importation, SubmoduleImportation,
};
use crate::helpers::{collect_call_path, from_relative_import};
use crate::call_path::{collect_call_path, CallPath};
use crate::helpers::from_relative_import;
use crate::scope::{Scope, ScopeId, ScopeKind, ScopeStack, Scopes};
use crate::types::{CallPath, RefEquality};
use crate::types::RefEquality;
use crate::typing::AnnotationKind;
use crate::visibility::{module_visibility, Modifier, VisibleScope};

View file

@ -1,7 +1,8 @@
use rustpython_parser::ast::Expr;
use crate::call_path::to_call_path;
use crate::context::Context;
use crate::helpers::{map_callable, to_call_path};
use crate::helpers::map_callable;
use crate::scope::{Scope, ScopeKind};
const CLASS_METHODS: [&str; 3] = ["__new__", "__init_subclass__", "__class_getitem__"];

View file

@ -10,11 +10,12 @@ use rustpython_parser::ast::{
KeywordData, Located, Location, MatchCase, Pattern, PatternKind, Stmt, StmtKind,
};
use rustpython_parser::{lexer, Mode, Tok};
use smallvec::{smallvec, SmallVec};
use smallvec::SmallVec;
use crate::call_path::CallPath;
use crate::context::Context;
use crate::source_code::{Generator, Indexer, Locator, Stylist};
use crate::types::{CallPath, Range};
use crate::types::Range;
use crate::visitor;
use crate::visitor::Visitor;
@ -49,54 +50,6 @@ pub fn unparse_constant(constant: &Constant, stylist: &Stylist) -> String {
generator.generate()
}
fn collect_call_path_inner<'a>(expr: &'a Expr, parts: &mut CallPath<'a>) -> bool {
match &expr.node {
ExprKind::Attribute { value, attr, .. } => {
if collect_call_path_inner(value, parts) {
parts.push(attr);
true
} else {
false
}
}
ExprKind::Name { id, .. } => {
parts.push(id);
true
}
_ => false,
}
}
/// Convert an `Expr` to its [`CallPath`] segments (like `["typing", "List"]`).
pub fn collect_call_path(expr: &Expr) -> CallPath {
let mut segments = smallvec![];
collect_call_path_inner(expr, &mut segments);
segments
}
/// Convert an `Expr` to its call path (like `List`, or `typing.List`).
pub fn compose_call_path(expr: &Expr) -> Option<String> {
let call_path = collect_call_path(expr);
if call_path.is_empty() {
None
} else {
Some(format_call_path(&call_path))
}
}
/// Format a call path for display.
pub fn format_call_path(call_path: &[&str]) -> String {
if call_path
.first()
.expect("Unable to format empty call path")
.is_empty()
{
call_path[1..].join(".")
} else {
call_path.join(".")
}
}
/// Return `true` if the `Expr` contains a reference to `${module}.${target}`.
pub fn contains_call_path(ctx: &Context, expr: &Expr, target: &[&str]) -> bool {
any_over_expr(expr, &|expr| {
@ -748,15 +701,6 @@ pub fn format_import_from_member(
full_name
}
/// Split a target string (like `typing.List`) into (`typing`, `List`).
pub fn to_call_path(target: &str) -> CallPath {
if target.contains('.') {
target.split('.').collect()
} else {
smallvec!["", target]
}
}
/// Create a module path from a (package, path) pair.
///
/// For example, if the package is `foo/bar` and the path is `foo/bar/baz.py`,

View file

@ -1,6 +1,7 @@
pub mod all;
pub mod binding;
pub mod branch_detection;
pub mod call_path;
pub mod cast;
pub mod comparable;
pub mod context;

View file

@ -1,7 +1,8 @@
use crate::context::Context;
use crate::helpers::collect_call_path;
use rustpython_parser::ast::{Expr, ExprKind};
use crate::call_path::collect_call_path;
use crate::context::Context;
#[derive(Copy, Clone)]
pub enum LoggingLevel {
Debug,

View file

@ -208,9 +208,10 @@ impl Utf8Index {
#[cfg(test)]
mod tests {
use crate::source_code::locator::{AsciiIndex, Index, Utf8Index};
use rustpython_parser::ast::Location;
use crate::source_code::locator::{AsciiIndex, Index, Utf8Index};
fn index_ascii(content: &str) -> AsciiIndex {
match Index::from(content) {
Index::Ascii(ascii) => ascii,

View file

@ -8,9 +8,9 @@ use rustpython_parser::ast::Location;
use rustpython_parser::lexer::LexResult;
use rustpython_parser::Tok;
use crate::source_code::Locator;
use ruff_rustpython::vendor;
use crate::source_code::Locator;
use crate::str::leading_quote;
use crate::types::Range;
@ -213,11 +213,12 @@ fn detect_line_ending(contents: &str) -> Option<LineEnding> {
#[cfg(test)]
mod tests {
use crate::source_code::stylist::{detect_line_ending, Indentation, LineEnding, Quote};
use crate::source_code::{Locator, Stylist};
use rustpython_parser::lexer::lex;
use rustpython_parser::Mode;
use crate::source_code::stylist::{detect_line_ending, Indentation, LineEnding, Quote};
use crate::source_code::{Locator, Stylist};
#[test]
fn indentation() {
let contents = r#"x = 1"#;

View file

@ -97,5 +97,3 @@ impl<'a> From<&RefEquality<'a, Expr>> for &'a Expr {
r.0
}
}
pub type CallPath<'a> = smallvec::SmallVec<[&'a str; 8]>;

View file

@ -2,9 +2,10 @@ use std::path::Path;
use rustpython_parser::ast::{Expr, Stmt, StmtKind};
use crate::call_path::collect_call_path;
use crate::call_path::CallPath;
use crate::context::Context;
use crate::helpers::{collect_call_path, map_callable};
use crate::types::CallPath;
use crate::helpers::map_callable;
#[derive(Debug, Clone, Copy)]
pub enum Modifier {