mirror of
https://github.com/astral-sh/ruff.git
synced 2025-07-30 08:23:53 +00:00
[pyflakes
] Improve error message for UndefinedName
when a builtin was added in a newer version than specified in Ruff config (F821
) (#13293)
This commit is contained in:
parent
b7cef6c999
commit
1d5bd89987
5 changed files with 223 additions and 165 deletions
|
@ -1,5 +1,6 @@
|
||||||
use ruff_diagnostics::Diagnostic;
|
use ruff_diagnostics::Diagnostic;
|
||||||
use ruff_python_semantic::Exceptions;
|
use ruff_python_semantic::Exceptions;
|
||||||
|
use ruff_python_stdlib::builtins::version_builtin_was_added;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
use crate::codes::Rule;
|
use crate::codes::Rule;
|
||||||
|
@ -35,9 +36,12 @@ pub(crate) fn unresolved_references(checker: &mut Checker) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let symbol_name = reference.name(checker.locator);
|
||||||
|
|
||||||
checker.diagnostics.push(Diagnostic::new(
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
pyflakes::rules::UndefinedName {
|
pyflakes::rules::UndefinedName {
|
||||||
name: reference.name(checker.locator).to_string(),
|
name: symbol_name.to_string(),
|
||||||
|
minor_version_builtin_added: version_builtin_was_added(symbol_name),
|
||||||
},
|
},
|
||||||
reference.range(),
|
reference.range(),
|
||||||
));
|
));
|
||||||
|
|
|
@ -208,6 +208,18 @@ mod tests {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn f821_with_builtin_added_on_new_py_version_but_old_target_version_specified() {
|
||||||
|
let diagnostics = test_snippet(
|
||||||
|
"PythonFinalizationError",
|
||||||
|
&LinterSettings {
|
||||||
|
target_version: crate::settings::types::PythonVersion::Py312,
|
||||||
|
..LinterSettings::for_rule(Rule::UndefinedName)
|
||||||
|
},
|
||||||
|
);
|
||||||
|
assert_messages!(diagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
#[test_case(Rule::UnusedVariable, Path::new("F841_4.py"))]
|
#[test_case(Rule::UnusedVariable, Path::new("F841_4.py"))]
|
||||||
#[test_case(Rule::UnusedImport, Path::new("__init__.py"))]
|
#[test_case(Rule::UnusedImport, Path::new("__init__.py"))]
|
||||||
#[test_case(Rule::UnusedImport, Path::new("F401_24/__init__.py"))]
|
#[test_case(Rule::UnusedImport, Path::new("F401_24/__init__.py"))]
|
||||||
|
|
|
@ -19,17 +19,35 @@ use ruff_macros::{derive_message_formats, violation};
|
||||||
/// return n * 2
|
/// return n * 2
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
/// ## Options
|
||||||
|
/// - [`target-version`]: Can be used to configure which symbols Ruff will understand
|
||||||
|
/// as being available in the `builtins` namespace.
|
||||||
|
///
|
||||||
/// ## References
|
/// ## References
|
||||||
/// - [Python documentation: Naming and binding](https://docs.python.org/3/reference/executionmodel.html#naming-and-binding)
|
/// - [Python documentation: Naming and binding](https://docs.python.org/3/reference/executionmodel.html#naming-and-binding)
|
||||||
#[violation]
|
#[violation]
|
||||||
pub struct UndefinedName {
|
pub struct UndefinedName {
|
||||||
pub(crate) name: String,
|
pub(crate) name: String,
|
||||||
|
pub(crate) minor_version_builtin_added: Option<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Violation for UndefinedName {
|
impl Violation for UndefinedName {
|
||||||
#[derive_message_formats]
|
#[derive_message_formats]
|
||||||
fn message(&self) -> String {
|
fn message(&self) -> String {
|
||||||
let UndefinedName { name } = self;
|
let UndefinedName {
|
||||||
format!("Undefined name `{name}`")
|
name,
|
||||||
|
minor_version_builtin_added,
|
||||||
|
} = self;
|
||||||
|
let tip = minor_version_builtin_added.map(|version_added| {
|
||||||
|
format!(
|
||||||
|
r#"Consider specifying `requires-python = ">= 3.{version_added}"` or `tool.ruff.target-version = "py3{version_added}"` in your `pyproject.toml` file."#
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(tip) = tip {
|
||||||
|
format!("Undefined name `{name}`. {tip}")
|
||||||
|
} else {
|
||||||
|
format!("Undefined name `{name}`")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_linter/src/rules/pyflakes/mod.rs
|
||||||
|
---
|
||||||
|
<filename>:1:1: F821 Undefined name `PythonFinalizationError`. Consider specifying `requires-python = ">= 3.13"` or `tool.ruff.target-version = "py313"` in your `pyproject.toml` file.
|
||||||
|
|
|
||||||
|
1 | PythonFinalizationError
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^ F821
|
||||||
|
|
|
|
@ -23,181 +23,181 @@ pub const MAGIC_GLOBALS: &[&str] = &[
|
||||||
"__file__",
|
"__file__",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
static ALWAYS_AVAILABLE_BUILTINS: &[&str] = &[
|
||||||
|
"ArithmeticError",
|
||||||
|
"AssertionError",
|
||||||
|
"AttributeError",
|
||||||
|
"BaseException",
|
||||||
|
"BlockingIOError",
|
||||||
|
"BrokenPipeError",
|
||||||
|
"BufferError",
|
||||||
|
"BytesWarning",
|
||||||
|
"ChildProcessError",
|
||||||
|
"ConnectionAbortedError",
|
||||||
|
"ConnectionError",
|
||||||
|
"ConnectionRefusedError",
|
||||||
|
"ConnectionResetError",
|
||||||
|
"DeprecationWarning",
|
||||||
|
"EOFError",
|
||||||
|
"Ellipsis",
|
||||||
|
"EnvironmentError",
|
||||||
|
"Exception",
|
||||||
|
"False",
|
||||||
|
"FileExistsError",
|
||||||
|
"FileNotFoundError",
|
||||||
|
"FloatingPointError",
|
||||||
|
"FutureWarning",
|
||||||
|
"GeneratorExit",
|
||||||
|
"IOError",
|
||||||
|
"ImportError",
|
||||||
|
"ImportWarning",
|
||||||
|
"IndentationError",
|
||||||
|
"IndexError",
|
||||||
|
"InterruptedError",
|
||||||
|
"IsADirectoryError",
|
||||||
|
"KeyError",
|
||||||
|
"KeyboardInterrupt",
|
||||||
|
"LookupError",
|
||||||
|
"MemoryError",
|
||||||
|
"ModuleNotFoundError",
|
||||||
|
"NameError",
|
||||||
|
"None",
|
||||||
|
"NotADirectoryError",
|
||||||
|
"NotImplemented",
|
||||||
|
"NotImplementedError",
|
||||||
|
"OSError",
|
||||||
|
"OverflowError",
|
||||||
|
"PendingDeprecationWarning",
|
||||||
|
"PermissionError",
|
||||||
|
"ProcessLookupError",
|
||||||
|
"RecursionError",
|
||||||
|
"ReferenceError",
|
||||||
|
"ResourceWarning",
|
||||||
|
"RuntimeError",
|
||||||
|
"RuntimeWarning",
|
||||||
|
"StopAsyncIteration",
|
||||||
|
"StopIteration",
|
||||||
|
"SyntaxError",
|
||||||
|
"SyntaxWarning",
|
||||||
|
"SystemError",
|
||||||
|
"SystemExit",
|
||||||
|
"TabError",
|
||||||
|
"TimeoutError",
|
||||||
|
"True",
|
||||||
|
"TypeError",
|
||||||
|
"UnboundLocalError",
|
||||||
|
"UnicodeDecodeError",
|
||||||
|
"UnicodeEncodeError",
|
||||||
|
"UnicodeError",
|
||||||
|
"UnicodeTranslateError",
|
||||||
|
"UnicodeWarning",
|
||||||
|
"UserWarning",
|
||||||
|
"ValueError",
|
||||||
|
"Warning",
|
||||||
|
"ZeroDivisionError",
|
||||||
|
"__build_class__",
|
||||||
|
"__debug__",
|
||||||
|
"__doc__",
|
||||||
|
"__import__",
|
||||||
|
"__loader__",
|
||||||
|
"__name__",
|
||||||
|
"__package__",
|
||||||
|
"__spec__",
|
||||||
|
"abs",
|
||||||
|
"all",
|
||||||
|
"any",
|
||||||
|
"ascii",
|
||||||
|
"bin",
|
||||||
|
"bool",
|
||||||
|
"breakpoint",
|
||||||
|
"bytearray",
|
||||||
|
"bytes",
|
||||||
|
"callable",
|
||||||
|
"chr",
|
||||||
|
"classmethod",
|
||||||
|
"compile",
|
||||||
|
"complex",
|
||||||
|
"copyright",
|
||||||
|
"credits",
|
||||||
|
"delattr",
|
||||||
|
"dict",
|
||||||
|
"dir",
|
||||||
|
"divmod",
|
||||||
|
"enumerate",
|
||||||
|
"eval",
|
||||||
|
"exec",
|
||||||
|
"exit",
|
||||||
|
"filter",
|
||||||
|
"float",
|
||||||
|
"format",
|
||||||
|
"frozenset",
|
||||||
|
"getattr",
|
||||||
|
"globals",
|
||||||
|
"hasattr",
|
||||||
|
"hash",
|
||||||
|
"help",
|
||||||
|
"hex",
|
||||||
|
"id",
|
||||||
|
"input",
|
||||||
|
"int",
|
||||||
|
"isinstance",
|
||||||
|
"issubclass",
|
||||||
|
"iter",
|
||||||
|
"len",
|
||||||
|
"license",
|
||||||
|
"list",
|
||||||
|
"locals",
|
||||||
|
"map",
|
||||||
|
"max",
|
||||||
|
"memoryview",
|
||||||
|
"min",
|
||||||
|
"next",
|
||||||
|
"object",
|
||||||
|
"oct",
|
||||||
|
"open",
|
||||||
|
"ord",
|
||||||
|
"pow",
|
||||||
|
"print",
|
||||||
|
"property",
|
||||||
|
"quit",
|
||||||
|
"range",
|
||||||
|
"repr",
|
||||||
|
"reversed",
|
||||||
|
"round",
|
||||||
|
"set",
|
||||||
|
"setattr",
|
||||||
|
"slice",
|
||||||
|
"sorted",
|
||||||
|
"staticmethod",
|
||||||
|
"str",
|
||||||
|
"sum",
|
||||||
|
"super",
|
||||||
|
"tuple",
|
||||||
|
"type",
|
||||||
|
"vars",
|
||||||
|
"zip",
|
||||||
|
];
|
||||||
|
static PY310_PLUS_BUILTINS: &[&str] = &["EncodingWarning", "aiter", "anext"];
|
||||||
|
static PY311_PLUS_BUILTINS: &[&str] = &["BaseExceptionGroup", "ExceptionGroup"];
|
||||||
|
static PY313_PLUS_BUILTINS: &[&str] = &["PythonFinalizationError"];
|
||||||
|
|
||||||
/// Return the list of builtins for the given Python minor version.
|
/// Return the list of builtins for the given Python minor version.
|
||||||
///
|
///
|
||||||
/// Intended to be kept in sync with [`is_python_builtin`].
|
/// Intended to be kept in sync with [`is_python_builtin`].
|
||||||
pub fn python_builtins(minor_version: u8, is_notebook: bool) -> Vec<&'static str> {
|
pub fn python_builtins(minor_version: u8, is_notebook: bool) -> Vec<&'static str> {
|
||||||
let mut builtins = vec![
|
let mut builtins = ALWAYS_AVAILABLE_BUILTINS.to_vec();
|
||||||
"ArithmeticError",
|
|
||||||
"AssertionError",
|
|
||||||
"AttributeError",
|
|
||||||
"BaseException",
|
|
||||||
"BlockingIOError",
|
|
||||||
"BrokenPipeError",
|
|
||||||
"BufferError",
|
|
||||||
"BytesWarning",
|
|
||||||
"ChildProcessError",
|
|
||||||
"ConnectionAbortedError",
|
|
||||||
"ConnectionError",
|
|
||||||
"ConnectionRefusedError",
|
|
||||||
"ConnectionResetError",
|
|
||||||
"DeprecationWarning",
|
|
||||||
"EOFError",
|
|
||||||
"Ellipsis",
|
|
||||||
"EnvironmentError",
|
|
||||||
"Exception",
|
|
||||||
"False",
|
|
||||||
"FileExistsError",
|
|
||||||
"FileNotFoundError",
|
|
||||||
"FloatingPointError",
|
|
||||||
"FutureWarning",
|
|
||||||
"GeneratorExit",
|
|
||||||
"IOError",
|
|
||||||
"ImportError",
|
|
||||||
"ImportWarning",
|
|
||||||
"IndentationError",
|
|
||||||
"IndexError",
|
|
||||||
"InterruptedError",
|
|
||||||
"IsADirectoryError",
|
|
||||||
"KeyError",
|
|
||||||
"KeyboardInterrupt",
|
|
||||||
"LookupError",
|
|
||||||
"MemoryError",
|
|
||||||
"ModuleNotFoundError",
|
|
||||||
"NameError",
|
|
||||||
"None",
|
|
||||||
"NotADirectoryError",
|
|
||||||
"NotImplemented",
|
|
||||||
"NotImplementedError",
|
|
||||||
"OSError",
|
|
||||||
"OverflowError",
|
|
||||||
"PendingDeprecationWarning",
|
|
||||||
"PermissionError",
|
|
||||||
"ProcessLookupError",
|
|
||||||
"RecursionError",
|
|
||||||
"ReferenceError",
|
|
||||||
"ResourceWarning",
|
|
||||||
"RuntimeError",
|
|
||||||
"RuntimeWarning",
|
|
||||||
"StopAsyncIteration",
|
|
||||||
"StopIteration",
|
|
||||||
"SyntaxError",
|
|
||||||
"SyntaxWarning",
|
|
||||||
"SystemError",
|
|
||||||
"SystemExit",
|
|
||||||
"TabError",
|
|
||||||
"TimeoutError",
|
|
||||||
"True",
|
|
||||||
"TypeError",
|
|
||||||
"UnboundLocalError",
|
|
||||||
"UnicodeDecodeError",
|
|
||||||
"UnicodeEncodeError",
|
|
||||||
"UnicodeError",
|
|
||||||
"UnicodeTranslateError",
|
|
||||||
"UnicodeWarning",
|
|
||||||
"UserWarning",
|
|
||||||
"ValueError",
|
|
||||||
"Warning",
|
|
||||||
"ZeroDivisionError",
|
|
||||||
"__build_class__",
|
|
||||||
"__debug__",
|
|
||||||
"__doc__",
|
|
||||||
"__import__",
|
|
||||||
"__loader__",
|
|
||||||
"__name__",
|
|
||||||
"__package__",
|
|
||||||
"__spec__",
|
|
||||||
"abs",
|
|
||||||
"all",
|
|
||||||
"any",
|
|
||||||
"ascii",
|
|
||||||
"bin",
|
|
||||||
"bool",
|
|
||||||
"breakpoint",
|
|
||||||
"bytearray",
|
|
||||||
"bytes",
|
|
||||||
"callable",
|
|
||||||
"chr",
|
|
||||||
"classmethod",
|
|
||||||
"compile",
|
|
||||||
"complex",
|
|
||||||
"copyright",
|
|
||||||
"credits",
|
|
||||||
"delattr",
|
|
||||||
"dict",
|
|
||||||
"dir",
|
|
||||||
"divmod",
|
|
||||||
"enumerate",
|
|
||||||
"eval",
|
|
||||||
"exec",
|
|
||||||
"exit",
|
|
||||||
"filter",
|
|
||||||
"float",
|
|
||||||
"format",
|
|
||||||
"frozenset",
|
|
||||||
"getattr",
|
|
||||||
"globals",
|
|
||||||
"hasattr",
|
|
||||||
"hash",
|
|
||||||
"help",
|
|
||||||
"hex",
|
|
||||||
"id",
|
|
||||||
"input",
|
|
||||||
"int",
|
|
||||||
"isinstance",
|
|
||||||
"issubclass",
|
|
||||||
"iter",
|
|
||||||
"len",
|
|
||||||
"license",
|
|
||||||
"list",
|
|
||||||
"locals",
|
|
||||||
"map",
|
|
||||||
"max",
|
|
||||||
"memoryview",
|
|
||||||
"min",
|
|
||||||
"next",
|
|
||||||
"object",
|
|
||||||
"oct",
|
|
||||||
"open",
|
|
||||||
"ord",
|
|
||||||
"pow",
|
|
||||||
"print",
|
|
||||||
"property",
|
|
||||||
"quit",
|
|
||||||
"range",
|
|
||||||
"repr",
|
|
||||||
"reversed",
|
|
||||||
"round",
|
|
||||||
"set",
|
|
||||||
"setattr",
|
|
||||||
"slice",
|
|
||||||
"sorted",
|
|
||||||
"staticmethod",
|
|
||||||
"str",
|
|
||||||
"sum",
|
|
||||||
"super",
|
|
||||||
"tuple",
|
|
||||||
"type",
|
|
||||||
"vars",
|
|
||||||
"zip",
|
|
||||||
];
|
|
||||||
|
|
||||||
if minor_version >= 10 {
|
if minor_version >= 10 {
|
||||||
builtins.extend(&["EncodingWarning", "aiter", "anext"]);
|
builtins.extend(PY310_PLUS_BUILTINS);
|
||||||
}
|
}
|
||||||
|
|
||||||
if minor_version >= 11 {
|
if minor_version >= 11 {
|
||||||
builtins.extend(&["BaseExceptionGroup", "ExceptionGroup"]);
|
builtins.extend(PY311_PLUS_BUILTINS);
|
||||||
}
|
}
|
||||||
|
|
||||||
if minor_version >= 13 {
|
if minor_version >= 13 {
|
||||||
builtins.push("PythonFinalizationError");
|
builtins.extend(PY313_PLUS_BUILTINS);
|
||||||
}
|
}
|
||||||
|
|
||||||
if is_notebook {
|
if is_notebook {
|
||||||
builtins.extend(IPYTHON_BUILTINS);
|
builtins.extend(IPYTHON_BUILTINS);
|
||||||
}
|
}
|
||||||
|
|
||||||
builtins
|
builtins
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -370,6 +370,22 @@ pub fn is_python_builtin(name: &str, minor_version: u8, is_notebook: bool) -> bo
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return `Some(version)`, where `version` corresponds to the Python minor version
|
||||||
|
/// in which the builtin was added
|
||||||
|
pub fn version_builtin_was_added(name: &str) -> Option<u8> {
|
||||||
|
if PY310_PLUS_BUILTINS.contains(&name) {
|
||||||
|
Some(10)
|
||||||
|
} else if PY311_PLUS_BUILTINS.contains(&name) {
|
||||||
|
Some(11)
|
||||||
|
} else if PY313_PLUS_BUILTINS.contains(&name) {
|
||||||
|
Some(13)
|
||||||
|
} else if ALWAYS_AVAILABLE_BUILTINS.contains(&name) {
|
||||||
|
Some(0)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns `true` if the given name is that of a Python builtin iterator.
|
/// Returns `true` if the given name is that of a Python builtin iterator.
|
||||||
pub fn is_iterator(name: &str) -> bool {
|
pub fn is_iterator(name: &str) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue