Implement F407

This commit is contained in:
Charlie Marsh 2022-09-11 17:50:24 -04:00
parent 549732b1da
commit 27ba8900eb
11 changed files with 70 additions and 2 deletions

View File

@ -124,7 +124,7 @@ ruff's goal is to achieve feature-parity with Flake8 when used (1) without any p
stylistic checks; limiting to Python 3 obviates the need for certain compatibility checks.)
Under those conditions, Flake8 implements about 58 rules, give or take. At time of writing, ruff
implements 30 rules. (Note that these 30 rules likely cover a disproportionate share of errors:
implements 31 rules. (Note that these 31 rules likely cover a disproportionate share of errors:
unused imports, undefined variables, etc.)
Of the unimplemented rules, ruff is missing:
@ -158,6 +158,7 @@ Beyond rule-set parity, ruff suffers from the following limitations vis-à-vis F
| F401 | UnusedImport | `...` imported but unused |
| F403 | ImportStarUsage | Unable to detect undefined names |
| F404 | LateFutureImport | from __future__ imports must occur at the beginning of the file |
| F407 | FutureFeatureNotDefined | future feature '...' is not defined |
| F541 | FStringMissingPlaceholders | f-string without any placeholders |
| F601 | MultiValueRepeatedKeyLiteral | Dictionary key literal repeated |
| F602 | MultiValueRepeatedKeyVariable | Dictionary key `...` repeated |

View File

@ -9,6 +9,7 @@ fn main() {
CheckKind::DoNotAssignLambda,
CheckKind::DuplicateArgumentName,
CheckKind::FStringMissingPlaceholders,
CheckKind::FutureFeatureNotDefined("...".to_string()),
CheckKind::IOError("...".to_string()),
CheckKind::IfTuple,
CheckKind::ImportStarUsage,

2
resources/test/fixtures/F407.py vendored Normal file
View File

@ -0,0 +1,2 @@
from __future__ import print_function
from __future__ import non_existent_feature

View File

@ -14,6 +14,7 @@ select = [
"F401",
"F403",
"F404",
"F407",
"F541",
"F601",
"F602",

View File

@ -1,3 +1,4 @@
use std::ops::Deref;
use std::path::Path;
use rustpython_parser::ast::{
@ -14,6 +15,7 @@ use crate::ast::{checks, operations, visitor};
use crate::autofix::fixer;
use crate::checks::{Check, CheckCode, CheckKind};
use crate::python::builtins::{BUILTINS, MAGIC_GLOBALS};
use crate::python::future::ALL_FEATURE_NAMES;
use crate::python::typing;
use crate::settings::Settings;
@ -391,7 +393,16 @@ where
},
);
if !self.futures_allowed && self.settings.select.contains(&CheckCode::F404)
if self.settings.select.contains(&CheckCode::F407)
&& !ALL_FEATURE_NAMES.contains(&alias.node.name.deref())
{
self.checks.push(Check::new(
CheckKind::FutureFeatureNotDefined(alias.node.name.to_string()),
stmt.location,
));
}
if self.settings.select.contains(&CheckCode::F404) && !self.futures_allowed
{
self.checks
.push(Check::new(CheckKind::LateFutureImport, stmt.location));

View File

@ -20,6 +20,7 @@ pub enum CheckCode {
F401,
F403,
F404,
F407,
F541,
F601,
F602,
@ -57,6 +58,7 @@ impl FromStr for CheckCode {
"F401" => Ok(CheckCode::F401),
"F403" => Ok(CheckCode::F403),
"F404" => Ok(CheckCode::F404),
"F407" => Ok(CheckCode::F407),
"F541" => Ok(CheckCode::F541),
"F601" => Ok(CheckCode::F601),
"F602" => Ok(CheckCode::F602),
@ -95,6 +97,7 @@ impl CheckCode {
CheckCode::F401 => "F401",
CheckCode::F403 => "F403",
CheckCode::F404 => "F404",
CheckCode::F407 => "F407",
CheckCode::F541 => "F541",
CheckCode::F601 => "F601",
CheckCode::F602 => "F602",
@ -131,6 +134,7 @@ impl CheckCode {
CheckCode::F401 => &LintSource::AST,
CheckCode::F403 => &LintSource::AST,
CheckCode::F404 => &LintSource::AST,
CheckCode::F407 => &LintSource::AST,
CheckCode::F541 => &LintSource::AST,
CheckCode::F601 => &LintSource::AST,
CheckCode::F602 => &LintSource::AST,
@ -174,6 +178,7 @@ pub enum CheckKind {
DoNotAssignLambda,
DuplicateArgumentName,
FStringMissingPlaceholders,
FutureFeatureNotDefined(String),
IOError(String),
IfTuple,
ImportStarUsage,
@ -209,6 +214,7 @@ impl CheckKind {
CheckKind::DefaultExceptNotLast => "DefaultExceptNotLast",
CheckKind::DuplicateArgumentName => "DuplicateArgumentName",
CheckKind::FStringMissingPlaceholders => "FStringMissingPlaceholders",
CheckKind::FutureFeatureNotDefined(_) => "FutureFeatureNotDefined",
CheckKind::IOError(_) => "IOError",
CheckKind::IfTuple => "IfTuple",
CheckKind::ImportStarUsage => "ImportStarUsage",
@ -246,6 +252,7 @@ impl CheckKind {
CheckKind::DefaultExceptNotLast => &CheckCode::F707,
CheckKind::DuplicateArgumentName => &CheckCode::F831,
CheckKind::FStringMissingPlaceholders => &CheckCode::F541,
CheckKind::FutureFeatureNotDefined(_) => &CheckCode::F407,
CheckKind::IOError(_) => &CheckCode::E902,
CheckKind::IfTuple => &CheckCode::F634,
CheckKind::ImportStarUsage => &CheckCode::F403,
@ -290,6 +297,9 @@ impl CheckKind {
CheckKind::FStringMissingPlaceholders => {
"f-string without any placeholders".to_string()
}
CheckKind::FutureFeatureNotDefined(name) => {
format!("future feature '{name}' is not defined")
}
CheckKind::IOError(name) => {
format!("No such file or directory: `{name}`")
}
@ -384,6 +394,7 @@ impl CheckKind {
CheckKind::DoNotAssignLambda => false,
CheckKind::DuplicateArgumentName => false,
CheckKind::FStringMissingPlaceholders => false,
CheckKind::FutureFeatureNotDefined(_) => false,
CheckKind::IOError(_) => false,
CheckKind::IfTuple => false,
CheckKind::ImportStarUsage => false,

View File

@ -535,6 +535,31 @@ mod tests {
Ok(())
}
#[test]
fn f407() -> Result<()> {
let mut actual = check_path(
Path::new("./resources/test/fixtures/F407.py"),
&settings::Settings {
line_length: 88,
exclude: vec![],
select: BTreeSet::from([CheckCode::F407]),
},
&fixer::Mode::Generate,
)?;
actual.sort_by_key(|check| check.location);
let expected = vec![Check {
kind: CheckKind::FutureFeatureNotDefined("non_existent_feature".to_string()),
location: Location::new(2, 1),
fix: None,
}];
assert_eq!(actual.len(), expected.len());
for i in 0..actual.len() {
assert_eq!(actual[i], expected[i]);
}
Ok(())
}
#[test]
fn f541() -> Result<()> {
let mut actual = check_path(

View File

@ -271,6 +271,7 @@ other-attribute = 1
CheckCode::F401,
CheckCode::F403,
CheckCode::F404,
CheckCode::F407,
CheckCode::F541,
CheckCode::F601,
CheckCode::F602,

View File

@ -1,2 +1,3 @@
pub mod builtins;
pub mod future;
pub mod typing;

13
src/python/future.rs Normal file
View File

@ -0,0 +1,13 @@
/// A copy of `__future__.all_feature_names`.
pub const ALL_FEATURE_NAMES: &[&str] = &[
"nested_scopes",
"generators",
"division",
"absolute_import",
"with_statement",
"print_function",
"unicode_literals",
"barry_as_FLUFL",
"generator_stop",
"annotations",
];

View File

@ -55,6 +55,7 @@ impl Settings {
CheckCode::E902,
CheckCode::F401,
CheckCode::F403,
CheckCode::F407,
CheckCode::F541,
CheckCode::F601,
CheckCode::F602,