Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ruff] Implemented used-dummy-variable (RUF052) #14611

Merged
merged 22 commits into from
Dec 3, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Applied proposed changes
Lokejoke committed Dec 1, 2024

Verified

This commit was signed with the committer’s verified signature.
jpkrohling Juraci Paixão Kröhling
commit d9e69be7ff0d5d114cade4afb20c3c8e6f54ebe9
22 changes: 15 additions & 7 deletions crates/ruff_linter/src/rules/ruff/rules/dummy_variable_accessed.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use ruff_diagnostics::{Diagnostic, Fix, FixAvailability, Violation};
use ruff_macros::{derive_message_formats, ViolationMetadata};
use ruff_python_ast::helpers::is_dunder;
use ruff_python_semantic::{Binding, BindingKind, SemanticModel};
use ruff_python_semantic::{Binding, BindingKind, ScopeId, SemanticModel};
use ruff_python_stdlib::{
builtins::is_python_builtin, identifiers::is_identifier, keyword::is_keyword,
};
@@ -114,7 +114,7 @@ pub(crate) fn dummy_variable_accessed(checker: &Checker, binding: &Binding) -> O
return None;
}

let possible_fix_kind = get_possible_fix_kind(name, checker);
let possible_fix_kind = get_possible_fix_kind(name, checker, binding.scope);

let mut diagnostic = Diagnostic::new(
DummyVariableAccessed {
@@ -127,7 +127,7 @@ pub(crate) fn dummy_variable_accessed(checker: &Checker, binding: &Binding) -> O
// If fix available
if let Some(fix_kind) = possible_fix_kind {
// Get the possible fix based on the scope
if let Some(fix) = get_possible_fix(name, fix_kind, semantic) {
if let Some(fix) = get_possible_fix(name, fix_kind, binding.scope, semantic) {
diagnostic.try_set_fix(|| {
Renamer::rename(name, &fix, scope, semantic, checker.stylist())
.map(|(edit, rest)| Fix::safe_edits(edit, rest))
@@ -152,7 +152,12 @@ enum ShadowedKind {
}

/// Suggests a potential alternative name to resolve a shadowing conflict.
fn get_possible_fix(name: &str, kind: ShadowedKind, semantic: &SemanticModel) -> Option<String> {
fn get_possible_fix(
name: &str,
kind: ShadowedKind,
scope_id: ScopeId,
semantic: &SemanticModel,
) -> Option<String> {
// Remove leading underscores for processing
let trimmed_name = name.trim_start_matches('_');

@@ -165,7 +170,7 @@ fn get_possible_fix(name: &str, kind: ShadowedKind, semantic: &SemanticModel) ->
};

// Ensure the fix name is not already taken in the scope or enclosing scopes
if !semantic.is_available(&fix_name) {
if !semantic.is_available_in_scope(&fix_name, scope_id) {
return None;
}

@@ -174,7 +179,7 @@ fn get_possible_fix(name: &str, kind: ShadowedKind, semantic: &SemanticModel) ->
}

/// Determines the kind of shadowing or conflict for a given variable name.
fn get_possible_fix_kind(name: &str, checker: &Checker) -> Option<ShadowedKind> {
fn get_possible_fix_kind(name: &str, checker: &Checker, scope_id: ScopeId) -> Option<ShadowedKind> {
Lokejoke marked this conversation as resolved.
Show resolved Hide resolved
// If the name starts with an underscore, we don't consider it
if !name.starts_with('_') {
return None;
@@ -196,7 +201,10 @@ fn get_possible_fix_kind(name: &str, checker: &Checker) -> Option<ShadowedKind>
return Some(ShadowedKind::BuiltIn);
}

if !checker.semantic().is_available(trimmed_name) {
if !checker
.semantic()
.is_available_in_scope(trimmed_name, scope_id)
{
return Some(ShadowedKind::Some);
}

30 changes: 24 additions & 6 deletions crates/ruff_python_semantic/src/model.rs
Original file line number Diff line number Diff line change
@@ -325,9 +325,15 @@ impl<'a> SemanticModel<'a> {
}

/// Return `true` if `member` is an "available" symbol, i.e., a symbol that has not been bound
/// in the current scope, or in any containing scope.
/// in the current scope currently being visited, or in any containing scope.
pub fn is_available(&self, member: &str) -> bool {
self.lookup_symbol(member)
self.is_available_in_scope(member, self.scope_id)
}

/// Return `true` if `member` is an "available" symbol in a given scope, i.e.,
/// a symbol that has not been bound in that current scope, or in any containing scope.
pub fn is_available_in_scope(&self, member: &str, scope_id: ScopeId) -> bool {
self.lookup_symbol_in_scope(member, scope_id, false)
.map(|binding_id| &self.bindings[binding_id])
.map_or(true, |binding| binding.kind.is_builtin())
}
@@ -620,10 +626,22 @@ impl<'a> SemanticModel<'a> {
}
}

/// Lookup a symbol in the current scope. This is a carbon copy of [`Self::resolve_load`], but
/// doesn't add any read references to the resolved symbol.
/// Lookup a symbol in the current scope.
pub fn lookup_symbol(&self, symbol: &str) -> Option<BindingId> {
if self.in_forward_reference() {
self.lookup_symbol_in_scope(symbol, self.scope_id, self.in_forward_reference())
}

/// Lookup a symbol in a certain scope
///
/// This is a carbon copy of [`Self::resolve_load`], but
/// doesn't add any read references to the resolved symbol.
pub fn lookup_symbol_in_scope(
&self,
symbol: &str,
scope_id: ScopeId,
in_forward_reference: bool,
) -> Option<BindingId> {
if in_forward_reference {
if let Some(binding_id) = self.scopes.global().get(symbol) {
if !self.bindings[binding_id].is_unbound() {
return Some(binding_id);
@@ -633,7 +651,7 @@ impl<'a> SemanticModel<'a> {

let mut seen_function = false;
let mut class_variables_visible = true;
for (index, scope_id) in self.scopes.ancestor_ids(self.scope_id).enumerate() {
for (index, scope_id) in self.scopes.ancestor_ids(scope_id).enumerate() {
let scope = &self.scopes[scope_id];
if scope.kind.is_class() {
if seen_function && matches!(symbol, "__class__") {