[flake8-pyi] Implement PYI044 (#5021)

## Summary

This implements PYI044. This rule checks if `from __future__ import
annotations` is used in stub files as it has no effect in stub files, since type
checkers automatically treat stubs as having those semantics.

Updates https://github.com/astral-sh/ruff/issues/848

## Test Plan

Added a test case and snapshots.
This commit is contained in:
Thomas de Zeeuw 2023-06-12 13:20:16 +02:00 committed by GitHub
parent 6a5f317362
commit 8161757229
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 73 additions and 0 deletions

View file

@ -0,0 +1,7 @@
# Bad import.
from __future__ import annotations # Not PYI044 (not a stubfile).
# Good imports.
from __future__ import Something
import sys
from socket import AF_INET

View file

@ -0,0 +1,7 @@
# Bad import.
from __future__ import annotations # PYI044.
# Good imports.
from __future__ import Something
import sys
from socket import AF_INET

View file

@ -1121,6 +1121,9 @@ where
if self.enabled(Rule::UnaliasedCollectionsAbcSetImport) {
flake8_pyi::rules::unaliased_collections_abc_set_import(self, import_from);
}
if self.enabled(Rule::FutureAnnotationsInStub) {
flake8_pyi::rules::from_future_import(self, import_from);
}
}
for alias in names {
if let Some("__future__") = module {

View file

@ -616,6 +616,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
(Flake8Pyi, "035") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::UnassignedSpecialVariableInStub),
(Flake8Pyi, "042") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::SnakeCaseTypeAlias),
(Flake8Pyi, "043") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::TSuffixedTypeAlias),
(Flake8Pyi, "044") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::FutureAnnotationsInStub),
(Flake8Pyi, "045") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::IterMethodReturnIterable),
(Flake8Pyi, "048") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::StubBodyMultipleStatements),
(Flake8Pyi, "050") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::NoReturnArgumentAnnotationInStub),

View file

@ -54,6 +54,8 @@ mod tests {
#[test_case(Rule::StubBodyMultipleStatements, Path::new("PYI048.pyi"))]
#[test_case(Rule::TSuffixedTypeAlias, Path::new("PYI043.py"))]
#[test_case(Rule::TSuffixedTypeAlias, Path::new("PYI043.pyi"))]
#[test_case(Rule::FutureAnnotationsInStub, Path::new("PYI044.py"))]
#[test_case(Rule::FutureAnnotationsInStub, Path::new("PYI044.pyi"))]
#[test_case(Rule::TypeCommentInStub, Path::new("PYI033.py"))]
#[test_case(Rule::TypeCommentInStub, Path::new("PYI033.pyi"))]
#[test_case(Rule::TypedArgumentDefaultInStub, Path::new("PYI011.py"))]

View file

@ -0,0 +1,33 @@
use rustpython_parser::ast::StmtImportFrom;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use crate::checkers::ast::Checker;
#[violation]
pub struct FutureAnnotationsInStub;
impl Violation for FutureAnnotationsInStub {
#[derive_message_formats]
fn message(&self) -> String {
format!("`from __future__ import annotations` has no effect in stub files, since type checkers automatically treat stubs as having those semantics")
}
}
/// PYI044
pub(crate) fn from_future_import(checker: &mut Checker, target: &StmtImportFrom) {
if let StmtImportFrom {
range,
module: Some(name),
names,
..
} = target
{
if name == "__future__" && names.iter().any(|alias| &*alias.name == "annotations") {
checker
.diagnostics
.push(Diagnostic::new(FutureAnnotationsInStub, *range));
}
}
}

View file

@ -4,6 +4,7 @@ pub(crate) use collections_named_tuple::*;
pub(crate) use docstring_in_stubs::*;
pub(crate) use duplicate_union_member::*;
pub(crate) use ellipsis_in_non_empty_class_body::*;
pub(crate) use future_annotations_in_stub::*;
pub(crate) use iter_method_return_iterable::*;
pub(crate) use no_return_argument_annotation::*;
pub(crate) use non_empty_stub_body::*;
@ -28,6 +29,7 @@ mod collections_named_tuple;
mod docstring_in_stubs;
mod duplicate_union_member;
mod ellipsis_in_non_empty_class_body;
mod future_annotations_in_stub;
mod iter_method_return_iterable;
mod no_return_argument_annotation;
mod non_empty_stub_body;

View file

@ -0,0 +1,4 @@
---
source: crates/ruff/src/rules/flake8_pyi/mod.rs
---

View file

@ -0,0 +1,13 @@
---
source: crates/ruff/src/rules/flake8_pyi/mod.rs
---
PYI044.pyi:2:1: PYI044 `from __future__ import annotations` has no effect in stub files, since type checkers automatically treat stubs as having those semantics
|
1 | # Bad import.
2 | from __future__ import annotations # PYI044.
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI044
3 |
4 | # Good imports.
|