Skip to content

Commit

Permalink
[flake8-pyi] Check for kwarg and vararg NoReturn type annotations (
Browse files Browse the repository at this point in the history
…#8948)

## Summary

Triggers `no-return-argument-annotation-in-stub` (`PYI050`) for vararg
and kwarg `NoReturn` type annotations.

Related to #8771.

## Test Plan

`cargo test`
  • Loading branch information
tjkuson authored Dec 1, 2023
1 parent 506be68 commit d66063b
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 12 deletions.
11 changes: 11 additions & 0 deletions crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI050.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,14 @@ def foo_no_return_typing_extensions(
def foo_no_return_kwarg(arg: int, *, arg2: NoReturn): ... # Error: PYI050
def foo_no_return_pos_only(arg: int, /, arg2: NoReturn): ... # Error: PYI050
def foo_never(arg: Never): ...
def foo_args(*args: NoReturn): ... # Error: PYI050
def foo_kwargs(**kwargs: NoReturn): ... # Error: PYI050
def foo_args_kwargs(*args: NoReturn, **kwargs: NoReturn): ... # Error: PYI050
def foo_int_args(*args: int): ...
def foo_int_kwargs(**kwargs: int): ...
def foo_int_args_kwargs(*args: int, **kwargs: int): ...
def foo_int_args_no_return(*args: int, **kwargs: NoReturn): ... # Error: PYI050
def foo_int_kwargs_no_return(*args: NoReturn, **kwargs: int): ... # Error: PYI050
def foo_args_never(*args: Never): ...
def foo_kwargs_never(**kwargs: Never): ...
def foo_args_kwargs_never(*args: Never, **kwargs: Never): ...
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::fmt;

use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::Parameters;
use ruff_python_ast::{Expr, Parameters};
use ruff_text_size::Ranged;

use crate::checkers::ast::Checker;
Expand Down Expand Up @@ -51,28 +51,49 @@ impl Violation for NoReturnArgumentAnnotationInStub {

/// PYI050
pub(crate) fn no_return_argument_annotation(checker: &mut Checker, parameters: &Parameters) {
// Ex) def func(arg: NoReturn): ...
// Ex) def func(arg: NoReturn, /): ...
// Ex) def func(*, arg: NoReturn): ...
for annotation in parameters
.posonlyargs
.iter()
.chain(&parameters.args)
.chain(&parameters.kwonlyargs)
.filter_map(|arg| arg.parameter.annotation.as_ref())
{
if checker.semantic().match_typing_expr(annotation, "NoReturn") {
checker.diagnostics.push(Diagnostic::new(
NoReturnArgumentAnnotationInStub {
module: if checker.settings.target_version >= Py311 {
TypingModule::Typing
} else {
TypingModule::TypingExtensions
},
},
annotation.range(),
));
check_no_return_argument_annotation(checker, annotation);
}

// Ex) def func(*args: NoReturn): ...
if let Some(arg) = &parameters.vararg {
if let Some(annotation) = &arg.annotation {
check_no_return_argument_annotation(checker, annotation);
}
}

// Ex) def func(**kwargs: NoReturn): ...
if let Some(arg) = &parameters.kwarg {
if let Some(annotation) = &arg.annotation {
check_no_return_argument_annotation(checker, annotation);
}
}
}

fn check_no_return_argument_annotation(checker: &mut Checker, annotation: &Expr) {
if checker.semantic().match_typing_expr(annotation, "NoReturn") {
checker.diagnostics.push(Diagnostic::new(
NoReturnArgumentAnnotationInStub {
module: if checker.settings.target_version >= Py311 {
TypingModule::Typing
} else {
TypingModule::TypingExtensions
},
},
annotation.range(),
));
}
}

#[derive(Debug, PartialEq, Eq)]
enum TypingModule {
Typing,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,67 @@ PYI050.pyi:11:47: PYI050 Prefer `typing.Never` over `NoReturn` for argument anno
11 | def foo_no_return_pos_only(arg: int, /, arg2: NoReturn): ... # Error: PYI050
| ^^^^^^^^ PYI050
12 | def foo_never(arg: Never): ...
13 | def foo_args(*args: NoReturn): ... # Error: PYI050
|

PYI050.pyi:13:21: PYI050 Prefer `typing.Never` over `NoReturn` for argument annotations
|
11 | def foo_no_return_pos_only(arg: int, /, arg2: NoReturn): ... # Error: PYI050
12 | def foo_never(arg: Never): ...
13 | def foo_args(*args: NoReturn): ... # Error: PYI050
| ^^^^^^^^ PYI050
14 | def foo_kwargs(**kwargs: NoReturn): ... # Error: PYI050
15 | def foo_args_kwargs(*args: NoReturn, **kwargs: NoReturn): ... # Error: PYI050
|

PYI050.pyi:14:26: PYI050 Prefer `typing.Never` over `NoReturn` for argument annotations
|
12 | def foo_never(arg: Never): ...
13 | def foo_args(*args: NoReturn): ... # Error: PYI050
14 | def foo_kwargs(**kwargs: NoReturn): ... # Error: PYI050
| ^^^^^^^^ PYI050
15 | def foo_args_kwargs(*args: NoReturn, **kwargs: NoReturn): ... # Error: PYI050
16 | def foo_int_args(*args: int): ...
|

PYI050.pyi:15:28: PYI050 Prefer `typing.Never` over `NoReturn` for argument annotations
|
13 | def foo_args(*args: NoReturn): ... # Error: PYI050
14 | def foo_kwargs(**kwargs: NoReturn): ... # Error: PYI050
15 | def foo_args_kwargs(*args: NoReturn, **kwargs: NoReturn): ... # Error: PYI050
| ^^^^^^^^ PYI050
16 | def foo_int_args(*args: int): ...
17 | def foo_int_kwargs(**kwargs: int): ...
|

PYI050.pyi:15:48: PYI050 Prefer `typing.Never` over `NoReturn` for argument annotations
|
13 | def foo_args(*args: NoReturn): ... # Error: PYI050
14 | def foo_kwargs(**kwargs: NoReturn): ... # Error: PYI050
15 | def foo_args_kwargs(*args: NoReturn, **kwargs: NoReturn): ... # Error: PYI050
| ^^^^^^^^ PYI050
16 | def foo_int_args(*args: int): ...
17 | def foo_int_kwargs(**kwargs: int): ...
|

PYI050.pyi:19:50: PYI050 Prefer `typing.Never` over `NoReturn` for argument annotations
|
17 | def foo_int_kwargs(**kwargs: int): ...
18 | def foo_int_args_kwargs(*args: int, **kwargs: int): ...
19 | def foo_int_args_no_return(*args: int, **kwargs: NoReturn): ... # Error: PYI050
| ^^^^^^^^ PYI050
20 | def foo_int_kwargs_no_return(*args: NoReturn, **kwargs: int): ... # Error: PYI050
21 | def foo_args_never(*args: Never): ...
|

PYI050.pyi:20:37: PYI050 Prefer `typing.Never` over `NoReturn` for argument annotations
|
18 | def foo_int_args_kwargs(*args: int, **kwargs: int): ...
19 | def foo_int_args_no_return(*args: int, **kwargs: NoReturn): ... # Error: PYI050
20 | def foo_int_kwargs_no_return(*args: NoReturn, **kwargs: int): ... # Error: PYI050
| ^^^^^^^^ PYI050
21 | def foo_args_never(*args: Never): ...
22 | def foo_kwargs_never(**kwargs: Never): ...
|


0 comments on commit d66063b

Please sign in to comment.