Use references for Export binding type (#3853)

This commit is contained in:
Charlie Marsh 2023-04-03 11:26:42 -04:00 committed by GitHub
parent 924bebbb4a
commit 25771cd4b9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 65 additions and 62 deletions

View file

@ -4452,7 +4452,23 @@ impl<'a> Checker<'a> {
} }
_ => false, _ => false,
} { } {
let (all_names, all_names_flags) = extract_all_names(&self.ctx, parent, current); let (all_names, all_names_flags) = {
let (mut names, flags) =
extract_all_names(parent, |name| self.ctx.is_builtin(name));
// Grab the existing bound __all__ values.
if let StmtKind::AugAssign { .. } = &parent.node {
if let Some(index) = current.get("__all__") {
if let BindingKind::Export(Export { names: existing }) =
&self.ctx.bindings[*index].kind
{
names.extend_from_slice(existing);
}
}
}
(names, flags)
};
if self.settings.rules.enabled(Rule::InvalidAllFormat) { if self.settings.rules.enabled(Rule::InvalidAllFormat) {
if matches!(all_names_flags, AllNamesFlags::INVALID_FORMAT) { if matches!(all_names_flags, AllNamesFlags::INVALID_FORMAT) {
@ -4749,7 +4765,7 @@ impl<'a> Checker<'a> {
// Mark anything referenced in `__all__` as used. // Mark anything referenced in `__all__` as used.
let all_bindings: Option<(Vec<BindingId>, Range)> = { let all_bindings: Option<(Vec<BindingId>, Range)> = {
let global_scope = self.ctx.global_scope(); let global_scope = self.ctx.global_scope();
let all_names: Option<(&Vec<String>, Range)> = global_scope let all_names: Option<(&Vec<&str>, Range)> = global_scope
.get("__all__") .get("__all__")
.map(|index| &self.ctx.bindings[*index]) .map(|index| &self.ctx.bindings[*index])
.and_then(|binding| match &binding.kind { .and_then(|binding| match &binding.kind {
@ -4761,7 +4777,7 @@ impl<'a> Checker<'a> {
( (
names names
.iter() .iter()
.filter_map(|name| global_scope.get(name.as_str()).copied()) .filter_map(|name| global_scope.get(name).copied())
.collect(), .collect(),
range, range,
) )
@ -4779,15 +4795,13 @@ impl<'a> Checker<'a> {
} }
// Extract `__all__` names from the global scope. // Extract `__all__` names from the global scope.
let all_names: Option<(Vec<&str>, Range)> = self let all_names: Option<(&[&str], Range)> = self
.ctx .ctx
.global_scope() .global_scope()
.get("__all__") .get("__all__")
.map(|index| &self.ctx.bindings[*index]) .map(|index| &self.ctx.bindings[*index])
.and_then(|binding| match &binding.kind { .and_then(|binding| match &binding.kind {
BindingKind::Export(Export { names }) => { BindingKind::Export(Export { names }) => Some((names.as_slice(), binding.range)),
Some((names.iter().map(String::as_str).collect(), binding.range))
}
_ => None, _ => None,
}); });
@ -4847,7 +4861,7 @@ impl<'a> Checker<'a> {
.dedup() .dedup()
.collect(); .collect();
if !sources.is_empty() { if !sources.is_empty() {
for &name in names { for &name in names.iter() {
if !scope.defines(name) { if !scope.defines(name) {
diagnostics.push(Diagnostic::new( diagnostics.push(Diagnostic::new(
pyflakes::rules::UndefinedLocalWithImportStarUsage { pyflakes::rules::UndefinedLocalWithImportStarUsage {

View file

@ -1,10 +1,6 @@
use bitflags::bitflags; use bitflags::bitflags;
use rustpython_parser::ast::{Constant, Expr, ExprKind, Stmt, StmtKind}; use rustpython_parser::ast::{Constant, Expr, ExprKind, Stmt, StmtKind};
use crate::binding::{BindingKind, Export};
use crate::context::Context;
use crate::scope::Scope;
bitflags! { bitflags! {
#[derive(Default)] #[derive(Default)]
pub struct AllNamesFlags: u32 { pub struct AllNamesFlags: u32 {
@ -14,29 +10,30 @@ bitflags! {
} }
/// Extract the names bound to a given __all__ assignment. /// Extract the names bound to a given __all__ assignment.
pub fn extract_all_names( ///
ctx: &Context, /// Accepts a closure that determines whether a given name (e.g., `"list"`) is a Python builtin.
stmt: &Stmt, pub fn extract_all_names<F>(stmt: &Stmt, is_builtin: F) -> (Vec<&str>, AllNamesFlags)
scope: &Scope, where
) -> (Vec<String>, AllNamesFlags) { F: Fn(&str) -> bool,
fn add_to_names(names: &mut Vec<String>, elts: &[Expr], flags: &mut AllNamesFlags) { {
fn add_to_names<'a>(elts: &'a [Expr], names: &mut Vec<&'a str>, flags: &mut AllNamesFlags) {
for elt in elts { for elt in elts {
if let ExprKind::Constant { if let ExprKind::Constant {
value: Constant::Str(value), value: Constant::Str(value),
.. ..
} = &elt.node } = &elt.node
{ {
names.push(value.to_string()); names.push(value);
} else { } else {
*flags |= AllNamesFlags::INVALID_OBJECT; *flags |= AllNamesFlags::INVALID_OBJECT;
} }
} }
} }
fn extract_elts<'a>( fn extract_elts<F>(expr: &Expr, is_builtin: F) -> (Option<&Vec<Expr>>, AllNamesFlags)
ctx: &'a Context, where
expr: &'a Expr, F: Fn(&str) -> bool,
) -> (Option<&'a Vec<Expr>>, AllNamesFlags) { {
match &expr.node { match &expr.node {
ExprKind::List { elts, .. } => { ExprKind::List { elts, .. } => {
return (Some(elts), AllNamesFlags::empty()); return (Some(elts), AllNamesFlags::empty());
@ -56,27 +53,28 @@ pub fn extract_all_names(
} => { } => {
// Allow `tuple()` and `list()` calls. // Allow `tuple()` and `list()` calls.
if keywords.is_empty() && args.len() <= 1 { if keywords.is_empty() && args.len() <= 1 {
if ctx.resolve_call_path(func).map_or(false, |call_path| { if let ExprKind::Name { id, .. } = &func.node {
call_path.as_slice() == ["", "tuple"] if id == "tuple" || id == "list" {
|| call_path.as_slice() == ["", "list"] if is_builtin(id) {
}) { if args.is_empty() {
if args.is_empty() { return (None, AllNamesFlags::empty());
return (None, AllNamesFlags::empty()); }
} match &args[0].node {
match &args[0].node { ExprKind::List { elts, .. }
ExprKind::List { elts, .. } | ExprKind::Set { elts, .. }
| ExprKind::Set { elts, .. } | ExprKind::Tuple { elts, .. } => {
| ExprKind::Tuple { elts, .. } => { return (Some(elts), AllNamesFlags::empty());
return (Some(elts), AllNamesFlags::empty()); }
ExprKind::ListComp { .. }
| ExprKind::SetComp { .. }
| ExprKind::GeneratorExp { .. } => {
// Allow comprehensions, even though we can't statically analyze
// them.
return (None, AllNamesFlags::empty());
}
_ => {}
}
} }
ExprKind::ListComp { .. }
| ExprKind::SetComp { .. }
| ExprKind::GeneratorExp { .. } => {
// Allow comprehensions, even though we can't statically analyze
// them.
return (None, AllNamesFlags::empty());
}
_ => {}
} }
} }
} }
@ -86,18 +84,9 @@ pub fn extract_all_names(
(None, AllNamesFlags::INVALID_FORMAT) (None, AllNamesFlags::INVALID_FORMAT)
} }
let mut names: Vec<String> = vec![]; let mut names: Vec<&str> = vec![];
let mut flags = AllNamesFlags::empty(); let mut flags = AllNamesFlags::empty();
// Grab the existing bound __all__ values.
if let StmtKind::AugAssign { .. } = &stmt.node {
if let Some(index) = scope.get("__all__") {
if let BindingKind::Export(Export { names: existing }) = &ctx.bindings[*index].kind {
names.extend_from_slice(existing);
}
}
}
if let Some(value) = match &stmt.node { if let Some(value) = match &stmt.node {
StmtKind::Assign { value, .. } => Some(value), StmtKind::Assign { value, .. } => Some(value),
StmtKind::AnnAssign { value, .. } => value.as_ref(), StmtKind::AnnAssign { value, .. } => value.as_ref(),
@ -109,10 +98,10 @@ pub fn extract_all_names(
let mut current_right = right; let mut current_right = right;
loop { loop {
// Process the right side, which should be a "real" value. // Process the right side, which should be a "real" value.
let (elts, new_flags) = extract_elts(ctx, current_right); let (elts, new_flags) = extract_elts(current_right, |expr| is_builtin(expr));
flags |= new_flags; flags |= new_flags;
if let Some(elts) = elts { if let Some(elts) = elts {
add_to_names(&mut names, elts, &mut flags); add_to_names(elts, &mut names, &mut flags);
} }
// Process the left side, which can be a "real" value or the "rest" of the // Process the left side, which can be a "real" value or the "rest" of the
@ -121,19 +110,19 @@ pub fn extract_all_names(
current_left = left; current_left = left;
current_right = right; current_right = right;
} else { } else {
let (elts, new_flags) = extract_elts(ctx, current_left); let (elts, new_flags) = extract_elts(current_left, |expr| is_builtin(expr));
flags |= new_flags; flags |= new_flags;
if let Some(elts) = elts { if let Some(elts) = elts {
add_to_names(&mut names, elts, &mut flags); add_to_names(elts, &mut names, &mut flags);
} }
break; break;
} }
} }
} else { } else {
let (elts, new_flags) = extract_elts(ctx, value); let (elts, new_flags) = extract_elts(value, |expr| is_builtin(expr));
flags |= new_flags; flags |= new_flags;
if let Some(elts) = elts { if let Some(elts) = elts {
add_to_names(&mut names, elts, &mut flags); add_to_names(elts, &mut names, &mut flags);
} }
} }
} }

View file

@ -207,9 +207,9 @@ pub struct StarImportation<'a> {
// FutureImportation // FutureImportation
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Export { pub struct Export<'a> {
/// The names of the bindings exported via `__all__`. /// The names of the bindings exported via `__all__`.
pub names: Vec<String>, pub names: Vec<&'a str>,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -258,7 +258,7 @@ pub enum BindingKind<'a> {
Builtin, Builtin,
ClassDefinition, ClassDefinition,
FunctionDefinition, FunctionDefinition,
Export(Export), Export(Export<'a>),
FutureImportation, FutureImportation,
Importation(Importation<'a>), Importation(Importation<'a>),
FromImportation(FromImportation<'a>), FromImportation(FromImportation<'a>),