mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-17 17:10:34 +00:00
Avoid using typing-imported symbols for runtime edits (#4649)
This commit is contained in:
parent
ccca11839a
commit
af433ac14d
14 changed files with 219 additions and 60 deletions
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue