Avoid using typing-imported symbols for runtime edits (#4649)

This commit is contained in:
Charlie Marsh 2023-05-26 20:36:37 -04:00 committed by GitHub
parent ccca11839a
commit af433ac14d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 219 additions and 60 deletions

View file

@ -4,7 +4,7 @@ use std::path::Path;
use bitflags::bitflags;
use nohash_hasher::{BuildNoHashHasher, IntMap};
use ruff_text_size::TextRange;
use rustpython_parser::ast::{Expr, Stmt};
use rustpython_parser::ast::{Expr, Ranged, Stmt};
use smallvec::smallvec;
use ruff_python_ast::call_path::{collect_call_path, from_unqualified_name, CallPath};
@ -350,7 +350,7 @@ impl<'a> SemanticModel<'a> {
&self,
module: &str,
member: &str,
) -> Option<(&Stmt, String)> {
) -> Option<ImportedName> {
self.scopes().enumerate().find_map(|(scope_index, scope)| {
scope.binding_ids().find_map(|binding_id| {
let binding = &self.bindings[binding_id];
@ -360,14 +360,18 @@ impl<'a> SemanticModel<'a> {
// `import sys as sys2` -> `sys2.exit`
BindingKind::Importation(Importation { name, full_name }) => {
if full_name == &module {
// Verify that `sys` isn't bound in an inner scope.
if self
.scopes()
.take(scope_index)
.all(|scope| scope.get(name).is_none())
{
if let Some(source) = binding.source {
return Some((self.stmts[source], format!("{name}.{member}")));
if let Some(source) = binding.source {
// Verify that `sys` isn't bound in an inner scope.
if self
.scopes()
.take(scope_index)
.all(|scope| scope.get(name).is_none())
{
return Some(ImportedName {
name: format!("{name}.{member}"),
range: self.stmts[source].range(),
context: binding.context,
});
}
}
}
@ -378,14 +382,18 @@ impl<'a> SemanticModel<'a> {
BindingKind::FromImportation(FromImportation { name, full_name }) => {
if let Some((target_module, target_member)) = full_name.split_once('.') {
if target_module == module && target_member == member {
// Verify that `join` isn't bound in an inner scope.
if self
.scopes()
.take(scope_index)
.all(|scope| scope.get(name).is_none())
{
if let Some(source) = binding.source {
return Some((self.stmts[source], (*name).to_string()));
if let Some(source) = binding.source {
// Verify that `join` isn't bound in an inner scope.
if self
.scopes()
.take(scope_index)
.all(|scope| scope.get(name).is_none())
{
return Some(ImportedName {
name: (*name).to_string(),
range: self.stmts[source].range(),
context: binding.context,
});
}
}
}
@ -395,14 +403,18 @@ impl<'a> SemanticModel<'a> {
// `import os.path ` -> `os.name`
BindingKind::SubmoduleImportation(SubmoduleImportation { name, .. }) => {
if name == &module {
// Verify that `os` isn't bound in an inner scope.
if self
.scopes()
.take(scope_index)
.all(|scope| scope.get(name).is_none())
{
if let Some(source) = binding.source {
return Some((self.stmts[source], format!("{name}.{member}")));
if let Some(source) = binding.source {
// Verify that `os` isn't bound in an inner scope.
if self
.scopes()
.take(scope_index)
.all(|scope| scope.get(name).is_none())
{
return Some(ImportedName {
name: format!("{name}.{member}"),
range: self.stmts[source].range(),
context: binding.context,
});
}
}
}
@ -911,3 +923,29 @@ pub enum ResolvedReference {
/// The reference is definitively unresolved.
NotFound,
}
#[derive(Debug)]
pub struct ImportedName {
/// The name to which the imported symbol is bound.
name: String,
/// The range at which the symbol is imported.
range: TextRange,
/// The context in which the symbol is imported.
context: ExecutionContext,
}
impl ImportedName {
pub fn into_name(self) -> String {
self.name
}
pub const fn context(&self) -> ExecutionContext {
self.context
}
}
impl Ranged for ImportedName {
fn range(&self) -> TextRange {
self.range
}
}