Compare commits

..

400 Commits

Author SHA1 Message Date
konsti
e87032d57d Explain check_docs_formatted.py error message (#6125)
## Summary

This is an error message only change to lead an implementor of a new
rule that has an unformatted or invalid bad example to the
right code.

## Test Plan

n/a
2023-07-27 15:53:52 +02:00
Charlie Marsh
0c2abf8316 Avoid walking past root when resolving imports (#6126)
## Summary

Noticed in #5954: we walk _past_ the root rather than stopping _at_ the
root when attempting to traverse along the parent path. It's effectively
an off-by-one bug.
2023-07-27 13:38:51 +00:00
konsti
6e85e0010f Fix windows test warnings (#6124)
See
https://github.com/astral-sh/ruff/actions/runs/5679922286/job/15392998698.
These didn't fail CI because we run clippy on linux only.
2023-07-27 14:03:53 +02:00
Micha Reiser
2ea0a0b28a Respect indent when measuring with MeasureMode::AllLines (#6120) 2023-07-27 13:34:36 +02:00
konsti
10abc8fd92 Unbreak main (#6123)
This fixes main breaking due to two merges.
2023-07-27 11:32:55 +00:00
konsti
61195bc7b1 Don't format trailing comma for lambda arguments (#5946)
**Summary** lambda arguments don't have parentheses, so they shouldn't
get a magic trailing comma either. This fixes some unstable formatting

**Test Plan** Added a regression test.

89 (from previously 145) instances of unstable formatting remaining.

```
$ cargo run --bin ruff_dev --release -- format-dev --stability-check --error-file formatter-ecosystem-errors.txt --multi-project target/checkouts > formatter-ecosystem-progress.txt
$ rg "Unstable formatting" target/formatter-ecosystem-errors.txt | wc -l
89
```

Closes #5892
2023-07-27 12:32:10 +02:00
Micha Reiser
9040554be5 Merge pull request #6119 from astral-sh/preserve-history 2023-07-27 12:12:57 +02:00
Micha Reiser
06ba1cf0ad Move RustPython Files 2023-07-27 11:46:18 +02:00
Micha Reiser
7a63a8f45a Merge branch 'main' of ../parser-move into preserve-history 2023-07-27 11:39:22 +02:00
Micha Reiser
c1bc0dc83d Delete RustPython files 2023-07-27 11:39:14 +02:00
Micha Reiser
90573975f3 Delete more unused files 2023-07-27 11:32:12 +02:00
Micha Reiser
802df97d55 Fix clippy issues 2023-07-27 11:31:10 +02:00
Micha Reiser
6f4ee32807 Move files to common directory 2023-07-27 11:27:37 +02:00
Zanie Blue
7ec979d022 Fix TypeAliasName to store name instead of load (#42)
Copies the fix in https://github.com/RustPython/Parser/pull/99
2023-07-26 17:53:34 -05:00
Micha Reiser
fab9cc5294 Remove parser ast re-export (#41) 2023-07-26 14:54:29 +02:00
Micha Reiser
ff7bab589e Prepare for moving the parser into the Ruff monorepo (#40) 2023-07-26 12:28:43 +02:00
Micha Reiser
593b46be5e perf: Cursor based lexer (#38) 2023-07-26 07:50:45 +02:00
David Szotten
13196fc500 fix the ranges of constants inside f-strings (#33) 2023-07-25 19:17:06 +02:00
Dhruv Manilawala
5ef4ccd632 Add line magic stmt and expr AST nodes (#31)
This PR introduces two new nodes for the parser to recognize the
`MagicCommand` tokens:

* `StmtLineMagic` for statement position i.e., magic commands on their
own line:
	```python
	%matplotlib inline
	!pwd
	```

* `ExprLineMagic` for expression position i.e., magic commands in an
assignment statement:
	```python
	# Only `?` and `!` are valid in this position
	dir = !pwd
	```

Both nodes are identical in their structure as in it contains the magic
kind and the command value as `String`.
2023-07-24 21:20:31 +05:30
Dhruv Manilawala
e363fb860e Lex Jupyter Magic in assignment value position (#30)
Emit `MagicCommand` token when it is the assignment value[^1] i.e., on
the right side of an assignment statement.

Examples:
```python
pwd = !pwd
foo = %timeit a = b
bar = %timeit a % 3
baz = %matplotlib \
        inline"
```

[^1]: Only `%` and `!` are valid in that position, other magic kinds are
not valid
2023-07-24 17:44:03 +05:30
Chris Pryer
4888d800fb Fix BoolOp python.org link (#37)
Noticed https://docs.python.org/3/library/ast.html#ast.boolop wasn't
working.
2023-07-22 14:10:59 -04:00
konsti
4d03b9b5b2 Fix empty lambda args range (#35)
Previously, empty lambda arguments (e.g. `lambda: 1`) would get the
range of the entire expression, which leads to incorrect comment
placement. Now empty lambda arguments get an empty range between the
`lambda` and the `:` tokens.
2023-07-21 17:41:19 +02:00
Zanie Blue
fb365c642b Implement Ranged for TypeParam (#32)
Required for https://github.com/astral-sh/ruff/pull/5927
2023-07-20 20:03:17 -04:00
konsti
db04fd4157 Refactor if statement, introduce nodes for elif and else (#22)
The old if layout couldn't differentiate between an else block with a
single if statement and an elif statement. Additionally we getting rid
of the recursion in favor of a single if struct with a vec of elif/else
children. This is accompanied by a big refactoring in ruff which removes
a bunch of TODOs and false negatives.
2023-07-18 13:14:40 +02:00
konstin
ff3e8ead36 Revert "snapshot"
This reverts commit 1bbec2b967.
2023-07-18 11:09:43 +02:00
konstin
1bbec2b967 snapshot 2023-07-18 11:08:14 +02:00
Dhruv Manilawala
510be51cfa Fix Jupyter Magic test snapshot (#29)
Sorry for the churn, I should've rebased and merged the original PR to
avoid this.
2023-07-17 23:06:53 -05:00
Dhruv Manilawala
3b4c8fffe5 Lex Jupyter line magic with Mode::Jupyter (#23)
Lex Jupyter line magic with `Mode::Jupyter`

This PR adds a new token `MagicCommand`[^1] which the lexer will
recognize when in `Mode::Jupyter`. The rules for the lexer is as
follows:
1. Given that we are at the start of line, skip the indentation and look
for [characters that represent the start of a magic
command](635815e8f1/IPython/core/inputtransformer2.py (L335-L346)),
determine the magic kind and capture all the characters following it as
the command string.
2. If the command extends multiple lines, the lexer will skip the line
continuation character (`\`) but only if it's followed by a newline
(`\n` or `\r`). The reason to skip this only in case of newline is
because they can occur in the command string which we should not skip:

	```rust
    //        Skip this backslash
    //        v
    //   !pwd \
    //      && ls -a | sed 's/^/\\    /'
    //                          ^^
    //                          Don't skip these backslashes
	```

3. The parser, when in `Mode::Jupyter`, will filter these tokens before
the parsing begins. There is a small caveat when the magic command is
indented. In the following example, when the parser filters out magic
command, it'll throw an indentation error:

	```python
	for i in range(5):
		!ls

	# What the parser will see
	for i in range(5):
	
	```

[^1]: I would prefer to have some other name as this not only represent
a line magic (`%`) but also shell command (`!`), help command (`?`) and
others. In original implementation, it's named as ["IPython
Syntax"](635815e8f1/IPython/core/inputtransformer2.py (L332))
2023-07-18 09:24:24 +05:30
Zanie
126652b684 Fix decorator ranges
Incorrectly merged LALRPOP file
2023-07-17 14:49:14 -05:00
Zanie
57e8712d76 Bump expected size of Stmt to 168 bytes 2023-07-17 14:49:14 -05:00
Zanie
78c6ede1c9 Format 2023-07-17 14:49:14 -05:00
Zanie Blue
a843a00f6b Add parsing of type alias statements i.e. the type keyword (#97)
Extends #95
Closes #82

Adds parsing of new `type` soft keyword for defining type aliases.

Supports type alias statements as defined in PEP 695 e.g.

```python
type IntOrStr = int | str

type ListOrSet[T] = list[T] | set[T]

type AnimalOrVegetable = Animal | "Vegetable"

type RecursiveList[T] = T | list[RecursiveList[T]]
```

All type parameter kinds are supported as in #95.

Builds on soft keyword abstractions introduced in https://github.com/RustPython/RustPython/pull/4519
2023-07-17 14:49:14 -05:00
Zanie Blue
f846f1ea42 Parse type parameters in function definitions (#96)
* Parse type parameters in function definitions
* Add test for combined items
2023-07-17 14:49:14 -05:00
Zanie
1d4b7a395f Consolidate tests and add coverage for trailing comma 2023-07-17 14:49:14 -05:00
Zanie
ce3ce0734b Add bound to test case test_parse_class_with_all_possible_generic_types 2023-07-17 14:49:14 -05:00
Zanie
b0e119f049 Add test for tuple bounds 2023-07-17 14:49:14 -05:00
Zanie
1f5e707829 Remove test for empty generic class Foo[]: ...
Not valid syntax
2023-07-17 14:49:14 -05:00
Zanie
ed7acfe477 Parse type parameters in class definitions 2023-07-17 14:49:14 -05:00
Zanie
c31b58eb39 Move type_param stubs into LALRPOP definition 2023-07-17 14:49:14 -05:00
Zanie
c0a3a20c63 Bump size assertion for Stmt from 136 to 160 bytes 2023-07-17 14:49:14 -05:00
Zanie
05ae26b935 Regenerate code with latest ASDL 2023-07-17 14:49:14 -05:00
David Szotten
b996b21ffc tuple constants are for optimisations, not source (#28)
my reading of https://docs.python.org/3/library/ast.html#ast.unparse and
https://discuss.python.org/t/ast-constant-value-tuple-s-and-frozenset-s/22578
is that tuple constants cannot come from parsing python source, they are
only for optimised bytecode

see also https://github.com/astral-sh/ruff/pull/5812
2023-07-17 15:46:37 -04:00
konsti
2d1f69cbb9 Remove asdl (#21)
This removes the ASDL code generation in favor of handwriting the AST. 

The motivations for moving away from the ASDL are:

* CPython compatibility is no longer a goal
* The ASDL grammar isn't as expressive as we would like
* The codegen scripts have a high complexity which makes extensions time
consuming
* We don't make heavy use of code generation (compared to e.g.
RustPython that generates Pyo3 bindings, a fold implementation etc).

We may want to revisit a grammar based code generation in the future,
e.g. by using [ungrammar](https://github.com/rust-analyzer/ungrammar)
2023-07-05 14:25:26 +02:00
konsti
0f2e295f3a impl Ranged for TextRange (#20)
This adds the missing implementation of `Ranged` for `TextRange` itself

```rust
impl Ranged for TextRange {
    fn range(&self) -> TextRange {
        *self
    }
}
```

This allows e.g. using `has_comments` with arbitrary ranges instead of
just a node.

It also adds .venv to the .gitignore
2023-07-02 10:11:06 +02:00
konstin
c174bbf1f2 impl<T> Ranged for &T where T: Ranged (#16)
In the example below, `arg` is `&Expr`, so `&Ranged`, but `entries()`
want a `T: Ranged`. This adds the missing bridge impl.

```rust
        let all_args = format_with(|f| {
            f.join_comma_separated()
                .entries(
                    // We have the parentheses from the call so the arguments never need any
                    args.iter()
                        .map(|arg| (arg, arg.format().with_options(Parenthesize::Never))),
                )
                .nodes(keywords.iter())
                .finish()
        });
```
2023-06-26 13:55:35 +02:00
Micha Reiser
8078663b6c Remove Range type parameter from AST nodes (#15) 2023-06-23 21:18:55 +01:00
Micha Reiser
f60e204b73 Fix range of keyword identifier (#14) 2023-06-22 10:00:25 +02:00
Micha Reiser
08ebbe40d7 Fix ArgWithDefault TextRange (#13) 2023-06-20 18:21:09 +02:00
Charlie Marsh
ed3b4eb72b Add TextRange to Identifier (#8)
## Summary

This PR adds `TextRange` to `Identifier`. Right now, the AST only
includes ranges for identifiers in certain cases (`Expr::Name`,
`Keyword`, etc.), namely when the identifier comprises an entire AST
node. In Ruff, we do additional ad-hoc lexing to extract identifiers
from source code.

One frequent example: given a function `def f(): ...`, we lex to find
the range of `f`, for use in diagnostics.

Another: `except ValueError as e`, for which the AST doesn't include a
range for `e`.

Note that, as an optimization, we avoid storing the `TextRange` for
`Expr::Name`, since it's already included.
2023-06-20 11:19:27 -04:00
Charlie Marsh
f0d200c8a1 Remove fold, unparse, and location features (#9) 2023-06-19 17:26:17 -04:00
Charlie Marsh
8d74eee750 Make malachite-bigint an optional dependency for rustpython-format (#12) 2023-06-19 16:31:25 -04:00
Charlie Marsh
21aa0b8d84 Optimize validate_arguments (#10) 2023-06-19 15:32:58 -04:00
Charlie Marsh
9cb00518e5 Update snapshot tests 2023-06-19 13:16:07 -04:00
Micha Reiser
6f65c5cba7 Add Decorator node (#7) 2023-06-19 12:53:05 -04:00
Micha Reiser
8a415fa61e Include argument parentheses in range (#5) 2023-06-19 12:51:18 -04:00
Micha Reiser
5054cbe84f Merge branch 'RustPython:main' into main 2023-06-19 12:38:24 -04:00
Jeong, YunWon
69d27d924c Rename unconventional nodes (#74) 2023-06-17 01:54:00 +09:00
Jeong, YunWon
5270020423 rustpython_ast python package (#79) 2023-06-17 01:32:27 +09:00
yt2b
edcfcb4a74 Fix bool format (#91)
* Fix format_bool

* Add test_format_bool
2023-06-15 14:56:03 +09:00
Jeong, YunWon
40a603208f Add Pylyzer
done by https://github.com/mtshiba/pylyzer/pull/37
2023-06-11 21:56:59 +09:00
Jeong, YunWon
b2f95e2848 Fix LinearLocator \r handling (#80) 2023-06-02 22:35:53 +09:00
Steve Shi
a2e3209c42 Replace num-bigint with malachite-bigint (#18)
Co-authored-by: Jeong YunWon <jeong@youknowone.org>
2023-06-02 17:06:18 +09:00
Jeong, YunWon
5e9e8a7589 Linear Locator (#46) 2023-06-01 13:53:31 +09:00
Jeong, YunWon
fdec727f80 New Arguments and Arg/ArgWithDefault AST representation (#59) 2023-06-01 01:15:23 +09:00
Jeong, YunWon
3fbf4f6804 Parse for expr and stmt variants + identifier, constant (#78) 2023-05-31 20:03:46 +09:00
Micha Reiser
fe25708d89 Merge pull request #76 from astral-sh/match-case-end-location 2023-05-31 09:48:42 +02:00
Micha Reiser
342cd19f50 Add safety comment 2023-05-31 09:37:48 +02:00
Micha Reiser
4a2c4aad0b Align MatchCase end location 2023-05-30 17:31:51 +02:00
Jeong, YunWon
ae3a477c97 Add parser deps to rustpython_ast_pyo3 (#75) 2023-05-29 16:48:05 +09:00
Jeong, YunWon
531aeb3511 Cmpop::as_str (#72)
* clean up pyo3 generation

* Cmpop::as_str
2023-05-29 01:53:54 +09:00
Jeong, YunWon
4de0cb1827 Parse Trait (#71) 2023-05-28 21:03:27 +09:00
Micha Reiser
5493c9f4e3 Avoid removing elements from the beginning of a vec (#69) 2023-05-25 19:44:49 +09:00
Jeong, YunWon
d23611db65 Merge pull request #65 from youknowone/refactor-pyo3
to_pyo3_ast to return &'py + Separate rustpython_ast_pyo3
2023-05-23 03:03:01 +09:00
Jeong YunWon
7d384d88d0 Separate rustpython_ast_pyo3 2023-05-23 02:50:12 +09:00
Jeong YunWon
b81273e9bc to_pyo3_ast to return &'py 2023-05-23 00:12:22 +09:00
Micha Reiser
335780aeea Merge pull request #2 from astral-sh/include-decorators-in-class-and-func-range 2023-05-22 13:46:38 +02:00
Jeong, YunWon
e1f02fced7 Fix Vec::to_pyo3_ast (#63) 2023-05-22 15:15:05 +09:00
Jeong, YunWon
d4084fb17a impl From<T> for ast::Ast (#62) 2023-05-21 22:44:07 +09:00
Micha Reiser
41a0ef8740 Add test 2023-05-19 10:14:46 +02:00
Micha Reiser
25cc1da319 Include decorators in Class and FunctionDef range 2023-05-19 08:18:43 +02:00
Micha Reiser
33a3c407a9 Merge pull request #60 from astral-sh/reduce-copy 2023-05-19 08:16:28 +02:00
Micha Reiser
fac0c25343 Update python.rs 2023-05-19 08:08:35 +02:00
Micha Reiser
4ff779c298 Update parser/src/python.lalrpop
Co-authored-by: Jeong, YunWon <69878+youknowone@users.noreply.github.com>
2023-05-18 22:32:05 +02:00
Micha Reiser
726884b287 Avoid allocating vecs for each statement 2023-05-18 21:56:17 +02:00
Micha Reiser
851f23668f Use named parameters to reducue copying 2023-05-18 21:41:06 +02:00
Jeong, YunWon
3654cf0bdf Add Ast as top level enum (#58) 2023-05-19 01:53:39 +09:00
Jeong, YunWon
6c5c311bab Fix range field order (#56)
* Skip validate_arguments when empty

* Fix `range` field order

* Fix unused variable
2023-05-18 21:44:01 +09:00
Jeong, YunWon
531e41ae2c Upgrade bitflags to remove clippy wanrings (#54)
* Upgrade bitflags to remove clippy wanrings

* Fix python lint warnings
2023-05-18 01:50:00 +09:00
Jeong, YunWon
b48834fe2d More flexible map_user and fold for new constructor nodes (#53)
* make fold.rs file

* Split user_map steps

* Fold for new constructor nodes
2023-05-18 00:16:04 +09:00
Jeong, YunWon
205ee80033 README (#47) 2023-05-17 18:01:00 +09:00
Jeong, YunWon
fc301ab1b0 Fix build (#45)
* remove dedent

* Revert parser-pyo3 crate
2023-05-16 23:54:49 +09:00
Jeong, YunWon
e820928f11 Add experimental pyo3-wrapper feature (#41)
* Fix pyo3 unit type value error

* Add experimental pyo3-wrapper feature

* location support
2023-05-16 23:45:31 +09:00
Jeong, YunWon
ff17f6e178 Add utilities to enum (#44)
* Add utilities to enum

* Fix unexpected pyo3 dependency propagation
2023-05-16 23:29:49 +09:00
Jeong YunWon
3bdf8a940a Hash for ConversionFlag 2023-05-16 22:56:17 +09:00
Jeong, YunWon
9d47d3d212 specialize ConversionFlag (#42)
* specialize ConversionFlag

* Change value of ConversionFlag to i8 and None to -1

* is_* methods to ConversionFlag
2023-05-16 22:52:50 +09:00
Jeong, YunWon
611dcc2e9b rustpython_ast + pyo3 (#25) 2023-05-16 18:06:54 +09:00
Jeong, YunWon
53de75efc3 Add all node classes to ast module (#40) 2023-05-16 16:55:26 +09:00
Jeong, YunWon
0c7d16b61a Add is_* methods to Tok (#39) 2023-05-16 16:08:25 +09:00
Jeong, YunWon
735c06d5f4 Fix full-lexer feature (#38) 2023-05-16 15:45:03 +09:00
Jeong, YunWon
02f13abf50 Add Node trait for node type information (#31) 2023-05-16 15:41:30 +09:00
Charlie Marsh
10dda125ff Always emit non-logical newlines for 'empty' lines (#27) 2023-05-15 14:13:05 -04:00
Jeong, YunWon
27e3873dc2 Add full-lexer feature (#36) 2023-05-16 02:21:34 +09:00
Micha Reiser
dd4cc25227 Reduce copying elements when parsing (#35) 2023-05-15 23:20:44 +09:00
Jeong, YunWon
b78001c953 Generic types to generic (#30) 2023-05-15 20:22:42 +09:00
Jeong, YunWon
718354673e Move range to node (#23)
* black + clippy

* Fix module generation
2023-05-15 16:20:22 +09:00
Micha Reiser
192379cede Move range from Attributed to Nodes (#22)
* Move `range` from `Attributed` to `Node`s

* No Attributed + custom for Range PoC

* Generate all located variants, generate enum implementations

* Implement `Copy` on simple enums

* Move `Suite` to `ranged` and `located`

* Update tests
---------

Co-authored-by: Jeong YunWon <jeong@youknowone.org>
2023-05-15 15:08:12 +09:00
Micha Reiser
a983f4383f Add format and cformat modules from RustPython (#24)
* Add `format` and `cformat` modules from `RustPython`

* Introduce `rustpython-format` crate

* Remove unused dependencies
2023-05-12 18:27:05 +09:00
Jeong, YunWon
947fb53d0b Field accessor and utilities (#20)
* Apply is-macro to Constant and ast nodes
2023-05-11 19:44:20 +09:00
Jeong, YunWon
2baad9ead6 Merge pull request #19 from RustPython/identifier
A few more Identifier utilities
2023-05-11 16:12:46 +09:00
Jeong YunWon
2af9805662 A few more Identifier utilities 2023-05-11 16:05:11 +09:00
Jeong, YunWon
5b2af304a2 Merge pull request #17 from youknowone/ruff
ruff integration support
2023-05-11 04:42:52 +09:00
Jeong YunWon
cbe4e8c5f3 Make parser location optional 2023-05-11 04:40:10 +09:00
Jeong YunWon
aabc96dde9 ruff integration support 2023-05-11 04:08:57 +09:00
Jeong YunWon
99e108dd53 *Escape::with_preferred_quote 2023-05-10 21:51:01 +09:00
Jeong YunWon
d6b6df5d1c ast::Int::to_usize 2023-05-10 21:31:02 +09:00
Jeong YunWon
822cac5aa0 parse_expression{=>_starts}_at 2023-05-10 21:25:58 +09:00
Jeong YunWon
aa101e4f26 fix ImportDots 2023-05-10 20:31:04 +09:00
Jeong, YunWon
cbc4cb286b Merge pull request #16 from youknowone/ast-int
Give identifier and int ast types
2023-05-10 20:19:01 +09:00
Jeong YunWon
6fa3d0f90a Fine-tune int types 2023-05-10 19:33:39 +09:00
Jeong YunWon
455bcc01a0 Give identifier and int ast types 2023-05-10 19:33:39 +09:00
Jeong, YunWon
d495cd9129 Merge pull request #15 from youknowone/spellchecker
Setup spell checker
2023-05-10 18:11:40 +09:00
Jeong YunWon
d8822d1091 spell check ast/asdl_rs.py 2023-05-10 18:05:39 +09:00
Jeong YunWon
75f6ce1ae5 Setup spell checker 2023-05-10 17:57:15 +09:00
Jeong, YunWon
41e9e7280a Merge pull request #12 from youknowone/visitor
Visitor
2023-05-10 17:52:25 +09:00
Jeong YunWon
17c8abcec1 asdl_rs.py to multiple file output 2023-05-10 17:41:01 +09:00
Jeong YunWon
e000b1c304 Remove redundant types 2023-05-10 17:15:01 +09:00
Jeong YunWon
243ca16b34 Fix visitor to fit in new structure 2023-05-10 17:14:44 +09:00
Joshua
4de9580b92 Generate a visitor trait to ast_gen.rs 2023-05-10 17:14:34 +09:00
Jeong, YunWon
5cf85f0b9d Merge pull request #13 from youknowone/locator
Python location transform and related refactoring
2023-05-10 17:04:30 +09:00
Jeong YunWon
4dc030ba9d Vendor SourceLocation from ruff 2023-05-10 17:00:12 +09:00
Jeong YunWon
1d366d52ab Let located only for python located stuff 2023-05-10 14:35:38 +09:00
Jeong YunWon
a3d9d8cb14 numerous refactoring
- Split parser core and compiler core. Fix #14
- AST int type to `u32`
- Updated asdl_rs.py and update_asdl.sh fix #6
- Use `ruff_python_ast::SourceLocation` for Python source location. Deleted our own Location.
- Renamed ast::Located to ast::Attributed to distinguish terms for TextSize and SourceLocation
- `ast::<Node>`s for TextSize located ast. `ast::located::<Node>` for Python source located ast.
- And also strictly renaming `located` to refer only python location related interfaces.
- `SourceLocator` to convert locations.
- New `source-code` features of to disable python locations when unnecessary.
- Also including fully merging https://github.com/astral-sh/RustPython/pull/4 closes #9
2023-05-10 14:35:38 +09:00
Jeong YunWon
09a6afdd04 Adapt SourceLocation 2023-05-09 20:34:48 +09:00
Jeong YunWon
a14e43e03a Separate byteoffset ast and located ast 2023-05-09 00:21:52 +09:00
Jeong YunWon
f47dfca4e3 Rename compiler Location to TextSize 2023-05-08 03:38:10 +09:00
Micha Reiser
58c35ab458 Replace row/column based Location with byte-offsets. 2023-05-08 03:38:10 +09:00
Jeong, YunWon
7b8844bd3e Merge pull request #11 from youknowone/refactor-asdl
Refactor ast to hold data as seperated type
2023-05-07 19:29:02 +09:00
Jeong YunWon
6d7358090b Refactor ast to hold data as seperated type 2023-05-07 19:20:47 +09:00
Jeong YunWon
9f1a538eba gitattribute 2023-05-07 17:20:52 +09:00
Jeong, YunWon
48920a034e Merge pull request #10 from youknowone/remove-compile-error
Remove CompileError
2023-05-06 17:20:44 +09:00
Jeong YunWon
13d6e275ef Remove CompileError 2023-05-06 17:15:18 +09:00
Jeong, YunWon
96eb80f5cf Merge pull request #8 from youknowone/python-lint
Add python lint
2023-05-06 14:42:15 +09:00
Jeong YunWon
a73bac9ed1 Add python lint 2023-05-06 14:34:31 +09:00
Jeong, YunWon
6b60f85cc4 Merge pull request #7 from youknowone/lalrpop
Embed generated parser + update lalrpop
2023-05-06 13:57:15 +09:00
Jeong YunWon
39b2dbe04d Update lalrpop to 0.20.0 2023-05-06 13:48:20 +09:00
Jeong YunWon
e1f70100ac Update parser/build.rs to embed python.rs 2023-05-06 05:28:30 +09:00
Jeong, YunWon
d66d935879 Merge pull request #5 from youknowone/ci
Add CI
2023-05-06 03:42:28 +09:00
Jeong YunWon
e28f333f23 Add CI 2023-05-06 03:39:20 +09:00
Jeong YunWon
0adcdd995d Remove unused workspace dependencies 2023-05-06 00:44:34 +09:00
Jeong YunWon
e6eb49ffb0 Set up workspace - Forked from
git@github.com:RustPython/RustPython.git ff5076b12c075b3e87c0ac2971e390b4a209d14f
2023-05-05 23:49:50 +09:00
Jeong YunWon
80109b1fe0 rustpython-literal 2023-05-05 21:49:12 +09:00
Jeong YunWon
bd64603950 Refactor common::bytes::repr using common::escape 2023-05-03 16:08:48 +09:00
Jeong YunWon
5b0e92d725 Refactor common::str::repr using common::escape 2023-05-03 13:47:04 +09:00
Micha Reiser
ae9d3c3193 Add Located::start, Located::end and impl Deref 2023-04-26 10:24:34 -06:00
Micha Reiser
3873414b30 Use Located::new over struct initializer 2023-04-25 18:10:13 -06:00
Jeong YunWon
a9f4b59f40 module objects' type as PyModule 2023-04-20 20:48:57 +09:00
Jeong YunWon
6d6155413b Update lalrpop 2023-03-25 22:18:02 +09:00
Jeong YunWon
f9b5469642 Update cspell for compiler 2023-03-16 22:39:09 +09:00
Charlie Marsh
ef38eb6b1a Include Derive feature with optional Serde dependency 2023-03-12 14:46:10 -04:00
Jeong YunWon
0ea53825db Merge pull request #4608 from coolreader18/bag-deser
Rework frozen modules and directly deserialize to CodeObject<Literal>
2023-03-10 05:19:23 +09:00
Noa
9d6ae774f8 Rework frozen modules and directly deserialize to CodeObject<Literal> 2023-03-06 13:45:33 -06:00
Charlie Marsh
55fc0e83f3 Treat match and case as soft keywords in lambda assignments (#4623) 2023-03-04 12:42:05 -05:00
Charlie Marsh
713dd2b91e Add optional serde dependency 2023-03-02 15:38:40 -05:00
Noa
969ea23d67 Address review comments 2023-03-01 21:11:59 -06:00
Noa
b80bbec8e6 Custom marshal enc/decoding impl 2023-03-01 20:47:21 -06:00
DimitrisJim
8f425e9ce2 Use insta to verify values. 2023-02-28 20:00:47 +09:00
Jeong YunWon
d7e2e7361e Use git version of unicode_names2 to avoid alias search failure 2023-02-28 20:00:47 +09:00
Charlie Marsh
c37e0c7f03 Use proper locations for sub-expressions in constant matches 2023-02-26 23:10:38 -05:00
Charlie Marsh
0d7b94817d Allow type variable tuple for *args 2023-02-23 08:39:59 -05:00
Jeong YunWon
f43e5b72e2 Merge pull request #4552 from charliermarsh/charlie/loc
Limit match range to end of last statement
2023-02-23 02:11:42 +09:00
Jeong YunWon
a19f294b0c Merge pull request #4543 from youknowone/flatten-parser
Flatten parser interface
2023-02-23 02:09:46 +09:00
Charlie Marsh
7ebef61c47 Limit match range to end of last statement 2023-02-22 11:25:50 -05:00
Charlie Marsh
b61f4d7b69 Allow trailing commas in MappingPattern 2023-02-22 10:02:41 -05:00
Jeong YunWon
e26369a34e use super::* from tests submodules 2023-02-22 21:01:39 +09:00
Jeong YunWon
97a08ee77b remove #[macro_use] 2023-02-22 20:41:27 +09:00
Jeong YunWon
cb8c6fb78d Flatten rustpython_parser interface 2023-02-22 20:32:31 +09:00
Jeong YunWon
8580e4ebb5 make_tokenizer -> lex to integrate terms
we don't distinguish scanner or tokenizer from lexer
2023-02-22 20:28:15 +09:00
Jeong YunWon
39fc23cf92 relocate feature-independent use 2023-02-22 20:28:03 +09:00
Jeong YunWon
66e3080173 Fix ModeParseError message 2023-02-22 20:28:03 +09:00
Jeong YunWon
1511b6631b Break down rustpython_parser::error module
because it doesn't share any common errors but specific error for each sub module
2023-02-22 20:28:01 +09:00
Charlie Marsh
2a8aa6f308 Always wrap in SoftKeywordTransformer 2023-02-21 19:18:42 -05:00
Charlie Marsh
dc628cab8f Expose SoftKeywordTransformer on public API 2023-02-21 19:00:32 -05:00
Jim Fasarakis-Hilliard
c137bc9d77 Merge pull request #4519 from charliermarsh/charlie/match
Add support for match statements to parser
2023-02-21 19:43:28 +02:00
Jeong YunWon
60180fd54c Merge pull request #4531 from charliermarsh/charlie/exception-groups
Implement except* syntax
2023-02-21 13:20:18 +09:00
Charlie Marsh
c7ed645cc6 Implement except* syntax 2023-02-21 12:19:54 +09:00
Charlie Marsh
8caa28f0f8 Update Python.asdl from CPython 3.11.1 2023-02-21 12:19:51 +09:00
Charlie Marsh
8aa3bc93f3 Allow starred expressions in subscripts 2023-02-20 17:59:35 -05:00
Charlie Marsh
f39ffef370 Update compiler/parser/src/soft_keywords.rs
Co-authored-by: Jim Fasarakis-Hilliard <d.f.hilliard@gmail.com>
2023-02-20 15:03:39 -05:00
Jeong YunWon
e093d2bee6 clean up soft-keyword transform 2023-02-20 15:03:39 -05:00
Charlie Marsh
ca5b474d45 Use muiltipeek 2023-02-20 15:03:39 -05:00
Charlie Marsh
2b43d45bd5 Add support for match statements to parser 2023-02-20 15:03:39 -05:00
John Pham
4bdc2d47c1 Make common::repr throw error instead of panic (#4520) 2023-02-19 22:09:54 +09:00
Jeong YunWon
09b82e41d5 Merge pull request #4490 from DimitrisJim/function_parser
Add tests, some comments, to function.rs.
2023-02-13 20:00:35 +09:00
Jeong YunWon
cc6d8a1c58 Merge pull request #4492 from DimitrisJim/doc_parser_uno
Document parser crate.
2023-02-13 17:25:14 +09:00
Dimitris Fasarakis Hilliard
07918f0a9a Document parser crate. 2023-02-12 17:58:19 +02:00
Dimitris Fasarakis Hilliard
a0786ea872 Add tests, some comments, to function.rs. 2023-02-11 23:07:57 +02:00
Dimitris Fasarakis Hilliard
4713b2b3ab Refactor: Join string and string_parser. 2023-02-11 18:05:06 +02:00
Jeong YunWon
e7f14ab9b8 Add test_generator_expression_argument 2023-02-11 05:20:39 +09:00
Charlie Marsh
56c73cc63d Use entire range for generators-as-arguments 2023-02-10 10:39:40 -05:00
Dimitris Fasarakis Hilliard
659f4dd8bf Document lexer. 2023-02-07 21:43:57 +02:00
Dimitris Fasarakis Hilliard
bd158089e0 Move NewLineHandler inline, don't check each character twice. 2023-02-07 20:58:53 +02:00
Jeong YunWon
a73bee7aae use workspace dependencies 2023-02-06 15:30:38 +09:00
Dimitris Fasarakis Hilliard
1468fe46ab Hint that the unwrap should always succeed. 2023-02-01 12:17:33 +02:00
Dimitris Fasarakis Hilliard
c9364718b4 Eat for comma. 2023-01-31 12:26:05 +02:00
Dimitris Fasarakis Hilliard
38cf933bcb Add initial capacities, use u32s for indents/spaces. 2023-01-31 12:26:05 +02:00
Dimitris Fasarakis Hilliard
838990ae15 Don't call is_emoji_presentation for each invocation of consume_normal 2023-01-31 12:26:05 +02:00
Dimitris Fasarakis Hilliard
aa0290bbfc Match on ascii start/continuation characters before calling functions. 2023-01-31 12:26:05 +02:00
Aarni Koskela
f74e44d1e8 Bump phf to 0.11 series
string_cache is still using phf_shared 0.10.0 though
2023-01-25 19:58:43 +02:00
Jeong YunWon
d9df131720 Merge pull request #4449 from harupy/fix-dict-spread-in-dict
Fix AST generated from a dict literal containing dict unpacking
2023-01-22 20:44:26 +09:00
harupy
f2ffe12f8c Fix comment 2023-01-22 00:06:52 +09:00
Anders Kaseorg
6dba8430be Fix end location for elif blocks
Since we parse an `elif:` block as an `If` node, its location should
include its `orelse` node like it would for an `if:` block.

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2023-01-17 22:39:44 -05:00
harupy
b26365b215 Remove useless String::from
Signed-off-by: harupy <hkawamura0130@gmail.com>
2023-01-16 21:27:57 +09:00
harupy
2d019930e9 Rename test 2023-01-15 23:36:07 +09:00
harupy
d5fc7c4c87 Improve test 2023-01-15 16:53:13 +09:00
harupy
393869c47c Add Option to Dict.keys field 2023-01-15 16:43:13 +09:00
harupy
4edd2bf78a Remove commented-out code 2023-01-15 16:10:51 +09:00
harupy
d3c4551629 Fix unparse 2023-01-15 13:11:55 +09:00
harupy
581f6e176c Fix dict spreading in dict literal 2023-01-15 13:01:59 +09:00
Jim Fasarakis-Hilliard
ac4d3c076c Merge pull request #4443 from bluetech/non-logical-newline-token-fixup
Fixup parse_tokens after "Add NonLogicalNewline token"
2023-01-14 12:49:04 +02:00
Ran Benita
e5fe037e38 Fixup parse_tokens after "Add NonLogicalNewline token"
I only updated `parse()` to ignore `NonLogicalNewline`, didn't notice
it's also needed in `parse_tokens()`.
2023-01-14 11:52:33 +02:00
Noa
872b9d4765 Switch from 64-bit instruction enum to out-of-line arg values 2023-01-12 23:05:17 -06:00
Ran Benita
674eeec29c Add NonLogicalNewline token
This token is completely ignored by the parser, but it's useful for
other users of the lexer, such as the Ruff linter. For example, the
token is helpful for a "trailing comma" lint.

The same idea exists in Python's `tokenize` module - there is a NEWLINE
token (logical newline), and a NL token (non-logical newline).

Fixes #4385.
2023-01-12 16:47:12 +02:00
Martin Fischer
4f1e7c6291 Fix docs.rs build for rustpython-parser
docs.rs failed to build the documentation of the recently released
rustpython-parser 0.2.0 because the build.rs script couldn't write the
parser.rs file because docs.rs builds the documentation in a sandbox
with a read-only filesystem.

This commit fixes this by writing the parser.rs file to the cargo output
directory instead, as recommended by the docs.rs documentation.[1]

Fixes #4436.

[1]: https://docs.rs/about/builds#read-only-directories
2023-01-11 09:58:10 +01:00
Noa
884a7bdb15 Bump all crate versions to 0.2.0 2023-01-11 00:14:28 -06:00
Jeong YunWon
7885344bcf first cspell dict 2023-01-09 19:57:23 +09:00
Jeong YunWon
e8fef39861 Merge pull request #4429 from youknowone/fix-format
Fix nightly clippy warnings
2023-01-07 21:38:41 +09:00
Jeong YunWon
509cf7ed0d Fix nightly clippy warnings 2023-01-07 21:07:10 +09:00
harupy
a4a5366504 Include comment text in token 2023-01-06 23:29:20 +09:00
harupy
2dfd053bed Implement Default for Location 2023-01-05 22:48:47 +09:00
harupy
7f552e4594 Address comments 2023-01-05 18:24:54 +09:00
harupy
9efa872023 Use try_from 2023-01-05 01:18:30 +09:00
harupy
fd8468c5eb Simplify string check 2023-01-04 23:57:50 +09:00
Jeong YunWon
958c7e33ad Merge pull request #4417 from harupy/add-with-offset-methods
Add `with_col_offset` and `with_row_offset` to `Location` for conveniece
2023-01-04 17:07:31 +09:00
Jeong YunWon
0f311cd5e5 Merge pull request #4413 from harupy/more-generic-window-impl
Update `CharWindow` in `compiler/parser/src/lexer.rs` to allow slicing
2023-01-04 17:06:06 +09:00
harupy
0365752bf3 Use Self 2023-01-04 13:19:23 +09:00
harupy
6d140426c1 Add with_col_offset and with_row_offset to Location 2023-01-04 13:16:03 +09:00
harupy
33a62789f7 Address comment 2023-01-04 00:03:18 +09:00
harupy
84dff79ddc Remove incorrect EmptyExpression in parse_formatted_value 2023-01-03 23:55:50 +09:00
harupy
300710f7db Improve CharWindow 2023-01-03 17:27:35 +09:00
harupy
515dceb07b Remove repetitive to_string in parse_escaped_char 2023-01-03 14:48:00 +09:00
Jeong YunWon
2858c315bf Merge pull request #4409 from harupy/improve-error-conversion-in-string-parser
Improve error conversion in `string_parsers.rs`
2023-01-03 14:39:01 +09:00
harupy
3b0fd61b3b Fix clippy error 2023-01-03 12:51:31 +09:00
harupy
9030679193 Improve error conversion in string_parsers.rs 2023-01-03 12:46:10 +09:00
Jeong YunWon
71ba4226c1 Merge pull request #4405 from harupy/use-drain
Use `drain` to simplify `compiler/parser/src/string_parser.rs`
2023-01-03 12:41:23 +09:00
Jeong YunWon
3d03439618 Merge pull request #4399 from branai/shell-continuing-fix
Fix IndentationError works differently with cpython in interective shell
2023-01-03 04:32:03 +09:00
harupy
39f410c909 Use drain 2023-01-03 01:23:44 +09:00
Jim Fasarakis-Hilliard
4222b13e6c Merge pull request #4404 from harupy/merge-match-arms
Merge match arms in `StringParser.parse_formatted_value`
2023-01-02 18:18:40 +02:00
Jim Fasarakis-Hilliard
d691527ead Merge pull request #4402 from harupy/remove-unreachable-if
Remove unreachable code in `compiler/parser/src/string_parser.rs`
2023-01-02 17:05:45 +02:00
harupy
40e2150f7e Merge match arms in parse_formatted_value 2023-01-02 23:16:51 +09:00
harupy
92bf96608c Fix match 2023-01-02 22:54:48 +09:00
harupy
fac6a857f6 Simplify code using match 2023-01-02 22:26:09 +09:00
harupy
68586f8e3c Remove unreachable code in compiler/parser/src/string_parser.rs 2023-01-02 20:48:40 +09:00
Jeong YunWon
f8787a9377 Move (c)format basic implementations to rustpython-common 2023-01-02 20:21:36 +09:00
Bijan Naimi
530a20cc96 forgot to add formatted errors.rs 2023-01-01 17:28:49 -08:00
Bijan Naimi
6cb57b2075 changed the shell logic for handling indents 2023-01-01 15:41:51 -08:00
Dimitris Fasarakis Hilliard
92b2574d52 Move tests for with into parser. 2023-01-01 21:36:07 +02:00
Jim Fasarakis-Hilliard
95fb938bd6 Merge pull request #4389 from harupy/4384-follow-up
Follow-up for #4384
2023-01-01 14:53:54 +02:00
harupy
9683314264 Remove unreachable code 2023-01-01 17:43:25 +09:00
harupy
d9bbeeb9b3 Fix NamedExpr location 2022-12-31 23:32:08 +09:00
harupy
68116d5c11 Move tests 2022-12-31 12:15:33 +09:00
Jeong YunWon
e164a41723 Merge pull request #4373 from andersk/named
Allow named expression in subscript and set comprehension
2022-12-31 10:58:09 +09:00
Jeong YunWon
aa32a73c5b Merge pull request #4379 from harupy/refactor-FStringParser
Refactor `FStringParser`
2022-12-31 10:56:52 +09:00
Jeong YunWon
9d7d629cef Merge pull request #4384 from harupy/parse-formatted-value
Fix the location of `FormattedValue`
2022-12-31 10:52:44 +09:00
harupy
439298e735 Fix FormattedValue location 2022-12-30 21:39:29 +09:00
anilbey
16173bf581 Update compiler/parser/src/error.rs
Co-authored-by: fanninpm <fanninpm@miamioh.edu>
2022-12-29 22:56:34 +01:00
Anil Tuncel
7e7b2eadee format using cargo fmt 2022-12-29 22:15:44 +01:00
Anil Tuncel
80116c768d arg name to be written upon duplicate kwargs error #4381 2022-12-29 22:06:41 +01:00
Nick Liu
200390c1ab format code 2022-12-29 22:49:26 +08:00
Nick Liu
24d2ab8b0a use is_none 2022-12-29 22:49:26 +08:00
Nick Liu
41f21a7b5d add arg_name in duplicate argument error msg 2022-12-29 22:49:26 +08:00
Nick Liu
63e4a36e27 added check: named arguments must follow bare star 2022-12-29 22:49:26 +08:00
Nick Liu
15ab44384c added lex error: DuplicateArguments 2022-12-29 22:49:26 +08:00
Jim Fasarakis-Hilliard
5380b85579 Merge pull request #4367 from andersk/star-order
Prohibit starred arguments after double-starred arguments
2022-12-29 16:27:16 +02:00
Harutaka Kawamura
b707f53f23 Update compiler/parser/src/fstring.rs
Co-authored-by: Zgarbul Andrey <zgarbul.andrey@gmail.com>
2022-12-29 08:10:33 +09:00
Anders Kaseorg
6439867f78 Allow named expression in set comprehension: {a := b for c in d}
Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-12-28 09:57:32 -08:00
Anders Kaseorg
1904d095f9 Allow named expression in subscript: a[b := c]
Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-12-28 09:57:32 -08:00
harupy
7e8f683808 Rename 2022-12-29 01:01:41 +09:00
harupy
37b1894834 Clean up FStringParser 2022-12-29 00:08:59 +09:00
Jim Fasarakis-Hilliard
201d08583a Merge pull request #4377 from andersk/duplicate-from
Remove duplicate declaration of "from" token
2022-12-28 11:19:54 +02:00
Anders Kaseorg
107b2e11ae Remove duplicate declaration of "from" token
Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-12-27 18:56:24 -08:00
Anders Kaseorg
661b210391 Prohibit starred arguments after double-starred arguments
CPython prohibits ‘f(**kwargs, *args)’; we should too.

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-12-27 12:49:50 -08:00
harupy
9aed9143fe Refactor parse_formatted_value 2022-12-28 00:23:25 +09:00
harupy
c21d0d9283 Fix make_tokenizer_located 2022-12-27 21:54:18 +09:00
Jeong YunWon
313fd7d28c Merge pull request #4359 from yt2b/check_bom
Add BOM check
2022-12-26 16:03:51 +09:00
Jeong YunWon
3aa0096212 Merge pull request #4358 from harupy/fix-slice-location
Fix `Slice` location
2022-12-26 16:03:09 +09:00
Jeong YunWon
53dec88029 Merge pull request #4356 from andersk/with-tuple-named
Fix parsing of tuple with named expression as context manager
2022-12-26 16:02:17 +09:00
yt2b
bd0c15d34e Fix comment 2022-12-26 09:30:12 +09:00
yt2b
ce0be73841 Add BOM check 2022-12-25 11:15:29 +09:00
harupy
b2ac4f60f1 Fix slice location 2022-12-25 09:37:07 +09:00
Anders Kaseorg
1fdfa5fe1b Fix parsing of tuple with named expression as context manager
Because the ‘with’ item grammar disallows named expressions, CPython
parses ‘with (a := 0, b := 1):’ as a tuple rather than two ‘with’
items.

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-12-24 13:15:53 -08:00
harupy
c0f390ebc6 Fix IfExp location 2022-12-25 00:33:02 +09:00
Anders Kaseorg
c387b5d523 Simplify parenthesized context manager parsing with LALRPOP conditions
Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-12-24 23:13:10 +09:00
harupy
ea95e1a715 Fix the location of BinOp 2022-12-21 22:05:05 +09:00
Jim Fasarakis-Hilliard
c16e08d59b Merge pull request #4340 from harupy/fix-locations-of-parethesized-expressions
Fix the start and end locations of `Tuple`
2022-12-18 15:17:40 +02:00
harupy
ff00460ff4 Fix locations of parethesized expressions 2022-12-18 20:53:30 +09:00
Anders Kaseorg
f4672e4256 Remove unnecessary boxing of ASDL product children
Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-12-14 02:30:02 -08:00
Dimitris Fasarakis Hilliard
6e89b3ab1a Fix end location in with statements. 2022-12-13 13:28:17 +02:00
Jim Fasarakis-Hilliard
6a174dae45 Merge pull request #4327 from harupy/fix-end-location-body
Fix end location of compound statements
2022-12-13 12:30:21 +02:00
Charlie Marsh
8936ab2f8d Set ExprContext::Store on parenthesized with expressions 2022-12-12 09:09:15 -05:00
harupy
165b979733 Refactor 2022-12-12 22:36:34 +09:00
harupy
06c92bb899 Update snapshot 2022-12-12 22:26:03 +09:00
harupy
1a657731dd Format 2022-12-12 22:18:26 +09:00
harupy
d8cfc7e84f Resolve conflict 2022-12-12 22:16:46 +09:00
harupy
de2e88656e Address comments
Signed-off-by: harupy <hkawamura0130@gmail.com>
2022-12-12 22:14:05 +09:00
Anders Kaseorg
052dee72b8 Parse Python 3.9+ parenthesized context managers
Since the upstream grammar for this is not LR(1), we abuse LALRPOP
macros and the Into/TryInto traits to build a cover grammar that
converts to either tuples or `with` items after additional validation.
It’s annoying and ugly, but something like this is basically our only
option short of switching to a more powerful parser algorithm.

Fixes #4145.

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-12-12 00:47:33 -08:00
Anders Kaseorg
751f9d304f Split and simplify some LALRPOP rules
Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-12-11 22:02:08 -08:00
harupy
d6f9dd0763 Use method chaining 2022-12-12 10:24:00 +09:00
harupy
5fc8c4d0b1 Fix other compound statements 2022-12-12 01:10:42 +09:00
harupy
1b7a272b77 Fix end location of nodes containing body 2022-12-11 12:35:28 +09:00
harupy
3abdc87076 Refactor 2022-12-10 22:01:42 +09:00
harupy
99b02be35a Fix 2022-12-10 18:45:36 +09:00
harupy
f99167d4ed Fix plain string 2022-12-10 18:09:26 +09:00
harupy
816e1e711c Fix the end location of an implicitly-concatenated string 2022-12-10 17:49:57 +09:00
Jeong YunWon
e2b28d07c8 Merge pull request #4310 from youknowone/fix-clippy
Fix nightly clippy warnings
2022-12-05 12:57:01 +09:00
Jeong YunWon
28785784b2 Fix nightly clippy warnings 2022-12-05 12:18:16 +09:00
harupy
6f6b7b2312 add tests 2022-12-04 05:59:51 +09:00
harupy
93cb05ebda Fix location 2022-12-04 05:45:15 +09:00
yt2b
e8200ab674 use bool.then 2022-11-24 09:23:20 +09:00
yt2b
492f09298f restore if expression 2022-11-23 11:30:54 +09:00
yt2b
35eea0b8ec Refactor lexer functions 2022-11-22 21:10:19 +09:00
yt2b
1eeddb521e Add test 2022-11-21 22:36:05 +09:00
Charlie Marsh
96a50810a6 Use match 2022-11-20 23:41:52 -05:00
Charlie Marsh
51b7dbb89c Use rustc-hash 2022-11-20 15:30:19 -05:00
Charlie Marsh
28a8c3a062 Implement some minor performance optimizations 2022-11-20 13:33:20 -05:00
Bongjun Jang
4e5626dfd5 Refactor lexer struct (#4257) 2022-11-19 21:43:34 +09:00
Jeong YunWon
f979d8dbc3 Apply let-else statements 2022-11-10 15:39:31 +09:00
Jim Fasarakis-Hilliard
cc084b4fec Merge pull request #4266 from charliermarsh/charlie/comments
Implement Tok::Comment
2022-11-07 23:20:15 +02:00
Charlie Marsh
bbeec36fdb Set comparator start location to beginning of comparison 2022-11-07 12:24:14 -05:00
Charlie Marsh
b6c230f3ca Implement Tok::Comment 2022-11-07 10:33:55 -05:00
dvermd
a5b59f3c9d improve col_offset in new line and lalr 2022-10-26 21:30:51 +02:00
dvermd
d5a208ca9d improve fstring parser
part of: #1671
2022-10-26 21:15:24 +02:00
dvermd
fa41a1e2f6 Fix ast types' _fields and use 0-based column 2022-10-27 03:09:38 +09:00
Charlie Marsh
952d70b9d1 Add expression context parsing 2022-10-17 15:20:33 -04:00
Charlie Marsh
02953b9fe6 Remove parse_program_tokens 2022-10-17 12:04:30 -04:00
Charlie Marsh
8adc74fe26 Expose a method to parse AST from tokens directly 2022-10-17 09:39:48 -04:00
Jeong YunWon
48c0cb5599 Merge pull request #4218 from charliermarsh/charlie/clone
Make AST nodes Clone-able
2022-10-17 13:47:21 +09:00
Jeong YunWon
2e33a3d0e9 Merge pull request #4223 from youknowone/nightly-clippy
Fix nightly clippy warnings
2022-10-17 13:28:53 +09:00
Charlie Marsh
1cc342e4ed Add end locations to all nodes (#4192) 2022-10-17 13:18:30 +09:00
Charlie Marsh
519718e65d Start simple string at quote mark 2022-10-16 11:25:46 -04:00
Charlie Marsh
5da5490b19 Make AST nodes Clone-able 2022-10-16 11:01:17 -04:00
Jeong YunWon
518cf728c3 Fix nightly clippy warnings 2022-10-16 02:38:50 +09:00
Charlie Marsh
3397737a76 Start string location at kind or quote prefix 2022-10-15 11:03:50 -04:00
dvermd
6211a3a3a8 Refactor fstrings (#4188) 2022-10-14 12:16:34 +09:00
fanninpm
e64d7d196d Merge pull request #4186 from andersk/arithmetic
Spell “arithmetic” correctly
2022-09-30 09:29:36 -04:00
Anders Kaseorg
f8b45e48e1 Spell “arithmetic” correctly
Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-09-28 17:58:30 -07:00
Jeong YunWon
f99e4789ed temporary fix of parser build 2022-09-21 16:49:35 +09:00
Jeong YunWon
86b847204e better parser build experience 2022-09-21 16:47:06 +09:00
Charlie Marsh
26b529f9dc Add PartialOrd to Location 2022-09-04 16:31:43 -04:00
Jeong YunWon
b21ed24025 Merge pull request #4116 from charliermarsh/charlie/f-string
Avoid creating unused JoinedStr in FStringParser
2022-08-23 19:30:36 +09:00
Jeong YunWon
a414677892 remove Mode from codegen root 2022-08-23 05:15:27 +09:00
Jeong YunWon
bfe4795b6c parser::Mode from compile::Mode 2022-08-23 05:08:24 +09:00
Jeong YunWon
40690b9761 use thiserror 2022-08-23 05:08:24 +09:00
Jeong YunWon
1fd898c14c new_syntax_error working without full compiler
Fix #4100
2022-08-23 05:08:24 +09:00
Jeong YunWon
42b95a9a95 Move out CompileError to core as generic form 2022-08-23 01:30:00 +09:00
Jeong YunWon
e8230efe1a Integrate CompileError to compiler-core::BaseError 2022-08-22 23:18:41 +09:00
Jeong YunWon
7fcc18daea integrate CodegenError to compiler-core::Error 2022-08-22 18:43:03 +09:00
Jeong YunWon
2b7bf79d29 Integrate ParseError to compiler-core::Error 2022-08-22 16:28:08 +09:00
Jeong YunWon
904fc477f1 integrate ast::Location into compilre-core::Location 2022-08-22 08:42:20 +09:00
Jeong YunWon
bfac0355dc Share location between compiler crates 2022-08-22 08:42:20 +09:00
Jeong YunWon
c16e650071 rustpython-bytecode -> rustpython-compiler-core 2022-08-22 08:42:20 +09:00
Jeong YunWon
acde8bb625 sort Cargo.toml dependencies 2022-08-22 08:42:20 +09:00
Jeong YunWon
7f99404618 clean up cargo.toml sort 2022-08-22 08:42:20 +09:00
Jeong YunWon
3742f9117b Add source_path to ParseError 2022-08-22 08:42:20 +09:00
Jeong YunWon
a66902406f Refactor Mode and partial parser/codegen for eval/exec 2022-08-22 08:42:20 +09:00
Charles Marsh
51b6571ee1 Fix f-string regression 2022-08-21 19:20:23 -04:00
Charles Marsh
2345bc895d Avoid creating unused JoinedStr in FStringParser 2022-08-21 16:59:36 -04:00
Jeong YunWon
53c48bf6b9 reorganize compiler crates 2022-08-22 04:42:42 +09:00
Jeong YunWon
ffacac05bb Clean up imports 2022-08-22 03:42:29 +09:00
Jeong YunWon
3bae0823f7 replace try_parse!() macro to closure 2022-08-12 08:35:34 +09:00
Jeong Yunwon
29644a30d7 Add compile::Mode::BlockExpr 2022-05-27 10:14:13 +09:00
Jeong Yunwon
07712eda58 Upgrade libraries 2022-04-18 00:09:22 +09:00
Noa
76728bb69d Run cargo upgrade 2021-10-30 19:42:35 -05:00
Noa
721c2709c8 Migrate to 2021 edition 2021-10-21 11:46:24 -05:00
Noa
7865cace06 Add unparse feature to ast 2021-10-17 21:55:27 -05:00
Noah
a9330765fa Add an ast optimizer 2021-04-05 09:46:30 -05:00
Noah
9941eef853 Upgrade dependencies 2021-03-25 08:06:56 -05:00
Noah
e494175cd2 Update the compiler to use the new asdl ast types 2021-01-14 12:37:37 -06:00
Noah
dd7560c494 Implement asdl in the ast and parser 2021-01-14 12:37:37 -06:00
Noah
6603b4a124 Use ahash/phf in parser 2021-01-08 17:09:09 -06:00
Noah
dab7e20a33 Use ahash in the compiler 2021-01-08 17:00:23 -06:00
Noah
13f3ec5b2b Update other stuff to use the root of rustpython_bytecode 2020-12-19 14:59:07 -06:00
Noah
452cd308d5 Optimize the size of Instruction 2020-12-14 14:25:19 -06:00
Noah
3c469eaa26 compiler/porcelain wrapper 2020-11-07 15:43:23 -06:00
Noah
0c5bf8e470 Split the ast from the parser, remove compiler dep on parser 2020-11-07 15:43:23 -06:00
Noah
3f52fdd50a Use disassembly for snapshot testing 2020-10-19 23:55:56 -05:00
Noah
f0c0897f23 Update compiler to output codeobj.constants, use insta for snapshot testing 2020-10-19 23:55:55 -05:00
Jeong YunWon
a35d4dc22a clean up PyComplex 2020-08-09 21:46:16 +09:00
Noah
5fe1bd3900 Update itertools 2020-07-26 15:21:39 -05:00
Noah
3737af157f Release 0.1.2
rustpython@0.1.2
rustpython-bytecode@0.1.2
rustpython-compiler@0.1.2
rustpython-derive@0.1.2
rustpython-parser@0.1.2
rustpython-vm@0.1.2
rustpython_freeze@0.1.2
rustpython_wasm@0.1.2

Generated by cargo-workspaces
2020-06-23 18:47:08 -05:00
Chris West (Faux)
ed01cd63ee upgrade low-risk deps 2019-10-10 21:07:26 +01:00
Seo Sanghyeon
1dd57971a9 Optimize BuildMap bytecode emission 2019-10-03 00:03:08 +09:00
Noah
d1ae5a7448 Bump crate versions 2019-09-25 11:57:38 -05:00
coolreader18
e89d6febea Make peephole optimizer a stream processor 2019-08-03 22:02:29 -05:00
Windel Bouwman
4dcad05aa0 Extend symtable module. 2019-07-19 22:05:35 +02:00
Windel Bouwman
b0d270b881 Set all versions to 0.1.0 2019-07-07 13:04:12 +02:00
Windel Bouwman
e3ccef2504 Change authors to team name. 2019-07-01 21:14:07 +02:00
Windel Bouwman
dcdd964e0c Change underscore into hyphen 2019-07-01 21:05:29 +02:00
Windel Bouwman
107442cc06 Make bytecode crate independent of parser crate. 2019-06-30 11:42:36 +02:00
Windel Bouwman
10957879db Move bytecode into own crate. 2019-06-30 11:01:40 +02:00
coolreader18
f0148f46d0 Split off bytecode compilation into a separate crate 2019-06-12 21:43:43 -05:00
324 changed files with 20387 additions and 20793 deletions

View File

@@ -50,10 +50,6 @@ jobs:
- crates/ruff_formatter/**
- crates/ruff_python_trivia/**
- crates/ruff_python_ast/**
- crates/ruff_source_file/**
- crates/ruff_python_index/**
- crates/ruff_text_size/**
- crates/ruff_python_parser/**
cargo-fmt:
@@ -335,7 +331,7 @@ jobs:
- name: "Cache rust"
uses: Swatinem/rust-cache@v2
- name: "Formatter progress"
run: scripts/formatter_ecosystem_checks.sh
run: scripts/formatter_progress.sh
- name: "Github step summary"
run: grep "similarity index" target/progress_projects_report.txt | sort > $GITHUB_STEP_SUMMARY
# CPython is not black formatted, so we run only the stability check

63
Cargo.lock generated
View File

@@ -133,12 +133,6 @@ dependencies = [
"os_str_bytes",
]
[[package]]
name = "arrayvec"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
[[package]]
name = "ascii-canvas"
version = "3.0.0"
@@ -800,7 +794,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "flake8-to-ruff"
version = "0.0.282"
version = "0.0.280"
dependencies = [
"anyhow",
"clap",
@@ -1033,7 +1027,6 @@ dependencies = [
"number_prefix",
"portable-atomic",
"unicode-width",
"vt100",
]
[[package]]
@@ -2042,7 +2035,7 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.0.282"
version = "0.0.280"
dependencies = [
"annotate-snippets 0.9.1",
"anyhow",
@@ -2141,7 +2134,7 @@ dependencies = [
[[package]]
name = "ruff_cli"
version = "0.0.282"
version = "0.0.280"
dependencies = [
"annotate-snippets 0.9.1",
"anyhow",
@@ -2201,6 +2194,7 @@ dependencies = [
"indoc",
"itertools",
"libcst",
"log",
"once_cell",
"pretty_assertions",
"rayon",
@@ -2224,9 +2218,6 @@ dependencies = [
"strum_macros",
"tempfile",
"toml",
"tracing",
"tracing-indicatif",
"tracing-subscriber",
]
[[package]]
@@ -2291,7 +2282,6 @@ dependencies = [
"rustc-hash",
"serde",
"smallvec",
"static_assertions",
]
[[package]]
@@ -3125,18 +3115,6 @@ dependencies = [
"valuable",
]
[[package]]
name = "tracing-indicatif"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b38ed3722d27705c3bd7ca0ccf29acc3d8e1c717b4cd87f97891a2c1834ea1af"
dependencies = [
"indicatif",
"tracing",
"tracing-core",
"tracing-subscriber",
]
[[package]]
name = "tracing-log"
version = "0.1.3"
@@ -3335,39 +3313,6 @@ version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "vt100"
version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84cd863bf0db7e392ba3bd04994be3473491b31e66340672af5d11943c6274de"
dependencies = [
"itoa",
"log",
"unicode-width",
"vte",
]
[[package]]
name = "vte"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5022b5fbf9407086c180e9557be968742d839e68346af7792b8592489732197"
dependencies = [
"arrayvec",
"utf8parse",
"vte_generate_state_changes",
]
[[package]]
name = "vte_generate_state_changes"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff"
dependencies = [
"proc-macro2",
"quote",
]
[[package]]
name = "wait-timeout"
version = "0.2.0"

View File

@@ -46,9 +46,6 @@ syn = { version = "2.0.15" }
test-case = { version = "3.0.0" }
thiserror = { version = "1.0.43" }
toml = { version = "0.7.2" }
tracing = "0.1.37"
tracing-indicatif = "0.3.4"
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
wsl = { version = "0.1.0" }
# v1.0.1

View File

@@ -140,7 +140,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com) hook:
```yaml
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.0.282
rev: v0.0.280
hooks:
- id: ruff
```

View File

@@ -1,6 +1,6 @@
[package]
name = "flake8-to-ruff"
version = "0.0.282"
version = "0.0.280"
description = """
Convert Flake8 configuration files to Ruff configuration files.
"""

View File

@@ -1,6 +1,6 @@
[package]
name = "ruff"
version = "0.0.282"
version = "0.0.280"
publish = false
authors = { workspace = true }
edition = { workspace = true }

View File

@@ -1,22 +0,0 @@
import typing
import sys
from typing import TypeAlias
_UnusedPrivateTypeAlias: TypeAlias = int | None
_T: typing.TypeAlias = str
# OK
_UsedPrivateTypeAlias: TypeAlias = int | None
def func(arg: _UsedPrivateTypeAlias) -> _UsedPrivateTypeAlias:
...
if sys.version_info > (3, 9):
_PrivateTypeAlias: TypeAlias = str | None
else:
_PrivateTypeAlias: TypeAlias = float | None
def func2(arg: _PrivateTypeAlias) -> None: ...

View File

@@ -1,22 +0,0 @@
import typing
import sys
from typing import TypeAlias
_UnusedPrivateTypeAlias: TypeAlias = int | None
_T: typing.TypeAlias = str
# OK
_UsedPrivateTypeAlias: TypeAlias = int | None
def func(arg: _UsedPrivateTypeAlias) -> _UsedPrivateTypeAlias:
...
if sys.version_info > (3, 9):
_PrivateTypeAlias: TypeAlias = str | None
else:
_PrivateTypeAlias: TypeAlias = float | None
def func2(arg: _PrivateTypeAlias) -> None: ...

View File

@@ -1,18 +0,0 @@
import typing
from typing import TypedDict
class _UnusedTypedDict(TypedDict):
foo: str
class _UnusedTypedDict2(typing.TypedDict):
bar: int
class _UsedTypedDict(TypedDict):
foo: bytes
class _CustomClass(_UsedTypedDict):
bar: list[int]

View File

@@ -1,32 +0,0 @@
import sys
import typing
from typing import TypedDict
class _UnusedTypedDict(TypedDict):
foo: str
class _UnusedTypedDict2(typing.TypedDict):
bar: int
# OK
class _UsedTypedDict(TypedDict):
foo: bytes
class _CustomClass(_UsedTypedDict):
bar: list[int]
if sys.version_info >= (3, 10):
class _UsedTypedDict2(TypedDict):
foo: int
else:
class _UsedTypedDict2(TypedDict):
foo: float
class _CustomClass2(_UsedTypedDict2):
bar: list[int]

View File

@@ -1,15 +1,7 @@
import contextlib
import pathlib
import pathlib as pl
from pathlib import Path
from pathlib import Path as P
# SIM115
f = open("foo.txt")
f = Path("foo.txt").open()
f = pathlib.Path("foo.txt").open()
f = pl.Path("foo.txt").open()
f = P("foo.txt").open()
data = f.read()
f.close()

View File

@@ -30,13 +30,3 @@ for key in list(obj.keys()):
(k for k in obj.keys()) # SIM118
key in (obj or {}).keys() # SIM118
from typing import KeysView
class Foo:
def keys(self) -> KeysView[object]:
...
def __contains__(self, key: object) -> bool:
return key in self.keys() # OK

View File

@@ -1,10 +0,0 @@
"""Regression test: ensure that we don't treat the export entry as a typing-only reference."""
from __future__ import annotations
from logging import getLogger
__all__ = ("getLogger",)
def foo() -> None:
pass

View File

@@ -1,2 +0,0 @@
import bar
import foo

View File

@@ -1,2 +0,0 @@
import foo
import bar

View File

@@ -1,23 +1,28 @@
# PERF203
for i in range(10):
try:
try: # PERF203
print(f"{i}")
except:
print("error")
# OK
try:
for i in range(10):
print(f"{i}")
except:
print("error")
# OK
i = 0
while i < 10:
while i < 10: # PERF203
try:
print(f"{i}")
except:
print("error")
i += 1
try:
i = 0
while i < 10:
print(f"{i}")
i += 1
except:
print("error")

View File

@@ -45,18 +45,3 @@ def f():
for i in items:
if i not in result:
result.append(i) # OK
def f():
fibonacci = [0, 1]
for i in range(20):
fibonacci.append(sum(fibonacci[-2:])) # OK
print(fibonacci)
def f():
foo = object()
foo.fibonacci = [0, 1]
for i in range(20):
foo.fibonacci.append(sum(foo.fibonacci[-2:])) # OK
print(foo.fibonacci)

View File

@@ -1,13 +0,0 @@
#: E241
a = (1, 2)
#: Okay
b = (1, 20)
#: E242
a = (1, 2) # tab before 2
#: Okay
b = (1, 20) # space before 20
#: E241 E241 E241
# issue 135
more_spaces = [a, b,
ef, +h,
c, -d]

View File

@@ -66,6 +66,3 @@ while 1:
#: E703:2:1
0\
;
#: E701:2:3
a = \
5;

View File

@@ -1,103 +0,0 @@
"""Test type parameters and aliases"""
# Type parameters in type alias statements
from some_module import Bar
type Foo[T] = T # OK
type Foo[T] = list[T] # OK
type Foo[T: ForwardA] = T # OK
type Foo[*Ts] = Bar[Ts] # OK
type Foo[**P] = Bar[P] # OK
class ForwardA: ...
# Types used in aliased assignment must exist
type Foo = DoesNotExist # F821: Undefined name `DoesNotExist`
type Foo = list[DoesNotExist] # F821: Undefined name `DoesNotExist`
# Type parameters do not escape alias scopes
type Foo[T] = T
T # F821: Undefined name `T` - not accessible afterward alias scope
# Type parameters in functions
def foo[T](t: T) -> T: return t # OK
async def afoo[T](t: T) -> T: return t # OK
def with_forward_ref[T: ForwardB](t: T) -> T: return t # OK
def can_access_inside[T](t: T) -> T: # OK
print(T) # OK
return t # OK
class ForwardB: ...
# Type parameters do not escape function scopes
from some_library import some_decorator
@some_decorator(T) # F821: Undefined name `T` - not accessible in decorators
def foo[T](t: T) -> None: ...
T # F821: Undefined name `T` - not accessible afterward function scope
# Type parameters in classes
class Foo[T](list[T]): ... # OK
class UsesForward[T: ForwardC](list[T]): ... # OK
class ForwardC: ...
class WithinBody[T](list[T]): # OK
t = T # OK
x: T # OK
def foo(self, x: T) -> T: # OK
return x
def foo(self):
T # OK
# Type parameters do not escape class scopes
from some_library import some_decorator
@some_decorator(T) # F821: Undefined name `T` - not accessible in decorators
class Foo[T](list[T]): ...
T # F821: Undefined name `T` - not accessible after class scope
# Types specified in bounds should exist
type Foo[T: DoesNotExist] = T # F821: Undefined name `DoesNotExist`
def foo[T: DoesNotExist](t: T) -> T: return t # F821: Undefined name `DoesNotExist`
class Foo[T: DoesNotExist](list[T]): ... # F821: Undefined name `DoesNotExist`
type Foo[T: (DoesNotExist1, DoesNotExist2)] = T # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
def foo[T: (DoesNotExist1, DoesNotExist2)](t: T) -> T: return t # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
class Foo[T: (DoesNotExist1, DoesNotExist2)](list[T]): ... # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
# Type parameters in nested classes
class Parent[T]:
t = T # OK
def can_use_class_variable(self, x: t) -> t: # OK
return x
class Child:
def can_access_parent_type_parameter(self, x: T) -> T: # OK
T # OK
return x
def cannot_access_parent_variable(self, x: t) -> t: # F821: Undefined name `T`
t # F821: Undefined name `t`
return x
# Type parameters in nested functions
def can_access_inside_nested[T](t: T) -> T: # OK
def bar(x: T) -> T: # OK
T # OK
return x
bar(t)

View File

@@ -63,10 +63,3 @@ def main():
for sys in range(5):
pass
import requests_mock as rm
def requests_mock(requests_mock: rm.Mocker):
print(rm.ANY)

View File

@@ -147,10 +147,3 @@ def f() -> None:
global CONSTANT
CONSTANT = 1
CONSTANT = 2
def f() -> None:
try:
print("hello")
except A as e :
print("oh no!")

View File

@@ -1,16 +0,0 @@
class Person:
def __init__(self):
self.name = "monty"
def __eq__(self, other):
return isinstance(other, Person) and other.name == self.name
class Language:
def __init__(self):
self.name = "python"
def __eq__(self, other):
return isinstance(other, Language) and other.name == self.name
def __hash__(self):
return hash(self.name)

View File

@@ -1,3 +1,5 @@
"""A mirror of UP037_1.py, with `from __future__ import annotations`."""
from __future__ import annotations
from typing import (

View File

@@ -0,0 +1,108 @@
"""A mirror of UP037_0.py, without `from __future__ import annotations`."""
from typing import (
Annotated,
Callable,
List,
Literal,
NamedTuple,
Tuple,
TypeVar,
TypedDict,
cast,
)
from mypy_extensions import Arg, DefaultArg, DefaultNamedArg, NamedArg, VarArg
def foo(var: "MyClass") -> "MyClass":
x: "MyClass"
def foo(*, inplace: "bool"):
pass
def foo(*args: "str", **kwargs: "int"):
pass
x: Tuple["MyClass"]
x: Callable[["MyClass"], None]
class Foo(NamedTuple):
x: "MyClass"
class D(TypedDict):
E: TypedDict("E", foo="int", total=False)
class D(TypedDict):
E: TypedDict("E", {"foo": "int"})
x: Annotated["str", "metadata"]
x: Arg("str", "name")
x: DefaultArg("str", "name")
x: NamedArg("str", "name")
x: DefaultNamedArg("str", "name")
x: DefaultNamedArg("str", name="name")
x: VarArg("str")
x: List[List[List["MyClass"]]]
x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
x: NamedTuple(typename="X", fields=[("foo", "int")])
X: MyCallable("X")
# OK
class D(TypedDict):
E: TypedDict("E")
x: Annotated[()]
x: DefaultNamedArg(name="name", quox="str")
x: DefaultNamedArg(name="name")
x: NamedTuple("X", [("foo",), ("bar",)])
x: NamedTuple("X", ["foo", "bar"])
x: NamedTuple()
x: Literal["foo", "bar"]
x = cast(x, "str")
def foo(x, *args, **kwargs):
...
def foo(*, inplace):
...
x: Annotated[1:2] = ...
x = TypeVar("x", "str", "int")
x = cast("str", x)
X = List["MyClass"]

View File

@@ -2,9 +2,21 @@ x = range(10)
# RUF015
list(x)[0]
list(x)[:1]
list(x)[:1:1]
list(x)[:1:2]
tuple(x)[0]
tuple(x)[:1]
tuple(x)[:1:1]
tuple(x)[:1:2]
list(i for i in x)[0]
list(i for i in x)[:1]
list(i for i in x)[:1:1]
list(i for i in x)[:1:2]
[i for i in x][0]
[i for i in x][:1]
[i for i in x][:1:1]
[i for i in x][:1:2]
# OK (not indexing (solely) the first element)
list(x)
@@ -17,9 +29,6 @@ list(x)[::]
[i for i in x]
[i for i in x][1]
[i for i in x][-1]
[i for i in x][:1]
[i for i in x][:1:1]
[i for i in x][:1:2]
[i for i in x][1:]
[i for i in x][:3:2]
[i for i in x][::2]

View File

@@ -1,5 +0,0 @@
import os # ruff: noqa: F401
def f():
x = 1

View File

@@ -1,6 +1,6 @@
use itertools::Itertools;
use std::collections::BTreeSet;
use itertools::Itertools;
use ruff_text_size::{TextLen, TextRange, TextSize};
use rustc_hash::{FxHashMap, FxHashSet};
@@ -59,30 +59,35 @@ fn apply_fixes<'a>(
})
.sorted_by(|(rule1, fix1), (rule2, fix2)| cmp_fix(*rule1, *rule2, fix1, fix2))
{
let mut edits = fix
.edits()
.iter()
.filter(|edit| !applied.contains(edit))
.peekable();
// If we already applied an identical fix as part of another correction, skip
// any re-application.
if fix.edits().iter().all(|edit| applied.contains(edit)) {
*fixed.entry(rule).or_default() += 1;
continue;
}
// If the fix contains at least one new edit, enforce isolation and positional requirements.
if let Some(first) = edits.peek() {
// If this fix requires isolation, and we've already applied another fix in the
// same isolation group, skip it.
if let IsolationLevel::Group(id) = fix.isolation() {
if !isolated.insert(id) {
continue;
}
}
// Best-effort approach: if this fix overlaps with a fix we've already applied,
// skip it.
if last_pos.map_or(false, |last_pos| {
fix.min_start()
.map_or(false, |fix_location| last_pos >= fix_location)
}) {
continue;
}
// If this fix overlaps with a fix we've already applied, skip it.
if last_pos.map_or(false, |last_pos| last_pos >= first.start()) {
// If this fix requires isolation, and we've already applied another fix in the
// same isolation group, skip it.
if let IsolationLevel::Group(id) = fix.isolation() {
if !isolated.insert(id) {
continue;
}
}
let mut applied_edits = Vec::with_capacity(fix.edits().len());
for edit in edits {
for edit in fix
.edits()
.iter()
.sorted_unstable_by_key(|edit| edit.start())
{
// Add all contents from `last_pos` to `fix.location`.
let slice = locator.slice(TextRange::new(last_pos.unwrap_or_default(), edit.start()));
output.push_str(slice);
@@ -98,10 +103,9 @@ fn apply_fixes<'a>(
// Track that the edit was applied.
last_pos = Some(edit.end());
applied_edits.push(edit);
applied.insert(edit);
}
applied.extend(applied_edits.drain(..));
*fixed.entry(rule).or_default() += 1;
}

View File

@@ -1,8 +1,7 @@
use ruff_diagnostics::{Diagnostic, Fix};
use crate::checkers::ast::Checker;
use crate::codes::Rule;
use crate::rules::{flake8_import_conventions, flake8_pyi, pyflakes, pylint};
use ruff_diagnostics::{Diagnostic, Fix};
/// Run lint rules over the [`Binding`]s.
pub(crate) fn bindings(checker: &mut Checker) {
@@ -11,7 +10,9 @@ pub(crate) fn bindings(checker: &mut Checker) {
Rule::InvalidAllObject,
Rule::UnaliasedCollectionsAbcSetImport,
Rule::UnconventionalImportAlias,
Rule::UnusedPrivateTypeVar,
Rule::UnusedVariable,
Rule::UnusedPrivateProtocol,
]) {
return;
}
@@ -64,6 +65,20 @@ pub(crate) fn bindings(checker: &mut Checker) {
checker.diagnostics.push(diagnostic);
}
}
if checker.enabled(Rule::UnusedPrivateTypeVar) {
if let Some(diagnostic) =
flake8_pyi::rules::unused_private_type_var(checker, binding)
{
checker.diagnostics.push(diagnostic);
}
}
if checker.enabled(Rule::UnusedPrivateProtocol) {
if let Some(diagnostic) =
flake8_pyi::rules::unused_private_protocol(checker, binding)
{
checker.diagnostics.push(diagnostic);
}
}
}
}
}

View File

@@ -5,7 +5,7 @@ use ruff_python_semantic::{Binding, BindingKind, ScopeKind};
use crate::checkers::ast::Checker;
use crate::codes::Rule;
use crate::rules::{flake8_pyi, flake8_type_checking, flake8_unused_arguments, pyflakes, pylint};
use crate::rules::{flake8_type_checking, flake8_unused_arguments, pyflakes, pylint};
/// Run lint rules over all deferred scopes in the [`SemanticModel`].
pub(crate) fn deferred_scopes(checker: &mut Checker) {
@@ -24,10 +24,6 @@ pub(crate) fn deferred_scopes(checker: &mut Checker) {
Rule::UnusedImport,
Rule::UnusedLambdaArgument,
Rule::UnusedMethodArgument,
Rule::UnusedPrivateProtocol,
Rule::UnusedPrivateTypeAlias,
Rule::UnusedPrivateTypeVar,
Rule::UnusedPrivateTypedDict,
Rule::UnusedStaticMethodArgument,
Rule::UnusedVariable,
]) {
@@ -218,21 +214,6 @@ pub(crate) fn deferred_scopes(checker: &mut Checker) {
}
}
if checker.is_stub {
if checker.enabled(Rule::UnusedPrivateTypeVar) {
flake8_pyi::rules::unused_private_type_var(checker, scope, &mut diagnostics);
}
if checker.enabled(Rule::UnusedPrivateProtocol) {
flake8_pyi::rules::unused_private_protocol(checker, scope, &mut diagnostics);
}
if checker.enabled(Rule::UnusedPrivateTypeAlias) {
flake8_pyi::rules::unused_private_type_alias(checker, scope, &mut diagnostics);
}
if checker.enabled(Rule::UnusedPrivateTypedDict) {
flake8_pyi::rules::unused_private_typed_dict(checker, scope, &mut diagnostics);
}
}
if matches!(
scope.kind,
ScopeKind::Function(_) | ScopeKind::AsyncFunction(_) | ScopeKind::Lambda(_)

View File

@@ -396,9 +396,6 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
flake8_django::rules::model_without_dunder_str(checker, class_def);
}
}
if checker.enabled(Rule::EqWithoutHash) {
pylint::rules::object_without_hash_method(checker, class_def);
}
if checker.enabled(Rule::GlobalStatement) {
pylint::rules::global_statement(checker, name);
}

View File

@@ -1,7 +1,8 @@
use ruff_python_ast::{Expr, TypeParam};
use ruff_python_semantic::{ScopeId, Snapshot};
use ruff_python_ast::Expr;
use ruff_text_size::TextRange;
use ruff_python_semantic::{ScopeId, Snapshot};
/// A collection of AST nodes that are deferred for later analysis.
/// Used to, e.g., store functions, whose bodies shouldn't be analyzed until all
/// module-level definitions have been analyzed.
@@ -10,7 +11,6 @@ pub(crate) struct Deferred<'a> {
pub(crate) scopes: Vec<ScopeId>,
pub(crate) string_type_definitions: Vec<(TextRange, &'a str, Snapshot)>,
pub(crate) future_type_definitions: Vec<(&'a Expr, Snapshot)>,
pub(crate) type_param_definitions: Vec<(&'a TypeParam, Snapshot)>,
pub(crate) functions: Vec<Snapshot>,
pub(crate) lambdas: Vec<(&'a Expr, Snapshot)>,
pub(crate) for_loops: Vec<Snapshot>,

View File

@@ -453,14 +453,12 @@ where
args,
decorator_list,
returns,
type_params,
..
})
| Stmt::AsyncFunctionDef(ast::StmtAsyncFunctionDef {
body,
args,
decorator_list,
type_params,
returns,
..
}) => {
@@ -474,12 +472,6 @@ where
// are enabled.
let runtime_annotation = !self.semantic.future_annotations();
self.semantic.push_scope(ScopeKind::Type);
for type_param in type_params {
self.visit_type_param(type_param);
}
for arg_with_default in args
.posonlyargs
.iter()
@@ -550,25 +542,18 @@ where
bases,
keywords,
decorator_list,
type_params,
..
},
) => {
for decorator in decorator_list {
self.visit_decorator(decorator);
}
self.semantic.push_scope(ScopeKind::Type);
for type_param in type_params {
self.visit_type_param(type_param);
}
for expr in bases {
self.visit_expr(expr);
}
for keyword in keywords {
self.visit_keyword(keyword);
}
for decorator in decorator_list {
self.visit_decorator(decorator);
}
let definition = docstrings::extraction::extract_definition(
ExtractionTarget::Class,
@@ -577,6 +562,7 @@ where
&self.semantic.definitions,
);
self.semantic.push_definition(definition);
self.semantic.push_scope(ScopeKind::Class(class_def));
// Extract any global bindings from the class body.
@@ -586,20 +572,6 @@ where
self.visit_body(body);
}
Stmt::TypeAlias(ast::StmtTypeAlias {
range: _range,
name,
type_params,
value,
}) => {
self.semantic.push_scope(ScopeKind::Type);
for type_param in type_params {
self.visit_type_param(type_param);
}
self.visit_expr(value);
self.semantic.pop_scope();
self.visit_expr(name);
}
Stmt::Try(ast::StmtTry {
body,
handlers,
@@ -743,9 +715,8 @@ where
| Stmt::AsyncFunctionDef(ast::StmtAsyncFunctionDef { name, .. }) => {
let scope_id = self.semantic.scope_id;
self.deferred.scopes.push(scope_id);
self.semantic.pop_scope(); // Function scope
self.semantic.pop_scope();
self.semantic.pop_definition();
self.semantic.pop_scope(); // Type parameter scope
self.add_binding(
name,
stmt.identifier(),
@@ -756,9 +727,8 @@ where
Stmt::ClassDef(ast::StmtClassDef { name, .. }) => {
let scope_id = self.semantic.scope_id;
self.deferred.scopes.push(scope_id);
self.semantic.pop_scope(); // Class scope
self.semantic.pop_scope();
self.semantic.pop_definition();
self.semantic.pop_scope(); // Type parameter scope
self.add_binding(
name,
stmt.identifier(),
@@ -1361,26 +1331,6 @@ where
self.visit_stmt(stmt);
}
}
fn visit_type_param(&mut self, type_param: &'b ast::TypeParam) {
// Step 1: Binding
match type_param {
ast::TypeParam::TypeVar(ast::TypeParamTypeVar { name, range, .. })
| ast::TypeParam::TypeVarTuple(ast::TypeParamTypeVarTuple { name, range })
| ast::TypeParam::ParamSpec(ast::TypeParamParamSpec { name, range }) => {
self.add_binding(
name.as_str(),
*range,
BindingKind::TypeParam,
BindingFlags::empty(),
);
}
}
// Step 2: Traversal
self.deferred
.type_param_definitions
.push((type_param, self.semantic.snapshot()));
}
}
impl<'a> Checker<'a> {
@@ -1597,10 +1547,10 @@ impl<'a> Checker<'a> {
}
fn handle_node_load(&mut self, expr: &Expr) {
let Expr::Name(expr) = expr else {
let Expr::Name(ast::ExprName { id, .. }) = expr else {
return;
};
self.semantic.resolve_load(expr);
self.semantic.resolve_load(id, expr.range());
}
fn handle_node_store(&mut self, id: &'a str, expr: &Expr) {
@@ -1731,7 +1681,6 @@ impl<'a> Checker<'a> {
}
fn visit_deferred_future_type_definitions(&mut self) {
let snapshot = self.semantic.snapshot();
while !self.deferred.future_type_definitions.is_empty() {
let type_definitions = std::mem::take(&mut self.deferred.future_type_definitions);
for (expr, snapshot) in type_definitions {
@@ -1742,29 +1691,9 @@ impl<'a> Checker<'a> {
self.visit_expr(expr);
}
}
self.semantic.restore(snapshot);
}
fn visit_deferred_type_param_definitions(&mut self) {
let snapshot = self.semantic.snapshot();
while !self.deferred.type_param_definitions.is_empty() {
let type_params = std::mem::take(&mut self.deferred.type_param_definitions);
for (type_param, snapshot) in type_params {
self.semantic.restore(snapshot);
if let ast::TypeParam::TypeVar(ast::TypeParamTypeVar {
bound: Some(bound), ..
}) = type_param
{
self.visit_expr(bound);
}
}
}
self.semantic.restore(snapshot);
}
fn visit_deferred_string_type_definitions(&mut self, allocator: &'a typed_arena::Arena<Expr>) {
let snapshot = self.semantic.snapshot();
while !self.deferred.string_type_definitions.is_empty() {
let type_definitions = std::mem::take(&mut self.deferred.string_type_definitions);
for (range, value, snapshot) in type_definitions {
@@ -1775,7 +1704,7 @@ impl<'a> Checker<'a> {
self.semantic.restore(snapshot);
if self.semantic.in_annotation() && self.semantic.future_annotations() {
if self.semantic.in_typing_only_annotation() {
if self.enabled(Rule::QuotedAnnotation) {
pyupgrade::rules::quoted_annotation(self, value, range);
}
@@ -1808,11 +1737,9 @@ impl<'a> Checker<'a> {
}
}
}
self.semantic.restore(snapshot);
}
fn visit_deferred_functions(&mut self) {
let snapshot = self.semantic.snapshot();
while !self.deferred.functions.is_empty() {
let deferred_functions = std::mem::take(&mut self.deferred.functions);
for snapshot in deferred_functions {
@@ -1830,11 +1757,9 @@ impl<'a> Checker<'a> {
}
}
}
self.semantic.restore(snapshot);
}
fn visit_deferred_lambdas(&mut self) {
let snapshot = self.semantic.snapshot();
while !self.deferred.lambdas.is_empty() {
let lambdas = std::mem::take(&mut self.deferred.lambdas);
for (expr, snapshot) in lambdas {
@@ -1853,13 +1778,10 @@ impl<'a> Checker<'a> {
}
}
}
self.semantic.restore(snapshot);
}
/// Run any lint rules that operate over the module exports (i.e., members of `__all__`).
fn visit_exports(&mut self) {
let snapshot = self.semantic.snapshot();
let exports: Vec<(&str, TextRange)> = self
.semantic
.global_scope()
@@ -1902,8 +1824,6 @@ impl<'a> Checker<'a> {
}
}
}
self.semantic.restore(snapshot);
}
}
@@ -1962,7 +1882,6 @@ pub(crate) fn check_ast(
checker.visit_deferred_functions();
checker.visit_deferred_lambdas();
checker.visit_deferred_future_type_definitions();
checker.visit_deferred_type_param_definitions();
let allocator = typed_arena::Arena::new();
checker.visit_deferred_string_type_definitions(&allocator);
checker.visit_exports();

View File

@@ -9,9 +9,9 @@ use ruff_source_file::Locator;
use crate::registry::{AsRule, Rule};
use crate::rules::pycodestyle::rules::logical_lines::{
extraneous_whitespace, indentation, missing_whitespace, missing_whitespace_after_keyword,
missing_whitespace_around_operator, space_after_comma, space_around_operator,
whitespace_around_keywords, whitespace_around_named_parameter_equals,
whitespace_before_comment, whitespace_before_parameters, LogicalLines, TokenFlags,
missing_whitespace_around_operator, space_around_operator, whitespace_around_keywords,
whitespace_around_named_parameter_equals, whitespace_before_comment,
whitespace_before_parameters, LogicalLines, TokenFlags,
};
use crate::settings::Settings;
@@ -61,9 +61,6 @@ pub(crate) fn check_logical_lines(
missing_whitespace_around_operator(&line, &mut context);
missing_whitespace(&line, should_fix_missing_whitespace, &mut context);
}
if line.flags().contains(TokenFlags::PUNCTUATION) {
space_after_comma(&line, &mut context);
}
if line
.flags()

View File

@@ -84,8 +84,6 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
(Pycodestyle, "E227") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::MissingWhitespaceAroundBitwiseOrShiftOperator),
(Pycodestyle, "E228") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::MissingWhitespaceAroundModuloOperator),
(Pycodestyle, "E231") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::MissingWhitespace),
(Pycodestyle, "E241") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::MultipleSpacesAfterComma),
(Pycodestyle, "E242") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::TabAfterComma),
(Pycodestyle, "E251") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::UnexpectedSpacesAroundKeywordParameterEquals),
(Pycodestyle, "E252") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::MissingWhitespaceAroundParameterEquals),
(Pycodestyle, "E261") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::TooFewSpacesBeforeInlineComment),
@@ -225,7 +223,6 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
(Pylint, "W0711") => (RuleGroup::Unspecified, rules::pylint::rules::BinaryOpException),
(Pylint, "W1508") => (RuleGroup::Unspecified, rules::pylint::rules::InvalidEnvvarDefault),
(Pylint, "W1509") => (RuleGroup::Unspecified, rules::pylint::rules::SubprocessPopenPreexecFn),
(Pylint, "W1641") => (RuleGroup::Nursery, rules::pylint::rules::EqWithoutHash),
(Pylint, "W2901") => (RuleGroup::Unspecified, rules::pylint::rules::RedefinedLoopName),
(Pylint, "W3301") => (RuleGroup::Unspecified, rules::pylint::rules::NestedMinMax),
@@ -654,9 +651,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
(Flake8Pyi, "044") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::FutureAnnotationsInStub),
(Flake8Pyi, "045") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::IterMethodReturnIterable),
(Flake8Pyi, "046") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::UnusedPrivateProtocol),
(Flake8Pyi, "047") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::UnusedPrivateTypeAlias),
(Flake8Pyi, "048") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::StubBodyMultipleStatements),
(Flake8Pyi, "049") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::UnusedPrivateTypedDict),
(Flake8Pyi, "050") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::NoReturnArgumentAnnotationInStub),
(Flake8Pyi, "052") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::UnannotatedAssignmentInStub),
(Flake8Pyi, "054") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::NumericLiteralTooLong),

View File

@@ -1,5 +1,4 @@
use std::cmp::Ordering;
use std::fmt::Display;
use std::fs::File;
use std::io::{BufReader, BufWriter, Read, Seek, SeekFrom, Write};
use std::iter;
@@ -42,20 +41,6 @@ pub fn round_trip(path: &Path) -> anyhow::Result<String> {
Ok(String::from_utf8(writer)?)
}
impl Display for SourceValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SourceValue::String(string) => f.write_str(string),
SourceValue::StringArray(string_array) => {
for string in string_array {
f.write_str(string)?;
}
Ok(())
}
}
}
}
impl Cell {
/// Return the [`SourceValue`] of the cell.
fn source(&self) -> &SourceValue {
@@ -422,11 +407,6 @@ impl Notebook {
self.content = transformed.to_string();
}
/// Return a slice of [`Cell`] in the Jupyter notebook.
pub fn cells(&self) -> &[Cell] {
&self.raw.cells
}
/// Return `true` if the notebook is a Python notebook, `false` otherwise.
pub fn is_python_notebook(&self) -> bool {
self.raw

View File

@@ -2,7 +2,7 @@ use std::fmt::{Display, Formatter};
use std::num::NonZeroUsize;
use colored::{Color, ColoredString, Colorize, Styles};
use itertools::Itertools;
use ruff_text_size::{TextRange, TextSize};
use similar::{ChangeTag, TextDiff};
@@ -38,7 +38,12 @@ impl Display for Diff<'_> {
let mut output = String::with_capacity(self.source_code.source_text().len());
let mut last_end = TextSize::default();
for edit in self.fix.edits() {
for edit in self
.fix
.edits()
.iter()
.sorted_unstable_by_key(|edit| edit.start())
{
output.push_str(
self.source_code
.slice(TextRange::new(last_end, edit.start())),

View File

@@ -12,7 +12,6 @@ use ruff_python_ast::Ranged;
use ruff_text_size::{TextLen, TextRange, TextSize};
use ruff_diagnostics::Diagnostic;
use ruff_python_trivia::indentation_at_offset;
use ruff_source_file::{LineEnding, Locator};
use crate::codes::NoqaCode;
@@ -249,34 +248,22 @@ impl FileExemption {
let path_display = relativize_path(path);
warn!("Invalid `# ruff: noqa` directive at {path_display}:{line}: {err}");
}
Ok(Some(exemption)) => {
if indentation_at_offset(range.start(), locator).is_none() {
#[allow(deprecated)]
let line = locator.compute_line_index(range.start());
let path_display = relativize_path(path);
warn!("Unexpected `# ruff: noqa` directive at {path_display}:{line}. File-level suppression comments must appear on their own line.");
continue;
}
match exemption {
ParsedFileExemption::All => {
return Some(Self::All);
Ok(Some(ParsedFileExemption::All)) => {
return Some(Self::All);
}
Ok(Some(ParsedFileExemption::Codes(codes))) => {
exempt_codes.extend(codes.into_iter().filter_map(|code| {
if let Ok(rule) = Rule::from_code(get_redirect_target(code).unwrap_or(code))
{
Some(rule.noqa_code())
} else {
#[allow(deprecated)]
let line = locator.compute_line_index(range.start());
let path_display = relativize_path(path);
warn!("Invalid code provided to `# ruff: noqa` at {path_display}:{line}: {code}");
None
}
ParsedFileExemption::Codes(codes) => {
exempt_codes.extend(codes.into_iter().filter_map(|code| {
if let Ok(rule) = Rule::from_code(get_redirect_target(code).unwrap_or(code))
{
Some(rule.noqa_code())
} else {
#[allow(deprecated)]
let line = locator.compute_line_index(range.start());
let path_display = relativize_path(path);
warn!("Invalid rule code provided to `# ruff: noqa` at {path_display}:{line}: {code}");
None
}
}));
}
}
}));
}
Ok(None) => {}
}

View File

@@ -303,7 +303,6 @@ impl Rule {
| Rule::MissingWhitespaceAroundOperator
| Rule::MissingWhitespaceAroundParameterEquals
| Rule::MultipleLeadingHashesForBlockComment
| Rule::MultipleSpacesAfterComma
| Rule::MultipleSpacesAfterKeyword
| Rule::MultipleSpacesAfterOperator
| Rule::MultipleSpacesBeforeKeyword
@@ -313,7 +312,6 @@ impl Rule {
| Rule::NoSpaceAfterBlockComment
| Rule::NoSpaceAfterInlineComment
| Rule::OverIndented
| Rule::TabAfterComma
| Rule::TabAfterKeyword
| Rule::TabAfterOperator
| Rule::TabBeforeKeyword

View File

@@ -242,7 +242,6 @@ impl Renamer {
// By default, replace the binding's name with the target name.
BindingKind::Annotation
| BindingKind::Argument
| BindingKind::TypeParam
| BindingKind::NamedExprAssignment
| BindingKind::UnpackedAssignment
| BindingKind::Assignment

View File

@@ -6,35 +6,6 @@ use ruff_macros::{derive_message_formats, violation};
use crate::checkers::ast::Checker;
/// ## What it does
/// Checks for `jinja2` templates that use `autoescape=False`.
///
/// ## Why is this bad?
/// `jinja2` templates that use `autoescape=False` are vulnerable to cross-site
/// scripting (XSS) attacks that allow attackers to execute arbitrary
/// JavaScript.
///
/// By default, `jinja2` sets `autoescape` to `False`, so it is important to
/// set `autoescape=True` or use the `select_autoescape` function to mitigate
/// XSS vulnerabilities.
///
/// ## Example
/// ```python
/// import jinja2
///
/// jinja2.Environment(loader=jinja2.FileSystemLoader("."))
/// ```
///
/// Use instead:
/// ```python
/// import jinja2
///
/// jinja2.Environment(loader=jinja2.FileSystemLoader("."), autoescape=True)
/// ```
///
/// ## References
/// - [Jinja documentation: API](https://jinja.palletsprojects.com/en/latest/api/#autoescaping)
/// - [Common Weakness Enumeration: CWE-94](https://cwe.mitre.org/data/definitions/94.html)
#[violation]
pub struct Jinja2AutoescapeFalse {
value: bool,

View File

@@ -6,24 +6,6 @@ use ruff_python_ast::helpers::find_keyword;
use crate::checkers::ast::Checker;
/// ## What it does
/// Checks for insecure `logging.config.listen` calls.
///
/// ## Why is this bad?
/// `logging.config.listen` starts a server that listens for logging
/// configuration requests. This is insecure as parts of the configuration are
/// passed to the built-in `eval` function, which can be used to execute
/// arbitrary code.
///
/// ## Example
/// ```python
/// import logging
///
/// logging.config.listen(9999)
/// ```
///
/// ## References
/// - [Python documentation: `logging.config.listen()`](https://docs.python.org/3/library/logging.config.html#logging.config.listen)
#[violation]
pub struct LoggingConfigInsecureListen;

View File

@@ -5,25 +5,6 @@ use ruff_macros::{derive_message_formats, violation};
use crate::checkers::ast::Checker;
/// ## What it does
/// Checks for `paramiko` calls.
///
/// ## Why is this bad?
/// `paramiko` calls allow users to execute arbitrary shell commands on a
/// remote machine. If the inputs to these calls are not properly sanitized,
/// they can be vulnerable to shell injection attacks.
///
/// ## Example
/// ```python
/// import paramiko
///
/// client = paramiko.SSHClient()
/// client.exec_command("echo $HOME")
/// ```
///
/// ## References
/// - [Common Weakness Enumeration: CWE-78](https://cwe.mitre.org/data/definitions/78.html)
/// - [Paramiko documentation: `SSHClient.exec_command()`](https://docs.paramiko.org/en/stable/api/client.html#paramiko.client.SSHClient.exec_command)
#[violation]
pub struct ParamikoCall;

View File

@@ -75,31 +75,6 @@ impl Violation for StartProcessWithNoShell {
}
}
/// ## What it does
/// Checks for the starting of a process with a partial executable path.
///
/// ## Why is this bad?
/// Starting a process with a partial executable path can allow attackers to
/// execute arbitrary executable by adjusting the `PATH` environment variable.
/// Consider using a full path to the executable instead.
///
/// ## Example
/// ```python
/// import subprocess
///
/// subprocess.Popen(["ruff", "check", "file.py"])
/// ```
///
/// Use instead:
/// ```python
/// import subprocess
///
/// subprocess.Popen(["/usr/bin/ruff", "check", "file.py"])
/// ```
///
/// ## References
/// - [Python documentation: `subprocess.Popen()`](https://docs.python.org/3/library/subprocess.html#subprocess.Popen)
/// - [Common Weakness Enumeration: CWE-78](https://cwe.mitre.org/data/definitions/78.html)
#[violation]
pub struct StartProcessWithPartialPath;
@@ -110,29 +85,6 @@ impl Violation for StartProcessWithPartialPath {
}
}
/// ## What it does
/// Checks for possible wildcard injections in calls to `subprocess.Popen()`.
///
/// ## Why is this bad?
/// Wildcard injections can lead to unexpected behavior if unintended files are
/// matched by the wildcard. Consider using a more specific path instead.
///
/// ## Example
/// ```python
/// import subprocess
///
/// subprocess.Popen(["chmod", "777", "*.py"])
/// ```
///
/// Use instead:
/// ```python
/// import subprocess
///
/// subprocess.Popen(["chmod", "777", "main.py"])
/// ```
///
/// ## References
/// - [Common Weakness Enumeration: CWE-78](https://cwe.mitre.org/data/definitions/78.html)
#[violation]
pub struct UnixCommandWildcardInjection;

View File

@@ -57,6 +57,7 @@ fn assignment(obj: &Expr, name: &str, value: &Expr, generator: Generator) -> Str
range: TextRange::default(),
})],
value: Box::new(value.clone()),
type_comment: None,
range: TextRange::default(),
});
generator.stmt(&stmt)

View File

@@ -8,40 +8,6 @@ use crate::checkers::ast::Checker;
use super::helpers;
/// ## What it does
/// Checks for usage of `datetime.datetime.today()`.
///
/// ## Why is this bad?
/// `datetime` objects are "naive" by default, in that they do not include
/// timezone information. "Naive" objects are easy to understand, but ignore
/// some aspects of reality, which can lead to subtle bugs. Timezone-aware
/// `datetime` objects are preferred, as they represent a specific moment in
/// time, unlike "naive" objects.
///
/// `datetime.datetime.today()` crates a "naive" object; instead, use
/// instead, use `datetime.datetime.now(tz=)` to create a timezone-aware
/// object.
///
/// ## Example
/// ```python
/// import datetime
///
/// datetime.datetime.today()
/// ```
///
/// Use instead:
/// ```python
/// import datetime
///
/// datetime.datetime.now(tz=datetime.timezone.utc)
/// ```
///
/// Or, for Python 3.11 and later:
/// ```python
/// import datetime
///
/// datetime.datetime.now(tz=datetime.UTC)
/// ```
#[violation]
pub struct CallDatetimeToday;
@@ -55,6 +21,12 @@ impl Violation for CallDatetimeToday {
}
}
/// Checks for `datetime.datetime.today()`. (DTZ002)
///
/// ## Why is this bad?
///
/// It uses the system local timezone.
/// Use `datetime.datetime.now(tz=)` instead.
pub(crate) fn call_datetime_today(checker: &mut Checker, func: &Expr, location: TextRange) {
if !checker
.semantic()

View File

@@ -14,12 +14,9 @@ use super::helpers;
///
/// ## Why is this bad?
/// `datetime` objects are "naive" by default, in that they do not include
/// timezone information. "Naive" objects are easy to understand, but ignore
/// some aspects of reality, which can lead to subtle bugs. Timezone-aware
/// `datetime` objects are preferred, as they represent a specific moment in
/// time, unlike "naive" objects.
///
/// By providing a `tzinfo` value, a `datetime` can be made timezone-aware.
/// timezone information. By providing a `tzinfo`, a `datetime` can be made
/// timezone "aware". "Naive" objects are easy to understand, but ignore some
/// aspects of reality, which can lead to subtle bugs.
///
/// ## Example
/// ```python

View File

@@ -286,6 +286,7 @@ fn generate_fix(
range: TextRange::default(),
})],
value: Box::new(exc_arg.clone()),
type_comment: None,
range: TextRange::default(),
});

View File

@@ -97,10 +97,6 @@ mod tests {
#[test_case(Rule::UnusedPrivateTypeVar, Path::new("PYI018.pyi"))]
#[test_case(Rule::UnusedPrivateProtocol, Path::new("PYI046.py"))]
#[test_case(Rule::UnusedPrivateProtocol, Path::new("PYI046.pyi"))]
#[test_case(Rule::UnusedPrivateTypeAlias, Path::new("PYI047.py"))]
#[test_case(Rule::UnusedPrivateTypeAlias, Path::new("PYI047.pyi"))]
#[test_case(Rule::UnusedPrivateTypedDict, Path::new("PYI049.py"))]
#[test_case(Rule::UnusedPrivateTypedDict, Path::new("PYI049.pyi"))]
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
let diagnostics = test_path(

View File

@@ -1,6 +1,8 @@
use ruff_python_ast::{self as ast, Expr, Ranged};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::{self as ast, Expr, Ranged};
use smallvec::SmallVec;
use crate::checkers::ast::Checker;
use crate::rules::flake8_pyi::helpers::traverse_union;
@@ -42,7 +44,7 @@ impl Violation for UnnecessaryLiteralUnion {
/// PYI030
pub(crate) fn unnecessary_literal_union<'a>(checker: &mut Checker, expr: &'a Expr) {
let mut literal_exprs = Vec::new();
let mut literal_exprs = SmallVec::<[&Box<Expr>; 1]>::new();
// Adds a member to `literal_exprs` if it is a `Literal` annotation
let mut collect_literal_expr = |expr: &'a Expr, _| {

View File

@@ -1,7 +1,7 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::{self as ast, Expr, Stmt};
use ruff_python_semantic::Scope;
use ruff_python_semantic::Binding;
use crate::checkers::ast::Checker;
@@ -73,255 +73,68 @@ impl Violation for UnusedPrivateProtocol {
}
}
/// ## What it does
/// Checks for the presence of unused private `typing.TypeAlias` definitions.
///
/// ## Why is this bad?
/// A private `typing.TypeAlias` that is defined but not used is likely a
/// mistake, and should either be used, made public, or removed to avoid
/// confusion.
///
/// ## Example
/// ```python
/// import typing
///
/// _UnusedTypeAlias: typing.TypeAlias = int
/// ```
///
/// Use instead:
/// ```python
/// import typing
///
/// _UsedTypeAlias: typing.TypeAlias = int
///
///
/// def func(arg: _UsedTypeAlias) -> _UsedTypeAlias:
/// ...
/// ```
#[violation]
pub struct UnusedPrivateTypeAlias {
name: String,
}
impl Violation for UnusedPrivateTypeAlias {
#[derive_message_formats]
fn message(&self) -> String {
let UnusedPrivateTypeAlias { name } = self;
format!("Private TypeAlias `{name}` is never used")
}
}
/// ## What it does
/// Checks for the presence of unused private `typing.TypedDict` definitions.
///
/// ## Why is this bad?
/// A private `typing.TypedDict` that is defined but not used is likely a
/// mistake, and should either be used, made public, or removed to avoid
/// confusion.
///
/// ## Example
/// ```python
/// import typing
///
///
/// class _UnusedPrivateTypedDict(typing.TypedDict):
/// foo: list[int]
/// ```
///
/// Use instead:
/// ```python
/// import typing
///
///
/// class _UsedPrivateTypedDict(typing.TypedDict):
/// foo: set[str]
///
///
/// def func(arg: _UsedPrivateTypedDict) -> _UsedPrivateTypedDict:
/// ...
/// ```
#[violation]
pub struct UnusedPrivateTypedDict {
name: String,
}
impl Violation for UnusedPrivateTypedDict {
#[derive_message_formats]
fn message(&self) -> String {
let UnusedPrivateTypedDict { name } = self;
format!("Private TypedDict `{name}` is never used")
}
}
/// PYI018
pub(crate) fn unused_private_type_var(
checker: &Checker,
scope: &Scope,
diagnostics: &mut Vec<Diagnostic>,
) {
for binding in scope
.binding_ids()
.map(|binding_id| checker.semantic().binding(binding_id))
{
if !(binding.kind.is_assignment() && binding.is_private_declaration()) {
continue;
}
if binding.is_used() {
continue;
}
let Some(source) = binding.source else {
continue;
};
let Stmt::Assign(ast::StmtAssign { targets, value, .. }) = checker.semantic().stmts[source]
else {
continue;
};
let [Expr::Name(ast::ExprName { id, .. })] = &targets[..] else {
continue;
};
let Expr::Call(ast::ExprCall { func, .. }) = value.as_ref() else {
continue;
};
if !checker.semantic().match_typing_expr(func, "TypeVar") {
continue;
}
diagnostics.push(Diagnostic::new(
UnusedPrivateTypeVar {
name: id.to_string(),
},
binding.range,
));
pub(crate) fn unused_private_type_var(checker: &Checker, binding: &Binding) -> Option<Diagnostic> {
if !(binding.kind.is_assignment() && binding.is_private_declaration()) {
return None;
}
if binding.is_used() {
return None;
}
let Some(source) = binding.source else {
return None;
};
let Stmt::Assign(ast::StmtAssign { targets, value, .. }) = checker.semantic().stmts[source]
else {
return None;
};
let [Expr::Name(ast::ExprName { id, .. })] = &targets[..] else {
return None;
};
let Expr::Call(ast::ExprCall { func, .. }) = value.as_ref() else {
return None;
};
if !checker.semantic().match_typing_expr(func, "TypeVar") {
return None;
}
Some(Diagnostic::new(
UnusedPrivateTypeVar {
name: id.to_string(),
},
binding.range,
))
}
/// PYI046
pub(crate) fn unused_private_protocol(
checker: &Checker,
scope: &Scope,
diagnostics: &mut Vec<Diagnostic>,
) {
for binding in scope
.binding_ids()
.map(|binding_id| checker.semantic().binding(binding_id))
{
if !(binding.kind.is_class_definition() && binding.is_private_declaration()) {
continue;
}
if binding.is_used() {
continue;
}
let Some(source) = binding.source else {
continue;
};
let Stmt::ClassDef(ast::StmtClassDef { name, bases, .. }) =
checker.semantic().stmts[source]
else {
continue;
};
if !bases
.iter()
.any(|base| checker.semantic().match_typing_expr(base, "Protocol"))
{
continue;
}
diagnostics.push(Diagnostic::new(
UnusedPrivateProtocol {
name: name.to_string(),
},
binding.range,
));
pub(crate) fn unused_private_protocol(checker: &Checker, binding: &Binding) -> Option<Diagnostic> {
if !(binding.kind.is_class_definition() && binding.is_private_declaration()) {
return None;
}
}
/// PYI047
pub(crate) fn unused_private_type_alias(
checker: &Checker,
scope: &Scope,
diagnostics: &mut Vec<Diagnostic>,
) {
for binding in scope
.binding_ids()
.map(|binding_id| checker.semantic().binding(binding_id))
{
if !(binding.kind.is_assignment() && binding.is_private_declaration()) {
continue;
}
if binding.is_used() {
continue;
}
let Some(source) = binding.source else {
continue;
};
let Stmt::AnnAssign(ast::StmtAnnAssign {
target, annotation, ..
}) = checker.semantic().stmts[source]
else {
continue;
};
let Some(ast::ExprName { id, .. }) = target.as_name_expr() else {
continue;
};
if !checker
.semantic()
.match_typing_expr(annotation, "TypeAlias")
{
continue;
}
diagnostics.push(Diagnostic::new(
UnusedPrivateTypeAlias {
name: id.to_string(),
},
binding.range,
));
if binding.is_used() {
return None;
}
}
/// PYI049
pub(crate) fn unused_private_typed_dict(
checker: &Checker,
scope: &Scope,
diagnostics: &mut Vec<Diagnostic>,
) {
for binding in scope
.binding_ids()
.map(|binding_id| checker.semantic().binding(binding_id))
let Some(source) = binding.source else {
return None;
};
let Stmt::ClassDef(ast::StmtClassDef { name, bases, .. }) = checker.semantic().stmts[source]
else {
return None;
};
if !bases
.iter()
.any(|base| checker.semantic().match_typing_expr(base, "Protocol"))
{
if !(binding.kind.is_class_definition() && binding.is_private_declaration()) {
continue;
}
if binding.is_used() {
continue;
}
let Some(source) = binding.source else {
continue;
};
let Stmt::ClassDef(ast::StmtClassDef { name, bases, .. }) =
checker.semantic().stmts[source]
else {
continue;
};
if !bases
.iter()
.any(|base| checker.semantic().match_typing_expr(base, "TypedDict"))
{
continue;
}
diagnostics.push(Diagnostic::new(
UnusedPrivateTypedDict {
name: name.to_string(),
},
binding.range,
));
return None;
}
Some(Diagnostic::new(
UnusedPrivateProtocol {
name: name.to_string(),
},
binding.range,
))
}

View File

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

View File

@@ -1,20 +0,0 @@
---
source: crates/ruff/src/rules/flake8_pyi/mod.rs
---
PYI047.pyi:6:1: PYI047 Private TypeAlias `_UnusedPrivateTypeAlias` is never used
|
6 | _UnusedPrivateTypeAlias: TypeAlias = int | None
| ^^^^^^^^^^^^^^^^^^^^^^^ PYI047
7 | _T: typing.TypeAlias = str
|
PYI047.pyi:7:1: PYI047 Private TypeAlias `_T` is never used
|
6 | _UnusedPrivateTypeAlias: TypeAlias = int | None
7 | _T: typing.TypeAlias = str
| ^^ PYI047
8 |
9 | # OK
|

View File

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

View File

@@ -1,18 +0,0 @@
---
source: crates/ruff/src/rules/flake8_pyi/mod.rs
---
PYI049.pyi:6:7: PYI049 Private TypedDict `_UnusedTypedDict` is never used
|
6 | class _UnusedTypedDict(TypedDict):
| ^^^^^^^^^^^^^^^^ PYI049
7 | foo: str
|
PYI049.pyi:10:7: PYI049 Private TypedDict `_UnusedTypedDict2` is never used
|
10 | class _UnusedTypedDict2(typing.TypedDict):
| ^^^^^^^^^^^^^^^^^ PYI049
11 | bar: int
|

View File

@@ -69,34 +69,6 @@ impl Violation for PytestCompositeAssertion {
}
}
/// ## What it does
/// Checks for `assert` statements in `except` clauses.
///
/// ## Why is this bad?
/// When testing for exceptions, `pytest.raises()` should be used instead of
/// `assert` statements in `except` clauses, as it's more explicit and
/// idiomatic. Further, `pytest.raises()` will fail if the exception is _not_
/// raised, unlike the `assert` statement.
///
/// ## Example
/// ```python
/// def test_foo():
/// try:
/// 1 / 0
/// except ZeroDivisionError as e:
/// assert e.args
/// ```
///
/// Use instead:
/// ```python
/// def test_foo():
/// with pytest.raises(ZeroDivisionError) as exc_info:
/// 1 / 0
/// assert exc_info.value.args
/// ```
///
/// ## References
/// - [`pytest` documentation: `pytest.raises`](https://docs.pytest.org/en/latest/reference/reference.html#pytest-raises)
#[violation]
pub struct PytestAssertInExcept {
name: String,

View File

@@ -45,7 +45,7 @@ use super::helpers::{is_empty_or_null_string, is_pytest_fail};
/// ```
///
/// ## References
/// - [`pytest` documentation: `pytest.fail`](https://docs.pytest.org/en/latest/reference/reference.html#pytest-fail)
/// - [API Reference: `pytest.fail`](https://docs.pytest.org/en/latest/reference/reference.html#pytest-fail)
#[violation]
pub struct PytestFailWithoutMessage;

View File

@@ -53,7 +53,7 @@ use super::helpers::{
/// - `flake8-pytest-style.fixture-parentheses`
///
/// ## References
/// - [`pytest` documentation: API Reference: Fixtures](https://docs.pytest.org/en/latest/reference/reference.html#fixtures-api)
/// - [API Reference: Fixtures](https://docs.pytest.org/en/latest/reference/reference.html#fixtures-api)
#[violation]
pub struct PytestFixtureIncorrectParenthesesStyle {
expected: Parentheses,
@@ -129,47 +129,6 @@ impl Violation for PytestIncorrectFixtureNameUnderscore {
}
}
/// ## What it does
/// Checks for `pytest` test functions that should be decorated with
/// `@pytest.mark.usefixtures`.
///
/// ## Why is this bad?
/// In `pytest`, fixture injection is used to activate fixtures in a test
/// function.
///
/// Fixtures can be injected either by passing them as parameters to the test
/// function, or by using the `@pytest.mark.usefixtures` decorator.
///
/// If the test function depends on the fixture being activated, but does not
/// use it in the test body or otherwise rely on its return value, prefer
/// the `@pytest.mark.usefixtures` decorator, to make the dependency explicit
/// and avoid the confusion caused by unused arguments.
///
/// ## Example
/// ```python
/// @pytest.fixture
/// def _patch_something():
/// ...
///
///
/// def test_foo(_patch_something):
/// ...
/// ```
///
/// Use instead:
/// ```python
/// @pytest.fixture
/// def _patch_something():
/// ...
///
///
/// @pytest.mark.usefixtures("_patch_something")
/// def test_foo():
/// ...
/// ```
///
/// ## References
/// - [`pytest` documentation: `pytest.mark.usefixtures`](https://docs.pytest.org/en/latest/reference/reference.html#pytest-mark-usefixtures)
#[violation]
pub struct PytestFixtureParamWithoutValue {
name: String,
@@ -217,7 +176,7 @@ impl Violation for PytestFixtureParamWithoutValue {
/// ```
///
/// ## References
/// - [`pytest` documentation: `yield_fixture` functions](https://docs.pytest.org/en/latest/yieldfixture.html)
/// - [`yield_fixture` functions](https://docs.pytest.org/en/latest/yieldfixture.html)
#[violation]
pub struct PytestDeprecatedYieldFixture;
@@ -228,49 +187,6 @@ impl Violation for PytestDeprecatedYieldFixture {
}
}
/// ## What it does
/// Checks for unnecessary `request.addfinalizer` usages in `pytest` fixtures.
///
/// ## Why is this bad?
/// `pytest` offers two ways to perform cleanup in fixture code. The first is
/// sequential (via the `yield` statement), the second callback-based (via
/// `request.addfinalizer`).
///
/// The sequential approach is more readable and should be preferred, unless
/// the fixture uses the "factory as fixture" pattern.
///
/// ## Example
/// ```python
/// @pytest.fixture()
/// def my_fixture(request):
/// resource = acquire_resource()
/// request.addfinalizer(resource.release)
/// return resource
/// ```
///
/// Use instead:
/// ```python
/// @pytest.fixture()
/// def my_fixture():
/// resource = acquire_resource()
/// yield resource
/// resource.release()
///
///
/// # "factory-as-fixture" pattern
/// @pytest.fixture()
/// def my_factory(request):
/// def create_resource(arg):
/// resource = acquire_resource(arg)
/// request.addfinalizer(resource.release)
/// return resource
///
/// return create_resource
/// ```
///
/// ## References
/// - [`pytest` documentation: Adding finalizers directly](https://docs.pytest.org/en/latest/how-to/fixtures.html#adding-finalizers-directly)
/// - [`pytest` documentation: Factories as fixtures](https://docs.pytest.org/en/latest/how-to/fixtures.html#factories-as-fixtures)
#[violation]
pub struct PytestFixtureFinalizerCallback;
@@ -280,39 +196,7 @@ impl Violation for PytestFixtureFinalizerCallback {
format!("Use `yield` instead of `request.addfinalizer`")
}
}
/// ## What it does
/// Checks for unnecessary `yield` expressions in `pytest` fixtures.
///
/// ## Why is this bad?
/// In `pytest` fixtures, the `yield` expression should only be used for fixtures
/// that include teardown code, to clean up the fixture after the test function
/// has finished executing.
///
/// ## Example
/// ```python
/// @pytest.fixture()
/// def my_fixture():
/// resource = acquire_resource()
/// yield resource
/// ```
///
/// Use instead:
/// ```python
/// @pytest.fixture()
/// def my_fixture_with_teardown():
/// resource = acquire_resource()
/// yield resource
/// resource.release()
///
///
/// @pytest.fixture()
/// def my_fixture_without_teardown():
/// resource = acquire_resource()
/// return resource
/// ```
///
/// ## References
/// - [`pytest` documentation: Teardown/Cleanup](https://docs.pytest.org/en/latest/how-to/fixtures.html#teardown-cleanup-aka-fixture-finalization)
#[violation]
pub struct PytestUselessYieldFixture {
name: String,

View File

@@ -9,37 +9,6 @@ use crate::registry::{AsRule, Rule};
use super::helpers::get_mark_decorators;
/// ## What it does
/// Checks for parameter-free `@pytest.mark.<marker>()` decorators with or
/// without parentheses, depending on the `flake8-pytest-style.mark-parentheses`
/// setting.
///
/// ## Why is this bad?
/// If a `@pytest.mark.<marker>()` doesn't take any arguments, the parentheses are
/// optional.
///
/// Either removing those unnecessary parentheses _or_ requiring them for all
/// fixtures is fine, but it's best to be consistent.
///
/// ## Example
/// ```python
/// @pytest.mark.foo
/// def test_something():
/// ...
/// ```
///
/// Use instead:
/// ```python
/// @pytest.mark.foo()
/// def test_something():
/// ...
/// ```
///
/// ## Options
/// - `flake8-pytest-style.mark-parentheses`
///
/// ## References
/// - [`pytest` documentation: Marks](https://docs.pytest.org/en/latest/reference/reference.html#marks)
#[violation]
pub struct PytestIncorrectMarkParenthesesStyle {
mark_name: String,

View File

@@ -66,7 +66,7 @@ impl Violation for PytestRaisesTooBroad {
/// ```
///
/// ## References
/// - [`pytest` documentation: `pytest.raises`](https://docs.pytest.org/en/latest/reference/reference.html#pytest-raises)
/// - [API Reference: `pytest.raises`](https://docs.pytest.org/en/latest/reference/reference.html#pytest-raises)
#[violation]
pub struct PytestRaisesWithoutException;

View File

@@ -566,6 +566,7 @@ fn ternary(target_var: &Expr, body_value: &Expr, test: &Expr, orelse_value: &Exp
let node1 = ast::StmtAssign {
targets: vec![target_var.clone()],
value: Box::new(node.into()),
type_comment: None,
range: TextRange::default(),
};
node1.into()
@@ -956,6 +957,7 @@ pub(crate) fn use_dict_get_with_default(checker: &mut Checker, stmt_if: &StmtIf)
let node5 = ast::StmtAssign {
targets: vec![node4],
value: Box::new(node3.into()),
type_comment: None,
range: TextRange::default(),
};
let contents = checker.generator().stmt(&node5.into());

View File

@@ -82,25 +82,13 @@ fn key_in_dict(
return;
}
let Expr::Attribute(ast::ExprAttribute { attr, value, .. }) = func.as_ref() else {
let Expr::Attribute(ast::ExprAttribute { attr, .. }) = func.as_ref() else {
return;
};
if attr != "keys" {
return;
}
// Ignore `self.keys()`, which will almost certainly be intentional, as in:
// ```python
// def __contains__(self, key: object) -> bool:
// return key in self.keys()
// ```
if value
.as_name_expr()
.map_or(false, |name| matches!(name.id.as_str(), "self"))
{
return;
}
// Slice exact content to preserve formatting.
let left_content = checker.locator().slice(left.range());
let Ok(value_content) =

View File

@@ -105,32 +105,13 @@ fn match_exit_stack(semantic: &SemanticModel) -> bool {
false
}
/// Return `true` if `func` is the builtin `open` or `pathlib.Path(...).open`.
fn is_open(checker: &mut Checker, func: &Expr) -> bool {
match func {
// pathlib.Path(...).open()
Expr::Attribute(ast::ExprAttribute { attr, value, .. }) if attr.as_str() == "open" => {
match value.as_ref() {
Expr::Call(ast::ExprCall { func, .. }) => checker
.semantic()
.resolve_call_path(func)
.map_or(false, |call_path| {
matches!(call_path.as_slice(), ["pathlib", "Path"])
}),
_ => false,
}
}
// open(...)
Expr::Name(ast::ExprName { id, .. }) => {
id.as_str() == "open" && checker.semantic().is_builtin("open")
}
_ => false,
}
}
/// SIM115
pub(crate) fn open_file_with_context_handler(checker: &mut Checker, func: &Expr) {
if !is_open(checker, func) {
let Expr::Name(ast::ExprName { id, .. }) = func else {
return;
};
if id.as_str() != "open" {
return;
}
@@ -139,6 +120,10 @@ pub(crate) fn open_file_with_context_handler(checker: &mut Checker, func: &Expr)
return;
}
if !checker.semantic().is_builtin("open") {
return;
}
// Ex) `with contextlib.ExitStack() as exit_stack: ...`
if match_exit_stack(checker.semantic()) {
return;

View File

@@ -1,63 +1,23 @@
---
source: crates/ruff/src/rules/flake8_simplify/mod.rs
---
SIM115.py:8:5: SIM115 Use context handler for opening files
|
7 | # SIM115
8 | f = open("foo.txt")
| ^^^^ SIM115
9 | f = Path("foo.txt").open()
10 | f = pathlib.Path("foo.txt").open()
|
SIM115.py:4:5: SIM115 Use context handler for opening files
|
3 | # SIM115
4 | f = open("foo.txt")
| ^^^^ SIM115
5 | data = f.read()
6 | f.close()
|
SIM115.py:9:5: SIM115 Use context handler for opening files
SIM115.py:31:9: SIM115 Use context handler for opening files
|
7 | # SIM115
8 | f = open("foo.txt")
9 | f = Path("foo.txt").open()
| ^^^^^^^^^^^^^^^^^^^^ SIM115
10 | f = pathlib.Path("foo.txt").open()
11 | f = pl.Path("foo.txt").open()
|
SIM115.py:10:5: SIM115 Use context handler for opening files
|
8 | f = open("foo.txt")
9 | f = Path("foo.txt").open()
10 | f = pathlib.Path("foo.txt").open()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM115
11 | f = pl.Path("foo.txt").open()
12 | f = P("foo.txt").open()
|
SIM115.py:11:5: SIM115 Use context handler for opening files
|
9 | f = Path("foo.txt").open()
10 | f = pathlib.Path("foo.txt").open()
11 | f = pl.Path("foo.txt").open()
| ^^^^^^^^^^^^^^^^^^^^^^^ SIM115
12 | f = P("foo.txt").open()
13 | data = f.read()
|
SIM115.py:12:5: SIM115 Use context handler for opening files
|
10 | f = pathlib.Path("foo.txt").open()
11 | f = pl.Path("foo.txt").open()
12 | f = P("foo.txt").open()
| ^^^^^^^^^^^^^^^^^ SIM115
13 | data = f.read()
14 | f.close()
|
SIM115.py:39:9: SIM115 Use context handler for opening files
|
37 | # SIM115
38 | with contextlib.ExitStack():
39 | f = open("filename")
29 | # SIM115
30 | with contextlib.ExitStack():
31 | f = open("filename")
| ^^^^ SIM115
40 |
41 | # OK
32 |
33 | # OK
|

View File

@@ -265,7 +265,6 @@ SIM118.py:30:8: SIM118 [*] Use `k in obj` instead of `k in obj.keys()`
30 |+(k for k in obj) # SIM118
31 31 |
32 32 | key in (obj or {}).keys() # SIM118
33 33 |
SIM118.py:32:1: SIM118 [*] Use `key in (obj or {})` instead of `key in (obj or {}).keys()`
|
@@ -273,8 +272,6 @@ SIM118.py:32:1: SIM118 [*] Use `key in (obj or {})` instead of `key in (obj or {
31 |
32 | key in (obj or {}).keys() # SIM118
| ^^^^^^^^^^^^^^^^^^^^^^^^^ SIM118
33 |
34 | from typing import KeysView
|
= help: Convert to `key in (obj or {})`
@@ -284,8 +281,5 @@ SIM118.py:32:1: SIM118 [*] Use `key in (obj or {})` instead of `key in (obj or {
31 31 |
32 |-key in (obj or {}).keys() # SIM118
32 |+key in (obj or {}) # SIM118
33 33 |
34 34 | from typing import KeysView
35 35 |

View File

@@ -15,13 +15,10 @@ mod tests {
use crate::test::{test_path, test_snippet};
use crate::{assert_messages, settings};
#[test_case(Rule::EmptyTypeCheckingBlock, Path::new("TCH005.py"))]
#[test_case(Rule::TypingOnlyFirstPartyImport, Path::new("TCH001.py"))]
#[test_case(Rule::TypingOnlyThirdPartyImport, Path::new("TCH002.py"))]
#[test_case(Rule::TypingOnlyStandardLibraryImport, Path::new("TCH003.py"))]
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_1.py"))]
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_10.py"))]
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_11.py"))]
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_12.py"))]
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_13.py"))]
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_14.pyi"))]
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_2.py"))]
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_3.py"))]
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_4.py"))]
@@ -30,10 +27,12 @@ mod tests {
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_7.py"))]
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_8.py"))]
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_9.py"))]
#[test_case(Rule::TypingOnlyFirstPartyImport, Path::new("TCH001.py"))]
#[test_case(Rule::TypingOnlyStandardLibraryImport, Path::new("TCH003.py"))]
#[test_case(Rule::TypingOnlyStandardLibraryImport, Path::new("snapshot.py"))]
#[test_case(Rule::TypingOnlyThirdPartyImport, Path::new("TCH002.py"))]
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_10.py"))]
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_11.py"))]
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_12.py"))]
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_13.py"))]
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_14.pyi"))]
#[test_case(Rule::EmptyTypeCheckingBlock, Path::new("TCH005.py"))]
#[test_case(Rule::TypingOnlyThirdPartyImport, Path::new("strict.py"))]
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
let snapshot = format!("{}_{}", rule_code.as_ref(), path.to_string_lossy());

View File

@@ -317,7 +317,10 @@ pub(crate) fn unused_arguments(
scope: &Scope,
diagnostics: &mut Vec<Diagnostic>,
) {
let Some(parent) = &checker.semantic().first_non_type_parent_scope(scope) else {
let Some(parent) = scope
.parent
.map(|scope_id| &checker.semantic().scopes[scope_id])
else {
return;
};

View File

@@ -5,7 +5,6 @@ use ruff_text_size::TextRange;
fn to_formatted_value_expr(inner: &Expr) -> Expr {
let node = ast::ExprFormattedValue {
value: Box::new(inner.clone()),
debug_text: None,
conversion: ConversionFlag::None,
format_spec: None,
range: TextRange::default(),

View File

@@ -9,24 +9,6 @@ use crate::checkers::ast::Checker;
use crate::registry::AsRule;
use crate::rules::flynt::helpers;
/// ## What it does
/// Checks for `str#join` calls that can be replaced with f-strings.
///
/// ## Why is this bad?
/// f-strings are more readable and generally preferred over `str#join` calls.
///
/// ## Example
/// ```python
/// " ".join((foo, bar))
/// ```
///
/// Use instead:
/// ```python
/// f"{foo} {bar}"
/// ```
///
/// ## References
/// - [Python documentation: f-strings](https://docs.python.org/3/reference/lexical_analysis.html#f-strings)
#[violation]
pub struct StaticJoinToFString {
expr: String,

View File

@@ -314,8 +314,6 @@ mod tests {
#[test_case(Path::new("add_newline_before_comments.py"))]
#[test_case(Path::new("as_imports_comments.py"))]
#[test_case(Path::new("bom_sorted.py"))]
#[test_case(Path::new("bom_unsorted.py"))]
#[test_case(Path::new("combine_as_imports.py"))]
#[test_case(Path::new("combine_import_from.py"))]
#[test_case(Path::new("comments.py"))]

View File

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

View File

@@ -1,18 +0,0 @@
---
source: crates/ruff/src/rules/isort/mod.rs
---
bom_unsorted.py:1:1: I001 [*] Import block is un-sorted or un-formatted
|
1 | import foo
| _^
2 | | import bar
|
= help: Organize imports
Fix
1 |-import foo
2 |-import bar
1 |+import bar
2 |+import foo

View File

@@ -9,6 +9,21 @@ use ruff_macros::{CacheKey, CombineOptions, ConfigurationOptions};
use crate::settings::types::IdentifierPattern;
const IGNORE_NAMES: [&str; 12] = [
"setUp",
"tearDown",
"setUpClass",
"tearDownClass",
"setUpModule",
"tearDownModule",
"asyncSetUp",
"asyncTearDown",
"setUpTestData",
"failureException",
"longMessage",
"maxDiff",
];
#[derive(
Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, CombineOptions,
)]
@@ -28,14 +43,6 @@ pub struct Options {
)]
/// A list of names (or patterns) to ignore when considering `pep8-naming` violations.
pub ignore_names: Option<Vec<String>>,
#[option(
default = r#"[]"#,
value_type = "list[str]",
example = r#"extend-ignore-names = ["callMethod"]"#
)]
/// Additional names (or patterns) to ignore when considering `pep8-naming` violations,
/// in addition to those included in `ignore-names`.
pub extend_ignore_names: Option<Vec<String>>,
#[option(
default = r#"[]"#,
value_type = "list[str]",
@@ -75,29 +82,12 @@ pub struct Settings {
pub staticmethod_decorators: Vec<String>,
}
fn default_ignore_names() -> Vec<String> {
vec![
"setUp".to_string(),
"tearDown".to_string(),
"setUpClass".to_string(),
"tearDownClass".to_string(),
"setUpModule".to_string(),
"tearDownModule".to_string(),
"asyncSetUp".to_string(),
"asyncTearDown".to_string(),
"setUpTestData".to_string(),
"failureException".to_string(),
"longMessage".to_string(),
"maxDiff".to_string(),
]
}
impl Default for Settings {
fn default() -> Self {
Self {
ignore_names: default_ignore_names()
.into_iter()
.map(|name| IdentifierPattern::new(&name).unwrap())
ignore_names: IGNORE_NAMES
.iter()
.map(|name| IdentifierPattern::new(name).unwrap())
.collect(),
classmethod_decorators: Vec::new(),
staticmethod_decorators: Vec::new(),
@@ -110,13 +100,18 @@ impl TryFrom<Options> for Settings {
fn try_from(options: Options) -> Result<Self, Self::Error> {
Ok(Self {
ignore_names: options
.ignore_names
.unwrap_or_else(default_ignore_names)
.into_iter()
.chain(options.extend_ignore_names.unwrap_or_default().into_iter())
.map(|name| IdentifierPattern::new(&name).map_err(SettingsError::InvalidIgnoreName))
.collect::<Result<Vec<_>, Self::Error>>()?,
ignore_names: match options.ignore_names {
Some(names) => names
.into_iter()
.map(|name| {
IdentifierPattern::new(&name).map_err(SettingsError::InvalidIgnoreName)
})
.collect::<Result<Vec<_>, Self::Error>>()?,
None => IGNORE_NAMES
.into_iter()
.map(|name| IdentifierPattern::new(name).unwrap())
.collect(),
},
classmethod_decorators: options.classmethod_decorators.unwrap_or_default(),
staticmethod_decorators: options.staticmethod_decorators.unwrap_or_default(),
})
@@ -157,7 +152,6 @@ impl From<Settings> for Options {
.map(|pattern| pattern.as_str().to_owned())
.collect(),
),
extend_ignore_names: None,
classmethod_decorators: Some(settings.classmethod_decorators),
staticmethod_decorators: Some(settings.staticmethod_decorators),
}

View File

@@ -2,7 +2,6 @@ use ruff_python_ast::{self as ast, Expr, Stmt};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::comparable::ComparableExpr;
use ruff_python_ast::helpers::any_over_expr;
use crate::checkers::ast::Checker;
@@ -130,13 +129,6 @@ pub(crate) fn manual_list_comprehension(checker: &mut Checker, target: &Expr, bo
return;
}
// Avoid, e.g., `for x in y: filtered.append(filtered[-1] * 2)`.
if any_over_expr(arg, &|expr| {
ComparableExpr::from(expr) == ComparableExpr::from(value)
}) {
return;
}
// Avoid if the value is used in the conditional test, e.g.,
//
// ```python

View File

@@ -1,7 +1,8 @@
use ruff_python_ast::{self as ast, Expr, Stmt};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::helpers::any_over_expr;
use ruff_python_ast::{self as ast, Expr, Stmt};
use crate::checkers::ast::Checker;
@@ -88,7 +89,7 @@ pub(crate) fn manual_list_copy(checker: &mut Checker, target: &Expr, body: &[Stm
return;
}
// Avoid, e.g., `for x in y: filtered[x].append(x)`.
// Avoid, e.g., `for x in y: filtered[x].append(x * x)`.
if any_over_expr(value, &|expr| {
expr.as_name_expr().map_or(false, |expr| expr.id == *id)
}) {

View File

@@ -67,15 +67,14 @@ pub(crate) fn try_except_in_loop(checker: &mut Checker, body: &[Stmt]) {
return;
}
let [Stmt::Try(ast::StmtTry { handlers, .. })] = body else {
return;
};
let Some(handler) = handlers.first() else {
return;
};
checker
.diagnostics
.push(Diagnostic::new(TryExceptInLoop, handler.range()));
checker.diagnostics.extend(body.iter().filter_map(|stmt| {
if let Stmt::Try(ast::StmtTry { handlers, .. }) = stmt {
handlers
.iter()
.next()
.map(|handler| Diagnostic::new(TryExceptInLoop, handler.range()))
} else {
None
}
}));
}

View File

@@ -1,16 +1,28 @@
---
source: crates/ruff/src/rules/perflint/mod.rs
---
PERF203.py:5:5: PERF203 `try`-`except` within a loop incurs performance overhead
PERF203.py:4:5: PERF203 `try`-`except` within a loop incurs performance overhead
|
3 | try:
4 | print(f"{i}")
5 | except:
2 | try: # PERF203
3 | print(f"{i}")
4 | except:
| _____^
6 | | print("error")
5 | | print("error")
| |______________________^ PERF203
7 |
8 | # OK
6 |
7 | try:
|
PERF203.py:17:5: PERF203 `try`-`except` within a loop incurs performance overhead
|
15 | try:
16 | print(f"{i}")
17 | except:
| _____^
18 | | print("error")
| |______________________^ PERF203
19 |
20 | i += 1
|

View File

@@ -70,7 +70,6 @@ mod tests {
#[test_case(Rule::IndentationWithInvalidMultiple, Path::new("E11.py"))]
#[test_case(Rule::IndentationWithInvalidMultipleComment, Path::new("E11.py"))]
#[test_case(Rule::MultipleLeadingHashesForBlockComment, Path::new("E26.py"))]
#[test_case(Rule::MultipleSpacesAfterComma, Path::new("E24.py"))]
#[test_case(Rule::MultipleSpacesAfterKeyword, Path::new("E27.py"))]
#[test_case(Rule::MultipleSpacesAfterOperator, Path::new("E22.py"))]
#[test_case(Rule::MultipleSpacesBeforeKeyword, Path::new("E27.py"))]
@@ -81,7 +80,6 @@ mod tests {
#[test_case(Rule::NoSpaceAfterBlockComment, Path::new("E26.py"))]
#[test_case(Rule::NoSpaceAfterInlineComment, Path::new("E26.py"))]
#[test_case(Rule::OverIndented, Path::new("E11.py"))]
#[test_case(Rule::TabAfterComma, Path::new("E24.py"))]
#[test_case(Rule::TabAfterKeyword, Path::new("E27.py"))]
#[test_case(Rule::TabAfterOperator, Path::new("E22.py"))]
#[test_case(Rule::TabBeforeKeyword, Path::new("E27.py"))]

View File

@@ -229,6 +229,7 @@ fn function(
decorator_list: vec![],
returns: Some(Box::new(return_type)),
type_params: vec![],
type_comment: None,
range: TextRange::default(),
});
return generator.stmt(&func);
@@ -241,6 +242,7 @@ fn function(
decorator_list: vec![],
returns: None,
type_params: vec![],
type_comment: None,
range: TextRange::default(),
});
generator.stmt(&func)

View File

@@ -5,26 +5,6 @@ use ruff_python_parser::TokenKind;
use crate::checkers::logical_lines::LogicalLinesContext;
use crate::rules::pycodestyle::rules::logical_lines::LogicalLine;
/// ## What it does
/// Checks for missing whitespace around all operators.
///
/// ## Why is this bad?
/// According to [PEP 8], there should be one space before and after all
/// operators.
///
/// ## Example
/// ```python
/// if number==42:
/// print('you have found the meaning of life')
/// ```
///
/// Use instead:
/// ```python
/// if number == 42:
/// print('you have found the meaning of life')
/// ```
///
/// [PEP 8]: https://peps.python.org/pep-0008/#pet-peeves
// E225
#[violation]
pub struct MissingWhitespaceAroundOperator;
@@ -36,24 +16,6 @@ impl Violation for MissingWhitespaceAroundOperator {
}
}
/// ## What it does
/// Checks for missing whitespace arithmetic operators.
///
/// ## Why is this bad?
/// According to [PEP 8], there should be one space before and after an
/// arithmetic operator (+, -, /, and *).
///
/// ## Example
/// ```python
/// number = 40+2
/// ```
///
/// Use instead:
/// ```python
/// number = 40 + 2
/// ```
///
/// [PEP 8]: https://peps.python.org/pep-0008/#pet-peeves
// E226
#[violation]
pub struct MissingWhitespaceAroundArithmeticOperator;
@@ -65,24 +27,6 @@ impl Violation for MissingWhitespaceAroundArithmeticOperator {
}
}
/// ## What it does
/// Checks for missing whitespace around bitwise and shift operators.
///
/// ## Why is this bad?
/// According to [PEP 8], there should be one space before and after bitwise and
/// shift operators (<<, >>, &, |, ^).
///
/// ## Example
/// ```python
/// x = 128<<1
/// ```
///
/// Use instead:
/// ```python
/// x = 128 << 1
/// ```
///
/// [PEP 8]: https://peps.python.org/pep-0008/#pet-peeves
// E227
#[violation]
pub struct MissingWhitespaceAroundBitwiseOrShiftOperator;
@@ -94,24 +38,6 @@ impl Violation for MissingWhitespaceAroundBitwiseOrShiftOperator {
}
}
/// ## What it does
/// Checks for missing whitespace around the modulo operator.
///
/// ## Why is this bad?
/// According to [PEP 8], the modulo operator (%) should have whitespace on
/// either side of it.
///
/// ## Example
/// ```python
/// remainder = 10%2
/// ```
///
/// Use instead:
/// ```python
/// remainder = 10 % 2
/// ```
///
/// [PEP 8]: https://peps.python.org/pep-0008/#other-recommendations
// E228
#[violation]
pub struct MissingWhitespaceAroundModuloOperator;

View File

@@ -120,57 +120,6 @@ impl Violation for MultipleSpacesAfterOperator {
}
}
/// ## What it does
/// Checks for extraneous tabs after a comma.
///
/// ## Why is this bad?
/// Commas should be followed by one space, never tabs.
///
/// ## Example
/// ```python
/// a = 4,\t5
/// ```
///
/// Use instead:
/// ```python
/// a = 4, 3
/// ```
///
#[violation]
pub struct TabAfterComma;
impl Violation for TabAfterComma {
#[derive_message_formats]
fn message(&self) -> String {
format!("Tab after comma")
}
}
/// ## What it does
/// Checks for extraneous whitespace after a comma.
///
/// ## Why is this bad?
/// According to the `black` code style, commas should be followed by a single space.
///
/// ## Example
/// ```python
/// a = 4, 5
/// ```
///
/// Use instead:
/// ```python
/// a = 4, 5
/// ```
#[violation]
pub struct MultipleSpacesAfterComma;
impl Violation for MultipleSpacesAfterComma {
#[derive_message_formats]
fn message(&self) -> String {
format!("Multiple spaces after comma")
}
}
/// E221, E222, E223, E224
pub(crate) fn space_around_operator(line: &LogicalLine, context: &mut LogicalLinesContext) {
let mut after_operator = false;
@@ -212,23 +161,6 @@ pub(crate) fn space_around_operator(line: &LogicalLine, context: &mut LogicalLin
}
}
/// E241, E242
pub(crate) fn space_after_comma(line: &LogicalLine, context: &mut LogicalLinesContext) {
for token in line.tokens() {
if matches!(token.kind(), TokenKind::Comma) {
match line.trailing_whitespace(token) {
(Whitespace::Tab, len) => {
context.push(TabAfterComma, TextRange::at(token.end(), len));
}
(Whitespace::Many, len) => {
context.push(MultipleSpacesAfterComma, TextRange::at(token.end(), len));
}
_ => {}
}
}
}
}
const fn is_operator_token(token: TokenKind) -> bool {
matches!(
token,

View File

@@ -8,11 +8,11 @@ use crate::checkers::logical_lines::LogicalLinesContext;
use crate::rules::pycodestyle::rules::logical_lines::LogicalLine;
/// ## What it does
/// Checks for extraneous whitespace immediately preceding an open parenthesis
/// Checks for extraneous whitespace immediately after an open parenthesis
/// or bracket.
///
/// ## Why is this bad?
/// According to [PEP 8], open parentheses and brackets should not be preceded
/// According to [PEP 8], open parentheses and brackets should not be followed
/// by any trailing whitespace.
///
/// ## Example

View File

@@ -1,40 +0,0 @@
---
source: crates/ruff/src/rules/pycodestyle/mod.rs
---
E24.py:2:8: E241 Multiple spaces after comma
|
1 | #: E241
2 | a = (1, 2)
| ^^ E241
3 | #: Okay
4 | b = (1, 20)
|
E24.py:11:18: E241 Multiple spaces after comma
|
9 | #: E241 E241 E241
10 | # issue 135
11 | more_spaces = [a, b,
| ^^^^ E241
12 | ef, +h,
13 | c, -d]
|
E24.py:12:19: E241 Multiple spaces after comma
|
10 | # issue 135
11 | more_spaces = [a, b,
12 | ef, +h,
| ^^ E241
13 | c, -d]
|
E24.py:13:18: E241 Multiple spaces after comma
|
11 | more_spaces = [a, b,
12 | ef, +h,
13 | c, -d]
| ^^^ E241
|

View File

@@ -1,14 +0,0 @@
---
source: crates/ruff/src/rules/pycodestyle/mod.rs
---
E24.py:6:8: E242 Tab after comma
|
4 | b = (1, 20)
5 | #: E242
6 | a = (1, 2) # tab before 2
| ^ E242
7 | #: Okay
8 | b = (1, 20) # space before 20
|

View File

@@ -70,8 +70,6 @@ E70.py:68:1: E703 [*] Statement ends with an unnecessary semicolon
67 | 0\
68 | ;
| ^ E703
69 | #: E701:2:3
70 | a = \
|
= help: Remove unnecessary semicolon
@@ -82,24 +80,5 @@ E70.py:68:1: E703 [*] Statement ends with an unnecessary semicolon
67 |-0\
68 |-;
67 |+0
69 68 | #: E701:2:3
70 69 | a = \
71 70 | 5;
E70.py:71:4: E703 [*] Statement ends with an unnecessary semicolon
|
69 | #: E701:2:3
70 | a = \
71 | 5;
| ^ E703
|
= help: Remove unnecessary semicolon
Fix
68 68 | ;
69 69 | #: E701:2:3
70 70 | a = \
71 |- 5;
71 |+ 5

View File

@@ -115,7 +115,6 @@ pub(crate) fn remove_exception_handler_assignment(
// Lex forwards, to the `:` token.
let following = SimpleTokenizer::starts_at(bound_exception.range.end(), locator.contents())
.skip_trivia()
.next()
.context("expected the exception name to be followed by a colon")?;
debug_assert!(matches!(following.kind, SimpleTokenKind::Colon));

View File

@@ -128,7 +128,6 @@ mod tests {
#[test_case(Rule::UndefinedName, Path::new("F821_14.py"))]
#[test_case(Rule::UndefinedName, Path::new("F821_15.py"))]
#[test_case(Rule::UndefinedName, Path::new("F821_16.py"))]
#[test_case(Rule::UndefinedName, Path::new("F821_17.py"))]
#[test_case(Rule::UndefinedExport, Path::new("F822_0.py"))]
#[test_case(Rule::UndefinedExport, Path::new("F822_1.py"))]
#[test_case(Rule::UndefinedExport, Path::new("F822_2.py"))]
@@ -2415,45 +2414,6 @@ mod tests {
);
}
#[test]
fn aliased_submodule_import() {
flakes(
r#"
import fu.bar as baz
import fu.bar as baz
baz
"#,
&[Rule::RedefinedWhileUnused],
);
flakes(
r#"
import fu.bar as baz
import baz
baz
"#,
&[Rule::RedefinedWhileUnused],
);
flakes(
r#"
import fu.bar as baz
import fu.bar as bop
baz, bop
"#,
&[],
);
flakes(
r#"
import foo.baz
import foo.baz as foo
foo
"#,
&[Rule::RedefinedWhileUnused],
);
}
#[test]
fn used_package_with_submodule_import() {
// Usage of package marks submodule imports as used.

View File

@@ -1,178 +0,0 @@
---
source: crates/ruff/src/rules/pyflakes/mod.rs
---
F821_17.py:16:12: F821 Undefined name `DoesNotExist`
|
14 | # Types used in aliased assignment must exist
15 |
16 | type Foo = DoesNotExist # F821: Undefined name `DoesNotExist`
| ^^^^^^^^^^^^ F821
17 | type Foo = list[DoesNotExist] # F821: Undefined name `DoesNotExist`
|
F821_17.py:17:17: F821 Undefined name `DoesNotExist`
|
16 | type Foo = DoesNotExist # F821: Undefined name `DoesNotExist`
17 | type Foo = list[DoesNotExist] # F821: Undefined name `DoesNotExist`
| ^^^^^^^^^^^^ F821
18 |
19 | # Type parameters do not escape alias scopes
|
F821_17.py:22:1: F821 Undefined name `T`
|
21 | type Foo[T] = T
22 | T # F821: Undefined name `T` - not accessible afterward alias scope
| ^ F821
23 |
24 | # Type parameters in functions
|
F821_17.py:39:17: F821 Undefined name `T`
|
37 | from some_library import some_decorator
38 |
39 | @some_decorator(T) # F821: Undefined name `T` - not accessible in decorators
| ^ F821
40 |
41 | def foo[T](t: T) -> None: ...
|
F821_17.py:42:1: F821 Undefined name `T`
|
41 | def foo[T](t: T) -> None: ...
42 | T # F821: Undefined name `T` - not accessible afterward function scope
| ^ F821
|
F821_17.py:64:17: F821 Undefined name `T`
|
63 | from some_library import some_decorator
64 | @some_decorator(T) # F821: Undefined name `T` - not accessible in decorators
| ^ F821
65 |
66 | class Foo[T](list[T]): ...
|
F821_17.py:67:1: F821 Undefined name `T`
|
66 | class Foo[T](list[T]): ...
67 | T # F821: Undefined name `T` - not accessible after class scope
| ^ F821
68 |
69 | # Types specified in bounds should exist
|
F821_17.py:71:13: F821 Undefined name `DoesNotExist`
|
69 | # Types specified in bounds should exist
70 |
71 | type Foo[T: DoesNotExist] = T # F821: Undefined name `DoesNotExist`
| ^^^^^^^^^^^^ F821
72 | def foo[T: DoesNotExist](t: T) -> T: return t # F821: Undefined name `DoesNotExist`
73 | class Foo[T: DoesNotExist](list[T]): ... # F821: Undefined name `DoesNotExist`
|
F821_17.py:72:12: F821 Undefined name `DoesNotExist`
|
71 | type Foo[T: DoesNotExist] = T # F821: Undefined name `DoesNotExist`
72 | def foo[T: DoesNotExist](t: T) -> T: return t # F821: Undefined name `DoesNotExist`
| ^^^^^^^^^^^^ F821
73 | class Foo[T: DoesNotExist](list[T]): ... # F821: Undefined name `DoesNotExist`
|
F821_17.py:73:14: F821 Undefined name `DoesNotExist`
|
71 | type Foo[T: DoesNotExist] = T # F821: Undefined name `DoesNotExist`
72 | def foo[T: DoesNotExist](t: T) -> T: return t # F821: Undefined name `DoesNotExist`
73 | class Foo[T: DoesNotExist](list[T]): ... # F821: Undefined name `DoesNotExist`
| ^^^^^^^^^^^^ F821
74 |
75 | type Foo[T: (DoesNotExist1, DoesNotExist2)] = T # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
|
F821_17.py:75:14: F821 Undefined name `DoesNotExist1`
|
73 | class Foo[T: DoesNotExist](list[T]): ... # F821: Undefined name `DoesNotExist`
74 |
75 | type Foo[T: (DoesNotExist1, DoesNotExist2)] = T # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
| ^^^^^^^^^^^^^ F821
76 | def foo[T: (DoesNotExist1, DoesNotExist2)](t: T) -> T: return t # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
77 | class Foo[T: (DoesNotExist1, DoesNotExist2)](list[T]): ... # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
|
F821_17.py:75:29: F821 Undefined name `DoesNotExist2`
|
73 | class Foo[T: DoesNotExist](list[T]): ... # F821: Undefined name `DoesNotExist`
74 |
75 | type Foo[T: (DoesNotExist1, DoesNotExist2)] = T # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
| ^^^^^^^^^^^^^ F821
76 | def foo[T: (DoesNotExist1, DoesNotExist2)](t: T) -> T: return t # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
77 | class Foo[T: (DoesNotExist1, DoesNotExist2)](list[T]): ... # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
|
F821_17.py:76:13: F821 Undefined name `DoesNotExist1`
|
75 | type Foo[T: (DoesNotExist1, DoesNotExist2)] = T # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
76 | def foo[T: (DoesNotExist1, DoesNotExist2)](t: T) -> T: return t # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
| ^^^^^^^^^^^^^ F821
77 | class Foo[T: (DoesNotExist1, DoesNotExist2)](list[T]): ... # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
|
F821_17.py:76:28: F821 Undefined name `DoesNotExist2`
|
75 | type Foo[T: (DoesNotExist1, DoesNotExist2)] = T # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
76 | def foo[T: (DoesNotExist1, DoesNotExist2)](t: T) -> T: return t # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
| ^^^^^^^^^^^^^ F821
77 | class Foo[T: (DoesNotExist1, DoesNotExist2)](list[T]): ... # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
|
F821_17.py:77:15: F821 Undefined name `DoesNotExist1`
|
75 | type Foo[T: (DoesNotExist1, DoesNotExist2)] = T # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
76 | def foo[T: (DoesNotExist1, DoesNotExist2)](t: T) -> T: return t # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
77 | class Foo[T: (DoesNotExist1, DoesNotExist2)](list[T]): ... # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
| ^^^^^^^^^^^^^ F821
78 |
79 | # Type parameters in nested classes
|
F821_17.py:77:30: F821 Undefined name `DoesNotExist2`
|
75 | type Foo[T: (DoesNotExist1, DoesNotExist2)] = T # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
76 | def foo[T: (DoesNotExist1, DoesNotExist2)](t: T) -> T: return t # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
77 | class Foo[T: (DoesNotExist1, DoesNotExist2)](list[T]): ... # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
| ^^^^^^^^^^^^^ F821
78 |
79 | # Type parameters in nested classes
|
F821_17.py:92:52: F821 Undefined name `t`
|
90 | return x
91 |
92 | def cannot_access_parent_variable(self, x: t) -> t: # F821: Undefined name `T`
| ^ F821
93 | t # F821: Undefined name `t`
94 | return x
|
F821_17.py:92:58: F821 Undefined name `t`
|
90 | return x
91 |
92 | def cannot_access_parent_variable(self, x: t) -> t: # F821: Undefined name `T`
| ^ F821
93 | t # F821: Undefined name `t`
94 | return x
|
F821_17.py:93:17: F821 Undefined name `t`
|
92 | def cannot_access_parent_variable(self, x: t) -> t: # F821: Undefined name `T`
93 | t # F821: Undefined name `t`
| ^ F821
94 | return x
|

View File

@@ -584,22 +584,4 @@ F841_3.py:139:17: F841 Local variable `x` is assigned to but never used
|
= help: Remove assignment to unused variable `x`
F841_3.py:155:17: F841 [*] Local variable `e` is assigned to but never used
|
153 | try:
154 | print("hello")
155 | except A as e :
| ^ F841
156 | print("oh no!")
|
= help: Remove assignment to unused variable `e`
Fix
152 152 | def f() -> None:
153 153 | try:
154 154 | print("hello")
155 |- except A as e :
155 |+ except A:
156 156 | print("oh no!")

View File

@@ -44,7 +44,7 @@ pub(super) fn in_dunder_init(semantic: &SemanticModel, settings: &Settings) -> b
if name != "__init__" {
return false;
}
let Some(parent) = semantic.first_non_type_parent_scope(scope) else {
let Some(parent) = scope.parent.map(|scope_id| &semantic.scopes[scope_id]) else {
return false;
};

View File

@@ -32,7 +32,6 @@ mod tests {
Path::new("repeated_isinstance_calls.py")
)]
#[test_case(Rule::ComparisonWithItself, Path::new("comparison_with_itself.py"))]
#[test_case(Rule::EqWithoutHash, Path::new("eq_without_hash.py"))]
#[test_case(Rule::ManualFromImport, Path::new("import_aliasing.py"))]
#[test_case(Rule::SingleStringSlots, Path::new("single_string_slots.py"))]
#[test_case(Rule::SysExitAlias, Path::new("sys_exit_alias_0.py"))]

View File

@@ -1,78 +0,0 @@
use ruff_python_ast::{self as ast, Ranged, Stmt};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use crate::checkers::ast::Checker;
/// ## What it does
/// Checks for classes that implement `__eq__` but not `__hash__`.
///
/// ## Why is this bad?
/// A class that implements `__eq__` but not `__hash__` will have its hash
/// method implicitly set to `None`. This will cause the class to be
/// unhashable, will in turn cause issues when using the class as a key in a
/// dictionary or a member of a set.
///
/// ## Known problems
/// Does not check for `__hash__` implementations in superclasses.
///
/// ## Example
/// ```python
/// class Person:
/// def __init__(self):
/// self.name = "monty"
///
/// def __eq__(self, other):
/// return isinstance(other, Person) and other.name == self.name
/// ```
///
/// Use instead:
/// ```python
/// class Person:
/// def __init__(self):
/// self.name = "monty"
///
/// def __eq__(self, other):
/// return isinstance(other, Person) and other.name == self.name
///
/// def __hash__(self):
/// return hash(self.name)
/// ```
#[violation]
pub struct EqWithoutHash;
impl Violation for EqWithoutHash {
#[derive_message_formats]
fn message(&self) -> String {
format!("Object does not implement `__hash__` method")
}
}
/// W1641
pub(crate) fn object_without_hash_method(
checker: &mut Checker,
ast::StmtClassDef { name, body, .. }: &ast::StmtClassDef,
) {
if has_eq_without_hash(body) {
checker
.diagnostics
.push(Diagnostic::new(EqWithoutHash, name.range()));
}
}
fn has_eq_without_hash(body: &[Stmt]) -> bool {
let mut has_hash = false;
let mut has_eq = false;
for statement in body {
let Stmt::FunctionDef(ast::StmtFunctionDef { name, .. }) = statement else {
continue;
};
match name.as_str() {
"__hash__" => has_hash = true,
"__eq__" => has_eq = true,
_ => {}
}
}
has_eq && !has_hash
}

View File

@@ -10,7 +10,6 @@ pub(crate) use comparison_of_constant::*;
pub(crate) use comparison_with_itself::*;
pub(crate) use continue_in_finally::*;
pub(crate) use duplicate_bases::*;
pub(crate) use eq_without_hash::*;
pub(crate) use global_statement::*;
pub(crate) use global_variable_not_assigned::*;
pub(crate) use import_self::*;
@@ -64,7 +63,6 @@ mod comparison_of_constant;
mod comparison_with_itself;
mod continue_in_finally;
mod duplicate_bases;
mod eq_without_hash;
mod global_statement;
mod global_variable_not_assigned;
mod import_self;

View File

@@ -1,12 +0,0 @@
---
source: crates/ruff/src/rules/pylint/mod.rs
---
eq_without_hash.py:1:7: PLW1641 Object does not implement `__hash__` method
|
1 | class Person:
| ^^^^^^ PLW1641
2 | def __init__(self):
3 | self.name = "monty"
|

View File

@@ -55,7 +55,8 @@ mod tests {
#[test_case(Rule::OutdatedVersionBlock, Path::new("UP036_5.py"))]
#[test_case(Rule::PrintfStringFormatting, Path::new("UP031_0.py"))]
#[test_case(Rule::PrintfStringFormatting, Path::new("UP031_1.py"))]
#[test_case(Rule::QuotedAnnotation, Path::new("UP037.py"))]
#[test_case(Rule::QuotedAnnotation, Path::new("UP037_0.py"))]
#[test_case(Rule::QuotedAnnotation, Path::new("UP037_1.py"))]
#[test_case(Rule::RedundantOpenModes, Path::new("UP015.py"))]
#[test_case(Rule::ReplaceStdoutStderr, Path::new("UP022.py"))]
#[test_case(Rule::ReplaceUniversalNewlines, Path::new("UP021.py"))]

View File

@@ -15,6 +15,11 @@ use crate::registry::Rule;
/// will always evaluate type annotations in a deferred manner, making
/// the quotes unnecessary.
///
/// Type annotations can also be unquoted in some other contexts, even
/// without `from __future__ import annotations`. For example, annotated
/// assignments within function bodies are not evaluated at runtime, and so can
/// be unquoted.
///
/// ## Example
/// ```python
/// from __future__ import annotations

View File

@@ -1,559 +0,0 @@
---
source: crates/ruff/src/rules/pyupgrade/mod.rs
---
UP037.py:18:14: UP037 [*] Remove quotes from type annotation
|
18 | def foo(var: "MyClass") -> "MyClass":
| ^^^^^^^^^ UP037
19 | x: "MyClass"
|
= help: Remove quotes
Fix
15 15 | from mypy_extensions import Arg, DefaultArg, DefaultNamedArg, NamedArg, VarArg
16 16 |
17 17 |
18 |-def foo(var: "MyClass") -> "MyClass":
18 |+def foo(var: MyClass) -> "MyClass":
19 19 | x: "MyClass"
20 20 |
21 21 |
UP037.py:18:28: UP037 [*] Remove quotes from type annotation
|
18 | def foo(var: "MyClass") -> "MyClass":
| ^^^^^^^^^ UP037
19 | x: "MyClass"
|
= help: Remove quotes
Fix
15 15 | from mypy_extensions import Arg, DefaultArg, DefaultNamedArg, NamedArg, VarArg
16 16 |
17 17 |
18 |-def foo(var: "MyClass") -> "MyClass":
18 |+def foo(var: "MyClass") -> MyClass:
19 19 | x: "MyClass"
20 20 |
21 21 |
UP037.py:19:8: UP037 [*] Remove quotes from type annotation
|
18 | def foo(var: "MyClass") -> "MyClass":
19 | x: "MyClass"
| ^^^^^^^^^ UP037
|
= help: Remove quotes
Fix
16 16 |
17 17 |
18 18 | def foo(var: "MyClass") -> "MyClass":
19 |- x: "MyClass"
19 |+ x: MyClass
20 20 |
21 21 |
22 22 | def foo(*, inplace: "bool"):
UP037.py:22:21: UP037 [*] Remove quotes from type annotation
|
22 | def foo(*, inplace: "bool"):
| ^^^^^^ UP037
23 | pass
|
= help: Remove quotes
Fix
19 19 | x: "MyClass"
20 20 |
21 21 |
22 |-def foo(*, inplace: "bool"):
22 |+def foo(*, inplace: bool):
23 23 | pass
24 24 |
25 25 |
UP037.py:26:16: UP037 [*] Remove quotes from type annotation
|
26 | def foo(*args: "str", **kwargs: "int"):
| ^^^^^ UP037
27 | pass
|
= help: Remove quotes
Fix
23 23 | pass
24 24 |
25 25 |
26 |-def foo(*args: "str", **kwargs: "int"):
26 |+def foo(*args: str, **kwargs: "int"):
27 27 | pass
28 28 |
29 29 |
UP037.py:26:33: UP037 [*] Remove quotes from type annotation
|
26 | def foo(*args: "str", **kwargs: "int"):
| ^^^^^ UP037
27 | pass
|
= help: Remove quotes
Fix
23 23 | pass
24 24 |
25 25 |
26 |-def foo(*args: "str", **kwargs: "int"):
26 |+def foo(*args: "str", **kwargs: int):
27 27 | pass
28 28 |
29 29 |
UP037.py:30:10: UP037 [*] Remove quotes from type annotation
|
30 | x: Tuple["MyClass"]
| ^^^^^^^^^ UP037
31 |
32 | x: Callable[["MyClass"], None]
|
= help: Remove quotes
Fix
27 27 | pass
28 28 |
29 29 |
30 |-x: Tuple["MyClass"]
30 |+x: Tuple[MyClass]
31 31 |
32 32 | x: Callable[["MyClass"], None]
33 33 |
UP037.py:32:14: UP037 [*] Remove quotes from type annotation
|
30 | x: Tuple["MyClass"]
31 |
32 | x: Callable[["MyClass"], None]
| ^^^^^^^^^ UP037
|
= help: Remove quotes
Fix
29 29 |
30 30 | x: Tuple["MyClass"]
31 31 |
32 |-x: Callable[["MyClass"], None]
32 |+x: Callable[[MyClass], None]
33 33 |
34 34 |
35 35 | class Foo(NamedTuple):
UP037.py:36:8: UP037 [*] Remove quotes from type annotation
|
35 | class Foo(NamedTuple):
36 | x: "MyClass"
| ^^^^^^^^^ UP037
|
= help: Remove quotes
Fix
33 33 |
34 34 |
35 35 | class Foo(NamedTuple):
36 |- x: "MyClass"
36 |+ x: MyClass
37 37 |
38 38 |
39 39 | class D(TypedDict):
UP037.py:40:27: UP037 [*] Remove quotes from type annotation
|
39 | class D(TypedDict):
40 | E: TypedDict("E", foo="int", total=False)
| ^^^^^ UP037
|
= help: Remove quotes
Fix
37 37 |
38 38 |
39 39 | class D(TypedDict):
40 |- E: TypedDict("E", foo="int", total=False)
40 |+ E: TypedDict("E", foo=int, total=False)
41 41 |
42 42 |
43 43 | class D(TypedDict):
UP037.py:44:31: UP037 [*] Remove quotes from type annotation
|
43 | class D(TypedDict):
44 | E: TypedDict("E", {"foo": "int"})
| ^^^^^ UP037
|
= help: Remove quotes
Fix
41 41 |
42 42 |
43 43 | class D(TypedDict):
44 |- E: TypedDict("E", {"foo": "int"})
44 |+ E: TypedDict("E", {"foo": int})
45 45 |
46 46 |
47 47 | x: Annotated["str", "metadata"]
UP037.py:47:14: UP037 [*] Remove quotes from type annotation
|
47 | x: Annotated["str", "metadata"]
| ^^^^^ UP037
48 |
49 | x: Arg("str", "name")
|
= help: Remove quotes
Fix
44 44 | E: TypedDict("E", {"foo": "int"})
45 45 |
46 46 |
47 |-x: Annotated["str", "metadata"]
47 |+x: Annotated[str, "metadata"]
48 48 |
49 49 | x: Arg("str", "name")
50 50 |
UP037.py:49:8: UP037 [*] Remove quotes from type annotation
|
47 | x: Annotated["str", "metadata"]
48 |
49 | x: Arg("str", "name")
| ^^^^^ UP037
50 |
51 | x: DefaultArg("str", "name")
|
= help: Remove quotes
Fix
46 46 |
47 47 | x: Annotated["str", "metadata"]
48 48 |
49 |-x: Arg("str", "name")
49 |+x: Arg(str, "name")
50 50 |
51 51 | x: DefaultArg("str", "name")
52 52 |
UP037.py:51:15: UP037 [*] Remove quotes from type annotation
|
49 | x: Arg("str", "name")
50 |
51 | x: DefaultArg("str", "name")
| ^^^^^ UP037
52 |
53 | x: NamedArg("str", "name")
|
= help: Remove quotes
Fix
48 48 |
49 49 | x: Arg("str", "name")
50 50 |
51 |-x: DefaultArg("str", "name")
51 |+x: DefaultArg(str, "name")
52 52 |
53 53 | x: NamedArg("str", "name")
54 54 |
UP037.py:53:13: UP037 [*] Remove quotes from type annotation
|
51 | x: DefaultArg("str", "name")
52 |
53 | x: NamedArg("str", "name")
| ^^^^^ UP037
54 |
55 | x: DefaultNamedArg("str", "name")
|
= help: Remove quotes
Fix
50 50 |
51 51 | x: DefaultArg("str", "name")
52 52 |
53 |-x: NamedArg("str", "name")
53 |+x: NamedArg(str, "name")
54 54 |
55 55 | x: DefaultNamedArg("str", "name")
56 56 |
UP037.py:55:20: UP037 [*] Remove quotes from type annotation
|
53 | x: NamedArg("str", "name")
54 |
55 | x: DefaultNamedArg("str", "name")
| ^^^^^ UP037
56 |
57 | x: DefaultNamedArg("str", name="name")
|
= help: Remove quotes
Fix
52 52 |
53 53 | x: NamedArg("str", "name")
54 54 |
55 |-x: DefaultNamedArg("str", "name")
55 |+x: DefaultNamedArg(str, "name")
56 56 |
57 57 | x: DefaultNamedArg("str", name="name")
58 58 |
UP037.py:57:20: UP037 [*] Remove quotes from type annotation
|
55 | x: DefaultNamedArg("str", "name")
56 |
57 | x: DefaultNamedArg("str", name="name")
| ^^^^^ UP037
58 |
59 | x: VarArg("str")
|
= help: Remove quotes
Fix
54 54 |
55 55 | x: DefaultNamedArg("str", "name")
56 56 |
57 |-x: DefaultNamedArg("str", name="name")
57 |+x: DefaultNamedArg(str, name="name")
58 58 |
59 59 | x: VarArg("str")
60 60 |
UP037.py:59:11: UP037 [*] Remove quotes from type annotation
|
57 | x: DefaultNamedArg("str", name="name")
58 |
59 | x: VarArg("str")
| ^^^^^ UP037
60 |
61 | x: List[List[List["MyClass"]]]
|
= help: Remove quotes
Fix
56 56 |
57 57 | x: DefaultNamedArg("str", name="name")
58 58 |
59 |-x: VarArg("str")
59 |+x: VarArg(str)
60 60 |
61 61 | x: List[List[List["MyClass"]]]
62 62 |
UP037.py:61:19: UP037 [*] Remove quotes from type annotation
|
59 | x: VarArg("str")
60 |
61 | x: List[List[List["MyClass"]]]
| ^^^^^^^^^ UP037
62 |
63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
|
= help: Remove quotes
Fix
58 58 |
59 59 | x: VarArg("str")
60 60 |
61 |-x: List[List[List["MyClass"]]]
61 |+x: List[List[List[MyClass]]]
62 62 |
63 63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
64 64 |
UP037.py:63:29: UP037 [*] Remove quotes from type annotation
|
61 | x: List[List[List["MyClass"]]]
62 |
63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
| ^^^^^ UP037
64 |
65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
= help: Remove quotes
Fix
60 60 |
61 61 | x: List[List[List["MyClass"]]]
62 62 |
63 |-x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
63 |+x: NamedTuple("X", [("foo", int), ("bar", "str")])
64 64 |
65 65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
66 66 |
UP037.py:63:45: UP037 [*] Remove quotes from type annotation
|
61 | x: List[List[List["MyClass"]]]
62 |
63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
| ^^^^^ UP037
64 |
65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
= help: Remove quotes
Fix
60 60 |
61 61 | x: List[List[List["MyClass"]]]
62 62 |
63 |-x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
63 |+x: NamedTuple("X", [("foo", "int"), ("bar", str)])
64 64 |
65 65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
66 66 |
UP037.py:65:29: UP037 [*] Remove quotes from type annotation
|
63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
64 |
65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
| ^^^^^ UP037
66 |
67 | x: NamedTuple(typename="X", fields=[("foo", "int")])
|
= help: Remove quotes
Fix
62 62 |
63 63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
64 64 |
65 |-x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
65 |+x: NamedTuple("X", fields=[(foo, "int"), ("bar", "str")])
66 66 |
67 67 | x: NamedTuple(typename="X", fields=[("foo", "int")])
68 68 |
UP037.py:65:36: UP037 [*] Remove quotes from type annotation
|
63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
64 |
65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
| ^^^^^ UP037
66 |
67 | x: NamedTuple(typename="X", fields=[("foo", "int")])
|
= help: Remove quotes
Fix
62 62 |
63 63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
64 64 |
65 |-x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
65 |+x: NamedTuple("X", fields=[("foo", int), ("bar", "str")])
66 66 |
67 67 | x: NamedTuple(typename="X", fields=[("foo", "int")])
68 68 |
UP037.py:65:45: UP037 [*] Remove quotes from type annotation
|
63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
64 |
65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
| ^^^^^ UP037
66 |
67 | x: NamedTuple(typename="X", fields=[("foo", "int")])
|
= help: Remove quotes
Fix
62 62 |
63 63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
64 64 |
65 |-x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
65 |+x: NamedTuple("X", fields=[("foo", "int"), (bar, "str")])
66 66 |
67 67 | x: NamedTuple(typename="X", fields=[("foo", "int")])
68 68 |
UP037.py:65:52: UP037 [*] Remove quotes from type annotation
|
63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
64 |
65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
| ^^^^^ UP037
66 |
67 | x: NamedTuple(typename="X", fields=[("foo", "int")])
|
= help: Remove quotes
Fix
62 62 |
63 63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
64 64 |
65 |-x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
65 |+x: NamedTuple("X", fields=[("foo", "int"), ("bar", str)])
66 66 |
67 67 | x: NamedTuple(typename="X", fields=[("foo", "int")])
68 68 |
UP037.py:67:24: UP037 [*] Remove quotes from type annotation
|
65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
66 |
67 | x: NamedTuple(typename="X", fields=[("foo", "int")])
| ^^^ UP037
68 |
69 | X: MyCallable("X")
|
= help: Remove quotes
Fix
64 64 |
65 65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
66 66 |
67 |-x: NamedTuple(typename="X", fields=[("foo", "int")])
67 |+x: NamedTuple(typename=X, fields=[("foo", "int")])
68 68 |
69 69 | X: MyCallable("X")
70 70 |
UP037.py:67:38: UP037 [*] Remove quotes from type annotation
|
65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
66 |
67 | x: NamedTuple(typename="X", fields=[("foo", "int")])
| ^^^^^ UP037
68 |
69 | X: MyCallable("X")
|
= help: Remove quotes
Fix
64 64 |
65 65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
66 66 |
67 |-x: NamedTuple(typename="X", fields=[("foo", "int")])
67 |+x: NamedTuple(typename="X", fields=[(foo, "int")])
68 68 |
69 69 | X: MyCallable("X")
70 70 |
UP037.py:67:45: UP037 [*] Remove quotes from type annotation
|
65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
66 |
67 | x: NamedTuple(typename="X", fields=[("foo", "int")])
| ^^^^^ UP037
68 |
69 | X: MyCallable("X")
|
= help: Remove quotes
Fix
64 64 |
65 65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
66 66 |
67 |-x: NamedTuple(typename="X", fields=[("foo", "int")])
67 |+x: NamedTuple(typename="X", fields=[("foo", int)])
68 68 |
69 69 | X: MyCallable("X")
70 70 |

Some files were not shown because too many files have changed in this diff Show More