diff --git a/README.md b/README.md
index fa3a70b66572a9..657b8fcfaae660 100644
--- a/README.md
+++ b/README.md
@@ -15,15 +15,15 @@ An extremely fast Python linter, written in Rust.
   <i>Linting the CPython codebase from scratch.</i>
 </p>
 
-- ⚡️  10-100x faster than existing linters
-- 🐍  Installable via `pip`
-- 🤝  Python 3.10 compatibility
-- 🛠️  `pyproject.toml` support
-- 📦  Built-in caching, to avoid re-analyzing unchanged files
-- 🔧  `--fix` support, for automatic error correction (e.g., automatically remove unused imports)
-- 👀  `--watch` support, for continuous file monitoring
-- ⚖️  [Near-parity](#how-does-ruff-compare-to-flake8) with the built-in Flake8 rule set
-- 🔌  Native re-implementations of popular Flake8 plugins, like [`flake8-docstrings`](https://pypi.org/project/flake8-docstrings/) ([`pydocstyle`](https://pypi.org/project/pydocstyle/))
+- ⚡️ 10-100x faster than existing linters
+- 🐍 Installable via `pip`
+- 🤝 Python 3.10 compatibility
+- 🛠️ `pyproject.toml` support
+- 📦 Built-in caching, to avoid re-analyzing unchanged files
+- 🔧 `--fix` support, for automatic error correction (e.g., automatically remove unused imports)
+- 👀 `--watch` support, for continuous file monitoring
+- ⚖️ [Near-parity](#how-does-ruff-compare-to-flake8) with the built-in Flake8 rule set
+- 🔌 Native re-implementations of popular Flake8 plugins, like [`flake8-docstrings`](https://pypi.org/project/flake8-docstrings/) ([`pydocstyle`](https://pypi.org/project/pydocstyle/))
 
 Ruff aims to be orders of magnitude faster than alternative tools while integrating more
 functionality behind a single, common interface. Ruff can be used to replace Flake8 (plus a variety
@@ -60,13 +60,13 @@ Read the [launch blog post](https://notes.crmarsh.com/python-tooling-could-be-mu
    12. [flake8-2020](#flake8-2020)
    13. [Ruff-specific rules](#ruff-specific-rules)
    14. [Meta rules](#meta-rules)
-5. [Editor Integrations](#editor-integrations)
-6. [FAQ](#faq)
-7. [Development](#development)
-8. [Releases](#releases)
-9. [Benchmarks](#benchmarks)
-10. [License](#license)
-11. [Contributing](#contributing)
+4. [Editor Integrations](#editor-integrations)
+5. [FAQ](#faq)
+6. [Development](#development)
+7. [Releases](#releases)
+8. [Benchmarks](#benchmarks)
+9. [License](#license)
+10. [Contributing](#contributing)
 
 ## Installation and Usage
 
@@ -105,6 +105,7 @@ repos:
 ```
 
 <!-- TODO(charlie): Remove this message a few versions after v0.0.86. -->
+
 _Note: prior to `v0.0.86`, `ruff-pre-commit` used `lint` (rather than `ruff`) as the hook ID._
 
 ## Configuration
@@ -257,7 +258,7 @@ Exclusions are based on globs, and can be either:
 ### Ignoring errors
 
 To omit a lint check entirely, add it to the "ignore" list via `--ignore` or `--extend-ignore`,
-either  on the command-line or in your `project.toml` file.
+either on the command-line or in your `project.toml` file.
 
 To ignore an error in-line, Ruff uses a `noqa` system similar to [Flake8](https://flake8.pycqa.org/en/3.1.1/user/ignoring-errors.html).
 To ignore an individual error, add `# noqa: {code}` to the end of the line, like so:
@@ -313,285 +314,286 @@ The 🛠 emoji indicates that a rule is automatically fixable by the `--fix` com
 
 For more, see [Pyflakes](https://pypi.org/project/pyflakes/2.5.0/) on PyPI.
 
-| Code | Name | Message | Fix |
-| ---- | ---- | ------- | --- |
-| F401 | UnusedImport | `...` imported but unused | 🛠 |
-| F402 | ImportShadowedByLoopVar | Import `...` from line 1 shadowed by loop variable |  |
-| F403 | ImportStarUsed | `from ... import *` used; unable to detect undefined names |  |
-| F404 | LateFutureImport | `from __future__` imports must occur at the beginning of the file |  |
-| F405 | ImportStarUsage | `...` may be undefined, or defined from star imports: `...` |  |
-| F406 | ImportStarNotPermitted | `from ... import *` only allowed at module level |  |
-| F407 | FutureFeatureNotDefined | Future feature `...` is not defined |  |
-| F541 | FStringMissingPlaceholders | f-string without any placeholders |  |
-| F601 | MultiValueRepeatedKeyLiteral | Dictionary key literal repeated |  |
-| F602 | MultiValueRepeatedKeyVariable | Dictionary key `...` repeated |  |
-| F621 | ExpressionsInStarAssignment | Too many expressions in star-unpacking assignment |  |
-| F622 | TwoStarredExpressions | Two starred expressions in assignment |  |
-| F631 | AssertTuple | Assert test is a non-empty tuple, which is always `True` |  |
-| F632 | IsLiteral | Use `==` and `!=` to compare constant literals | 🛠 |
-| F633 | InvalidPrintSyntax | Use of `>>` is invalid with `print` function |  |
-| F634 | IfTuple | If test is a tuple, which is always `True` |  |
-| F701 | BreakOutsideLoop | `break` outside loop |  |
-| F702 | ContinueOutsideLoop | `continue` not properly in loop |  |
-| F704 | YieldOutsideFunction | `yield` or `yield from` statement outside of a function |  |
-| F706 | ReturnOutsideFunction | `return` statement outside of a function/method |  |
-| F707 | DefaultExceptNotLast | An `except:` block as not the last exception handler |  |
-| F722 | ForwardAnnotationSyntaxError | Syntax error in forward annotation: `...` |  |
-| F821 | UndefinedName | Undefined name `...` |  |
-| F822 | UndefinedExport | Undefined name `...` in `__all__` |  |
-| F823 | UndefinedLocal | Local variable `...` referenced before assignment |  |
-| F831 | DuplicateArgumentName | Duplicate argument name in function definition |  |
-| F841 | UnusedVariable | Local variable `...` is assigned to but never used |  |
-| F901 | RaiseNotImplemented | `raise NotImplemented` should be `raise NotImplementedError` | 🛠 |
+| Code | Name                          | Message                                                           | Fix |
+| ---- | ----------------------------- | ----------------------------------------------------------------- | --- |
+| F401 | UnusedImport                  | `...` imported but unused                                         | 🛠   |
+| F402 | ImportShadowedByLoopVar       | Import `...` from line 1 shadowed by loop variable                |     |
+| F403 | ImportStarUsed                | `from ... import *` used; unable to detect undefined names        |     |
+| F404 | LateFutureImport              | `from __future__` imports must occur at the beginning of the file |     |
+| F405 | ImportStarUsage               | `...` may be undefined, or defined from star imports: `...`       |     |
+| F406 | ImportStarNotPermitted        | `from ... import *` only allowed at module level                  |     |
+| F407 | FutureFeatureNotDefined       | Future feature `...` is not defined                               |     |
+| F541 | FStringMissingPlaceholders    | f-string without any placeholders                                 |     |
+| F601 | MultiValueRepeatedKeyLiteral  | Dictionary key literal repeated                                   |     |
+| F602 | MultiValueRepeatedKeyVariable | Dictionary key `...` repeated                                     |     |
+| F621 | ExpressionsInStarAssignment   | Too many expressions in star-unpacking assignment                 |     |
+| F622 | TwoStarredExpressions         | Two starred expressions in assignment                             |     |
+| F631 | AssertTuple                   | Assert test is a non-empty tuple, which is always `True`          |     |
+| F632 | IsLiteral                     | Use `==` and `!=` to compare constant literals                    | 🛠   |
+| F633 | InvalidPrintSyntax            | Use of `>>` is invalid with `print` function                      |     |
+| F634 | IfTuple                       | If test is a tuple, which is always `True`                        |     |
+| F701 | BreakOutsideLoop              | `break` outside loop                                              |     |
+| F702 | ContinueOutsideLoop           | `continue` not properly in loop                                   |     |
+| F704 | YieldOutsideFunction          | `yield` or `yield from` statement outside of a function           |     |
+| F706 | ReturnOutsideFunction         | `return` statement outside of a function/method                   |     |
+| F707 | DefaultExceptNotLast          | An `except:` block as not the last exception handler              |     |
+| F722 | ForwardAnnotationSyntaxError  | Syntax error in forward annotation: `...`                         |     |
+| F821 | UndefinedName                 | Undefined name `...`                                              |     |
+| F822 | UndefinedExport               | Undefined name `...` in `__all__`                                 |     |
+| F823 | UndefinedLocal                | Local variable `...` referenced before assignment                 |     |
+| F831 | DuplicateArgumentName         | Duplicate argument name in function definition                    |     |
+| F841 | UnusedVariable                | Local variable `...` is assigned to but never used                |     |
+| F901 | RaiseNotImplemented           | `raise NotImplemented` should be `raise NotImplementedError`      | 🛠   |
 
 ### pycodestyle
 
 For more, see [pycodestyle](https://pypi.org/project/pycodestyle/2.9.1/) on PyPI.
 
-| Code | Name | Message | Fix |
-| ---- | ---- | ------- | --- |
-| E402 | ModuleImportNotAtTopOfFile | Module level import not at top of file |  |
-| E501 | LineTooLong | Line too long (89 > 88 characters) |  |
-| E711 | NoneComparison | Comparison to `None` should be `cond is None` |  |
-| E712 | TrueFalseComparison | Comparison to `True` should be `cond is True` |  |
-| E713 | NotInTest | Test for membership should be `not in` |  |
-| E714 | NotIsTest | Test for object identity should be `is not` |  |
-| E721 | TypeComparison | Do not compare types, use `isinstance()` |  |
-| E722 | DoNotUseBareExcept | Do not use bare `except` |  |
-| E731 | DoNotAssignLambda | Do not assign a lambda expression, use a def |  |
-| E741 | AmbiguousVariableName | Ambiguous variable name: `...` |  |
-| E742 | AmbiguousClassName | Ambiguous class name: `...` |  |
-| E743 | AmbiguousFunctionName | Ambiguous function name: `...` |  |
-| E902 | IOError | IOError: `...` |  |
-| E999 | SyntaxError | SyntaxError: `...` |  |
-| W292 | NoNewLineAtEndOfFile | No newline at end of file |  |
-| W605 | InvalidEscapeSequence | Invalid escape sequence: '\c' |  |
+| Code | Name                       | Message                                       | Fix |
+| ---- | -------------------------- | --------------------------------------------- | --- |
+| E402 | ModuleImportNotAtTopOfFile | Module level import not at top of file        |     |
+| E501 | LineTooLong                | Line too long (89 > 88 characters)            |     |
+| E711 | NoneComparison             | Comparison to `None` should be `cond is None` |     |
+| E712 | TrueFalseComparison        | Comparison to `True` should be `cond is True` |     |
+| E713 | NotInTest                  | Test for membership should be `not in`        |     |
+| E714 | NotIsTest                  | Test for object identity should be `is not`   |     |
+| E721 | TypeComparison             | Do not compare types, use `isinstance()`      |     |
+| E722 | DoNotUseBareExcept         | Do not use bare `except`                      |     |
+| E731 | DoNotAssignLambda          | Do not assign a lambda expression, use a def  |     |
+| E741 | AmbiguousVariableName      | Ambiguous variable name: `...`                |     |
+| E742 | AmbiguousClassName         | Ambiguous class name: `...`                   |     |
+| E743 | AmbiguousFunctionName      | Ambiguous function name: `...`                |     |
+| E902 | IOError                    | IOError: `...`                                |     |
+| E999 | SyntaxError                | SyntaxError: `...`                            |     |
+| W292 | NoNewLineAtEndOfFile       | No newline at end of file                     |     |
+| W605 | InvalidEscapeSequence      | Invalid escape sequence: '\c'                 |     |
 
 ### isort
 
 For more, see [isort](https://pypi.org/project/isort/5.10.1/) on PyPI.
 
-| Code | Name | Message | Fix |
-| ---- | ---- | ------- | --- |
-| I001 | UnsortedImports | Import block is un-sorted or un-formatted | 🛠 |
+| Code | Name            | Message                                   | Fix |
+| ---- | --------------- | ----------------------------------------- | --- |
+| I001 | UnsortedImports | Import block is un-sorted or un-formatted | 🛠   |
 
 ### pydocstyle
 
 For more, see [pydocstyle](https://pypi.org/project/pydocstyle/6.1.1/) on PyPI.
 
-| Code | Name | Message | Fix |
-| ---- | ---- | ------- | --- |
-| D100 | PublicModule | Missing docstring in public module |  |
-| D101 | PublicClass | Missing docstring in public class |  |
-| D102 | PublicMethod | Missing docstring in public method |  |
-| D103 | PublicFunction | Missing docstring in public function |  |
-| D104 | PublicPackage | Missing docstring in public package |  |
-| D105 | MagicMethod | Missing docstring in magic method |  |
-| D106 | PublicNestedClass | Missing docstring in public nested class |  |
-| D107 | PublicInit | Missing docstring in `__init__` |  |
-| D200 | FitsOnOneLine | One-line docstring should fit on one line |  |
-| D201 | NoBlankLineBeforeFunction | No blank lines allowed before function docstring (found 1) | 🛠 |
-| D202 | NoBlankLineAfterFunction | No blank lines allowed after function docstring (found 1) | 🛠 |
-| D203 | OneBlankLineBeforeClass | 1 blank line required before class docstring | 🛠 |
-| D204 | OneBlankLineAfterClass | 1 blank line required after class docstring | 🛠 |
-| D205 | BlankLineAfterSummary | 1 blank line required between summary line and description | 🛠 |
-| D206 | IndentWithSpaces | Docstring should be indented with spaces, not tabs |  |
-| D207 | NoUnderIndentation | Docstring is under-indented | 🛠 |
-| D208 | NoOverIndentation | Docstring is over-indented | 🛠 |
-| D209 | NewLineAfterLastParagraph | Multi-line docstring closing quotes should be on a separate line | 🛠 |
-| D210 | NoSurroundingWhitespace | No whitespaces allowed surrounding docstring text | 🛠 |
-| D211 | NoBlankLineBeforeClass | No blank lines allowed before class docstring | 🛠 |
-| D212 | MultiLineSummaryFirstLine | Multi-line docstring summary should start at the first line |  |
-| D213 | MultiLineSummarySecondLine | Multi-line docstring summary should start at the second line |  |
-| D214 | SectionNotOverIndented | Section is over-indented ("Returns") | 🛠 |
-| D215 | SectionUnderlineNotOverIndented | Section underline is over-indented ("Returns") | 🛠 |
-| D300 | UsesTripleQuotes | Use """triple double quotes""" |  |
-| D400 | EndsInPeriod | First line should end with a period |  |
-| D402 | NoSignature | First line should not be the function's signature |  |
-| D403 | FirstLineCapitalized | First word of the first line should be properly capitalized |  |
-| D404 | NoThisPrefix | First word of the docstring should not be 'This' |  |
-| D405 | CapitalizeSectionName | Section name should be properly capitalized ("returns") | 🛠 |
-| D406 | NewLineAfterSectionName | Section name should end with a newline ("Returns") | 🛠 |
-| D407 | DashedUnderlineAfterSection | Missing dashed underline after section ("Returns") | 🛠 |
-| D408 | SectionUnderlineAfterName | Section underline should be in the line following the section's name ("Returns") | 🛠 |
-| D409 | SectionUnderlineMatchesSectionLength | Section underline should match the length of its name ("Returns") | 🛠 |
-| D410 | BlankLineAfterSection | Missing blank line after section ("Returns") | 🛠 |
-| D411 | BlankLineBeforeSection | Missing blank line before section ("Returns") | 🛠 |
-| D412 | NoBlankLinesBetweenHeaderAndContent | No blank lines allowed between a section header and its content ("Returns") | 🛠 |
-| D413 | BlankLineAfterLastSection | Missing blank line after last section ("Returns") | 🛠 |
-| D414 | NonEmptySection | Section has no content ("Returns") |  |
-| D415 | EndsInPunctuation | First line should end with a period, question mark, or exclamation point |  |
-| D416 | SectionNameEndsInColon | Section name should end with a colon ("Returns") | 🛠 |
-| D417 | DocumentAllArguments | Missing argument descriptions in the docstring: `x`, `y` |  |
-| D418 | SkipDocstring | Function decorated with `@overload` shouldn't contain a docstring |  |
-| D419 | NonEmpty | Docstring is empty |  |
+| Code | Name                                 | Message                                                                          | Fix |
+| ---- | ------------------------------------ | -------------------------------------------------------------------------------- | --- |
+| D100 | PublicModule                         | Missing docstring in public module                                               |     |
+| D101 | PublicClass                          | Missing docstring in public class                                                |     |
+| D102 | PublicMethod                         | Missing docstring in public method                                               |     |
+| D103 | PublicFunction                       | Missing docstring in public function                                             |     |
+| D104 | PublicPackage                        | Missing docstring in public package                                              |     |
+| D105 | MagicMethod                          | Missing docstring in magic method                                                |     |
+| D106 | PublicNestedClass                    | Missing docstring in public nested class                                         |     |
+| D107 | PublicInit                           | Missing docstring in `__init__`                                                  |     |
+| D200 | FitsOnOneLine                        | One-line docstring should fit on one line                                        |     |
+| D201 | NoBlankLineBeforeFunction            | No blank lines allowed before function docstring (found 1)                       | 🛠   |
+| D202 | NoBlankLineAfterFunction             | No blank lines allowed after function docstring (found 1)                        | 🛠   |
+| D203 | OneBlankLineBeforeClass              | 1 blank line required before class docstring                                     | 🛠   |
+| D204 | OneBlankLineAfterClass               | 1 blank line required after class docstring                                      | 🛠   |
+| D205 | BlankLineAfterSummary                | 1 blank line required between summary line and description                       | 🛠   |
+| D206 | IndentWithSpaces                     | Docstring should be indented with spaces, not tabs                               |     |
+| D207 | NoUnderIndentation                   | Docstring is under-indented                                                      | 🛠   |
+| D208 | NoOverIndentation                    | Docstring is over-indented                                                       | 🛠   |
+| D209 | NewLineAfterLastParagraph            | Multi-line docstring closing quotes should be on a separate line                 | 🛠   |
+| D210 | NoSurroundingWhitespace              | No whitespaces allowed surrounding docstring text                                | 🛠   |
+| D211 | NoBlankLineBeforeClass               | No blank lines allowed before class docstring                                    | 🛠   |
+| D212 | MultiLineSummaryFirstLine            | Multi-line docstring summary should start at the first line                      |     |
+| D213 | MultiLineSummarySecondLine           | Multi-line docstring summary should start at the second line                     |     |
+| D214 | SectionNotOverIndented               | Section is over-indented ("Returns")                                             | 🛠   |
+| D215 | SectionUnderlineNotOverIndented      | Section underline is over-indented ("Returns")                                   | 🛠   |
+| D300 | UsesTripleQuotes                     | Use """triple double quotes"""                                                   |     |
+| D400 | EndsInPeriod                         | First line should end with a period                                              |     |
+| D402 | NoSignature                          | First line should not be the function's signature                                |     |
+| D403 | FirstLineCapitalized                 | First word of the first line should be properly capitalized                      |     |
+| D404 | NoThisPrefix                         | First word of the docstring should not be 'This'                                 |     |
+| D405 | CapitalizeSectionName                | Section name should be properly capitalized ("returns")                          | 🛠   |
+| D406 | NewLineAfterSectionName              | Section name should end with a newline ("Returns")                               | 🛠   |
+| D407 | DashedUnderlineAfterSection          | Missing dashed underline after section ("Returns")                               | 🛠   |
+| D408 | SectionUnderlineAfterName            | Section underline should be in the line following the section's name ("Returns") | 🛠   |
+| D409 | SectionUnderlineMatchesSectionLength | Section underline should match the length of its name ("Returns")                | 🛠   |
+| D410 | BlankLineAfterSection                | Missing blank line after section ("Returns")                                     | 🛠   |
+| D411 | BlankLineBeforeSection               | Missing blank line before section ("Returns")                                    | 🛠   |
+| D412 | NoBlankLinesBetweenHeaderAndContent  | No blank lines allowed between a section header and its content ("Returns")      | 🛠   |
+| D413 | BlankLineAfterLastSection            | Missing blank line after last section ("Returns")                                | 🛠   |
+| D414 | NonEmptySection                      | Section has no content ("Returns")                                               |     |
+| D415 | EndsInPunctuation                    | First line should end with a period, question mark, or exclamation point         |     |
+| D416 | SectionNameEndsInColon               | Section name should end with a colon ("Returns")                                 | 🛠   |
+| D417 | DocumentAllArguments                 | Missing argument descriptions in the docstring: `x`, `y`                         |     |
+| D418 | SkipDocstring                        | Function decorated with `@overload` shouldn't contain a docstring                |     |
+| D419 | NonEmpty                             | Docstring is empty                                                               |     |
 
 ### pyupgrade
 
 For more, see [pyupgrade](https://pypi.org/project/pyupgrade/3.2.0/) on PyPI.
 
-| Code | Name | Message | Fix |
-| ---- | ---- | ------- | --- |
-| U001 | UselessMetaclassType | `__metaclass__ = type` is implied | 🛠 |
-| U002 | UnnecessaryAbspath | `abspath(__file__)` is unnecessary in Python 3.9 and later | 🛠 |
-| U003 | TypeOfPrimitive | Use `str` instead of `type(...)` | 🛠 |
-| U004 | UselessObjectInheritance | Class `...` inherits from object | 🛠 |
-| U005 | DeprecatedUnittestAlias | `assertEquals` is deprecated, use `assertEqual` instead | 🛠 |
-| U006 | UsePEP585Annotation | Use `list` instead of `List` for type annotations | 🛠 |
-| U007 | UsePEP604Annotation | Use `X \| Y` for type annotations | 🛠 |
-| U008 | SuperCallWithParameters | Use `super()` instead of `super(__class__, self)` | 🛠 |
-| U009 | PEP3120UnnecessaryCodingComment | utf-8 encoding declaration is unnecessary | 🛠 |
-| U010 | UnnecessaryFutureImport | Unnessary __future__ import `...` for target Python version |  |
-| U011 | UnnecessaryLRUCacheParams | Unnessary parameters to functools.lru_cache | 🛠 |
+| Code | Name                            | Message                                                     | Fix |
+| ---- | ------------------------------- | ----------------------------------------------------------- | --- |
+| U001 | UselessMetaclassType            | `__metaclass__ = type` is implied                           | 🛠   |
+| U002 | UnnecessaryAbspath              | `abspath(__file__)` is unnecessary in Python 3.9 and later  | 🛠   |
+| U003 | TypeOfPrimitive                 | Use `str` instead of `type(...)`                            | 🛠   |
+| U004 | UselessObjectInheritance        | Class `...` inherits from object                            | 🛠   |
+| U005 | DeprecatedUnittestAlias         | `assertEquals` is deprecated, use `assertEqual` instead     | 🛠   |
+| U006 | UsePEP585Annotation             | Use `list` instead of `List` for type annotations           | 🛠   |
+| U007 | UsePEP604Annotation             | Use `X \| Y` for type annotations                           | 🛠   |
+| U008 | SuperCallWithParameters         | Use `super()` instead of `super(__class__, self)`           | 🛠   |
+| U009 | PEP3120UnnecessaryCodingComment | utf-8 encoding declaration is unnecessary                   | 🛠   |
+| U010 | UnnecessaryFutureImport         | Unnessary **future** import `...` for target Python version |     |
+| U011 | UnnecessaryLRUCacheParams       | Unnessary parameters to functools.lru_cache                 | 🛠   |
 
 ### pep8-naming
 
 For more, see [pep8-naming](https://pypi.org/project/pep8-naming/0.13.2/) on PyPI.
 
-| Code | Name | Message | Fix |
-| ---- | ---- | ------- | --- |
-| N801 | InvalidClassName | Class name `...` should use CapWords convention  |  |
-| N802 | InvalidFunctionName | Function name `...` should be lowercase |  |
-| N803 | InvalidArgumentName | Argument name `...` should be lowercase |  |
-| N804 | InvalidFirstArgumentNameForClassMethod | First argument of a class method should be named `cls` |  |
-| N805 | InvalidFirstArgumentNameForMethod | First argument of a method should be named `self` |  |
-| N806 | NonLowercaseVariableInFunction | Variable `...` in function should be lowercase |  |
-| N807 | DunderFunctionName | Function name should not start and end with `__` |  |
-| N811 | ConstantImportedAsNonConstant | Constant `...` imported as non-constant `...` |  |
-| N812 | LowercaseImportedAsNonLowercase | Lowercase `...` imported as non-lowercase `...` |  |
-| N813 | CamelcaseImportedAsLowercase | Camelcase `...` imported as lowercase `...` |  |
-| N814 | CamelcaseImportedAsConstant | Camelcase `...` imported as constant `...` |  |
-| N815 | MixedCaseVariableInClassScope | Variable `mixedCase` in class scope should not be mixedCase |  |
-| N816 | MixedCaseVariableInGlobalScope | Variable `mixedCase` in global scope should not be mixedCase |  |
-| N817 | CamelcaseImportedAsAcronym | Camelcase `...` imported as acronym `...` |  |
-| N818 | ErrorSuffixOnExceptionName | Exception name `...` should be named with an Error suffix |  |
+| Code | Name                                   | Message                                                      | Fix |
+| ---- | -------------------------------------- | ------------------------------------------------------------ | --- |
+| N801 | InvalidClassName                       | Class name `...` should use CapWords convention              |     |
+| N802 | InvalidFunctionName                    | Function name `...` should be lowercase                      |     |
+| N803 | InvalidArgumentName                    | Argument name `...` should be lowercase                      |     |
+| N804 | InvalidFirstArgumentNameForClassMethod | First argument of a class method should be named `cls`       |     |
+| N805 | InvalidFirstArgumentNameForMethod      | First argument of a method should be named `self`            |     |
+| N806 | NonLowercaseVariableInFunction         | Variable `...` in function should be lowercase               |     |
+| N807 | DunderFunctionName                     | Function name should not start and end with `__`             |     |
+| N811 | ConstantImportedAsNonConstant          | Constant `...` imported as non-constant `...`                |     |
+| N812 | LowercaseImportedAsNonLowercase        | Lowercase `...` imported as non-lowercase `...`              |     |
+| N813 | CamelcaseImportedAsLowercase           | Camelcase `...` imported as lowercase `...`                  |     |
+| N814 | CamelcaseImportedAsConstant            | Camelcase `...` imported as constant `...`                   |     |
+| N815 | MixedCaseVariableInClassScope          | Variable `mixedCase` in class scope should not be mixedCase  |     |
+| N816 | MixedCaseVariableInGlobalScope         | Variable `mixedCase` in global scope should not be mixedCase |     |
+| N817 | CamelcaseImportedAsAcronym             | Camelcase `...` imported as acronym `...`                    |     |
+| N818 | ErrorSuffixOnExceptionName             | Exception name `...` should be named with an Error suffix    |     |
 
 ### flake8-comprehensions
 
 For more, see [flake8-comprehensions](https://pypi.org/project/flake8-comprehensions/3.10.1/) on PyPI.
 
-| Code | Name | Message | Fix |
-| ---- | ---- | ------- | --- |
-| C400 | UnnecessaryGeneratorList | Unnecessary generator (rewrite as a `list` comprehension) | 🛠 |
-| C401 | UnnecessaryGeneratorSet | Unnecessary generator (rewrite as a `set` comprehension) | 🛠 |
-| C402 | UnnecessaryGeneratorDict | Unnecessary generator (rewrite as a `dict` comprehension) | 🛠 |
-| C403 | UnnecessaryListComprehensionSet | Unnecessary `list` comprehension (rewrite as a `set` comprehension) | 🛠 |
-| C404 | UnnecessaryListComprehensionDict | Unnecessary `list` comprehension (rewrite as a `dict` comprehension) | 🛠 |
-| C405 | UnnecessaryLiteralSet | Unnecessary `(list\|tuple)` literal (rewrite as a `set` literal) | 🛠 |
-| C406 | UnnecessaryLiteralDict | Unnecessary `(list\|tuple)` literal (rewrite as a `dict` literal) | 🛠 |
-| C408 | UnnecessaryCollectionCall | Unnecessary `(dict\|list\|tuple)` call (rewrite as a literal) | 🛠 |
-| C409 | UnnecessaryLiteralWithinTupleCall | Unnecessary `(list\|tuple)` literal passed to `tuple()` (remove the outer call to `tuple()`) | 🛠 |
-| C410 | UnnecessaryLiteralWithinListCall | Unnecessary `(list\|tuple)` literal passed to `list()` (rewrite as a `list` literal) | 🛠 |
-| C411 | UnnecessaryListCall | Unnecessary `list` call (remove the outer call to `list()`) | 🛠 |
-| C413 | UnnecessaryCallAroundSorted | Unnecessary `(list\|reversed)` call around `sorted()` |  |
-| C414 | UnnecessaryDoubleCastOrProcess | Unnecessary `(list\|reversed\|set\|sorted\|tuple)` call within `(list\|set\|sorted\|tuple)()` |  |
-| C415 | UnnecessarySubscriptReversal | Unnecessary subscript reversal of iterable within `(reversed\|set\|sorted)()` |  |
-| C416 | UnnecessaryComprehension | Unnecessary `(list\|set)` comprehension (rewrite using `(list\|set)()`) | 🛠 |
-| C417 | UnnecessaryMap | Unnecessary `map` usage (rewrite using a `(list\|set\|dict)` comprehension) |  |
+| Code | Name                              | Message                                                                                       | Fix |
+| ---- | --------------------------------- | --------------------------------------------------------------------------------------------- | --- |
+| C400 | UnnecessaryGeneratorList          | Unnecessary generator (rewrite as a `list` comprehension)                                     | 🛠   |
+| C401 | UnnecessaryGeneratorSet           | Unnecessary generator (rewrite as a `set` comprehension)                                      | 🛠   |
+| C402 | UnnecessaryGeneratorDict          | Unnecessary generator (rewrite as a `dict` comprehension)                                     | 🛠   |
+| C403 | UnnecessaryListComprehensionSet   | Unnecessary `list` comprehension (rewrite as a `set` comprehension)                           | 🛠   |
+| C404 | UnnecessaryListComprehensionDict  | Unnecessary `list` comprehension (rewrite as a `dict` comprehension)                          | 🛠   |
+| C405 | UnnecessaryLiteralSet             | Unnecessary `(list\|tuple)` literal (rewrite as a `set` literal)                              | 🛠   |
+| C406 | UnnecessaryLiteralDict            | Unnecessary `(list\|tuple)` literal (rewrite as a `dict` literal)                             | 🛠   |
+| C408 | UnnecessaryCollectionCall         | Unnecessary `(dict\|list\|tuple)` call (rewrite as a literal)                                 | 🛠   |
+| C409 | UnnecessaryLiteralWithinTupleCall | Unnecessary `(list\|tuple)` literal passed to `tuple()` (remove the outer call to `tuple()`)  | 🛠   |
+| C410 | UnnecessaryLiteralWithinListCall  | Unnecessary `(list\|tuple)` literal passed to `list()` (rewrite as a `list` literal)          | 🛠   |
+| C411 | UnnecessaryListCall               | Unnecessary `list` call (remove the outer call to `list()`)                                   | 🛠   |
+| C413 | UnnecessaryCallAroundSorted       | Unnecessary `(list\|reversed)` call around `sorted()`                                         |     |
+| C414 | UnnecessaryDoubleCastOrProcess    | Unnecessary `(list\|reversed\|set\|sorted\|tuple)` call within `(list\|set\|sorted\|tuple)()` |     |
+| C415 | UnnecessarySubscriptReversal      | Unnecessary subscript reversal of iterable within `(reversed\|set\|sorted)()`                 |     |
+| C416 | UnnecessaryComprehension          | Unnecessary `(list\|set)` comprehension (rewrite using `(list\|set)()`)                       | 🛠   |
+| C417 | UnnecessaryMap                    | Unnecessary `map` usage (rewrite using a `(list\|set\|dict)` comprehension)                   |     |
 
 ### flake8-bugbear
 
 For more, see [flake8-bugbear](https://pypi.org/project/flake8-bugbear/22.10.27/) on PyPI.
 
-| Code | Name | Message | Fix |
-| ---- | ---- | ------- | --- |
-| B002 | UnaryPrefixIncrement | Python does not support the unary prefix increment. |  |
-| B003 | AssignmentToOsEnviron | Assigning to `os.environ` doesn't clear the environment. |  |
-| B004 | UnreliableCallableCheck |  Using `hasattr(x, '__call__')` to test if x is callable is unreliable. Use `callable(x)` for consistent results. |  |
-| B005 | StripWithMultiCharacters | Using `.strip()` with multi-character strings is misleading the reader. |  |
-| B006 | MutableArgumentDefault | Do not use mutable data structures for argument defaults. |  |
-| B007 | UnusedLoopControlVariable | Loop control variable `i` not used within the loop body. | 🛠 |
-| B008 | FunctionCallArgumentDefault | Do not perform function calls in argument defaults. |  |
-| B009 | GetAttrWithConstant | Do not call `getattr` with a constant attribute value, it is not any safer than normal property access. | 🛠 |
-| B010 | SetAttrWithConstant | Do not call `setattr` with a constant attribute value, it is not any safer than normal property access. |  |
-| B011 | DoNotAssertFalse | Do not `assert False` (`python -O` removes these calls), raise `AssertionError()` | 🛠 |
-| B013 | RedundantTupleInExceptionHandler | A length-one tuple literal is redundant. Write `except ValueError:` instead of `except (ValueError,):`. |  |
-| B014 | DuplicateHandlerException | Exception handler with duplicate exception: `ValueError` | 🛠 |
-| B015 | UselessComparison | Pointless comparison. This comparison does nothing but waste CPU instructions. Either prepend `assert` or remove it. |  |
-| B016 | CannotRaiseLiteral | Cannot raise a literal. Did you intend to return it or raise an Exception? |  |
-| B017 | NoAssertRaisesException | `assertRaises(Exception):` should be considered evil. |  |
-| B018 | UselessExpression | Found useless expression. Either assign it to a variable or remove it. |  |
-| B025 | DuplicateTryBlockException | try-except block with duplicate exception `Exception` |  |
-| B026 | StarArgUnpackingAfterKeywordArg | Star-arg unpacking after a keyword argument is strongly discouraged. |  |
+| Code | Name                             | Message                                                                                                              | Fix |
+| ---- | -------------------------------- | -------------------------------------------------------------------------------------------------------------------- | --- |
+| B002 | UnaryPrefixIncrement             | Python does not support the unary prefix increment.                                                                  |     |
+| B003 | AssignmentToOsEnviron            | Assigning to `os.environ` doesn't clear the environment.                                                             |     |
+| B004 | UnreliableCallableCheck          | Using `hasattr(x, '__call__')` to test if x is callable is unreliable. Use `callable(x)` for consistent results.     |     |
+| B005 | StripWithMultiCharacters         | Using `.strip()` with multi-character strings is misleading the reader.                                              |     |
+| B006 | MutableArgumentDefault           | Do not use mutable data structures for argument defaults.                                                            |     |
+| B007 | UnusedLoopControlVariable        | Loop control variable `i` not used within the loop body.                                                             | 🛠   |
+| B008 | FunctionCallArgumentDefault      | Do not perform function calls in argument defaults.                                                                  |     |
+| B009 | GetAttrWithConstant              | Do not call `getattr` with a constant attribute value, it is not any safer than normal property access.              | 🛠   |
+| B010 | SetAttrWithConstant              | Do not call `setattr` with a constant attribute value, it is not any safer than normal property access.              |     |
+| B011 | DoNotAssertFalse                 | Do not `assert False` (`python -O` removes these calls), raise `AssertionError()`                                    | 🛠   |
+| B013 | RedundantTupleInExceptionHandler | A length-one tuple literal is redundant. Write `except ValueError:` instead of `except (ValueError,):`.              |     |
+| B014 | DuplicateHandlerException        | Exception handler with duplicate exception: `ValueError`                                                             | 🛠   |
+| B015 | UselessComparison                | Pointless comparison. This comparison does nothing but waste CPU instructions. Either prepend `assert` or remove it. |     |
+| B016 | CannotRaiseLiteral               | Cannot raise a literal. Did you intend to return it or raise an Exception?                                           |     |
+| B017 | NoAssertRaisesException          | `assertRaises(Exception):` should be considered evil.                                                                |     |
+| B018 | UselessExpression                | Found useless expression. Either assign it to a variable or remove it.                                               |     |
+| B019 | CachedInstanceMethod             | Use of `functools.lru_cache` or `functools.cache` on methods can lead to memory leaks.                               |     |
+| B025 | DuplicateTryBlockException       | try-except block with duplicate exception `Exception`                                                                |     |
+| B026 | StarArgUnpackingAfterKeywordArg  | Star-arg unpacking after a keyword argument is strongly discouraged.                                                 |     |
 
 ### flake8-builtins
 
 For more, see [flake8-builtins](https://pypi.org/project/flake8-builtins/2.0.1/) on PyPI.
 
-| Code | Name | Message | Fix |
-| ---- | ---- | ------- | --- |
-| A001 | BuiltinVariableShadowing | Variable `...` is shadowing a python builtin |  |
-| A002 | BuiltinArgumentShadowing | Argument `...` is shadowing a python builtin |  |
-| A003 | BuiltinAttributeShadowing | Class attribute `...` is shadowing a python builtin |  |
+| Code | Name                      | Message                                             | Fix |
+| ---- | ------------------------- | --------------------------------------------------- | --- |
+| A001 | BuiltinVariableShadowing  | Variable `...` is shadowing a python builtin        |     |
+| A002 | BuiltinArgumentShadowing  | Argument `...` is shadowing a python builtin        |     |
+| A003 | BuiltinAttributeShadowing | Class attribute `...` is shadowing a python builtin |     |
 
 ### flake8-print
 
 For more, see [flake8-print](https://pypi.org/project/flake8-print/5.0.0/) on PyPI.
 
-| Code | Name | Message | Fix |
-| ---- | ---- | ------- | --- |
-| T201 | PrintFound | `print` found | 🛠 |
-| T203 | PPrintFound | `pprint` found | 🛠 |
+| Code | Name        | Message        | Fix |
+| ---- | ----------- | -------------- | --- |
+| T201 | PrintFound  | `print` found  | 🛠   |
+| T203 | PPrintFound | `pprint` found | 🛠   |
 
 ### flake8-quotes
 
 For more, see [flake8-quotes](https://pypi.org/project/flake8-quotes/3.3.1/) on PyPI.
 
-| Code | Name | Message | Fix |
-| ---- | ---- | ------- | --- |
-| Q000 | BadQuotesInlineString | Single quotes found but double quotes preferred |  |
-| Q001 | BadQuotesMultilineString | Single quote multiline found but double quotes preferred |  |
-| Q002 | BadQuotesDocstring | Single quote docstring found but double quotes preferred |  |
-| Q003 | AvoidQuoteEscape | Change outer quotes to avoid escaping inner quotes |  |
+| Code | Name                     | Message                                                  | Fix |
+| ---- | ------------------------ | -------------------------------------------------------- | --- |
+| Q000 | BadQuotesInlineString    | Single quotes found but double quotes preferred          |     |
+| Q001 | BadQuotesMultilineString | Single quote multiline found but double quotes preferred |     |
+| Q002 | BadQuotesDocstring       | Single quote docstring found but double quotes preferred |     |
+| Q003 | AvoidQuoteEscape         | Change outer quotes to avoid escaping inner quotes       |     |
 
 ### flake8-annotations
 
 For more, see [flake8-annotations](https://pypi.org/project/flake8-annotations/2.9.1/) on PyPI.
 
-| Code | Name | Message | Fix |
-| ---- | ---- | ------- | --- |
-| ANN001 | MissingTypeFunctionArgument | Missing type annotation for function argument `...` |  |
-| ANN002 | MissingTypeArgs | Missing type annotation for `*...` |  |
-| ANN003 | MissingTypeKwargs | Missing type annotation for `**...` |  |
-| ANN101 | MissingTypeSelf | Missing type annotation for `...` in method |  |
-| ANN102 | MissingTypeCls | Missing type annotation for `...` in classmethod |  |
-| ANN201 | MissingReturnTypePublicFunction | Missing return type annotation for public function `...` |  |
-| ANN202 | MissingReturnTypePrivateFunction | Missing return type annotation for private function `...` |  |
-| ANN204 | MissingReturnTypeMagicMethod | Missing return type annotation for magic method `...` |  |
-| ANN205 | MissingReturnTypeStaticMethod | Missing return type annotation for staticmethod `...` |  |
-| ANN206 | MissingReturnTypeClassMethod | Missing return type annotation for classmethod `...` |  |
-| ANN401 | DynamicallyTypedExpression | Dynamically typed expressions (typing.Any) are disallowed in `...` |  |
+| Code   | Name                             | Message                                                            | Fix |
+| ------ | -------------------------------- | ------------------------------------------------------------------ | --- |
+| ANN001 | MissingTypeFunctionArgument      | Missing type annotation for function argument `...`                |     |
+| ANN002 | MissingTypeArgs                  | Missing type annotation for `*...`                                 |     |
+| ANN003 | MissingTypeKwargs                | Missing type annotation for `**...`                                |     |
+| ANN101 | MissingTypeSelf                  | Missing type annotation for `...` in method                        |     |
+| ANN102 | MissingTypeCls                   | Missing type annotation for `...` in classmethod                   |     |
+| ANN201 | MissingReturnTypePublicFunction  | Missing return type annotation for public function `...`           |     |
+| ANN202 | MissingReturnTypePrivateFunction | Missing return type annotation for private function `...`          |     |
+| ANN204 | MissingReturnTypeMagicMethod     | Missing return type annotation for magic method `...`              |     |
+| ANN205 | MissingReturnTypeStaticMethod    | Missing return type annotation for staticmethod `...`              |     |
+| ANN206 | MissingReturnTypeClassMethod     | Missing return type annotation for classmethod `...`               |     |
+| ANN401 | DynamicallyTypedExpression       | Dynamically typed expressions (typing.Any) are disallowed in `...` |     |
 
 ### flake8-2020
 
 For more, see [flake8-2020](https://pypi.org/project/flake8-2020/1.7.0/) on PyPI.
 
-| Code | Name | Message | Fix |
-| ---- | ---- | ------- | --- |
-| YTT101 | SysVersionSlice3Referenced | `sys.version[:3]` referenced (python3.10), use `sys.version_info` |  |
-| YTT102 | SysVersion2Referenced | `sys.version[2]` referenced (python3.10), use `sys.version_info` |  |
-| YTT103 | SysVersionCmpStr3 | `sys.version` compared to string (python3.10), use `sys.version_info` |  |
-| YTT201 | SysVersionInfo0Eq3Referenced | `sys.version_info[0] == 3` referenced (python4), use `>=` |  |
-| YTT202 | SixPY3Referenced | `six.PY3` referenced (python4), use `not six.PY2` |  |
-| YTT203 | SysVersionInfo1CmpInt | `sys.version_info[1]` compared to integer (python4), compare `sys.version_info` to tuple |  |
-| YTT204 | SysVersionInfoMinorCmpInt | `sys.version_info.minor` compared to integer (python4), compare `sys.version_info` to tuple |  |
-| YTT301 | SysVersion0Referenced | `sys.version[0]` referenced (python10), use `sys.version_info` |  |
-| YTT302 | SysVersionCmpStr10 | `sys.version` compared to string (python10), use `sys.version_info` |  |
-| YTT303 | SysVersionSlice1Referenced | `sys.version[:1]` referenced (python10), use `sys.version_info` |  |
+| Code   | Name                         | Message                                                                                     | Fix |
+| ------ | ---------------------------- | ------------------------------------------------------------------------------------------- | --- |
+| YTT101 | SysVersionSlice3Referenced   | `sys.version[:3]` referenced (python3.10), use `sys.version_info`                           |     |
+| YTT102 | SysVersion2Referenced        | `sys.version[2]` referenced (python3.10), use `sys.version_info`                            |     |
+| YTT103 | SysVersionCmpStr3            | `sys.version` compared to string (python3.10), use `sys.version_info`                       |     |
+| YTT201 | SysVersionInfo0Eq3Referenced | `sys.version_info[0] == 3` referenced (python4), use `>=`                                   |     |
+| YTT202 | SixPY3Referenced             | `six.PY3` referenced (python4), use `not six.PY2`                                           |     |
+| YTT203 | SysVersionInfo1CmpInt        | `sys.version_info[1]` compared to integer (python4), compare `sys.version_info` to tuple    |     |
+| YTT204 | SysVersionInfoMinorCmpInt    | `sys.version_info.minor` compared to integer (python4), compare `sys.version_info` to tuple |     |
+| YTT301 | SysVersion0Referenced        | `sys.version[0]` referenced (python10), use `sys.version_info`                              |     |
+| YTT302 | SysVersionCmpStr10           | `sys.version` compared to string (python10), use `sys.version_info`                         |     |
+| YTT303 | SysVersionSlice1Referenced   | `sys.version[:1]` referenced (python10), use `sys.version_info`                             |     |
 
 ### Ruff-specific rules
 
-| Code | Name | Message | Fix |
-| ---- | ---- | ------- | --- |
-| RUF001 | AmbiguousUnicodeCharacterString | String contains ambiguous unicode character '𝐁' (did you mean 'B'?) | 🛠 |
-| RUF002 | AmbiguousUnicodeCharacterDocstring | Docstring contains ambiguous unicode character '𝐁' (did you mean 'B'?) | 🛠 |
-| RUF003 | AmbiguousUnicodeCharacterComment | Comment contains ambiguous unicode character '𝐁' (did you mean 'B'?) |  |
+| Code   | Name                               | Message                                                                | Fix |
+| ------ | ---------------------------------- | ---------------------------------------------------------------------- | --- |
+| RUF001 | AmbiguousUnicodeCharacterString    | String contains ambiguous unicode character '𝐁' (did you mean 'B'?)    | 🛠   |
+| RUF002 | AmbiguousUnicodeCharacterDocstring | Docstring contains ambiguous unicode character '𝐁' (did you mean 'B'?) | 🛠   |
+| RUF003 | AmbiguousUnicodeCharacterComment   | Comment contains ambiguous unicode character '𝐁' (did you mean 'B'?)   |     |
 
 ### Meta rules
 
-| Code | Name | Message | Fix |
-| ---- | ---- | ------- | --- |
-| M001 | UnusedNOQA | Unused `noqa` directive | 🛠 |
+| Code | Name       | Message                 | Fix |
+| ---- | ---------- | ----------------------- | --- |
+| M001 | UnusedNOQA | Unused `noqa` directive | 🛠   |
 
 <!-- End auto-generated sections. -->
 
@@ -684,7 +686,7 @@ including:
 - [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
 - [`flake8-annotations`](https://pypi.org/project/flake8-annotations/)
 - [`flake8-comprehensions`](https://pypi.org/project/flake8-comprehensions/)
-- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) (20/32)
+- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) (21/32)
 - [`flake8-2020`](https://pypi.org/project/flake8-2020/)
 - [`pyupgrade`](https://pypi.org/project/pyupgrade/) (14/34)
 - [`autoflake`](https://pypi.org/project/autoflake/) (1/7)
@@ -708,7 +710,7 @@ Today, Ruff can be used to replace Flake8 when used with any of the following pl
 - [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
 - [`flake8-annotations`](https://pypi.org/project/flake8-annotations/)
 - [`flake8-comprehensions`](https://pypi.org/project/flake8-comprehensions/)
-- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) (20/32)
+- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) (21/32)
 - [`flake8-2020`](https://pypi.org/project/flake8-2020/)
 
 Ruff can also replace [`isort`](https://pypi.org/project/isort/), [`yesqa`](https://github.com/asottile/yesqa),
diff --git a/resources/test/fixtures/B019.py b/resources/test/fixtures/B019.py
new file mode 100644
index 00000000000000..55d32aa1bad6eb
--- /dev/null
+++ b/resources/test/fixtures/B019.py
@@ -0,0 +1,108 @@
+"""
+Should emit:
+B019 - on lines 73, 77, 81, 85, 89, 93, 97, 101
+"""
+import functools
+from functools import cache, cached_property, lru_cache
+
+
+def some_other_cache():
+    ...
+
+
+@functools.cache
+def compute_func(self, y):
+    ...
+
+
+class Foo:
+    def __init__(self, x):
+        self.x = x
+
+    def compute_method(self, y):
+        ...
+
+    @some_other_cache
+    def user_cached_instance_method(self, y):
+        ...
+
+    @classmethod
+    @functools.cache
+    def cached_classmethod(cls, y):
+        ...
+
+    @classmethod
+    @cache
+    def other_cached_classmethod(cls, y):
+        ...
+
+    @classmethod
+    @functools.lru_cache
+    def lru_cached_classmethod(cls, y):
+        ...
+
+    @classmethod
+    @lru_cache
+    def other_lru_cached_classmethod(cls, y):
+        ...
+
+    @staticmethod
+    @functools.cache
+    def cached_staticmethod(y):
+        ...
+
+    @staticmethod
+    @cache
+    def other_cached_staticmethod(y):
+        ...
+
+    @staticmethod
+    @functools.lru_cache
+    def lru_cached_staticmethod(y):
+        ...
+
+    @staticmethod
+    @lru_cache
+    def other_lru_cached_staticmethod(y):
+        ...
+
+    @functools.cached_property
+    def some_cached_property(self):
+        ...
+
+    @cached_property
+    def some_other_cached_property(self):
+        ...
+
+    # Remaining methods should emit B019
+    @functools.cache
+    def cached_instance_method(self, y):
+        ...
+
+    @cache
+    def another_cached_instance_method(self, y):
+        ...
+
+    @functools.cache()
+    def called_cached_instance_method(self, y):
+        ...
+
+    @cache()
+    def another_called_cached_instance_method(self, y):
+        ...
+
+    @functools.lru_cache
+    def lru_cached_instance_method(self, y):
+        ...
+
+    @lru_cache
+    def another_lru_cached_instance_method(self, y):
+        ...
+
+    @functools.lru_cache()
+    def called_lru_cached_instance_method(self, y):
+        ...
+
+    @lru_cache()
+    def another_called_lru_cached_instance_method(self, y):
+        ...
diff --git a/src/check_ast.rs b/src/check_ast.rs
index ede664ac3f9ac8..0caf1f9b6a1cad 100644
--- a/src/check_ast.rs
+++ b/src/check_ast.rs
@@ -346,6 +346,9 @@ where
                 if self.settings.enabled.contains(&CheckCode::B018) {
                     flake8_bugbear::plugins::useless_expression(self, body);
                 }
+                if self.settings.enabled.contains(&CheckCode::B019) {
+                    flake8_bugbear::plugins::cached_instance_method(self, decorator_list);
+                }
 
                 self.check_builtin_shadowing(name, Range::from_located(stmt), true);
 
diff --git a/src/checks.rs b/src/checks.rs
index f8e9a8e6019b21..325d9830691429 100644
--- a/src/checks.rs
+++ b/src/checks.rs
@@ -93,6 +93,7 @@ pub enum CheckCode {
     B016,
     B017,
     B018,
+    B019,
     B025,
     B026,
     // flake8-comprehensions
@@ -380,6 +381,7 @@ pub enum CheckKind {
     CannotRaiseLiteral,
     NoAssertRaisesException,
     UselessExpression,
+    CachedInstanceMethod,
     DuplicateTryBlockException(String),
     StarArgUnpackingAfterKeywordArg,
     // flake8-comprehensions
@@ -610,6 +612,7 @@ impl CheckCode {
             CheckCode::B016 => CheckKind::CannotRaiseLiteral,
             CheckCode::B017 => CheckKind::NoAssertRaisesException,
             CheckCode::B018 => CheckKind::UselessExpression,
+            CheckCode::B019 => CheckKind::CachedInstanceMethod,
             CheckCode::B025 => CheckKind::DuplicateTryBlockException("Exception".to_string()),
             CheckCode::B026 => CheckKind::StarArgUnpackingAfterKeywordArg,
             // flake8-comprehensions
@@ -841,6 +844,7 @@ impl CheckCode {
             CheckCode::B016 => CheckCategory::Flake8Bugbear,
             CheckCode::B017 => CheckCategory::Flake8Bugbear,
             CheckCode::B018 => CheckCategory::Flake8Bugbear,
+            CheckCode::B019 => CheckCategory::Flake8Bugbear,
             CheckCode::B025 => CheckCategory::Flake8Bugbear,
             CheckCode::B026 => CheckCategory::Flake8Bugbear,
             CheckCode::C400 => CheckCategory::Flake8Comprehensions,
@@ -1036,6 +1040,7 @@ impl CheckKind {
             CheckKind::CannotRaiseLiteral => &CheckCode::B016,
             CheckKind::NoAssertRaisesException => &CheckCode::B017,
             CheckKind::UselessExpression => &CheckCode::B018,
+            CheckKind::CachedInstanceMethod => &CheckCode::B019,
             CheckKind::DuplicateTryBlockException(_) => &CheckCode::B025,
             CheckKind::StarArgUnpackingAfterKeywordArg => &CheckCode::B026,
             // flake8-comprehensions
@@ -1388,6 +1393,11 @@ impl CheckKind {
             CheckKind::UselessExpression => {
                 "Found useless expression. Either assign it to a variable or remove it.".to_string()
             }
+            CheckKind::CachedInstanceMethod => {
+                "Use of `functools.lru_cache` or `functools.cache` on \
+                                            methods can lead to memory leaks."
+                    .to_string()
+            }
             CheckKind::DuplicateTryBlockException(name) => {
                 format!("try-except block with duplicate exception `{name}`")
             }
diff --git a/src/checks_gen.rs b/src/checks_gen.rs
index a45d6ed14c1023..daf0409ce075fa 100644
--- a/src/checks_gen.rs
+++ b/src/checks_gen.rs
@@ -53,6 +53,7 @@ pub enum CheckCodePrefix {
     B016,
     B017,
     B018,
+    B019,
     B02,
     B025,
     B026,
@@ -368,6 +369,7 @@ impl CheckCodePrefix {
                 CheckCode::B016,
                 CheckCode::B017,
                 CheckCode::B018,
+                CheckCode::B019,
                 CheckCode::B025,
                 CheckCode::B026,
             ],
@@ -388,6 +390,7 @@ impl CheckCodePrefix {
                 CheckCode::B016,
                 CheckCode::B017,
                 CheckCode::B018,
+                CheckCode::B019,
                 CheckCode::B025,
                 CheckCode::B026,
             ],
@@ -418,6 +421,7 @@ impl CheckCodePrefix {
                 CheckCode::B016,
                 CheckCode::B017,
                 CheckCode::B018,
+                CheckCode::B019,
             ],
             CheckCodePrefix::B010 => vec![CheckCode::B010],
             CheckCodePrefix::B011 => vec![CheckCode::B011],
@@ -427,6 +431,7 @@ impl CheckCodePrefix {
             CheckCodePrefix::B016 => vec![CheckCode::B016],
             CheckCodePrefix::B017 => vec![CheckCode::B017],
             CheckCodePrefix::B018 => vec![CheckCode::B018],
+            CheckCodePrefix::B019 => vec![CheckCode::B019],
             CheckCodePrefix::B02 => vec![CheckCode::B025, CheckCode::B026],
             CheckCodePrefix::B025 => vec![CheckCode::B025],
             CheckCodePrefix::B026 => vec![CheckCode::B026],
@@ -1134,6 +1139,7 @@ impl CheckCodePrefix {
             CheckCodePrefix::B016 => PrefixSpecificity::Explicit,
             CheckCodePrefix::B017 => PrefixSpecificity::Explicit,
             CheckCodePrefix::B018 => PrefixSpecificity::Explicit,
+            CheckCodePrefix::B019 => PrefixSpecificity::Explicit,
             CheckCodePrefix::B02 => PrefixSpecificity::Tens,
             CheckCodePrefix::B025 => PrefixSpecificity::Explicit,
             CheckCodePrefix::B026 => PrefixSpecificity::Explicit,
diff --git a/src/flake8_bugbear/plugins/cached_instance_method.rs b/src/flake8_bugbear/plugins/cached_instance_method.rs
new file mode 100644
index 00000000000000..df2c99b92dbaf5
--- /dev/null
+++ b/src/flake8_bugbear/plugins/cached_instance_method.rs
@@ -0,0 +1,32 @@
+use rustpython_ast::Expr;
+
+use crate::ast::helpers::compose_call_path;
+use crate::ast::types::Range;
+use crate::ast::types::ScopeKind;
+use crate::check_ast::Checker;
+use crate::checks::{Check, CheckKind};
+
+const CACHE_FUNCTIONS: [&str; 4] = [
+    "functools.lru_cache",
+    "functools.cache",
+    "lru_cache",
+    "cache",
+];
+
+pub fn cached_instance_method(checker: &mut Checker, decorator_list: &[Expr]) {
+    if matches!(checker.current_scope().kind, ScopeKind::Class(_)) {
+        for decorator in decorator_list {
+            if let Some(decorator_path) = compose_call_path(decorator) {
+                if decorator_path == "classmethod" || decorator_path == "staticmethod" {
+                    return;
+                }
+                if CACHE_FUNCTIONS.contains(&decorator_path.as_str()) {
+                    checker.add_check(Check::new(
+                        CheckKind::CachedInstanceMethod,
+                        Range::from_located(decorator),
+                    ));
+                }
+            }
+        }
+    }
+}
diff --git a/src/flake8_bugbear/plugins/mod.rs b/src/flake8_bugbear/plugins/mod.rs
index 2b2781690b8cda..186076725021e8 100644
--- a/src/flake8_bugbear/plugins/mod.rs
+++ b/src/flake8_bugbear/plugins/mod.rs
@@ -1,6 +1,7 @@
 pub use assert_false::assert_false;
 pub use assert_raises_exception::assert_raises_exception;
 pub use assignment_to_os_environ::assignment_to_os_environ;
+pub use cached_instance_method::cached_instance_method;
 pub use cannot_raise_literal::cannot_raise_literal;
 pub use duplicate_exceptions::{duplicate_exceptions, duplicate_handler_exceptions};
 pub use function_call_argument_default::function_call_argument_default;
@@ -19,6 +20,7 @@ pub use useless_expression::useless_expression;
 mod assert_false;
 mod assert_raises_exception;
 mod assignment_to_os_environ;
+mod cached_instance_method;
 mod cannot_raise_literal;
 mod duplicate_exceptions;
 mod function_call_argument_default;
diff --git a/src/linter.rs b/src/linter.rs
index 4350b1c47c8024..2351893f428911 100644
--- a/src/linter.rs
+++ b/src/linter.rs
@@ -342,6 +342,7 @@ mod tests {
     #[test_case(CheckCode::B016, Path::new("B016.py"); "B016")]
     #[test_case(CheckCode::B017, Path::new("B017.py"); "B017")]
     #[test_case(CheckCode::B018, Path::new("B018.py"); "B018")]
+    #[test_case(CheckCode::B019, Path::new("B019.py"); "B019")]
     #[test_case(CheckCode::B025, Path::new("B025.py"); "B025")]
     #[test_case(CheckCode::B026, Path::new("B026.py"); "B026")]
     #[test_case(CheckCode::C400, Path::new("C400.py"); "C400")]
diff --git a/src/snapshots/ruff__linter__tests__B019_B019.py.snap b/src/snapshots/ruff__linter__tests__B019_B019.py.snap
new file mode 100644
index 00000000000000..7980a548efb9b1
--- /dev/null
+++ b/src/snapshots/ruff__linter__tests__B019_B019.py.snap
@@ -0,0 +1,69 @@
+---
+source: src/linter.rs
+expression: checks
+---
+- kind: CachedInstanceMethod
+  location:
+    row: 78
+    column: 5
+  end_location:
+    row: 78
+    column: 20
+  fix: ~
+- kind: CachedInstanceMethod
+  location:
+    row: 82
+    column: 5
+  end_location:
+    row: 82
+    column: 10
+  fix: ~
+- kind: CachedInstanceMethod
+  location:
+    row: 86
+    column: 5
+  end_location:
+    row: 86
+    column: 22
+  fix: ~
+- kind: CachedInstanceMethod
+  location:
+    row: 90
+    column: 5
+  end_location:
+    row: 90
+    column: 12
+  fix: ~
+- kind: CachedInstanceMethod
+  location:
+    row: 94
+    column: 5
+  end_location:
+    row: 94
+    column: 24
+  fix: ~
+- kind: CachedInstanceMethod
+  location:
+    row: 98
+    column: 5
+  end_location:
+    row: 98
+    column: 14
+  fix: ~
+- kind: CachedInstanceMethod
+  location:
+    row: 102
+    column: 5
+  end_location:
+    row: 102
+    column: 26
+  fix: ~
+- kind: CachedInstanceMethod
+  location:
+    row: 106
+    column: 5
+  end_location:
+    row: 106
+    column: 16
+  fix: ~
+