mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-03 18:28:24 +00:00
Add documentation for mccabe, isort, and flake8-annotations (#2691)
This commit is contained in:
parent
8a98cfc4b8
commit
7d5fb0de8a
21 changed files with 713 additions and 23 deletions
|
@ -16,6 +16,25 @@ use crate::visibility;
|
|||
use crate::visibility::Visibility;
|
||||
|
||||
define_violation!(
|
||||
/// ### What it does
|
||||
/// Checks that function arguments have type annotations.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Type annotations are a good way to document the types of function arguments. They also
|
||||
/// help catch bugs, when used alongside a type checker, by ensuring that the types of
|
||||
/// any provided arguments match expectation.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```python
|
||||
/// def foo(x):
|
||||
/// ...
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// def foo(x: int):
|
||||
/// ...
|
||||
/// ```
|
||||
pub struct MissingTypeFunctionArgument {
|
||||
pub name: String,
|
||||
}
|
||||
|
@ -29,6 +48,25 @@ impl Violation for MissingTypeFunctionArgument {
|
|||
}
|
||||
|
||||
define_violation!(
|
||||
/// ### What it does
|
||||
/// Checks that function `*args` arguments have type annotations.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Type annotations are a good way to document the types of function arguments. They also
|
||||
/// help catch bugs, when used alongside a type checker, by ensuring that the types of
|
||||
/// any provided arguments match expectation.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```python
|
||||
/// def foo(*args):
|
||||
/// ...
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// def foo(*args: int):
|
||||
/// ...
|
||||
/// ```
|
||||
pub struct MissingTypeArgs {
|
||||
pub name: String,
|
||||
}
|
||||
|
@ -42,6 +80,25 @@ impl Violation for MissingTypeArgs {
|
|||
}
|
||||
|
||||
define_violation!(
|
||||
/// ### What it does
|
||||
/// Checks that function `**kwargs` arguments have type annotations.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Type annotations are a good way to document the types of function arguments. They also
|
||||
/// help catch bugs, when used alongside a type checker, by ensuring that the types of
|
||||
/// any provided arguments match expectation.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```python
|
||||
/// def foo(**kwargs):
|
||||
/// ...
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// def foo(**kwargs: int):
|
||||
/// ...
|
||||
/// ```
|
||||
pub struct MissingTypeKwargs {
|
||||
pub name: String,
|
||||
}
|
||||
|
@ -55,6 +112,30 @@ impl Violation for MissingTypeKwargs {
|
|||
}
|
||||
|
||||
define_violation!(
|
||||
/// ### What it does
|
||||
/// Checks that instance method `self` arguments have type annotations.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Type annotations are a good way to document the types of function arguments. They also
|
||||
/// help catch bugs, when used alongside a type checker, by ensuring that the types of
|
||||
/// any provided arguments match expectation.
|
||||
///
|
||||
/// Note that many type checkers will infer the type of `self` automatically, so this
|
||||
/// annotation is not strictly necessary.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```python
|
||||
/// class Foo:
|
||||
/// def bar(self):
|
||||
/// ...
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// class Foo:
|
||||
/// def bar(self: "Foo"):
|
||||
/// ...
|
||||
/// ```
|
||||
pub struct MissingTypeSelf {
|
||||
pub name: String,
|
||||
}
|
||||
|
@ -68,6 +149,32 @@ impl Violation for MissingTypeSelf {
|
|||
}
|
||||
|
||||
define_violation!(
|
||||
/// ### What it does
|
||||
/// Checks that class method `cls` arguments have type annotations.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Type annotations are a good way to document the types of function arguments. They also
|
||||
/// help catch bugs, when used alongside a type checker, by ensuring that the types of
|
||||
/// any provided arguments match expectation.
|
||||
///
|
||||
/// Note that many type checkers will infer the type of `cls` automatically, so this
|
||||
/// annotation is not strictly necessary.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```python
|
||||
/// class Foo:
|
||||
/// @classmethod
|
||||
/// def bar(cls):
|
||||
/// ...
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// class Foo:
|
||||
/// @classmethod
|
||||
/// def bar(cls: Type["Foo"]):
|
||||
/// ...
|
||||
/// ```
|
||||
pub struct MissingTypeCls {
|
||||
pub name: String,
|
||||
}
|
||||
|
@ -81,6 +188,25 @@ impl Violation for MissingTypeCls {
|
|||
}
|
||||
|
||||
define_violation!(
|
||||
/// ### What it does
|
||||
/// Checks that public functions and methods have return type annotations.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Type annotations are a good way to document the return types of functions. They also
|
||||
/// help catch bugs, when used alongside a type checker, by ensuring that the types of
|
||||
/// any returned values, and the types expected by callers, match expectation.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```python
|
||||
/// def add(a, b):
|
||||
/// return a + b
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// def add(a: int, b: int) -> int:
|
||||
/// return a + b
|
||||
/// ```
|
||||
pub struct MissingReturnTypePublicFunction {
|
||||
pub name: String,
|
||||
}
|
||||
|
@ -94,6 +220,25 @@ impl Violation for MissingReturnTypePublicFunction {
|
|||
}
|
||||
|
||||
define_violation!(
|
||||
/// ### What it does
|
||||
/// Checks that private functions and methods have return type annotations.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Type annotations are a good way to document the return types of functions. They also
|
||||
/// help catch bugs, when used alongside a type checker, by ensuring that the types of
|
||||
/// any returned values, and the types expected by callers, match expectation.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```python
|
||||
/// def _add(a, b):
|
||||
/// return a + b
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// def _add(a: int, b: int) -> int:
|
||||
/// return a + b
|
||||
/// ```
|
||||
pub struct MissingReturnTypePrivateFunction {
|
||||
pub name: String,
|
||||
}
|
||||
|
@ -107,6 +252,38 @@ impl Violation for MissingReturnTypePrivateFunction {
|
|||
}
|
||||
|
||||
define_violation!(
|
||||
/// ### What it does
|
||||
/// Checks that "special" methods, like `__init__`, `__new__`, and `__call__`, have
|
||||
/// return type annotations.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Type annotations are a good way to document the return types of functions. They also
|
||||
/// help catch bugs, when used alongside a type checker, by ensuring that the types of
|
||||
/// any returned values, and the types expected by callers, match expectation.
|
||||
///
|
||||
/// Note that type checkers often allow you to omit the return type annotation for
|
||||
/// `__init__` methods, as long as at least one argument has a type annotation. To
|
||||
/// opt-in to this behavior, use the `mypy-init-return` setting in your `pyproject.toml`
|
||||
/// or `ruff.toml` file:
|
||||
///
|
||||
/// ```toml
|
||||
/// [tool.ruff.flake8-annotations]
|
||||
/// mypy-init-return = true
|
||||
/// ```
|
||||
///
|
||||
/// ### Example
|
||||
/// ```python
|
||||
/// class Foo:
|
||||
/// def __init__(self, x: int):
|
||||
/// self.x = x
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// class Foo:
|
||||
/// def __init__(self, x: int) -> None:
|
||||
/// self.x = x
|
||||
/// ```
|
||||
pub struct MissingReturnTypeSpecialMethod {
|
||||
pub name: String,
|
||||
}
|
||||
|
@ -124,6 +301,29 @@ impl AlwaysAutofixableViolation for MissingReturnTypeSpecialMethod {
|
|||
}
|
||||
|
||||
define_violation!(
|
||||
/// ### What it does
|
||||
/// Checks that static methods have return type annotations.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Type annotations are a good way to document the return types of functions. They also
|
||||
/// help catch bugs, when used alongside a type checker, by ensuring that the types of
|
||||
/// any returned values, and the types expected by callers, match expectation.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```python
|
||||
/// class Foo:
|
||||
/// @staticmethod
|
||||
/// def bar():
|
||||
/// return 1
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// class Foo:
|
||||
/// @staticmethod
|
||||
/// def bar() -> int:
|
||||
/// return 1
|
||||
/// ```
|
||||
pub struct MissingReturnTypeStaticMethod {
|
||||
pub name: String,
|
||||
}
|
||||
|
@ -137,6 +337,29 @@ impl Violation for MissingReturnTypeStaticMethod {
|
|||
}
|
||||
|
||||
define_violation!(
|
||||
/// ### What it does
|
||||
/// Checks that class methods have return type annotations.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Type annotations are a good way to document the return types of functions. They also
|
||||
/// help catch bugs, when used alongside a type checker, by ensuring that the types of
|
||||
/// any returned values, and the types expected by callers, match expectation.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```python
|
||||
/// class Foo:
|
||||
/// @classmethod
|
||||
/// def bar(cls):
|
||||
/// return 1
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// class Foo:
|
||||
/// @classmethod
|
||||
/// def bar(cls) -> int:
|
||||
/// return 1
|
||||
/// ```
|
||||
pub struct MissingReturnTypeClassMethod {
|
||||
pub name: String,
|
||||
}
|
||||
|
@ -150,6 +373,25 @@ impl Violation for MissingReturnTypeClassMethod {
|
|||
}
|
||||
|
||||
define_violation!(
|
||||
/// ### What it does
|
||||
/// Checks that an expression is annotated with a more specific type than `Any`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `Any` is a type that can be anything, and it is the default type for
|
||||
/// unannotated expressions. It is better to be explicit about the type of an
|
||||
/// expression, and to use `Any` only when it is really needed.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```python
|
||||
/// def foo(x: Any):
|
||||
/// ...
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// def foo(x: int):
|
||||
/// ...
|
||||
/// ```
|
||||
pub struct DynamicallyTypedExpression {
|
||||
pub name: String,
|
||||
}
|
||||
|
|
|
@ -15,6 +15,26 @@ use crate::source_code::{Locator, Stylist};
|
|||
use crate::violation::AlwaysAutofixableViolation;
|
||||
|
||||
define_violation!(
|
||||
/// ### What it does
|
||||
/// Adds any required imports, as specified by the user, to the top of the file.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// In some projects, certain imports are required to be present in all files. For
|
||||
/// example, some projects assume that `from __future__ import annotations` is enabled,
|
||||
/// and thus require that import to be present in all files. Omitting a "required" import
|
||||
/// (as specified by the user) can cause errors or unexpected behavior.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```python
|
||||
/// import typing
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// from __future__ import annotations
|
||||
///
|
||||
/// import typing
|
||||
/// ```
|
||||
pub struct MissingRequiredImport(pub String);
|
||||
);
|
||||
impl AlwaysAutofixableViolation for MissingRequiredImport {
|
||||
|
|
|
@ -19,6 +19,24 @@ use crate::source_code::{Indexer, Locator, Stylist};
|
|||
use crate::violation::AlwaysAutofixableViolation;
|
||||
|
||||
define_violation!(
|
||||
/// ### What it does
|
||||
/// De-duplicates, groups, and sorts imports based on the provided `isort` settings.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Consistency is good. Use a common convention for imports to make your code
|
||||
/// more readable and idiomatic.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```python
|
||||
/// import pandas
|
||||
/// import numpy as np
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// import numpy as np
|
||||
/// import pandas
|
||||
/// ```
|
||||
pub struct UnsortedImports;
|
||||
);
|
||||
impl AlwaysAutofixableViolation for UnsortedImports {
|
||||
|
|
|
@ -7,6 +7,43 @@ use crate::source_code::Locator;
|
|||
use crate::violation::Violation;
|
||||
|
||||
define_violation!(
|
||||
/// ### What it does
|
||||
/// Checks for functions with a high `McCabe` complexity.
|
||||
///
|
||||
/// The `McCabe` complexity of a function is a measure of the complexity of the
|
||||
/// control flow graph of the function. It is calculated by adding one to the
|
||||
/// number of decision points in the function. A decision point is a place in
|
||||
/// the code where the program has a choice of two or more paths to follow.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Functions with a high complexity are hard to understand and maintain.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```python
|
||||
/// def foo(a, b, c):
|
||||
/// if a:
|
||||
/// if b:
|
||||
/// if c:
|
||||
/// return 1
|
||||
/// else:
|
||||
/// return 2
|
||||
/// else:
|
||||
/// return 3
|
||||
/// else:
|
||||
/// return 4
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// def foo(a, b, c):
|
||||
/// if not a:
|
||||
/// return 4
|
||||
/// if not b:
|
||||
/// return 3
|
||||
/// if not c:
|
||||
/// return 2
|
||||
/// return 1
|
||||
/// ```
|
||||
pub struct FunctionIsTooComplex {
|
||||
pub name: String,
|
||||
pub complexity: usize,
|
||||
|
|
|
@ -30,20 +30,13 @@ impl Parse for LintMeta {
|
|||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let attrs = input.call(Attribute::parse_outer)?;
|
||||
|
||||
let mut in_code = false;
|
||||
let mut explanation = String::new();
|
||||
for attr in &attrs {
|
||||
if let Some(lit) = parse_attr(["doc"], attr) {
|
||||
let value = lit.value();
|
||||
let line = value.strip_prefix(' ').unwrap_or(&value);
|
||||
if line.starts_with("```") {
|
||||
explanation += line;
|
||||
explanation.push('\n');
|
||||
in_code = !in_code;
|
||||
} else if !(in_code && line.starts_with("# ")) {
|
||||
explanation += line;
|
||||
explanation.push('\n');
|
||||
}
|
||||
explanation.push_str(line);
|
||||
explanation.push('\n');
|
||||
} else {
|
||||
return Err(Error::new_spanned(attr, "unexpected attribute"));
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue