mirror of https://github.com/astral-sh/ruff
Support `pyright: ignore` comments (#3941)
This commit is contained in:
parent
eb0dd74040
commit
10da3bc8dd
|
|
@ -1,11 +1,16 @@
|
|||
x = 1 # type: ignore
|
||||
x = 1 # type ignore
|
||||
x = 1 # type:ignore
|
||||
x = 1 # type: ignore[attr-defined] # type: ignore
|
||||
|
||||
x = 1
|
||||
x = 1 # type ignore
|
||||
x = 1 # type ignore # noqa
|
||||
x = 1 # type: ignore[attr-defined]
|
||||
x = 1 # type: ignore[attr-defined, name-defined]
|
||||
x = 1 # type: ignore[attr-defined] # type: ignore[type-mismatch]
|
||||
x = 1 # type: ignore[type-mismatch] # noqa
|
||||
x = 1 # type: ignore [attr-defined]
|
||||
x = 1 # type: ignore [attr-defined, name-defined]
|
||||
x = 1 # type: ignore [type-mismatch] # noqa
|
||||
x = 1 # type: Union[int, str]
|
||||
x = 1 # type: ignoreme
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
x = 1 # pyright: ignore
|
||||
x = 1 # pyright:ignore
|
||||
x = 1 # pyright: ignore[attr-defined] # pyright: ignore
|
||||
|
||||
x = 1
|
||||
x = 1 # pyright ignore
|
||||
x = 1 # pyright ignore # noqa
|
||||
x = 1 # pyright: ignore[attr-defined]
|
||||
x = 1 # pyright: ignore[attr-defined, name-defined]
|
||||
x = 1 # pyright: ignore[attr-defined] # pyright: ignore[type-mismatch]
|
||||
x = 1 # pyright: ignore[type-mismatch] # noqa
|
||||
x = 1 # pyright: ignore [attr-defined]
|
||||
x = 1 # pyright: ignore [attr-defined, name-defined]
|
||||
x = 1 # pyright: ignore [type-mismatch] # noqa
|
||||
x = 1 # pyright: Union[int, str]
|
||||
x = 1 # pyright: ignoreme
|
||||
|
|
@ -76,9 +76,7 @@ pub fn check_physical_lines(
|
|||
}
|
||||
|
||||
if enforce_blanket_type_ignore {
|
||||
if let Some(diagnostic) = blanket_type_ignore(index, line) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
blanket_type_ignore(&mut diagnostics, index, line);
|
||||
}
|
||||
|
||||
if enforce_blanket_noqa {
|
||||
|
|
|
|||
|
|
@ -35,11 +35,6 @@ impl Violation for TypeCommentInStub {
|
|||
}
|
||||
}
|
||||
|
||||
static TYPE_COMMENT_REGEX: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r"^#\s*type:\s*([^#]+)(\s*#.*?)?$").unwrap());
|
||||
static TYPE_IGNORE_REGEX: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r"^#\s*type:\s*ignore([^#]+)?(\s*#.*?)?$").unwrap());
|
||||
|
||||
/// PYI033
|
||||
pub fn type_comment_in_stub(tokens: &[LexResult]) -> Vec<Diagnostic> {
|
||||
let mut diagnostics = vec![];
|
||||
|
|
@ -60,3 +55,9 @@ pub fn type_comment_in_stub(tokens: &[LexResult]) -> Vec<Diagnostic> {
|
|||
|
||||
diagnostics
|
||||
}
|
||||
|
||||
static TYPE_COMMENT_REGEX: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r"^#\s*(type|pyright):\s*([^#]+)(\s*#.*?)?$").unwrap());
|
||||
|
||||
static TYPE_IGNORE_REGEX: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r"^#\s*(type|pyright):\s*ignore([^#]+)?(\s*#.*?)?$").unwrap());
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ mod tests {
|
|||
#[test_case(Rule::DeprecatedLogWarn, Path::new("PGH002_0.py"); "PGH002_0")]
|
||||
#[test_case(Rule::DeprecatedLogWarn, Path::new("PGH002_1.py"); "PGH002_1")]
|
||||
#[test_case(Rule::BlanketTypeIgnore, Path::new("PGH003_0.py"); "PGH003_0")]
|
||||
#[test_case(Rule::BlanketTypeIgnore, Path::new("PGH003_1.py"); "PGH003_1")]
|
||||
#[test_case(Rule::BlanketNOQA, Path::new("PGH004_0.py"); "PGH004_0")]
|
||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use anyhow::{anyhow, Result};
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use rustpython_parser::ast::Location;
|
||||
|
|
@ -16,18 +17,85 @@ impl Violation for BlanketTypeIgnore {
|
|||
}
|
||||
}
|
||||
|
||||
static BLANKET_TYPE_IGNORE_REGEX: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r"# type:? *ignore($|\s)").unwrap());
|
||||
|
||||
/// PGH003 - use of blanket type ignore comments
|
||||
pub fn blanket_type_ignore(lineno: usize, line: &str) -> Option<Diagnostic> {
|
||||
BLANKET_TYPE_IGNORE_REGEX.find(line).map(|m| {
|
||||
Diagnostic::new(
|
||||
BlanketTypeIgnore,
|
||||
Range::new(
|
||||
Location::new(lineno + 1, m.start()),
|
||||
Location::new(lineno + 1, m.end()),
|
||||
),
|
||||
)
|
||||
})
|
||||
/// PGH003
|
||||
pub fn blanket_type_ignore(diagnostics: &mut Vec<Diagnostic>, lineno: usize, line: &str) {
|
||||
for match_ in TYPE_IGNORE_PATTERN.find_iter(line) {
|
||||
if let Ok(codes) = parse_type_ignore_tag(line[match_.end()..].trim()) {
|
||||
if codes.is_empty() {
|
||||
let start = line[..match_.start()].chars().count();
|
||||
let end = start + line[match_.start()..match_.end()].chars().count();
|
||||
diagnostics.push(Diagnostic::new(
|
||||
BlanketTypeIgnore,
|
||||
Range::new(
|
||||
Location::new(lineno + 1, start),
|
||||
Location::new(lineno + 1, end),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Match, e.g., `# type: ignore` or `# type: ignore[attr-defined]`.
|
||||
// See: https://github.com/python/mypy/blob/b43e0d34247a6d1b3b9d9094d184bbfcb9808bb9/mypy/fastparse.py#L248
|
||||
static TYPE_IGNORE_PATTERN: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r"#\s*(type|pyright):\s*ignore\s*").unwrap());
|
||||
|
||||
// Match, e.g., `[attr-defined]` or `[attr-defined, misc]`.
|
||||
// See: https://github.com/python/mypy/blob/b43e0d34247a6d1b3b9d9094d184bbfcb9808bb9/mypy/fastparse.py#L327
|
||||
static TYPE_IGNORE_TAG_PATTERN: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r"^\s*\[(?P<codes>[^]#]*)]\s*(#.*)?$").unwrap());
|
||||
|
||||
/// Parse the optional `[...]` tag in a `# type: ignore[...]` comment.
|
||||
///
|
||||
/// Returns a list of error codes to ignore, or an empty list if the tag is
|
||||
/// a blanket ignore.
|
||||
fn parse_type_ignore_tag(tag: &str) -> Result<Vec<&str>> {
|
||||
// See: https://github.com/python/mypy/blob/b43e0d34247a6d1b3b9d9094d184bbfcb9808bb9/mypy/fastparse.py#L316
|
||||
// No tag -- ignore all errors.
|
||||
let trimmed = tag.trim();
|
||||
if trimmed.is_empty() || trimmed.starts_with('#') {
|
||||
return Ok(vec![]);
|
||||
}
|
||||
|
||||
// Parse comma-separated list of error codes.
|
||||
TYPE_IGNORE_TAG_PATTERN
|
||||
.captures(tag)
|
||||
.map(|captures| {
|
||||
captures
|
||||
.name("codes")
|
||||
.unwrap()
|
||||
.as_str()
|
||||
.split(',')
|
||||
.map(str::trim)
|
||||
.collect()
|
||||
})
|
||||
.ok_or_else(|| anyhow!("Invalid type ignore tag: {tag}"))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
#[test]
|
||||
fn type_ignore_tag() {
|
||||
let tag = "";
|
||||
let result = super::parse_type_ignore_tag(tag);
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(result.unwrap(), Vec::<&str>::new());
|
||||
|
||||
let tag = "[attr-defined]";
|
||||
let result = super::parse_type_ignore_tag(tag);
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(result.unwrap(), vec!["attr-defined"]);
|
||||
|
||||
let tag = " [attr-defined]";
|
||||
let result = super::parse_type_ignore_tag(tag);
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(result.unwrap(), vec!["attr-defined"]);
|
||||
|
||||
let tag = "[attr-defined, misc]";
|
||||
let result = super::parse_type_ignore_tag(tag);
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(result.unwrap(), vec!["attr-defined", "misc"]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,24 +5,24 @@ PGH003_0.py:1:8: PGH003 Use specific rule codes when ignoring type issues
|
|||
|
|
||||
1 | x = 1 # type: ignore
|
||||
| ^^^^^^^^^^^^^^ PGH003
|
||||
2 | x = 1 # type ignore
|
||||
3 | x = 1 # type:ignore
|
||||
2 | x = 1 # type:ignore
|
||||
3 | x = 1 # type: ignore[attr-defined] # type: ignore
|
||||
|
|
||||
|
||||
PGH003_0.py:2:8: PGH003 Use specific rule codes when ignoring type issues
|
||||
|
|
||||
2 | x = 1 # type: ignore
|
||||
3 | x = 1 # type ignore
|
||||
3 | x = 1 # type:ignore
|
||||
| ^^^^^^^^^^^^^ PGH003
|
||||
4 | x = 1 # type:ignore
|
||||
4 | x = 1 # type: ignore[attr-defined] # type: ignore
|
||||
|
|
||||
|
||||
PGH003_0.py:3:8: PGH003 Use specific rule codes when ignoring type issues
|
||||
PGH003_0.py:3:38: PGH003 Use specific rule codes when ignoring type issues
|
||||
|
|
||||
3 | x = 1 # type: ignore
|
||||
4 | x = 1 # type ignore
|
||||
5 | x = 1 # type:ignore
|
||||
| ^^^^^^^^^^^^^ PGH003
|
||||
4 | x = 1 # type:ignore
|
||||
5 | x = 1 # type: ignore[attr-defined] # type: ignore
|
||||
| ^^^^^^^^^^^^^^ PGH003
|
||||
6 |
|
||||
7 | x = 1
|
||||
|
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
---
|
||||
source: crates/ruff/src/rules/pygrep_hooks/mod.rs
|
||||
---
|
||||
PGH003_1.py:1:8: PGH003 Use specific rule codes when ignoring type issues
|
||||
|
|
||||
1 | x = 1 # pyright: ignore
|
||||
| ^^^^^^^^^^^^^^^^^ PGH003
|
||||
2 | x = 1 # pyright:ignore
|
||||
3 | x = 1 # pyright: ignore[attr-defined] # pyright: ignore
|
||||
|
|
||||
|
||||
PGH003_1.py:2:8: PGH003 Use specific rule codes when ignoring type issues
|
||||
|
|
||||
2 | x = 1 # pyright: ignore
|
||||
3 | x = 1 # pyright:ignore
|
||||
| ^^^^^^^^^^^^^^^^ PGH003
|
||||
4 | x = 1 # pyright: ignore[attr-defined] # pyright: ignore
|
||||
|
|
||||
|
||||
PGH003_1.py:3:41: PGH003 Use specific rule codes when ignoring type issues
|
||||
|
|
||||
3 | x = 1 # pyright: ignore
|
||||
4 | x = 1 # pyright:ignore
|
||||
5 | x = 1 # pyright: ignore[attr-defined] # pyright: ignore
|
||||
| ^^^^^^^^^^^^^^^^^ PGH003
|
||||
6 |
|
||||
7 | x = 1
|
||||
|
|
||||
|
||||
|
||||
Loading…
Reference in New Issue