Add some more test coverage for del statements (#4913)

This commit is contained in:
Charlie Marsh 2023-06-06 21:40:23 -04:00 committed by GitHub
parent 780d153ae8
commit b56a799417
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 124 additions and 22 deletions

View file

@ -11,18 +11,19 @@ mod tests {
use anyhow::Result;
use regex::Regex;
use ruff_textwrap::dedent;
use rustpython_parser::lexer::LexResult;
use test_case::test_case;
use ruff_diagnostics::Diagnostic;
use ruff_python_ast::source_code::{Indexer, Locator, Stylist};
use ruff_textwrap::dedent;
use crate::linter::{check_path, LinterResult};
use crate::registry::{AsRule, Linter, Rule};
use crate::rules::pyflakes;
use crate::settings::{flags, Settings};
use crate::test::test_path;
use crate::{assert_messages, directives, settings};
use crate::test::{test_path, test_snippet};
use crate::{assert_messages, directives};
#[test_case(Rule::UnusedImport, Path::new("F401_0.py"))]
#[test_case(Rule::UnusedImport, Path::new("F401_1.py"))]
@ -134,7 +135,7 @@ mod tests {
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
let diagnostics = test_path(
Path::new("pyflakes").join(path).as_path(),
&settings::Settings::for_rule(rule_code),
&Settings::for_rule(rule_code),
)?;
assert_messages!(snapshot, diagnostics);
Ok(())
@ -144,9 +145,9 @@ mod tests {
fn f841_dummy_variable_rgx() -> Result<()> {
let diagnostics = test_path(
Path::new("pyflakes/F841_0.py"),
&settings::Settings {
&Settings {
dummy_variable_rgx: Regex::new(r"^z$").unwrap(),
..settings::Settings::for_rule(Rule::UnusedVariable)
..Settings::for_rule(Rule::UnusedVariable)
},
)?;
assert_messages!(diagnostics);
@ -157,7 +158,7 @@ mod tests {
fn init() -> Result<()> {
let diagnostics = test_path(
Path::new("pyflakes/__init__.py"),
&settings::Settings::for_rules(vec![Rule::UndefinedName, Rule::UndefinedExport]),
&Settings::for_rules(vec![Rule::UndefinedName, Rule::UndefinedExport]),
)?;
assert_messages!(diagnostics);
Ok(())
@ -167,7 +168,7 @@ mod tests {
fn default_builtins() -> Result<()> {
let diagnostics = test_path(
Path::new("pyflakes/builtins.py"),
&settings::Settings::for_rules(vec![Rule::UndefinedName]),
&Settings::for_rules(vec![Rule::UndefinedName]),
)?;
assert_messages!(diagnostics);
Ok(())
@ -177,9 +178,9 @@ mod tests {
fn extra_builtins() -> Result<()> {
let diagnostics = test_path(
Path::new("pyflakes/builtins.py"),
&settings::Settings {
&Settings {
builtins: vec!["_".to_string()],
..settings::Settings::for_rules(vec![Rule::UndefinedName])
..Settings::for_rules(vec![Rule::UndefinedName])
},
)?;
assert_messages!(diagnostics);
@ -190,7 +191,7 @@ mod tests {
fn default_typing_modules() -> Result<()> {
let diagnostics = test_path(
Path::new("pyflakes/typing_modules.py"),
&settings::Settings::for_rules(vec![Rule::UndefinedName]),
&Settings::for_rules(vec![Rule::UndefinedName]),
)?;
assert_messages!(diagnostics);
Ok(())
@ -200,9 +201,9 @@ mod tests {
fn extra_typing_modules() -> Result<()> {
let diagnostics = test_path(
Path::new("pyflakes/typing_modules.py"),
&settings::Settings {
&Settings {
typing_modules: vec!["airflow.typing_compat".to_string()],
..settings::Settings::for_rules(vec![Rule::UndefinedName])
..Settings::for_rules(vec![Rule::UndefinedName])
},
)?;
assert_messages!(diagnostics);
@ -213,7 +214,7 @@ mod tests {
fn future_annotations() -> Result<()> {
let diagnostics = test_path(
Path::new("pyflakes/future_annotations.py"),
&settings::Settings::for_rules(vec![Rule::UnusedImport, Rule::UndefinedName]),
&Settings::for_rules(vec![Rule::UnusedImport, Rule::UndefinedName]),
)?;
assert_messages!(diagnostics);
Ok(())
@ -223,7 +224,7 @@ mod tests {
fn multi_statement_lines() -> Result<()> {
let diagnostics = test_path(
Path::new("pyflakes/multi_statement_lines.py"),
&settings::Settings::for_rule(Rule::UnusedImport),
&Settings::for_rule(Rule::UnusedImport),
)?;
assert_messages!(diagnostics);
Ok(())
@ -233,9 +234,9 @@ mod tests {
fn relative_typing_module() -> Result<()> {
let diagnostics = test_path(
Path::new("pyflakes/project/foo/bar.py"),
&settings::Settings {
&Settings {
typing_modules: vec!["foo.typical".to_string()],
..settings::Settings::for_rules(vec![Rule::UndefinedName])
..Settings::for_rules(vec![Rule::UndefinedName])
},
)?;
assert_messages!(diagnostics);
@ -246,9 +247,9 @@ mod tests {
fn nested_relative_typing_module() -> Result<()> {
let diagnostics = test_path(
Path::new("pyflakes/project/foo/bop/baz.py"),
&settings::Settings {
&Settings {
typing_modules: vec!["foo.typical".to_string()],
..settings::Settings::for_rules(vec![Rule::UndefinedName])
..Settings::for_rules(vec![Rule::UndefinedName])
},
)?;
assert_messages!(diagnostics);
@ -261,7 +262,7 @@ mod tests {
let diagnostics = test_path(
Path::new("pyflakes/F401_15.py"),
&Settings {
pyflakes: super::settings::Settings {
pyflakes: pyflakes::settings::Settings {
extend_generics: vec!["django.db.models.ForeignKey".to_string()],
},
..Settings::for_rules(vec![Rule::UnusedImport])
@ -271,11 +272,54 @@ mod tests {
Ok(())
}
#[test_case(
r#"
import os
def f():
import os
# Despite this `del`, `import os` in `f` should still be flagged as shadowing an unused
# import. (This is a false negative.)
del os
"#,
"del_shadowed_global_import_in_local_scope"
)]
#[test_case(
r#"
import os
def f():
import os
# Despite this `del`, `import os` in `f` should still be flagged as shadowing an unused
# import. (This is a false negative, but is consistent with Pyflakes.)
del os
"#,
"del_shadowed_global_import_in_global_scope"
)]
#[test_case(
r#"
def f():
import os
import os
# Despite this `del`, `import os` should still be flagged as shadowing an unused
# import.
del os
"#,
"del_shadowed_local_import_in_local_scope"
)]
fn contents(contents: &str, snapshot: &str) {
let diagnostics = test_snippet(contents, &Settings::for_rules(&Linter::Pyflakes));
assert_messages!(snapshot, diagnostics);
}
/// A re-implementation of the Pyflakes test runner.
/// Note that all tests marked with `#[ignore]` should be considered TODOs.
fn flakes(contents: &str, expected: &[Rule]) {
let contents = dedent(contents);
let settings = settings::Settings::for_rules(&Linter::Pyflakes);
let settings = Settings::for_rules(&Linter::Pyflakes);
let tokens: Vec<LexResult> = ruff_rustpython::tokenize(&contents);
let locator = Locator::new(&contents);
let stylist = Stylist::from_tokens(&tokens, &locator);
@ -287,7 +331,7 @@ mod tests {
&indexer,
);
let LinterResult {
data: (mut diagnostics, _imports),
data: (mut diagnostics, ..),
..
} = check_path(
Path::new("<filename>"),

View file

@ -0,0 +1,24 @@
---
source: crates/ruff/src/rules/pyflakes/mod.rs
---
<filename>:5:12: F401 [*] `os` imported but unused
|
5 | def f():
6 | import os
| ^^ F401
7 |
8 | # Despite this `del`, `import os` in `f` should still be flagged as shadowing an unused
|
= help: Remove unused import: `os`
Fix
2 2 | import os
3 3 |
4 4 | def f():
5 |- import os
5 |+ pass
6 6 |
7 7 | # Despite this `del`, `import os` in `f` should still be flagged as shadowing an unused
8 8 | # import. (This is a false negative, but is consistent with Pyflakes.)

View file

@ -0,0 +1,20 @@
---
source: crates/ruff/src/rules/pyflakes/mod.rs
---
<filename>:2:8: F401 [*] `os` imported but unused
|
2 | import os
| ^^ F401
3 |
4 | def f():
|
= help: Remove unused import: `os`
Fix
1 1 |
2 |-import os
3 2 |
4 3 | def f():
5 4 | import os

View file

@ -0,0 +1,14 @@
---
source: crates/ruff/src/rules/pyflakes/mod.rs
---
<filename>:4:12: F811 Redefinition of unused `os` from line 3
|
4 | def f():
5 | import os
6 | import os
| ^^ F811
7 |
8 | # Despite this `del`, `import os` should still be flagged as shadowing an unused
|