CallPath newtype wrapper (#10201)

## Summary

This PR changes the `CallPath` type alias to a newtype wrapper. 

A newtype wrapper allows us to limit the API and to experiment with
alternative ways to implement matching on `CallPath`s.



## Test Plan

`cargo test`
This commit is contained in:
Micha Reiser 2024-03-03 16:54:24 +01:00 committed by GitHub
parent fb05d218c3
commit e725b6fdaf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
165 changed files with 551 additions and 433 deletions

View file

@ -1,6 +1,6 @@
//! Analysis rules for the `typing` module.
use ruff_python_ast::call_path::{from_qualified_name, from_unqualified_name, CallPath};
use ruff_python_ast::call_path::CallPath;
use ruff_python_ast::helpers::{any_over_expr, is_const_false, map_subscript};
use ruff_python_ast::{self as ast, Expr, Int, Operator, ParameterWithDefault, Parameters, Stmt};
use ruff_python_stdlib::typing::{
@ -43,27 +43,27 @@ pub fn match_annotated_subscript<'a>(
extend_generics: &[String],
) -> Option<SubscriptKind> {
semantic.resolve_call_path(expr).and_then(|call_path| {
if is_standard_library_literal(call_path.as_slice()) {
if is_standard_library_literal(call_path.segments()) {
return Some(SubscriptKind::Literal);
}
if is_standard_library_generic(call_path.as_slice())
if is_standard_library_generic(call_path.segments())
|| extend_generics
.iter()
.map(|target| from_qualified_name(target))
.map(|target| CallPath::from_qualified_name(target))
.any(|target| call_path == target)
{
return Some(SubscriptKind::Generic);
}
if is_pep_593_generic_type(call_path.as_slice()) {
if is_pep_593_generic_type(call_path.segments()) {
return Some(SubscriptKind::PEP593Annotation);
}
for module in typing_modules {
let module_call_path: CallPath = from_unqualified_name(module);
let module_call_path: CallPath = CallPath::from_unqualified_name(module);
if call_path.starts_with(&module_call_path) {
if let Some(member) = call_path.last() {
if let Some(member) = call_path.segments().last() {
if is_literal_member(member) {
return Some(SubscriptKind::Literal);
}
@ -106,7 +106,7 @@ pub fn to_pep585_generic(expr: &Expr, semantic: &SemanticModel) -> Option<Module
.then(|| semantic.resolve_call_path(expr))
.flatten()
.and_then(|call_path| {
let [module, member] = call_path.as_slice() else {
let [module, member] = call_path.segments() else {
return None;
};
as_pep_585_generic(module, member).map(|(module, member)| {
@ -122,7 +122,7 @@ pub fn to_pep585_generic(expr: &Expr, semantic: &SemanticModel) -> Option<Module
/// Return whether a given expression uses a PEP 585 standard library generic.
pub fn is_pep585_generic(expr: &Expr, semantic: &SemanticModel) -> bool {
semantic.resolve_call_path(expr).is_some_and(|call_path| {
let [module, name] = call_path.as_slice() else {
let [module, name] = call_path.segments() else {
return false;
};
has_pep_585_generic(module, name)
@ -218,8 +218,8 @@ pub fn is_immutable_annotation(
match expr {
Expr::Name(_) | Expr::Attribute(_) => {
semantic.resolve_call_path(expr).is_some_and(|call_path| {
is_immutable_non_generic_type(call_path.as_slice())
|| is_immutable_generic_type(call_path.as_slice())
is_immutable_non_generic_type(call_path.segments())
|| is_immutable_generic_type(call_path.segments())
|| extend_immutable_calls
.iter()
.any(|target| call_path == *target)
@ -227,9 +227,9 @@ pub fn is_immutable_annotation(
}
Expr::Subscript(ast::ExprSubscript { value, slice, .. }) => {
semantic.resolve_call_path(value).is_some_and(|call_path| {
if is_immutable_generic_type(call_path.as_slice()) {
if is_immutable_generic_type(call_path.segments()) {
true
} else if matches!(call_path.as_slice(), ["typing", "Union"]) {
} else if matches!(call_path.segments(), ["typing", "Union"]) {
if let Expr::Tuple(ast::ExprTuple { elts, .. }) = slice.as_ref() {
elts.iter().all(|elt| {
is_immutable_annotation(elt, semantic, extend_immutable_calls)
@ -237,9 +237,9 @@ pub fn is_immutable_annotation(
} else {
false
}
} else if matches!(call_path.as_slice(), ["typing", "Optional"]) {
} else if matches!(call_path.segments(), ["typing", "Optional"]) {
is_immutable_annotation(slice, semantic, extend_immutable_calls)
} else if is_pep_593_generic_type(call_path.as_slice()) {
} else if is_pep_593_generic_type(call_path.segments()) {
if let Expr::Tuple(ast::ExprTuple { elts, .. }) = slice.as_ref() {
elts.first().is_some_and(|elt| {
is_immutable_annotation(elt, semantic, extend_immutable_calls)
@ -273,7 +273,7 @@ pub fn is_immutable_func(
extend_immutable_calls: &[CallPath],
) -> bool {
semantic.resolve_call_path(func).is_some_and(|call_path| {
is_immutable_return_type(call_path.as_slice())
is_immutable_return_type(call_path.segments())
|| extend_immutable_calls
.iter()
.any(|target| call_path == *target)
@ -285,7 +285,7 @@ pub fn is_mutable_func(func: &Expr, semantic: &SemanticModel) -> bool {
semantic
.resolve_call_path(func)
.as_ref()
.map(CallPath::as_slice)
.map(CallPath::segments)
.is_some_and(is_mutable_return_type)
}
@ -337,7 +337,7 @@ pub fn is_sys_version_block(stmt: &ast::StmtIf, semantic: &SemanticModel) -> boo
any_over_expr(test, &|expr| {
semantic.resolve_call_path(expr).is_some_and(|call_path| {
matches!(call_path.as_slice(), ["sys", "version_info" | "platform"])
matches!(call_path.segments(), ["sys", "version_info" | "platform"])
})
})
}
@ -620,7 +620,7 @@ impl TypeChecker for IoBaseChecker {
return true;
}
matches!(
call_path.as_slice(),
call_path.segments(),
[
"io",
"IOBase"
@ -654,7 +654,7 @@ impl TypeChecker for IoBaseChecker {
if let Expr::Call(ast::ExprCall { func, .. }) = value.as_ref() {
return semantic.resolve_call_path(func).is_some_and(|call_path| {
matches!(
call_path.as_slice(),
call_path.segments(),
[
"pathlib",
"Path" | "PurePath" | "PurePosixPath" | "PureWindowsPath"
@ -670,7 +670,7 @@ impl TypeChecker for IoBaseChecker {
.resolve_call_path(func.as_ref())
.is_some_and(|call_path| {
matches!(
call_path.as_slice(),
call_path.segments(),
["io", "open" | "open_code"] | ["os" | "", "open"]
)
})