Compare commits

..

102 Commits

Author SHA1 Message Date
Dhruv Manilawala
975be9c1c6 Bump version to 0.6.9 (#13624) 2024-10-04 18:51:13 +05:30
Zanie Blue
99e4566fce Mark FURB118 fix as unsafe (#13613)
Closes https://github.com/astral-sh/ruff/issues/13421
2024-10-03 21:39:22 +00:00
Simon Høxbro Hansen
7ad07c2c5d Add allow-unused-imports setting for unused-import rule (F401) (#13601)
## Summary
Resolves https://github.com/astral-sh/ruff/issues/9962 by allowing a
configuration setting `allowed-unused-imports`

TODO:
- [x] Figure out the correct name and place for the setting; currently,
I have added it top level.
- [x] The comparison is pretty naive. I tried using `glob::Pattern` but
couldn't get it to work in the configuration.
- [x] Add tests
- [x] Update documentations

## Test Plan

`cargo test`
2024-10-03 19:44:44 +00:00
Bernát Gábor
4aefe52393 Support ruff discovery in pip build environments (#13591)
Resolves https://github.com/astral-sh/ruff/issues/13321.

Contents of overlay:
```bash
/private/var/folders/v0/l8q3ghks2gs5ns2_p63tyqh40000gq/T/pip-build-env-e0ukpbvo/overlay/bin:
total 26M
-rwxr-xr-x 1 bgabor8 staff 26M Oct  1 08:22 ruff
drwxr-xr-x 3 bgabor8 staff  96 Oct  1 08:22 .
drwxr-xr-x 4 bgabor8 staff 128 Oct  1 08:22 ..
```

Python executable:
```bash
'/Users/bgabor8/git/github/ruff-find-bin-during-build/.venv/bin/python'
```
PATH is:
```bash
['/private/var/folders/v0/l8q3ghks2gs5ns2_p63tyqh40000gq/T/pip-build-env-e0ukpbvo/overlay/bin',
 '/private/var/folders/v0/l8q3ghks2gs5ns2_p63tyqh40000gq/T/pip-build-env-e0ukpbvo/normal/bin',
'/Library/Frameworks/Python.framework/Versions/3.11/bin',
'/Library/Frameworks/Python.framework/Versions/3.12/bin',
```
Not sure where to add tests, there does not seem to be any existing one.
Can someone help me with that?
2024-10-03 17:38:07 +00:00
Zanie Blue
cc1f766622 Preserve trivia (i.e. comments) in PLR5501 (#13573)
Closes https://github.com/astral-sh/ruff/issues/13545

As described in the issue, we move comments before the inner `if`
statement to before the newly constructed `elif` statement (previously
`else`).
2024-10-03 10:22:20 -05:00
Bernát Gábor
fdd0a22c03 Move to maintained mirror of prettier (#13592)
https://github.com/pre-commit/mirrors-prettier has been archived and is
no longer maintained.

Signed-off-by: Bernát Gábor <bgabor8@bloomberg.net>
2024-10-03 15:35:27 +01:00
cake-monotone
3728d5b3a2 [pyupgrade] Fix UP043 to apply to collections.abc.Generator and collections.abc.AsyncGenerator (#13611)
## Summary

fix #13602 

Currently, `UP043` only applies to typing.Generator, but it should also
support collections.abc.Generator.

This update ensures `UP043` correctly handles both
`collections.abc.Generator` and `collections.abc.AsyncGenerator`

### UP043
> `UP043`
> Python 3.13 introduced the ability for type parameters to specify
default values. As such, the default type arguments for some types in
the standard library (e.g., Generator, AsyncGenerator) are now optional.
> Omitting type parameters that match the default values can make the
code more concise and easier to read.

```py
Generator[int, None, None] -> Generator[int]
```
2024-10-03 13:06:15 +01:00
Dhruv Manilawala
7e3894f5b3 Avoid short circuiting B017 for multiple context managers (#13609)
## Summary

fixes: #13603
2024-10-03 15:35:05 +05:30
Charlie Marsh
c3b40da0d2 Use backticks for code in red-knot messages (#13599)
## Summary

...and remove periods from messages that don't span more than a single
sentence.

This is more consistent with how we present user-facing messages in uv
(which has a defined style guide).
2024-10-02 03:14:28 +00:00
Charlie Marsh
ef45185dbc Allow users to provide custom diagnostic messages when unwrapping calls (#13597)
## Summary

You can now call `return_ty_result` to operate on a `Result` directly
thereby using your own diagnostics, as in:

```rust
return dunder_getitem_method
    .call(self.db, &[slice_ty])
    .return_ty_result(self.db, value.as_ref().into(), self)
    .unwrap_or_else(|err| {
        self.add_diagnostic(
            (&**value).into(),
            "call-non-callable",
            format_args!(
                "Method `__getitem__` is not callable on object of type '{}'.",
                value_ty.display(self.db),
            ),
        );
        err.return_ty()
    });
```
2024-10-01 21:22:13 +00:00
Charlie Marsh
961fc98344 Use __class_getitem__ for more specific non-subscript errors (#13596) 2024-10-01 18:16:00 +00:00
Charlie Marsh
0a6dc8e1b8 Support __getitem__ type inference for subscripts (#13579)
## Summary

Follow-up to https://github.com/astral-sh/ruff/pull/13562, to add
support for "arbitrary" subscript operations.
2024-10-01 18:04:16 +00:00
Charlie Marsh
8d54996ffb Avoid indirection in class.__call__ lookup (#13595) 2024-10-01 18:01:36 +00:00
Alex Waygood
73e884b232 [red-knot] [minor] Improve helper methods for builtin types (#13594) 2024-10-01 18:38:33 +01:00
Charlie Marsh
edba60106b Support classes that implement __call__ (#13580)
## Summary

This looked straightforward and removes some TODOs.
2024-10-01 17:15:46 +00:00
Alex Waygood
043fba7a57 [red-knot] Fix a few details around Type::call (#13593) 2024-10-01 16:49:09 +00:00
Alex Waygood
20d997784d ruff_benchmark: open all tomllib files in the red-knot benchmark (#13589) 2024-10-01 17:47:36 +01:00
Alex Waygood
82324678cf Rename the ruff_vendored crate to red_knot_vendored (#13586) 2024-10-01 16:16:59 +01:00
Zanie Blue
cfd5d63917 Use operator specific messaging in division by zero diagnostics (#13588)
Requested at
https://github.com/astral-sh/ruff/pull/13576#discussion_r1782530971
2024-10-01 08:58:38 -05:00
Alex Waygood
2a36b47f13 [red-knot] Remove Type::RevealType (#13567) 2024-10-01 10:01:03 +00:00
Tom Gillam
6322639aca Fix tiny typo in _typos.toml (#13583) 2024-10-01 10:54:00 +01:00
github-actions[bot]
360af1bc32 Sync vendored typeshed stubs (#13578)
Close and reopen this PR to trigger CI

Co-authored-by: typeshedbot <>
2024-10-01 08:05:19 +01:00
Zanie Blue
3af3f74c66 Update dedent_to to support blocks that are composed of comments (#13572)
While looking into https://github.com/astral-sh/ruff/issues/13545 I
noticed that we return `None` here if you pass a block of comments. This
is annoying because it causes `adjust_indentation` to fall back to
LibCST which panics when it cannot find a statement.
2024-10-01 04:38:03 +00:00
Zanie Blue
45f01e7872 Add diagnostic for integer division by zero (#13576)
Adds a diagnostic for division by the integer zero in `//`, `/`, and
`%`.

Doesn't handle `<int> / 0.0` because we don't track the values of float
literals.
2024-09-30 22:38:52 +00:00
Simon
6cdf996af6 [red-knot] feat: introduce a new [Type::Todo] variant (#13548)
This variant shows inference that is not yet implemented..

## Summary

PR #13500 reopened the idea of adding a new type variant to keep track
of not-implemented features in Red Knot.

It was based off of #12986 with a more generic approach of keeping track
of different kind of unknowns. Discussion in #13500 agreed that keeping
track of different `Unknown` is complicated for now, and this feature is
better achieved through a new variant of `Type`.

### Requirements

Requirements for this implementation can be summed up with some extracts
of comment from @carljm on the previous PR

> So at the moment we are leaning towards simplifying this PR to just
use a new top-level variant, which behaves like Any and Unknown but
represents inference that is not yet implemented in red-knot.

> I think the general rule should be that Todo should propagate only
when the presence of the input Todo caused the output to be unknown.
>
> To take a specific example, the inferred result of addition must be
Unknown if either operand is Unknown. That is, Unknown + X will always
be Unknown regardless of what X is. (Same for X + Unknown.) In this
case, I believe that Unknown + Todo (or Todo + Unknown) should result in
Unknown, not result in Todo. If we fix the upstream source of the Todo,
the result would still be Unknown, so it's not useful to propagate the
Todo in this case: it wrongly suggests that the output is unknown
because of a todo item.

## Test Plan

This PR does not introduce new tests, but it did required to edit some
tests with the display of `[Type::Todo]` (currently `@Todo`), which
suggests that those test are placeholders requirements for features we
don't support yet.
2024-09-30 14:28:06 -07:00
Zanie Blue
9d8a4c0057 Improve display of assert_public_ty assertion failures (#13577)
While working on https://github.com/astral-sh/ruff/pull/13576 I noticed
that it was really hard to tell which assertion failed in some of these
test cases. This could be expanded to elsewhere, but I've heard this
test suite format won't be around for long?
2024-09-30 16:12:26 -05:00
Charlie Marsh
c9c748a79e Add some basic subscript type inference (#13562)
## Summary

Just for tuples and strings -- the easiest cases. I think most of the
rest require generic support?
2024-09-30 16:50:46 -04:00
Zanie Blue
32c746bd82 Fix inference when integers are divided (#13575)
Fixes the `Operator::Div` case and adds `Operator::FloorDiv` support

Closes https://github.com/astral-sh/ruff/issues/13570
2024-09-30 15:50:37 -05:00
Zanie Blue
e76f77d711 Use uv in contribution document (#13540) 2024-09-30 14:42:59 -05:00
Charlie Marsh
d86b73eb3d Add unary inference for integer and boolean literals (#13559)
## Summary

Just trying to familiarize myself with the general patterns, testing,
etc.

Part of https://github.com/astral-sh/ruff/issues/12701.
2024-09-30 16:29:06 +00:00
Alex Waygood
5f4b282327 [red-knot] Allow calling bool() with no arguments (#13568) 2024-09-30 13:18:01 +00:00
aditya pillai
d9267132d6 Fix leftover references to red_knot_python_semantic/vendor/ (#13561)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2024-09-30 11:32:02 +00:00
renovate[bot]
5118166d21 Update NPM Development dependencies (#13560) 2024-09-29 22:09:47 -04:00
renovate[bot]
6fb1d6037a Update pre-commit dependencies (#13558) 2024-09-29 21:50:50 -04:00
renovate[bot]
9237813e0c Update Rust crate tempfile to v3.13.0 (#13557) 2024-09-29 21:50:46 -04:00
renovate[bot]
3bebde3ccc Update Rust crate regex to v1.11.0 (#13556) 2024-09-29 21:50:40 -04:00
renovate[bot]
6c5cbad533 Update dependency ruff to v0.6.8 (#13555) 2024-09-29 21:50:28 -04:00
renovate[bot]
7a2f8d4463 Update dependency react-resizable-panels to v2.1.4 (#13554) 2024-09-29 21:50:22 -04:00
renovate[bot]
ad87ea948d Update Rust crate syn to v2.0.79 (#13553) 2024-09-29 21:50:16 -04:00
renovate[bot]
acfc34d615 Update Rust crate libc to v0.2.159 (#13552) 2024-09-29 21:50:10 -04:00
Charlie Marsh
668730cc28 Link to astral-sh/ruff-action (#13551) 2024-09-29 23:49:24 +00:00
Edouard Choinière
bee498d635 [flake8-use-pathlib] Fix typo in link to Path.stat (PTH116) (#13546)
## Summary

<!-- What's the purpose of the change? What does it do, and why? -->

There was a typo in the links of the docs of PTH116, where Path.stat
used to link to Path.group.
Another rule, PTH202, does it correctly: 

ec72e675d9/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_getsize.rs (L33)

This PR only fixes a one word typo.

## Test Plan

<!-- How was it tested? -->
I did not test that the doc generation framework picked up these
changes, I assume it will do it successfully.
2024-09-28 12:01:41 -04:00
TomerBin
ec72e675d9 Red Knot - Infer the return value of bool() (#13538)
## Summary
Following #13449, this PR adds custom handling for the bool constructor,
so when the input type has statically known truthiness value, it will be
used as the return value of the bool function.
For example, in the following snippet x will now be resolved to
`Literal[True]` instead of `bool`.
```python
x = bool(1)
```

## Test Plan
Some cargo tests were added.
2024-09-27 12:11:55 -07:00
Simon
1639488082 [red-knot] support fstring expressions (#13511)
<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

Implement inference for `f-string`, contributes to #12701.

### First Implementation

When looking at the way `mypy` handles things, I noticed the following:
- No variables (e.g. `f"hello"`) ⇒ `LiteralString`
- Any variable (e.g. `f"number {1}"`) ⇒ `str`

My first commit (1ba5d0f13fdf70ed8b2b1a41433b32fc9085add2) implements
exactly this logic, except that we deal with string literals just like
`infer_string_literal_expression` (if below `MAX_STRING_LITERAL_SIZE`,
show `Literal["exact string"]`)

### Second Implementation

My second commit (90326ce9af5549af7b4efae89cd074ddf68ada14) pushes
things a bit further to handle cases where the expression within the
`f-string` are all literal values (string representation known at static
time).

Here's an example of when this could happen in code:
```python
BASE_URL = "https://httpbin.org"
VERSION = "v1"
endpoint = f"{BASE_URL}/{VERSION}/post"  # Literal["https://httpbin.org/v1/post"]
```
As this can be sightly more costly (additional allocations), I don't
know if we want this feature.

## Test Plan

- Added a test `fstring_expression` covering all cases I can think of

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2024-09-27 10:29:21 -07:00
Micha Reiser
f3e464ea4c refactor: Simplify quote selection logic (#13536) 2024-09-27 14:40:28 +02:00
Micha Reiser
253f5f269a refactor: Rename FormatStringContinuation to FormatImplicitConcatenatedString (#13531) 2024-09-27 08:24:50 +00:00
Micha Reiser
c046101b79 Fix codeblock dynamic line length calculation for indented examples (#13523) 2024-09-27 09:09:07 +02:00
Zanie Blue
7706f561a9 Do not offer an invalid fix for PLR1716 when the comparisons contain parenthesis (#13527)
Related to https://github.com/astral-sh/ruff/issues/13524

Doesn't offer a valid fix, opting to instead just not offer a fix at
all. If someone points me to a good way to handle parenthesis here I'm
down to try to fix the fix separately, but it looks quite hard.
2024-09-26 19:01:06 +00:00
Henry Jiang
f5e3662446 Remove jemalloc crate when building on AIX (#13529)
## Summary
Building ruff on AIX breaks on `tiki-jemalloc-sys` due to OS header
incompatibility

## Test Plan
`cargo test`

Co-authored-by: Henry Jiang <henry.jiang1@ibm.com>
2024-09-26 13:20:54 -04:00
Junzhuo ZHOU
a354d9ead6 Expose internal types as public access (#13509) 2024-09-26 17:34:30 +02:00
Zanie Blue
58a8e9c511 Fix handling of slices in tuples for FURB118, e.g., x[:, 1] (#13518)
There was already handling for the singleton `x[:]` case but not the
tuple case.

Closes https://github.com/astral-sh/ruff/issues/13508
2024-09-26 14:20:03 +00:00
ukyen
e83388dcea Don't raise D208 when last line is non-empty (#13372)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-09-26 14:53:21 +02:00
Micha Reiser
ae39ce56c0 Bump version to 0.6.8 (#13522) 2024-09-26 14:09:03 +02:00
Micha Reiser
ff2d214e11 Don't skip over imports and other nodes containing nested statements in import collector (#13521) 2024-09-26 11:57:05 +00:00
Micha Reiser
9442cd8fae Parenthesize match..case if guards (#13513) 2024-09-26 06:44:33 +00:00
Micha Reiser
8012707348 Align formatting of patterns in match-cases with expression formatting in clause headers (#13510) 2024-09-26 08:35:22 +02:00
Charlie Marsh
d7ffe46054 Disable the typeset plugin (#13517)
## Summary

There seems to be a bad interaction between enabling anchorlinks and the
`typeset` plugin. I think the former is more important than the
latter... so disabling the latter for now.

## Test Plan

Before:

![Screenshot 2024-09-25 at 7 53
21 PM](https://github.com/user-attachments/assets/bf7c70bb-19ab-4ece-9709-4c297f8ba67b)

After:

![Screenshot 2024-09-25 at 7 53
12 PM](https://github.com/user-attachments/assets/e767a575-1664-4288-aecb-82e8b1b1a7bd)
2024-09-25 23:58:35 +00:00
haarisr
7c83af419c red-knot: Implement the not operator for all Type variants (#13432)
Signed-off-by: haaris <haarisrahman@gmail.com>
Co-authored-by: Carl Meyer <carl@oddbird.net>
2024-09-25 13:44:19 -07:00
Zanie Blue
bbb044ebda Detect tuples bound to variadic positional arguments i.e. *args (#13512)
In https://github.com/astral-sh/ruff/pull/13503, we added supported for
detecting variadic keyword arguments as dictionaries, here we use the
same strategy for detecting variadic positional arguments as tuples.
2024-09-25 10:03:25 -05:00
Zanie Blue
481065238b Avoid UP028 false negatives with non-reference shadowed bindings of loop variables (#13504)
Closes https://github.com/astral-sh/ruff/issues/13266

Avoids false negatives for shadowed bindings that aren't actually
references to the loop variable. There are some shadowed bindings we
need to support still, e.g., `del` requires the loop variable to exist.
2024-09-25 10:03:09 -05:00
Zanie Blue
11f06e0d55 Detect SIM910 when using variadic keyword arguments, i.e., **kwargs (#13503)
Closes https://github.com/astral-sh/ruff/issues/13493
2024-09-25 10:02:59 -05:00
Dylan
f27a8b8c7a [internal] ComparableExpr (f)strings and bytes made invariant under concatenation (#13301) 2024-09-25 16:58:57 +02:00
Vince van Noort
ca0ae0a484 [pylint] Implement boolean-chained-comparison (R1716) (#13435)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-09-25 09:14:12 +00:00
TomerBin
be1d5e3368 [red-knot] Add Type::bool and boolean expression inference (#13449) 2024-09-25 00:02:26 +00:00
Simon Brugman
03503f7f56 C401 message missing closing parenthesis (#13498) 2024-09-24 14:55:32 +02:00
Charlie Marsh
ff4b6d11fa Detect basic wildcard imports in ruff analyze graph (#13486)
## Summary

I guess we can just ignore the `*` entirely for now? This will add the
`__init__.py` for anything that's importing a package.
2024-09-23 18:09:00 -04:00
Charlie Marsh
96e7f3f96f Exit gracefully on broken pipe errors (#13485)
## Summary

Closes https://github.com/astral-sh/ruff/issues/13483.

Closes https://github.com/astral-sh/ruff/issues/13442.

## Test Plan

```
❯ cargo run analyze graph ../django | head -n 10
   Compiling ruff v0.6.7 (/Users/crmarsh/workspace/ruff/crates/ruff)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.63s
     Running `target/debug/ruff analyze graph ../django`
warning: `ruff analyze graph` is experimental and may change without warning
{
  "/Users/crmarsh/workspace/django/django/__init__.py": [
    "/Users/crmarsh/workspace/django/django/apps/__init__.py",
    "/Users/crmarsh/workspace/django/django/conf/__init__.py",
    "/Users/crmarsh/workspace/django/django/urls/__init__.py",
    "/Users/crmarsh/workspace/django/django/utils/log.py",
    "/Users/crmarsh/workspace/django/django/utils/version.py"
  ],
  "/Users/crmarsh/workspace/django/django/__main__.py": [
    "/Users/crmarsh/workspace/django/django/core/management/__init__.py"
```
2024-09-23 13:48:43 +00:00
Charlie Marsh
90dc7438ee Avoid panic when analyze graph hits broken pipe (#13484)
## Summary

I think we should also make the change that @BurntSushi recommended in
the linked issue, but this gets rid of the panic.

See: https://github.com/astral-sh/ruff/issues/13483

See: https://github.com/astral-sh/ruff/issues/13442

## Test Plan

```
warning: `ruff analyze graph` is experimental and may change without warning
{
  "/Users/crmarsh/workspace/django/django/__init__.py": [
    "/Users/crmarsh/workspace/django/django/apps/__init__.py",
    "/Users/crmarsh/workspace/django/django/conf/__init__.py",
    "/Users/crmarsh/workspace/django/django/urls/__init__.py",
    "/Users/crmarsh/workspace/django/django/utils/log.py",
    "/Users/crmarsh/workspace/django/django/utils/version.py"
  ],
  "/Users/crmarsh/workspace/django/django/__main__.py": [
    "/Users/crmarsh/workspace/django/django/core/management/__init__.py"
ruff failed
  Cause: Broken pipe (os error 32)
```
2024-09-23 09:43:09 -04:00
Micha Reiser
3e99ab141c Update Salsa (#13480) 2024-09-23 14:04:04 +02:00
renovate[bot]
115745a8ac Update dependency monaco-editor to ^0.52.0 (#13475)
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [monaco-editor](https://redirect.github.com/microsoft/monaco-editor) |
[`^0.51.0` ->
`^0.52.0`](https://renovatebot.com/diffs/npm/monaco-editor/0.51.0/0.52.0)
|
[![age](https://developer.mend.io/api/mc/badges/age/npm/monaco-editor/0.52.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/monaco-editor/0.52.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/monaco-editor/0.51.0/0.52.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/monaco-editor/0.51.0/0.52.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>microsoft/monaco-editor (monaco-editor)</summary>

###
[`v0.52.0`](https://redirect.github.com/microsoft/monaco-editor/blob/HEAD/CHANGELOG.md#0520)

[Compare
Source](https://redirect.github.com/microsoft/monaco-editor/compare/v0.51.0...v0.52.0)

-   Comment added inside of `IModelContentChangedEvent`

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOC44MC4wIiwidXBkYXRlZEluVmVyIjoiMzguODAuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-23 09:55:12 +02:00
renovate[bot]
8bb59d7216 Update Rust crate unicode_names2 to v1.3.0 (#13474)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [unicode_names2](https://redirect.github.com/progval/unicode_names2) |
workspace.dependencies | minor | `1.2.2` -> `1.3.0` |

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOC44MC4wIiwidXBkYXRlZEluVmVyIjoiMzguODAuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-23 09:54:44 +02:00
renovate[bot]
47aac060de Update Rust crate insta to v1.40.0 (#13472)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-23 09:40:02 +02:00
Steve C
7c55330534 Fix formatting for analyze direction values (#13476) 2024-09-23 09:18:28 +02:00
renovate[bot]
047d77c60b Update pre-commit dependencies (#13467) 2024-09-22 22:54:34 -04:00
renovate[bot]
18fddd458a Update dependency eslint to v8.57.1 (#13465) 2024-09-22 22:54:14 -04:00
Charlie Marsh
db76000521 Use anchorlinks rather than permalinks (#13471)
## Summary

See: https://github.com/astral-sh/uv/pull/7626
2024-09-23 02:44:45 +00:00
renovate[bot]
a2ed1e1cd1 Update Rust crate thiserror to v1.0.64 (#13462) 2024-09-22 22:32:45 -04:00
renovate[bot]
7457679582 Update Rust crate dashmap to v6.1.0 (#13470) 2024-09-22 22:32:26 -04:00
renovate[bot]
1d352872ba Update Rust crate codspeed-criterion-compat to v2.7.2 (#13469) 2024-09-22 22:32:20 -04:00
renovate[bot]
c8b905bc96 Update NPM Development dependencies (#13468) 2024-09-22 22:32:11 -04:00
renovate[bot]
5b593d0397 Update dependency ruff to v0.6.7 (#13466) 2024-09-22 22:32:02 -04:00
renovate[bot]
c5c5acda23 Update Rust crate unicode-normalization to v0.1.24 (#13464) 2024-09-22 22:31:53 -04:00
renovate[bot]
26747aae75 Update Rust crate unicode-ident to v1.0.13 (#13463) 2024-09-22 22:31:47 -04:00
renovate[bot]
85b825a2a1 Update Rust crate syn to v2.0.77 (#13461) 2024-09-22 22:31:40 -04:00
renovate[bot]
9e764ef6d0 Update Rust crate serde_json to v1.0.128 (#13460) 2024-09-23 02:03:47 +00:00
renovate[bot]
0e325a53ef Update Rust crate serde to v1.0.210 (#13459) 2024-09-23 02:03:15 +00:00
renovate[bot]
2a136cfb57 Update Rust crate pretty_assertions to v1.4.1 (#13458) 2024-09-23 02:02:12 +00:00
renovate[bot]
7749164d4a Update Rust crate ordermap to v0.5.3 (#13457) 2024-09-23 02:01:44 +00:00
renovate[bot]
da50e14524 Update Rust crate lsp-server to v0.7.7 (#13456) 2024-09-23 02:00:23 +00:00
renovate[bot]
1886b731a5 Update Rust crate ignore to v0.4.23 (#13455) 2024-09-22 22:00:06 -04:00
renovate[bot]
364eddc95a Update Rust crate globset to v0.4.15 (#13454) 2024-09-22 22:00:01 -04:00
renovate[bot]
48fb340e3b Update Rust crate filetime to v0.2.25 (#13453) 2024-09-22 21:59:50 -04:00
renovate[bot]
71bb4d3bdc Update Rust crate clap to v4.5.18 (#13452) 2024-09-22 21:59:44 -04:00
renovate[bot]
5c20f570d0 Update Rust crate anyhow to v1.0.89 (#13451) 2024-09-23 01:58:14 +00:00
Charlie Marsh
7441da287f Skip traversal for non-compound statements (#13441)
## Summary

None of these can contain imports.
2024-09-21 20:47:30 +00:00
Charlie Marsh
c2a5179d75 Reuse BTreeSets in module resolver (#13440)
## Summary

For dependencies, there's no reason to re-allocate here, since we know
the paths are unique.
2024-09-21 20:14:32 +00:00
Charlie Marsh
17c4690b5e Bump version to v0.6.7 (#13439) 2024-09-21 13:16:36 -04:00
Charlie Marsh
f06d44e6e5 Use forget for module resolver database (#13438)
## Summary

A tiny bit faster and the `red-knot` CLI does the same thing.
2024-09-21 17:00:02 +00:00
Micha Reiser
653c09001a Use an empty vendored file system in Ruff (#13436)
## Summary

This PR changes removes the typeshed stubs from the vendored file system
shipped with ruff
and instead ships an empty "typeshed".

Making the typeshed files optional required extracting the typshed files
into a new `ruff_vendored` crate. I do like this even if all our builds
always include typeshed because it means `red_knot_python_semantic`
contains less code that needs compiling.

This also allows us to use deflate because the compression algorithm
doesn't matter for an archive containing a single, empty file.

## Test Plan

`cargo test`

I verified with ` cargo tree -f "{p} {f}" -p <package> ` that:

* red_knot_wasm: enables `deflate` compression
* red_knot: enables `zstd` compression
* `ruff`: uses stored


I'm not quiet sure how to build the binary that maturin builds but
comparing the release artifact size with `strip = true` shows a `1.5MB`
size reduction

---------

Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
2024-09-21 16:31:42 +00:00
Micha Reiser
8921fbb54c vendored_typeshed_versions should use db.vendored (#13434) 2024-09-21 16:35:06 +02:00
Charlie Marsh
3018303c87 Avoid parsing with Salsa (#13437)
## Summary

For reasons I haven't investigated, this speeds up the resolver about 2x
(from 6.404s to 3.612s on an extremely large codebase).

## Test Plan

\cc @BurntSushi 

```
[andrew@duff rippling]$ time ruff analyze graph --preview > /dev/null

real    3.274
user    16.039
sys     7.609
maxmem  11631 MB
faults  0
[andrew@duff rippling]$ time ruff-patch analyze graph --preview > /dev/null

real    1.841
user    14.625
sys     3.639
maxmem  7173 MB
faults  0
[andrew@duff rippling]$ time ruff-patch2 analyze graph --preview > /dev/null

real    2.087
user    15.333
sys     4.869
maxmem  8642 MB
faults  0
```

Where that's `main`, then (`ruff-patch`) using the version with no
`File`, no `SemanticModel`, then (`ruff-patch2`) using `File`.
2024-09-21 13:52:16 +00:00
haarisr
6c303b2445 red-knot: Add not unary operator for boolean literals (#13422)
## Summary

Contributes to #12701

## Test Plan

Added test for boolean literals

Signed-off-by: haaris <haarisrahman@gmail.com>
2024-09-20 15:24:38 -07:00
729 changed files with 8337 additions and 1537 deletions

View File

@@ -37,13 +37,13 @@ jobs:
- name: Sync typeshed
id: sync
run: |
rm -rf ruff/crates/red_knot_python_semantic/vendor/typeshed
mkdir ruff/crates/red_knot_python_semantic/vendor/typeshed
cp typeshed/README.md ruff/crates/red_knot_python_semantic/vendor/typeshed
cp typeshed/LICENSE ruff/crates/red_knot_python_semantic/vendor/typeshed
cp -r typeshed/stdlib ruff/crates/red_knot_python_semantic/vendor/typeshed/stdlib
rm -rf ruff/crates/red_knot_python_semantic/vendor/typeshed/stdlib/@tests
git -C typeshed rev-parse HEAD > ruff/crates/red_knot_python_semantic/vendor/typeshed/source_commit.txt
rm -rf ruff/crates/red_knot_vendored/vendor/typeshed
mkdir ruff/crates/red_knot_vendored/vendor/typeshed
cp typeshed/README.md ruff/crates/red_knot_vendored/vendor/typeshed
cp typeshed/LICENSE ruff/crates/red_knot_vendored/vendor/typeshed
cp -r typeshed/stdlib ruff/crates/red_knot_vendored/vendor/typeshed/stdlib
rm -rf ruff/crates/red_knot_vendored/vendor/typeshed/stdlib/@tests
git -C typeshed rev-parse HEAD > ruff/crates/red_knot_vendored/vendor/typeshed/source_commit.txt
- name: Commit the changes
id: commit
if: ${{ steps.sync.outcome == 'success' }}

View File

@@ -2,7 +2,7 @@ fail_fast: true
exclude: |
(?x)^(
crates/red_knot_python_semantic/vendor/.*|
crates/red_knot_vendored/vendor/.*|
crates/red_knot_workspace/resources/.*|
crates/ruff_linter/resources/.*|
crates/ruff_linter/src/rules/.*/snapshots/.*|
@@ -17,7 +17,7 @@ exclude: |
repos:
- repo: https://github.com/abravalheri/validate-pyproject
rev: v0.19
rev: v0.20.2
hooks:
- id: validate-pyproject
@@ -35,7 +35,7 @@ repos:
)$
- repo: https://github.com/igorshubovych/markdownlint-cli
rev: v0.41.0
rev: v0.42.0
hooks:
- id: markdownlint-fix
exclude: |
@@ -45,7 +45,7 @@ repos:
)$
- repo: https://github.com/crate-ci/typos
rev: v1.24.5
rev: v1.24.6
hooks:
- id: typos
@@ -59,7 +59,7 @@ repos:
pass_filenames: false # This makes it a lot faster
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.5
rev: v0.6.8
hooks:
- id: ruff-format
- id: ruff
@@ -68,8 +68,8 @@ repos:
require_serial: true
# Prettier
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v3.1.0
- repo: https://github.com/rbubley/mirrors-prettier
rev: v3.3.3
hooks:
- id: prettier
types: [yaml]

View File

@@ -1,5 +1,79 @@
# Changelog
## 0.6.9
### Preview features
- Fix codeblock dynamic line length calculation for indented docstring examples ([#13523](https://github.com/astral-sh/ruff/pull/13523))
- \[`refurb`\] Mark `FURB118` fix as unsafe ([#13613](https://github.com/astral-sh/ruff/pull/13613))
### Rule changes
- \[`pydocstyle`\] Don't raise `D208` when last line is non-empty ([#13372](https://github.com/astral-sh/ruff/pull/13372))
- \[`pylint`\] Preserve trivia (i.e. comments) in `PLR5501` autofix ([#13573](https://github.com/astral-sh/ruff/pull/13573))
### Configuration
- \[`pyflakes`\] Add `allow-unused-imports` setting for `unused-import` rule (`F401`) ([#13601](https://github.com/astral-sh/ruff/pull/13601))
### Bug fixes
- Support ruff discovery in pip build environments ([#13591](https://github.com/astral-sh/ruff/pull/13591))
- \[`flake8-bugbear`\] Avoid short circuiting `B017` for multiple context managers ([#13609](https://github.com/astral-sh/ruff/pull/13609))
- \[`pylint`\] Do not offer an invalid fix for `PLR1716` when the comparisons contain parenthesis ([#13527](https://github.com/astral-sh/ruff/pull/13527))
- \[`pyupgrade`\] Fix `UP043` to apply to `collections.abc.Generator` and `collections.abc.AsyncGenerator` ([#13611](https://github.com/astral-sh/ruff/pull/13611))
- \[`refurb`\] Fix handling of slices in tuples for `FURB118`, e.g., `x[:, 1]` ([#13518](https://github.com/astral-sh/ruff/pull/13518))
### Documentation
- Update GitHub Action link to `astral-sh/ruff-action` ([#13551](https://github.com/astral-sh/ruff/pull/13551))
## 0.6.8
### Preview features
- Remove unnecessary parentheses around `match case` clauses ([#13510](https://github.com/astral-sh/ruff/pull/13510))
- Parenthesize overlong `if` guards in `match..case` clauses ([#13513](https://github.com/astral-sh/ruff/pull/13513))
- Detect basic wildcard imports in `ruff analyze graph` ([#13486](https://github.com/astral-sh/ruff/pull/13486))
- \[`pylint`\] Implement `boolean-chained-comparison` (`R1716`) ([#13435](https://github.com/astral-sh/ruff/pull/13435))
### Rule changes
- \[`lake8-simplify`\] Detect `SIM910` when using variadic keyword arguments, i.e., `**kwargs` ([#13503](https://github.com/astral-sh/ruff/pull/13503))
- \[`pyupgrade`\] Avoid false negatives with non-reference shadowed bindings of loop variables (`UP028`) ([#13504](https://github.com/astral-sh/ruff/pull/13504))
### Bug fixes
- Detect tuples bound to variadic positional arguments i.e. `*args` ([#13512](https://github.com/astral-sh/ruff/pull/13512))
- Exit gracefully on broken pipe errors ([#13485](https://github.com/astral-sh/ruff/pull/13485))
- Avoid panic when analyze graph hits broken pipe ([#13484](https://github.com/astral-sh/ruff/pull/13484))
### Performance
- Reuse `BTreeSets` in module resolver ([#13440](https://github.com/astral-sh/ruff/pull/13440))
- Skip traversal for non-compound statements ([#13441](https://github.com/astral-sh/ruff/pull/13441))
## 0.6.7
### Preview features
- Add Python version support to ruff analyze CLI ([#13426](https://github.com/astral-sh/ruff/pull/13426))
- Add `exclude` support to `ruff analyze` ([#13425](https://github.com/astral-sh/ruff/pull/13425))
- Fix parentheses around return type annotations ([#13381](https://github.com/astral-sh/ruff/pull/13381))
### Rule changes
- \[`pycodestyle`\] Fix: Don't autofix if the first line ends in a question mark? (D400) ([#13399](https://github.com/astral-sh/ruff/pull/13399))
### Bug fixes
- Respect `lint.exclude` in ruff check `--add-noqa` ([#13427](https://github.com/astral-sh/ruff/pull/13427))
### Performance
- Avoid tracking module resolver files in Salsa ([#13437](https://github.com/astral-sh/ruff/pull/13437))
- Use `forget` for module resolver database ([#13438](https://github.com/astral-sh/ruff/pull/13438))
## 0.6.6
### Preview features

View File

@@ -29,16 +29,14 @@ You'll also need [Insta](https://insta.rs/docs/) to update snapshot tests:
cargo install cargo-insta
```
And you'll need pre-commit to run some validation checks:
```shell
pipx install pre-commit # or `pip install pre-commit` if you have a virtualenv
```
You'll need [uv](https://docs.astral.sh/uv/getting-started/installation/) (or `pipx` and `pip`) to
run Python utility commands.
You can optionally install pre-commit hooks to automatically run the validation checks
when making a commit:
```shell
uv tool install pre-commit
pre-commit install
```
@@ -66,7 +64,7 @@ and that it passes both the lint and test validation checks:
```shell
cargo clippy --workspace --all-targets --all-features -- -D warnings # Rust linting
RUFF_UPDATE_SCHEMA=1 cargo test # Rust testing and updating ruff.schema.json
pre-commit run --all-files --show-diff-on-failure # Rust and Python formatting, Markdown and Python linting, etc.
uvx pre-commit run --all-files --show-diff-on-failure # Rust and Python formatting, Markdown and Python linting, etc.
```
These checks will run on GitHub Actions when you open your pull request, but running them locally
@@ -267,26 +265,20 @@ To preview any changes to the documentation locally:
1. Install the [Rust toolchain](https://www.rust-lang.org/tools/install).
1. Install MkDocs and Material for MkDocs with:
```shell
pip install -r docs/requirements.txt
```
1. Generate the MkDocs site with:
```shell
python scripts/generate_mkdocs.py
uv run --no-project --isolated --with-requirements docs/requirements.txt scripts/generate_mkdocs.py
```
1. Run the development server with:
```shell
# For contributors.
mkdocs serve -f mkdocs.public.yml
uvx --with-requirements docs/requirements.txt -- mkdocs serve -f mkdocs.public.yml
# For members of the Astral org, which has access to MkDocs Insiders via sponsorship.
mkdocs serve -f mkdocs.insiders.yml
uvx --with-requirements docs/requirements-insiders.txt -- mkdocs serve -f mkdocs.insiders.yml
```
The documentation should then be available locally at
@@ -368,9 +360,8 @@ GitHub Actions will run your changes against a number of real-world projects fro
report on any linter or formatter differences. You can also run those checks locally via:
```shell
pip install -e ./python/ruff-ecosystem
ruff-ecosystem check ruff "./target/debug/ruff"
ruff-ecosystem format ruff "./target/debug/ruff"
uvx --from ./python/ruff-ecosystem ruff-ecosystem check ruff "./target/debug/ruff"
uvx --from ./python/ruff-ecosystem ruff-ecosystem format ruff "./target/debug/ruff"
```
See the [ruff-ecosystem package](https://github.com/astral-sh/ruff/tree/main/python/ruff-ecosystem) for more details.

187
Cargo.lock generated
View File

@@ -129,9 +129,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.86"
version = "1.0.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6"
[[package]]
name = "append-only-vec"
@@ -225,7 +225,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c"
dependencies = [
"memchr",
"regex-automata 0.4.6",
"regex-automata 0.4.8",
"serde",
]
@@ -353,9 +353,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.5.16"
version = "4.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019"
checksum = "b0956a43b323ac1afaffc053ed5c4b7c1f1800bacd1683c353aabbb752515dd3"
dependencies = [
"clap_builder",
"clap_derive",
@@ -363,9 +363,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.5.15"
version = "4.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6"
checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b"
dependencies = [
"anstream",
"anstyle",
@@ -406,9 +406,9 @@ dependencies = [
[[package]]
name = "clap_derive"
version = "4.5.13"
version = "4.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0"
checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
dependencies = [
"heck",
"proc-macro2",
@@ -437,9 +437,9 @@ dependencies = [
[[package]]
name = "codspeed"
version = "2.6.0"
version = "2.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a104ac948e0188b921eb3fcbdd55dcf62e542df4c7ab7e660623f6288302089"
checksum = "450a0e9df9df1c154156f4344f99d8f6f6e69d0fc4de96ef6e2e68b2ec3bce97"
dependencies = [
"colored",
"libc",
@@ -448,9 +448,9 @@ dependencies = [
[[package]]
name = "codspeed-criterion-compat"
version = "2.6.0"
version = "2.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "722c36bdc62d9436d027256ce2627af81ac7a596dfc7d13d849d0d212448d7fe"
checksum = "8eb1a6cb9c20e177fde58cdef97c1c7c9264eb1424fe45c4fccedc2fb078a569"
dependencies = [
"codspeed",
"colored",
@@ -722,9 +722,9 @@ dependencies = [
[[package]]
name = "dashmap"
version = "6.0.1"
version = "6.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "804c8821570c3f8b70230c2ba75ffa5c0f9a4189b9a432b6656c536712acae28"
checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf"
dependencies = [
"cfg-if",
"crossbeam-utils",
@@ -879,9 +879,9 @@ dependencies = [
[[package]]
name = "fastrand"
version = "2.0.2"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984"
checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
[[package]]
name = "fern"
@@ -894,9 +894,9 @@ dependencies = [
[[package]]
name = "filetime"
version = "0.2.24"
version = "0.2.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf401df4a4e3872c4fe8151134cf483738e74b67fc934d6532c882b3d24a4550"
checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586"
dependencies = [
"cfg-if",
"libc",
@@ -987,15 +987,15 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "globset"
version = "0.4.14"
version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1"
checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19"
dependencies = [
"aho-corasick",
"bstr",
"log",
"regex-automata 0.4.6",
"regex-syntax 0.8.3",
"regex-automata 0.4.8",
"regex-syntax 0.8.5",
]
[[package]]
@@ -1106,15 +1106,15 @@ dependencies = [
[[package]]
name = "ignore"
version = "0.4.22"
version = "0.4.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1"
checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b"
dependencies = [
"crossbeam-deque",
"globset",
"log",
"memchr",
"regex-automata 0.4.6",
"regex-automata 0.4.8",
"same-file",
"walkdir",
"winapi-util",
@@ -1142,9 +1142,9 @@ dependencies = [
[[package]]
name = "indexmap"
version = "2.4.0"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c"
checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5"
dependencies = [
"equivalent",
"hashbrown",
@@ -1193,9 +1193,9 @@ dependencies = [
[[package]]
name = "insta"
version = "1.39.0"
version = "1.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "810ae6042d48e2c9e9215043563a58a80b877bc863228a74cf10c49d4620a6f5"
checksum = "6593a41c7a73841868772495db7dc1e8ecab43bb5c0b6da2059246c4b506ab60"
dependencies = [
"console",
"globset",
@@ -1347,9 +1347,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.158"
version = "0.2.159"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
[[package]]
name = "libcst"
@@ -1405,9 +1405,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
[[package]]
name = "linux-raw-sys"
version = "0.4.13"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
[[package]]
name = "lock_api"
@@ -1427,9 +1427,9 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
name = "lsp-server"
version = "0.7.6"
version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "248f65b78f6db5d8e1b1604b4098a28b43d21a8eb1deeca22b1c421b276c7095"
checksum = "550446e84739dcaf6d48a4a093973850669e13e8a34d8f8d64851041be267cd9"
dependencies = [
"crossbeam-channel",
"log",
@@ -1644,9 +1644,9 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "ordermap"
version = "0.5.2"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61d7d835be600a7ac71b24e39c92fe6fad9e818b3c71bfc379e3ba65e327d77f"
checksum = "31f2bd7b03bf2c767e1bb7b91505dbe022833776e60480275e6f2fb0db0c7503"
dependencies = [
"indexmap",
]
@@ -1934,9 +1934,9 @@ dependencies = [
[[package]]
name = "pretty_assertions"
version = "1.4.0"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66"
checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d"
dependencies = [
"diff",
"yansi",
@@ -2083,9 +2083,8 @@ dependencies = [
"countme",
"hashbrown",
"insta",
"once_cell",
"ordermap",
"path-slash",
"red_knot_vendored",
"ruff_db",
"ruff_index",
"ruff_python_ast",
@@ -2102,8 +2101,6 @@ dependencies = [
"test-case",
"thiserror",
"tracing",
"walkdir",
"zip",
]
[[package]]
@@ -2130,6 +2127,17 @@ dependencies = [
"tracing-subscriber",
]
[[package]]
name = "red_knot_vendored"
version = "0.0.0"
dependencies = [
"once_cell",
"path-slash",
"ruff_db",
"walkdir",
"zip",
]
[[package]]
name = "red_knot_wasm"
version = "0.0.0"
@@ -2155,6 +2163,7 @@ dependencies = [
"notify",
"rayon",
"red_knot_python_semantic",
"red_knot_vendored",
"ruff_cache",
"ruff_db",
"ruff_python_ast",
@@ -2196,14 +2205,14 @@ dependencies = [
[[package]]
name = "regex"
version = "1.10.6"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata 0.4.6",
"regex-syntax 0.8.3",
"regex-automata 0.4.8",
"regex-syntax 0.8.5",
]
[[package]]
@@ -2217,13 +2226,13 @@ dependencies = [
[[package]]
name = "regex-automata"
version = "0.4.6"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax 0.8.3",
"regex-syntax 0.8.5",
]
[[package]]
@@ -2234,9 +2243,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]]
name = "regex-syntax"
version = "0.8.3"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "ring"
@@ -2255,7 +2264,7 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.6.6"
version = "0.6.9"
dependencies = [
"anyhow",
"argfile",
@@ -2327,6 +2336,7 @@ dependencies = [
"ruff_python_formatter",
"ruff_python_parser",
"ruff_python_trivia",
"rustc-hash 2.0.0",
"serde",
"serde_json",
"tikv-jemallocator",
@@ -2353,7 +2363,7 @@ version = "0.0.0"
dependencies = [
"camino",
"countme",
"dashmap 6.0.1",
"dashmap 6.1.0",
"filetime",
"ignore",
"insta",
@@ -2450,15 +2460,18 @@ version = "0.1.0"
dependencies = [
"anyhow",
"clap",
"once_cell",
"red_knot_python_semantic",
"ruff_cache",
"ruff_db",
"ruff_linter",
"ruff_macros",
"ruff_python_ast",
"ruff_python_parser",
"salsa",
"schemars",
"serde",
"zip",
]
[[package]]
@@ -2471,7 +2484,7 @@ dependencies = [
[[package]]
name = "ruff_linter"
version = "0.6.6"
version = "0.6.9"
dependencies = [
"aho-corasick",
"annotate-snippets 0.9.2",
@@ -2791,7 +2804,7 @@ dependencies = [
[[package]]
name = "ruff_wasm"
version = "0.6.6"
version = "0.6.9"
dependencies = [
"console_error_panic_hook",
"console_log",
@@ -2874,9 +2887,9 @@ checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152"
[[package]]
name = "rustix"
version = "0.38.34"
version = "0.38.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811"
dependencies = [
"bitflags 2.6.0",
"errno",
@@ -2932,12 +2945,12 @@ checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
[[package]]
name = "salsa"
version = "0.18.0"
source = "git+https://github.com/salsa-rs/salsa.git?rev=f608ff8b24f07706492027199f51132244034f29#f608ff8b24f07706492027199f51132244034f29"
source = "git+https://github.com/salsa-rs/salsa.git?rev=4a7c955255e707e64e43f3ce5eabb771ae067768#4a7c955255e707e64e43f3ce5eabb771ae067768"
dependencies = [
"append-only-vec",
"arc-swap",
"crossbeam",
"dashmap 6.0.1",
"dashmap 6.1.0",
"hashlink",
"indexmap",
"lazy_static",
@@ -2952,12 +2965,12 @@ dependencies = [
[[package]]
name = "salsa-macro-rules"
version = "0.1.0"
source = "git+https://github.com/salsa-rs/salsa.git?rev=f608ff8b24f07706492027199f51132244034f29#f608ff8b24f07706492027199f51132244034f29"
source = "git+https://github.com/salsa-rs/salsa.git?rev=4a7c955255e707e64e43f3ce5eabb771ae067768#4a7c955255e707e64e43f3ce5eabb771ae067768"
[[package]]
name = "salsa-macros"
version = "0.18.0"
source = "git+https://github.com/salsa-rs/salsa.git?rev=f608ff8b24f07706492027199f51132244034f29#f608ff8b24f07706492027199f51132244034f29"
source = "git+https://github.com/salsa-rs/salsa.git?rev=4a7c955255e707e64e43f3ce5eabb771ae067768#4a7c955255e707e64e43f3ce5eabb771ae067768"
dependencies = [
"heck",
"proc-macro2",
@@ -3019,9 +3032,9 @@ checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
[[package]]
name = "serde"
version = "1.0.209"
version = "1.0.210"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09"
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
dependencies = [
"serde_derive",
]
@@ -3039,9 +3052,9 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.209"
version = "1.0.210"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170"
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
dependencies = [
"proc-macro2",
"quote",
@@ -3061,9 +3074,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.127"
version = "1.0.128"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad"
checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8"
dependencies = [
"itoa",
"memchr",
@@ -3233,9 +3246,9 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
[[package]]
name = "syn"
version = "2.0.76"
version = "2.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525"
checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
dependencies = [
"proc-macro2",
"quote",
@@ -3255,9 +3268,9 @@ dependencies = [
[[package]]
name = "tempfile"
version = "3.12.0"
version = "3.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64"
checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b"
dependencies = [
"cfg-if",
"fastrand",
@@ -3330,18 +3343,18 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.63"
version = "1.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.63"
version = "1.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3"
dependencies = [
"proc-macro2",
"quote",
@@ -3602,15 +3615,15 @@ checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
[[package]]
name = "unicode-ident"
version = "1.0.12"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
[[package]]
name = "unicode-normalization"
version = "0.1.23"
version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5"
checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956"
dependencies = [
"tinyvec",
]
@@ -3623,9 +3636,9 @@ checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
[[package]]
name = "unicode_names2"
version = "1.2.2"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "addeebf294df7922a1164f729fb27ebbbcea99cc32b3bf08afab62757f707677"
checksum = "d1673eca9782c84de5f81b82e4109dcfb3611c8ba0d52930ec4a9478f547b2dd"
dependencies = [
"phf",
"unicode_names2_generator",
@@ -3633,9 +3646,9 @@ dependencies = [
[[package]]
name = "unicode_names2_generator"
version = "1.2.2"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f444b8bba042fe3c1251ffaca35c603f2dc2ccc08d595c65a8c4f76f3e8426c0"
checksum = "b91e5b84611016120197efd7dc93ef76774f4e084cd73c9fb3ea4a86c570c56e"
dependencies = [
"getopts",
"log",
@@ -4121,9 +4134,9 @@ checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"
[[package]]
name = "yansi"
version = "0.5.1"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
[[package]]
name = "yansi-term"

View File

@@ -14,7 +14,7 @@ license = "MIT"
[workspace.dependencies]
ruff = { path = "crates/ruff" }
ruff_cache = { path = "crates/ruff_cache" }
ruff_db = { path = "crates/ruff_db" }
ruff_db = { path = "crates/ruff_db", default-features = false }
ruff_diagnostics = { path = "crates/ruff_diagnostics" }
ruff_formatter = { path = "crates/ruff_formatter" }
ruff_graph = { path = "crates/ruff_graph" }
@@ -34,11 +34,12 @@ ruff_python_trivia = { path = "crates/ruff_python_trivia" }
ruff_server = { path = "crates/ruff_server" }
ruff_source_file = { path = "crates/ruff_source_file" }
ruff_text_size = { path = "crates/ruff_text_size" }
red_knot_vendored = { path = "crates/red_knot_vendored" }
ruff_workspace = { path = "crates/ruff_workspace" }
red_knot_python_semantic = { path = "crates/red_knot_python_semantic" }
red_knot_server = { path = "crates/red_knot_server" }
red_knot_workspace = { path = "crates/red_knot_workspace" }
red_knot_workspace = { path = "crates/red_knot_workspace", default-features = false }
aho-corasick = { version = "1.1.3" }
annotate-snippets = { version = "0.9.2", features = ["color"] }
@@ -111,7 +112,7 @@ rand = { version = "0.8.5" }
rayon = { version = "1.10.0" }
regex = { version = "1.10.2" }
rustc-hash = { version = "2.0.0" }
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "f608ff8b24f07706492027199f51132244034f29" }
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "4a7c955255e707e64e43f3ce5eabb771ae067768" }
schemars = { version = "0.8.16" }
seahash = { version = "4.1.0" }
serde = { version = "1.0.197", features = ["derive"] }

View File

@@ -136,8 +136,8 @@ curl -LsSf https://astral.sh/ruff/install.sh | sh
powershell -c "irm https://astral.sh/ruff/install.ps1 | iex"
# For a specific version.
curl -LsSf https://astral.sh/ruff/0.6.6/install.sh | sh
powershell -c "irm https://astral.sh/ruff/0.6.6/install.ps1 | iex"
curl -LsSf https://astral.sh/ruff/0.6.9/install.sh | sh
powershell -c "irm https://astral.sh/ruff/0.6.9/install.ps1 | iex"
```
You can also install Ruff via [Homebrew](https://formulae.brew.sh/formula/ruff), [Conda](https://anaconda.org/conda-forge/ruff),
@@ -170,7 +170,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com/) hook via [`ruff
```yaml
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.6.6
rev: v0.6.9
hooks:
# Run the linter.
- id: ruff
@@ -182,7 +182,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com/) hook via [`ruff
Ruff can also be used as a [VS Code extension](https://github.com/astral-sh/ruff-vscode) or with [various other editors](https://docs.astral.sh/ruff/editors/setup).
Ruff can also be used as a [GitHub Action](https://github.com/features/actions) via
[`ruff-action`](https://github.com/chartboost/ruff-action):
[`ruff-action`](https://github.com/astral-sh/ruff-action):
```yaml
name: Ruff
@@ -192,7 +192,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: chartboost/ruff-action@v1
- uses: astral-sh/ruff-action@v1
```
### Configuration<a id="configuration"></a>

View File

@@ -1,6 +1,6 @@
[files]
# https://github.com/crate-ci/typos/issues/868
extend-exclude = ["crates/red_knot_python_semantic/vendor/**/*", "**/resources/**/*", "**/snapshots/**/*"]
extend-exclude = ["crates/red_knot_vendored/vendor/**/*", "**/resources/**/*", "**/snapshots/**/*"]
[default.extend-words]
"arange" = "arange" # e.g. `numpy.arange`
@@ -8,7 +8,7 @@ hel = "hel"
whos = "whos"
spawnve = "spawnve"
ned = "ned"
pn = "pn" # `import panel as pd` is a thing
pn = "pn" # `import panel as pn` is a thing
poit = "poit"
BA = "BA" # acronym for "Bad Allowed", used in testing.
jod = "jod" # e.g., `jod-thread`

View File

@@ -13,9 +13,8 @@ license.workspace = true
[dependencies]
red_knot_python_semantic = { workspace = true }
red_knot_workspace = { workspace = true }
red_knot_workspace = { workspace = true, features = ["zstd"] }
red_knot_server = { workspace = true }
ruff_db = { workspace = true, features = ["os", "cache"] }
anyhow = { workspace = true }

View File

@@ -160,7 +160,7 @@ fn run() -> anyhow::Result<ExitStatus> {
SystemPathBuf::from_path_buf(cwd)
.map_err(|path| {
anyhow!(
"The current working directory '{}' contains non-unicode characters. Red Knot only supports unicode paths.",
"The current working directory `{}` contains non-Unicode characters. Red Knot only supports Unicode paths.",
path.display()
)
})?
@@ -174,7 +174,7 @@ fn run() -> anyhow::Result<ExitStatus> {
Ok(SystemPath::absolute(cwd, &cli_base_path))
} else {
Err(anyhow!(
"Provided current-directory path '{cwd}' is not a directory."
"Provided current-directory path `{cwd}` is not a directory"
))
}
})

View File

@@ -42,14 +42,14 @@ impl TestCase {
fn stop_watch(&mut self) -> Vec<watch::ChangeEvent> {
self.try_stop_watch(Duration::from_secs(10))
.expect("Expected watch changes but observed none.")
.expect("Expected watch changes but observed none")
}
fn try_stop_watch(&mut self, timeout: Duration) -> Option<Vec<watch::ChangeEvent>> {
let watcher = self
.watcher
.take()
.expect("Cannot call `stop_watch` more than once.");
.expect("Cannot call `stop_watch` more than once");
let mut all_events = self
.changes_receiver
@@ -72,7 +72,7 @@ impl TestCase {
#[cfg(unix)]
fn take_watch_changes(&self) -> Vec<watch::ChangeEvent> {
self.try_take_watch_changes(Duration::from_secs(10))
.expect("Expected watch changes but observed none.")
.expect("Expected watch changes but observed none")
}
fn try_take_watch_changes(&self, timeout: Duration) -> Option<Vec<watch::ChangeEvent>> {
@@ -150,14 +150,14 @@ where
let absolute_path = workspace_path.join(relative_path);
if let Some(parent) = absolute_path.parent() {
std::fs::create_dir_all(parent).with_context(|| {
format!("Failed to create parent directory for file '{relative_path}'.",)
format!("Failed to create parent directory for file `{relative_path}`")
})?;
}
let mut file = std::fs::File::create(absolute_path.as_std_path())
.with_context(|| format!("Failed to open file '{relative_path}'"))?;
.with_context(|| format!("Failed to open file `{relative_path}`"))?;
file.write_all(content.as_bytes())
.with_context(|| format!("Failed to write to file '{relative_path}'"))?;
.with_context(|| format!("Failed to write to file `{relative_path}`"))?;
file.sync_data()?;
}
@@ -194,7 +194,7 @@ where
let root_path = SystemPath::from_std_path(temp_dir.path()).ok_or_else(|| {
anyhow!(
"Temp directory '{}' is not a valid UTF-8 path.",
"Temporary directory `{}` is not a valid UTF-8 path.",
temp_dir.path().display()
)
})?;
@@ -209,7 +209,7 @@ where
let workspace_path = root_path.join("workspace");
std::fs::create_dir_all(workspace_path.as_std_path())
.with_context(|| format!("Failed to create workspace directory '{workspace_path}'",))?;
.with_context(|| format!("Failed to create workspace directory `{workspace_path}`"))?;
setup_files
.setup(&root_path, &workspace_path)
@@ -233,7 +233,7 @@ where
}))
{
std::fs::create_dir_all(path.as_std_path())
.with_context(|| format!("Failed to create search path '{path}'"))?;
.with_context(|| format!("Failed to create search path `{path}`"))?;
}
let configuration = Configuration {
@@ -665,7 +665,7 @@ fn directory_deleted() -> anyhow::Result<()> {
let bar = case.system_file(case.workspace_path("bar.py")).unwrap();
assert!(resolve_module(case.db().upcast(), ModuleName::new_static("sub.a").unwrap()).is_some(),);
assert!(resolve_module(case.db().upcast(), ModuleName::new_static("sub.a").unwrap()).is_some());
let sub_path = case.workspace_path("sub");

View File

@@ -24,7 +24,6 @@ bitflags = { workspace = true }
camino = { workspace = true }
compact_str = { workspace = true }
countme = { workspace = true }
once_cell = { workspace = true }
ordermap = { workspace = true }
salsa = { workspace = true }
thiserror = { workspace = true }
@@ -35,25 +34,14 @@ smallvec = { workspace = true }
static_assertions = { workspace = true }
test-case = { workspace = true }
[build-dependencies]
path-slash = { workspace = true }
walkdir = { workspace = true }
[target.'cfg(not(target_arch = "powerpc64"))'.build-dependencies]
zip = { workspace = true, features = ["deflate", "zstd"] }
[target.'cfg(target_arch = "powerpc64")'.build-dependencies]
zip = { workspace = true, features = ["deflate"] }
[dev-dependencies]
ruff_db = { workspace = true, features = ["os", "testing"] }
ruff_python_parser = { workspace = true }
red_knot_vendored = { workspace = true }
anyhow = { workspace = true }
insta = { workspace = true }
tempfile = { workspace = true }
walkdir = { workspace = true }
zip = { workspace = true }
[lints]
workspace = true

View File

@@ -11,7 +11,6 @@ pub trait Db: SourceDb + Upcast<dyn SourceDb> {
pub(crate) mod tests {
use std::sync::Arc;
use crate::module_resolver::vendored_typeshed_stubs;
use ruff_db::files::{File, Files};
use ruff_db::system::{DbWithTestSystem, System, TestSystem};
use ruff_db::vendored::VendoredFileSystem;
@@ -33,7 +32,7 @@ pub(crate) mod tests {
Self {
storage: salsa::Storage::default(),
system: TestSystem::default(),
vendored: vendored_typeshed_stubs().clone(),
vendored: red_knot_vendored::file_system().clone(),
events: std::sync::Arc::default(),
files: Files::default(),
}

View File

@@ -4,9 +4,7 @@ use rustc_hash::FxHasher;
pub use db::Db;
pub use module_name::ModuleName;
pub use module_resolver::{
resolve_module, system_module_search_paths, vendored_typeshed_stubs, Module,
};
pub use module_resolver::{resolve_module, system_module_search_paths, Module};
pub use program::{Program, ProgramSettings, SearchPathSettings, SitePackages};
pub use python_version::PythonVersion;
pub use semantic_model::{HasTy, SemanticModel};

View File

@@ -4,7 +4,6 @@ pub use module::Module;
pub use resolver::resolve_module;
pub(crate) use resolver::{file_to_module, SearchPaths};
use ruff_db::system::SystemPath;
pub use typeshed::vendored_typeshed_stubs;
use crate::module_resolver::resolver::search_paths;
use crate::Db;

View File

@@ -1,20 +1,21 @@
use rustc_hash::{FxBuildHasher, FxHashSet};
use std::borrow::Cow;
use std::iter::FusedIterator;
use std::ops::Deref;
use rustc_hash::{FxBuildHasher, FxHashSet};
use ruff_db::files::{File, FilePath, FileRootKind};
use ruff_db::system::{DirectoryEntry, System, SystemPath, SystemPathBuf};
use ruff_db::vendored::{VendoredFileSystem, VendoredPath};
use super::module::{Module, ModuleKind};
use super::path::{ModulePath, SearchPath, SearchPathValidationError};
use crate::db::Db;
use crate::module_name::ModuleName;
use crate::module_resolver::typeshed::{vendored_typeshed_versions, TypeshedVersions};
use crate::site_packages::VirtualEnvironment;
use crate::{Program, PythonVersion, SearchPathSettings, SitePackages};
use super::module::{Module, ModuleKind};
use super::path::{ModulePath, SearchPath, SearchPathValidationError};
/// Resolves a module name to a module.
pub fn resolve_module(db: &dyn Db, module_name: ModuleName) -> Option<Module> {
let interned_name = ModuleNameIngredient::new(db, module_name);
@@ -35,14 +36,14 @@ pub(crate) fn resolve_module_query<'db>(
let _span = tracing::trace_span!("resolve_module", %name).entered();
let Some((search_path, module_file, kind)) = resolve_name(db, name) else {
tracing::debug!("Module '{name}' not found in the search paths.");
tracing::debug!("Module `{name}` not found in search paths");
return None;
};
let module = Module::new(name.clone(), kind, search_path, module_file);
tracing::trace!(
"Resolved module '{name}' to '{path}'.",
"Resolved module `{name}` to `{path}`",
path = module_file.path(db)
);
@@ -136,7 +137,7 @@ pub(crate) struct SearchPaths {
/// for the first `site-packages` path
site_packages: Vec<SearchPath>,
typeshed_versions: ResolvedTypeshedVersions,
typeshed_versions: TypeshedVersions,
}
impl SearchPaths {
@@ -202,11 +203,11 @@ impl SearchPaths {
let search_path = SearchPath::custom_stdlib(db, &custom_typeshed)?;
(ResolvedTypeshedVersions::Custom(parsed), search_path)
(parsed, search_path)
} else {
tracing::debug!("Using vendored stdlib");
(
ResolvedTypeshedVersions::Vendored(vendored_typeshed_versions()),
vendored_typeshed_versions(db),
SearchPath::vendored_stdlib(),
)
};
@@ -279,23 +280,6 @@ impl SearchPaths {
}
}
#[derive(Debug, PartialEq, Eq)]
enum ResolvedTypeshedVersions {
Vendored(&'static TypeshedVersions),
Custom(TypeshedVersions),
}
impl Deref for ResolvedTypeshedVersions {
type Target = TypeshedVersions;
fn deref(&self) -> &Self::Target {
match self {
ResolvedTypeshedVersions::Vendored(versions) => versions,
ResolvedTypeshedVersions::Custom(versions) => versions,
}
}
}
/// Collect all dynamic search paths. For each `site-packages` path:
/// - Collect that `site-packages` path
/// - Collect any search paths listed in `.pth` files in that `site-packages` directory
@@ -340,7 +324,7 @@ pub(crate) fn dynamic_resolution_paths(db: &dyn Db) -> Vec<SearchPath> {
let site_packages_root = files
.root(db.upcast(), site_packages_dir)
.expect("Site-package root to have been created.");
.expect("Site-package root to have been created");
// This query needs to be re-executed each time a `.pth` file
// is added, modified or removed from the `site-packages` directory.

View File

@@ -4,25 +4,19 @@ use std::num::{NonZeroU16, NonZeroUsize};
use std::ops::{RangeFrom, RangeInclusive};
use std::str::FromStr;
use once_cell::sync::Lazy;
use rustc_hash::FxHashMap;
use super::vendored::vendored_typeshed_stubs;
use crate::db::Db;
use crate::module_name::ModuleName;
use crate::{Program, PythonVersion};
static VENDORED_VERSIONS: Lazy<TypeshedVersions> = Lazy::new(|| {
pub(in crate::module_resolver) fn vendored_typeshed_versions(db: &dyn Db) -> TypeshedVersions {
TypeshedVersions::from_str(
&vendored_typeshed_stubs()
&db.vendored()
.read_to_string("stdlib/VERSIONS")
.unwrap(),
.expect("The vendored typeshed stubs should contain a VERSIONS file"),
)
.unwrap()
});
pub(crate) fn vendored_typeshed_versions() -> &'static TypeshedVersions {
&VENDORED_VERSIONS
.expect("The VERSIONS file in the vendored typeshed stubs should be well-formed")
}
pub(crate) fn typeshed_versions(db: &dyn Db) -> &TypeshedVersions {
@@ -332,6 +326,8 @@ mod tests {
use insta::assert_snapshot;
use crate::db::tests::TestDb;
use super::*;
const TYPESHED_STDLIB_DIR: &str = "stdlib";
@@ -353,12 +349,9 @@ mod tests {
#[test]
fn can_parse_vendored_versions_file() {
let versions_data = include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/vendor/typeshed/stdlib/VERSIONS"
));
let db = TestDb::new();
let versions = TypeshedVersions::from_str(versions_data).unwrap();
let versions = vendored_typeshed_versions(&db);
assert!(versions.len() > 100);
assert!(versions.len() < 1000);
@@ -395,9 +388,10 @@ mod tests {
#[test]
fn typeshed_versions_consistent_with_vendored_stubs() {
const VERSIONS_DATA: &str = include_str!("../../../vendor/typeshed/stdlib/VERSIONS");
let vendored_typeshed_dir = Path::new("vendor/typeshed").canonicalize().unwrap();
let vendored_typeshed_versions = TypeshedVersions::from_str(VERSIONS_DATA).unwrap();
let db = TestDb::new();
let vendored_typeshed_versions = vendored_typeshed_versions(&db);
let vendored_typeshed_dir =
Path::new(env!("CARGO_MANIFEST_DIR")).join("../red_knot_vendored/vendor/typeshed");
let mut empty_iterator = true;

View File

@@ -1,8 +0,0 @@
pub use self::vendored::vendored_typeshed_stubs;
pub(super) use self::versions::{
typeshed_versions, vendored_typeshed_versions, TypeshedVersions, TypeshedVersionsParseError,
TypeshedVersionsQueryResult,
};
mod vendored;
mod versions;

View File

@@ -3,6 +3,7 @@ use ruff_db::parsed::ParsedModule;
use ruff_python_ast as ast;
use crate::ast_node_ref::AstNodeRef;
use crate::module_resolver::file_to_module;
use crate::node_key::NodeKey;
use crate::semantic_index::symbol::{FileScopeId, ScopeId, ScopedSymbolId};
use crate::Db;
@@ -45,6 +46,14 @@ impl<'db> Definition<'db> {
pub(crate) fn is_binding(self, db: &'db dyn Db) -> bool {
self.kind(db).category().is_binding()
}
/// Return true if this is a symbol was defined in the `typing` or `typing_extensions` modules
pub(crate) fn is_typing_definition(self, db: &'db dyn Db) -> bool {
file_to_module(db, self.file(db)).is_some_and(|module| {
module.search_path().is_standard_library()
&& matches!(&**module.name(), "typing" | "typing_extensions")
})
}
}
#[derive(Copy, Clone, Debug)]

View File

@@ -192,7 +192,7 @@ impl VirtualEnvironment {
} else {
tracing::warn!(
"Failed to resolve `sys.prefix` of the system Python installation \
from the `home` value in the `pyvenv.cfg` file at '{}'. \
from the `home` value in the `pyvenv.cfg` file at `{}`. \
System site-packages will not be used for module resolution.",
venv_path.join("pyvenv.cfg")
);
@@ -426,7 +426,7 @@ impl Deref for SysPrefixPath {
impl fmt::Display for SysPrefixPath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "`sys.prefix` path '{}'", self.0)
write!(f, "`sys.prefix` path `{}`", self.0)
}
}
@@ -483,7 +483,7 @@ impl Deref for PythonHomePath {
impl fmt::Display for PythonHomePath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "`home` location '{}'", self.0)
write!(f, "`home` location `{}`", self.0)
}
}

View File

@@ -169,7 +169,7 @@ fn bindings_ty<'db>(
let first = all_types
.next()
.expect("bindings_ty should never be called with zero definitions and no unbound_ty.");
.expect("bindings_ty should never be called with zero definitions and no unbound_ty");
if let Some(second) = all_types.next() {
UnionType::from_elements(db, [first, second].into_iter().chain(all_types))
@@ -204,7 +204,7 @@ fn declarations_ty<'db>(
let mut all_types = undeclared_ty.into_iter().chain(decl_types);
let first = all_types.next().expect(
"declarations_ty must not be called with zero declarations and no may-be-undeclared.",
"declarations_ty must not be called with zero declarations and no may-be-undeclared",
);
let mut conflicting: Vec<Type<'db>> = vec![];
@@ -221,9 +221,9 @@ fn declarations_ty<'db>(
first
};
if conflicting.is_empty() {
DeclaredTypeResult::Ok(declared_ty)
Ok(declared_ty)
} else {
DeclaredTypeResult::Err((
Err((
declared_ty,
[first].into_iter().chain(conflicting).collect(),
))
@@ -233,31 +233,38 @@ fn declarations_ty<'db>(
/// Unique ID for a type.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum Type<'db> {
/// the dynamic type: a statically-unknown set of values
/// The dynamic type: a statically-unknown set of values
Any,
/// the empty set of values
/// The empty set of values
Never,
/// unknown type (either no annotation, or some kind of type error)
/// equivalent to Any, or possibly to object in strict mode
/// Unknown type (either no annotation, or some kind of type error).
/// Equivalent to Any, or possibly to object in strict mode
Unknown,
/// name does not exist or is not bound to any value (this represents an error, but with some
/// Name does not exist or is not bound to any value (this represents an error, but with some
/// leniency options it could be silently resolved to Unknown in some cases)
Unbound,
/// the None object -- TODO remove this in favor of Instance(types.NoneType)
/// The None object -- TODO remove this in favor of Instance(types.NoneType)
None,
/// a specific function object
/// Temporary type for symbols that can't be inferred yet because of missing implementations.
/// Behaves equivalently to `Any`.
///
/// This variant should eventually be removed once red-knot is spec-compliant.
///
/// General rule: `Todo` should only propagate when the presence of the input `Todo` caused the
/// output to be unknown. An output should only be `Todo` if fixing all `Todo` inputs to be not
/// `Todo` would change the output type.
Todo,
/// A specific function object
Function(FunctionType<'db>),
/// The `typing.reveal_type` function, which has special `__call__` behavior.
RevealTypeFunction(FunctionType<'db>),
/// a specific module object
/// A specific module object
Module(File),
/// a specific class object
/// A specific class object
Class(ClassType<'db>),
/// the set of Python objects with the given class in their __class__'s method resolution order
/// The set of Python objects with the given class in their __class__'s method resolution order
Instance(ClassType<'db>),
/// the set of objects in any of the types in the union
/// The set of objects in any of the types in the union
Union(UnionType<'db>),
/// the set of objects in all of the types in the intersection
/// The set of objects in all of the types in the intersection
Intersection(IntersectionType<'db>),
/// An integer literal
IntLiteral(i64),
@@ -335,16 +342,14 @@ impl<'db> Type<'db> {
pub const fn into_function_type(self) -> Option<FunctionType<'db>> {
match self {
Type::Function(function_type) | Type::RevealTypeFunction(function_type) => {
Some(function_type)
}
Type::Function(function_type) => Some(function_type),
_ => None,
}
}
pub fn expect_function(self) -> FunctionType<'db> {
self.into_function_type()
.expect("Expected a variant wrapping a FunctionType")
.expect("Expected a Type::Function variant")
}
pub const fn into_int_literal_type(self) -> Option<i64> {
@@ -380,12 +385,28 @@ impl<'db> Type<'db> {
}
}
pub fn builtin_str_instance(db: &'db dyn Db) -> Self {
builtins_symbol_ty(db, "str").to_instance(db)
}
pub fn builtin_int_instance(db: &'db dyn Db) -> Self {
builtins_symbol_ty(db, "int").to_instance(db)
}
pub fn is_stdlib_symbol(&self, db: &'db dyn Db, module_name: &str, name: &str) -> bool {
match self {
Type::Class(class) => class.is_stdlib_symbol(db, module_name, name),
Type::Function(function) | Type::RevealTypeFunction(function) => {
function.is_stdlib_symbol(db, module_name, name)
}
Type::Function(function) => function.is_stdlib_symbol(db, module_name, name),
_ => false,
}
}
/// Return true if the type is a class or a union of classes.
pub fn is_class(&self, db: &'db dyn Db) -> bool {
match self {
Type::Union(union) => union.elements(db).iter().all(|ty| ty.is_class(db)),
Type::Class(_) => true,
// / TODO include type[X], once we add that type
_ => false,
}
}
@@ -398,8 +419,8 @@ impl<'db> Type<'db> {
return true;
}
match (self, target) {
(Type::Unknown | Type::Any, _) => false,
(_, Type::Unknown | Type::Any) => false,
(Type::Unknown | Type::Any | Type::Todo, _) => false,
(_, Type::Unknown | Type::Any | Type::Todo) => false,
(Type::Never, _) => true,
(_, Type::Never) => false,
(Type::IntLiteral(_), Type::Instance(class))
@@ -434,8 +455,8 @@ impl<'db> Type<'db> {
/// [assignable to]: https://typing.readthedocs.io/en/latest/spec/concepts.html#the-assignable-to-or-consistent-subtyping-relation
pub(crate) fn is_assignable_to(self, db: &'db dyn Db, target: Type<'db>) -> bool {
match (self, target) {
(Type::Unknown | Type::Any, _) => true,
(_, Type::Unknown | Type::Any) => true,
(Type::Unknown | Type::Any | Type::Todo, _) => true,
(_, Type::Unknown | Type::Any | Type::Todo) => true,
(ty, Type::Union(union)) => union
.elements(db)
.iter()
@@ -471,53 +492,104 @@ impl<'db> Type<'db> {
Type::Any => Type::Any,
Type::Never => {
// TODO: attribute lookup on Never type
Type::Unknown
Type::Todo
}
Type::Unknown => Type::Unknown,
Type::Unbound => Type::Unbound,
Type::None => {
// TODO: attribute lookup on None type
Type::Unknown
Type::Todo
}
Type::Function(_) | Type::RevealTypeFunction(_) => {
Type::Function(_) => {
// TODO: attribute lookup on function type
Type::Unknown
Type::Todo
}
Type::Module(file) => global_symbol_ty(db, *file, name),
Type::Class(class) => class.class_member(db, name),
Type::Instance(_) => {
// TODO MRO? get_own_instance_member, get_instance_member
Type::Unknown
Type::Todo
}
Type::Union(union) => union.map(db, |element| element.member(db, name)),
Type::Intersection(_) => {
// TODO perform the get_member on each type in the intersection
// TODO return the intersection of those results
Type::Unknown
Type::Todo
}
Type::IntLiteral(_) => {
// TODO raise error
Type::Unknown
Type::Todo
}
Type::BooleanLiteral(_) => Type::Unknown,
Type::BooleanLiteral(_) => Type::Todo,
Type::StringLiteral(_) => {
// TODO defer to `typing.LiteralString`/`builtins.str` methods
// from typeshed's stubs
Type::Unknown
Type::Todo
}
Type::LiteralString => {
// TODO defer to `typing.LiteralString`/`builtins.str` methods
// from typeshed's stubs
Type::Unknown
Type::Todo
}
Type::BytesLiteral(_) => {
// TODO defer to Type::Instance(<bytes from typeshed>).member
Type::Unknown
Type::Todo
}
Type::Tuple(_) => {
// TODO: implement tuple methods
Type::Unknown
Type::Todo
}
Type::Todo => Type::Todo,
}
}
/// Resolves the boolean value of a type.
///
/// This is used to determine the value that would be returned
/// when `bool(x)` is called on an object `x`.
fn bool(&self, db: &'db dyn Db) -> Truthiness {
match self {
Type::Any | Type::Todo | Type::Never | Type::Unknown | Type::Unbound => {
Truthiness::Ambiguous
}
Type::None => Truthiness::AlwaysFalse,
Type::Function(_) => Truthiness::AlwaysTrue,
Type::Module(_) => Truthiness::AlwaysTrue,
Type::Class(_) => {
// TODO: lookup `__bool__` and `__len__` methods on the class's metaclass
// More info in https://docs.python.org/3/library/stdtypes.html#truth-value-testing
Truthiness::Ambiguous
}
Type::Instance(_) => {
// TODO: lookup `__bool__` and `__len__` methods on the instance's class
// More info in https://docs.python.org/3/library/stdtypes.html#truth-value-testing
Truthiness::Ambiguous
}
Type::Union(union) => {
let union_elements = union.elements(db);
let first_element_truthiness = union_elements[0].bool(db);
if first_element_truthiness.is_ambiguous() {
return Truthiness::Ambiguous;
}
if !union_elements
.iter()
.skip(1)
.all(|element| element.bool(db) == first_element_truthiness)
{
return Truthiness::Ambiguous;
}
first_element_truthiness
}
Type::Intersection(_) => {
// TODO
Truthiness::Ambiguous
}
Type::IntLiteral(num) => Truthiness::from(*num != 0),
Type::BooleanLiteral(bool) => Truthiness::from(*bool),
Type::StringLiteral(str) => Truthiness::from(!str.value(db).is_empty()),
Type::LiteralString => Truthiness::Ambiguous,
Type::BytesLiteral(bytes) => Truthiness::from(!bytes.value(db).is_empty()),
Type::Tuple(items) => Truthiness::from(!items.elements(db).is_empty()),
}
}
@@ -528,21 +600,48 @@ impl<'db> Type<'db> {
fn call(self, db: &'db dyn Db, arg_types: &[Type<'db>]) -> CallOutcome<'db> {
match self {
// TODO validate typed call arguments vs callable signature
Type::Function(function_type) => CallOutcome::callable(function_type.return_type(db)),
Type::RevealTypeFunction(function_type) => CallOutcome::revealed(
function_type.return_type(db),
*arg_types.first().unwrap_or(&Type::Unknown),
),
Type::Function(function_type) => match function_type.kind(db) {
FunctionKind::Ordinary => CallOutcome::callable(function_type.return_type(db)),
FunctionKind::RevealType => CallOutcome::revealed(
function_type.return_type(db),
*arg_types.first().unwrap_or(&Type::Unknown),
),
},
// TODO annotated return type on `__new__` or metaclass `__call__`
Type::Class(class) => CallOutcome::callable(Type::Instance(class)),
Type::Class(class) => {
// If the class is the builtin-bool class (for example `bool(1)`), we try to return
// the specific truthiness value of the input arg, `Literal[True]` for the example above.
let is_bool = class.is_stdlib_symbol(db, "builtins", "bool");
CallOutcome::callable(if is_bool {
arg_types
.first()
.map(|arg| arg.bool(db).into_type(db))
.unwrap_or(Type::BooleanLiteral(false))
} else {
Type::Instance(class)
})
}
// TODO: handle classes which implement the `__call__` protocol
Type::Instance(_instance_ty) => CallOutcome::callable(Type::Unknown),
Type::Instance(class) => {
// Since `__call__` is a dunder, we need to access it as an attribute on the class
// rather than the instance (matching runtime semantics).
let dunder_call_method = class.class_member(db, "__call__");
if dunder_call_method.is_unbound() {
CallOutcome::not_callable(self)
} else {
let args = std::iter::once(self)
.chain(arg_types.iter().copied())
.collect::<Vec<_>>();
dunder_call_method.call(db, &args)
}
}
// `Any` is callable, and its return type is also `Any`.
Type::Any => CallOutcome::callable(Type::Any),
Type::Todo => CallOutcome::callable(Type::Todo),
Type::Unknown => CallOutcome::callable(Type::Unknown),
Type::Union(union) => CallOutcome::union(
@@ -550,12 +649,11 @@ impl<'db> Type<'db> {
union
.elements(db)
.iter()
.map(|elem| elem.call(db, arg_types))
.collect::<Box<[CallOutcome<'db>]>>(),
.map(|elem| elem.call(db, arg_types)),
),
// TODO: intersection types
Type::Intersection(_) => CallOutcome::callable(Type::Unknown),
Type::Intersection(_) => CallOutcome::callable(Type::Todo),
_ => CallOutcome::not_callable(self),
}
@@ -576,6 +674,12 @@ impl<'db> Type<'db> {
};
}
if let Type::Unknown | Type::Any = self {
// Explicit handling of `Unknown` and `Any` necessary until `type[Unknown]` and
// `type[Any]` are not defined as `Todo` anymore.
return IterationOutcome::Iterable { element_ty: self };
}
// `self` represents the type of the iterable;
// `__iter__` and `__next__` are both looked up on the class of the iterable:
let iterable_meta_type = self.to_meta_type(db);
@@ -584,7 +688,7 @@ impl<'db> Type<'db> {
if !dunder_iter_method.is_unbound() {
let CallOutcome::Callable {
return_ty: iterator_ty,
} = dunder_iter_method.call(db, &[])
} = dunder_iter_method.call(db, &[self])
else {
return IterationOutcome::NotIterable {
not_iterable_ty: self,
@@ -593,7 +697,7 @@ impl<'db> Type<'db> {
let dunder_next_method = iterator_ty.to_meta_type(db).member(db, "__next__");
return dunder_next_method
.call(db, &[])
.call(db, &[self])
.return_ty(db)
.map(|element_ty| IterationOutcome::Iterable { element_ty })
.unwrap_or(IterationOutcome::NotIterable {
@@ -610,7 +714,7 @@ impl<'db> Type<'db> {
let dunder_get_item_method = iterable_meta_type.member(db, "__getitem__");
dunder_get_item_method
.call(db, &[])
.call(db, &[self, builtins_symbol_ty(db, "int").to_instance(db)])
.return_ty(db)
.map(|element_ty| IterationOutcome::Iterable { element_ty })
.unwrap_or(IterationOutcome::NotIterable {
@@ -622,19 +726,19 @@ impl<'db> Type<'db> {
pub fn to_instance(&self, db: &'db dyn Db) -> Type<'db> {
match self {
Type::Any => Type::Any,
Type::Todo => Type::Todo,
Type::Unknown => Type::Unknown,
Type::Unbound => Type::Unknown,
Type::Never => Type::Never,
Type::Class(class) => Type::Instance(*class),
Type::Union(union) => union.map(db, |element| element.to_instance(db)),
// TODO: we can probably do better here: --Alex
Type::Intersection(_) => Type::Unknown,
Type::Intersection(_) => Type::Todo,
// TODO: calling `.to_instance()` on any of these should result in a diagnostic,
// since they already indicate that the object is an instance of some kind:
Type::BooleanLiteral(_)
| Type::BytesLiteral(_)
| Type::Function(_)
| Type::RevealTypeFunction(_)
| Type::Instance(_)
| Type::Module(_)
| Type::IntLiteral(_)
@@ -657,20 +761,59 @@ impl<'db> Type<'db> {
Type::BooleanLiteral(_) => builtins_symbol_ty(db, "bool"),
Type::BytesLiteral(_) => builtins_symbol_ty(db, "bytes"),
Type::IntLiteral(_) => builtins_symbol_ty(db, "int"),
Type::Function(_) | Type::RevealTypeFunction(_) => types_symbol_ty(db, "FunctionType"),
Type::Function(_) => types_symbol_ty(db, "FunctionType"),
Type::Module(_) => types_symbol_ty(db, "ModuleType"),
Type::Tuple(_) => builtins_symbol_ty(db, "tuple"),
Type::None => typeshed_symbol_ty(db, "NoneType"),
// TODO not accurate if there's a custom metaclass...
Type::Class(_) => builtins_symbol_ty(db, "type"),
// TODO can we do better here? `type[LiteralString]`?
Type::StringLiteral(_) | Type::LiteralString => builtins_symbol_ty(db, "str"),
// TODO: `type[Any]`?
Type::Any => Type::Any,
Type::Any => Type::Todo,
// TODO: `type[Unknown]`?
Type::Unknown => Type::Unknown,
Type::Unknown => Type::Todo,
// TODO intersections
Type::Intersection(_) => Type::Unknown,
Type::Tuple(_) => builtins_symbol_ty(db, "tuple"),
Type::Intersection(_) => Type::Todo,
Type::Todo => Type::Todo,
}
}
/// Return the string representation of this type when converted to string as it would be
/// provided by the `__str__` method.
///
/// When not available, this should fall back to the value of `[Type::repr]`.
/// Note: this method is used in the builtins `format`, `print`, `str.format` and `f-strings`.
#[must_use]
pub fn str(&self, db: &'db dyn Db) -> Type<'db> {
match self {
Type::IntLiteral(_) | Type::BooleanLiteral(_) => self.repr(db),
Type::StringLiteral(_) | Type::LiteralString => *self,
// TODO: handle more complex types
_ => Type::builtin_str_instance(db),
}
}
/// Return the string representation of this type as it would be provided by the `__repr__`
/// method at runtime.
#[must_use]
pub fn repr(&self, db: &'db dyn Db) -> Type<'db> {
match self {
Type::IntLiteral(number) => Type::StringLiteral(StringLiteralType::new(db, {
number.to_string().into_boxed_str()
})),
Type::BooleanLiteral(true) => {
Type::StringLiteral(StringLiteralType::new(db, "True".into()))
}
Type::BooleanLiteral(false) => {
Type::StringLiteral(StringLiteralType::new(db, "False".into()))
}
Type::StringLiteral(literal) => Type::StringLiteral(StringLiteralType::new(db, {
format!("'{}'", literal.value(db).escape_default()).into()
})),
Type::LiteralString => Type::LiteralString,
// TODO: handle more complex types
_ => Type::builtin_str_instance(db),
}
}
}
@@ -701,12 +844,12 @@ enum CallOutcome<'db> {
impl<'db> CallOutcome<'db> {
/// Create a new `CallOutcome::Callable` with given return type.
fn callable(return_ty: Type<'db>) -> CallOutcome {
fn callable(return_ty: Type<'db>) -> CallOutcome<'db> {
CallOutcome::Callable { return_ty }
}
/// Create a new `CallOutcome::NotCallable` with given not-callable type.
fn not_callable(not_callable_ty: Type<'db>) -> CallOutcome {
fn not_callable(not_callable_ty: Type<'db>) -> CallOutcome<'db> {
CallOutcome::NotCallable { not_callable_ty }
}
@@ -719,10 +862,13 @@ impl<'db> CallOutcome<'db> {
}
/// Create a new `CallOutcome::Union` with given wrapped outcomes.
fn union(called_ty: Type<'db>, outcomes: impl Into<Box<[CallOutcome<'db>]>>) -> CallOutcome {
fn union(
called_ty: Type<'db>,
outcomes: impl IntoIterator<Item = CallOutcome<'db>>,
) -> CallOutcome<'db> {
CallOutcome::Union {
called_ty,
outcomes: outcomes.into(),
outcomes: outcomes.into_iter().collect(),
}
}
@@ -754,15 +900,73 @@ impl<'db> CallOutcome<'db> {
}
}
/// Get the return type of the call, emitting diagnostics if needed.
/// Get the return type of the call, emitting default diagnostics if needed.
fn unwrap_with_diagnostic<'a>(
&self,
db: &'db dyn Db,
node: ast::AnyNodeRef,
builder: &'a mut TypeInferenceBuilder<'db>,
) -> Type<'db> {
match self.return_ty_result(db, node, builder) {
Ok(return_ty) => return_ty,
Err(NotCallableError::Type {
not_callable_ty,
return_ty,
}) => {
builder.add_diagnostic(
node,
"call-non-callable",
format_args!(
"Object of type `{}` is not callable",
not_callable_ty.display(db)
),
);
return_ty
}
Err(NotCallableError::UnionElement {
not_callable_ty,
called_ty,
return_ty,
}) => {
builder.add_diagnostic(
node,
"call-non-callable",
format_args!(
"Object of type `{}` is not callable (due to union element `{}`)",
called_ty.display(db),
not_callable_ty.display(db),
),
);
return_ty
}
Err(NotCallableError::UnionElements {
not_callable_tys,
called_ty,
return_ty,
}) => {
builder.add_diagnostic(
node,
"call-non-callable",
format_args!(
"Object of type `{}` is not callable (due to union elements {})",
called_ty.display(db),
not_callable_tys.display(db),
),
);
return_ty
}
}
}
/// Get the return type of the call as a result.
fn return_ty_result<'a>(
&self,
db: &'db dyn Db,
node: ast::AnyNodeRef,
builder: &'a mut TypeInferenceBuilder<'db>,
) -> Result<Type<'db>, NotCallableError<'db>> {
match self {
Self::Callable { return_ty } => *return_ty,
Self::Callable { return_ty } => Ok(*return_ty),
Self::RevealType {
return_ty,
revealed_ty,
@@ -770,21 +974,14 @@ impl<'db> CallOutcome<'db> {
builder.add_diagnostic(
node,
"revealed-type",
format_args!("Revealed type is '{}'.", revealed_ty.display(db)),
format_args!("Revealed type is `{}`", revealed_ty.display(db)),
);
*return_ty
}
Self::NotCallable { not_callable_ty } => {
builder.add_diagnostic(
node,
"call-non-callable",
format_args!(
"Object of type '{}' is not callable.",
not_callable_ty.display(db)
),
);
Type::Unknown
Ok(*return_ty)
}
Self::NotCallable { not_callable_ty } => Err(NotCallableError::Type {
not_callable_ty: *not_callable_ty,
return_ty: Type::Unknown,
}),
Self::Union {
outcomes,
called_ty,
@@ -813,41 +1010,75 @@ impl<'db> CallOutcome<'db> {
};
union_builder = union_builder.add(return_ty);
}
let return_ty = union_builder.build();
match not_callable[..] {
[] => {}
[elem] => builder.add_diagnostic(
node,
"call-non-callable",
format_args!(
"Object of type '{}' is not callable (due to union element '{}').",
called_ty.display(db),
elem.display(db),
),
),
_ if not_callable.len() == outcomes.len() => builder.add_diagnostic(
node,
"call-non-callable",
format_args!(
"Object of type '{}' is not callable.",
called_ty.display(db)
),
),
_ => builder.add_diagnostic(
node,
"call-non-callable",
format_args!(
"Object of type '{}' is not callable (due to union elements {}).",
called_ty.display(db),
not_callable.display(db),
),
),
[] => Ok(return_ty),
[elem] => Err(NotCallableError::UnionElement {
not_callable_ty: elem,
called_ty: *called_ty,
return_ty,
}),
_ if not_callable.len() == outcomes.len() => Err(NotCallableError::Type {
not_callable_ty: *called_ty,
return_ty,
}),
_ => Err(NotCallableError::UnionElements {
not_callable_tys: not_callable.into_boxed_slice(),
called_ty: *called_ty,
return_ty,
}),
}
union_builder.build()
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
enum NotCallableError<'db> {
/// The type is not callable.
Type {
not_callable_ty: Type<'db>,
return_ty: Type<'db>,
},
/// A single union element is not callable.
UnionElement {
not_callable_ty: Type<'db>,
called_ty: Type<'db>,
return_ty: Type<'db>,
},
/// Multiple (but not all) union elements are not callable.
UnionElements {
not_callable_tys: Box<[Type<'db>]>,
called_ty: Type<'db>,
return_ty: Type<'db>,
},
}
impl<'db> NotCallableError<'db> {
/// The return type that should be used when a call is not callable.
fn return_ty(&self) -> Type<'db> {
match self {
Self::Type { return_ty, .. } => *return_ty,
Self::UnionElement { return_ty, .. } => *return_ty,
Self::UnionElements { return_ty, .. } => *return_ty,
}
}
/// The resolved type that was not callable.
///
/// For unions, returns the union type itself, which may contain a mix of callable and
/// non-callable types.
fn called_ty(&self) -> Type<'db> {
match self {
Self::Type {
not_callable_ty, ..
} => *not_callable_ty,
Self::UnionElement { called_ty, .. } => *called_ty,
Self::UnionElements { called_ty, .. } => *called_ty,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum IterationOutcome<'db> {
Iterable { element_ty: Type<'db> },
@@ -870,12 +1101,57 @@ impl<'db> IterationOutcome<'db> {
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum Truthiness {
/// For an object `x`, `bool(x)` will always return `True`
AlwaysTrue,
/// For an object `x`, `bool(x)` will always return `False`
AlwaysFalse,
/// For an object `x`, `bool(x)` could return either `True` or `False`
Ambiguous,
}
impl Truthiness {
const fn is_ambiguous(self) -> bool {
matches!(self, Truthiness::Ambiguous)
}
const fn negate(self) -> Self {
match self {
Self::AlwaysTrue => Self::AlwaysFalse,
Self::AlwaysFalse => Self::AlwaysTrue,
Self::Ambiguous => Self::Ambiguous,
}
}
fn into_type(self, db: &dyn Db) -> Type {
match self {
Self::AlwaysTrue => Type::BooleanLiteral(true),
Self::AlwaysFalse => Type::BooleanLiteral(false),
Self::Ambiguous => builtins_symbol_ty(db, "bool").to_instance(db),
}
}
}
impl From<bool> for Truthiness {
fn from(value: bool) -> Self {
if value {
Truthiness::AlwaysTrue
} else {
Truthiness::AlwaysFalse
}
}
}
#[salsa::interned]
pub struct FunctionType<'db> {
/// name of the function at definition
#[return_ref]
pub name: ast::name::Name,
/// Is this a function that we special-case somehow? If so, which one?
kind: FunctionKind,
definition: Definition<'db>,
/// types of all decorators on this function
@@ -891,15 +1167,6 @@ impl<'db> FunctionType<'db> {
})
}
/// Return true if this is a symbol with given name from `typing` or `typing_extensions`.
pub(crate) fn is_typing_symbol(self, db: &'db dyn Db, name: &str) -> bool {
name == self.name(db)
&& file_to_module(db, self.definition(db).file(db)).is_some_and(|module| {
module.search_path().is_standard_library()
&& matches!(&**module.name(), "typing" | "typing_extensions")
})
}
pub fn has_decorator(self, db: &dyn Db, decorator: Type<'_>) -> bool {
self.decorators(db).contains(&decorator)
}
@@ -917,7 +1184,7 @@ impl<'db> FunctionType<'db> {
// rather than from `bar`'s return annotation
// in order to determine the type that `bar` returns
if !function_stmt_node.decorator_list.is_empty() {
return Type::Unknown;
return Type::Todo;
}
function_stmt_node
@@ -926,7 +1193,7 @@ impl<'db> FunctionType<'db> {
.map(|returns| {
if function_stmt_node.is_async {
// TODO: generic `types.CoroutineType`!
Type::Unknown
Type::Todo
} else {
definition_expression_ty(db, definition, returns.as_ref())
}
@@ -935,6 +1202,15 @@ impl<'db> FunctionType<'db> {
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default, Hash)]
pub enum FunctionKind {
/// Just a normal function for which we have no particular special casing
#[default]
Ordinary,
/// `builtins.reveal_type`, `typing.reveal_type` or `typing_extensions.reveal_type`
RevealType,
}
#[salsa::interned]
pub struct ClassType<'db> {
/// Name of the class at definition
@@ -1072,7 +1348,10 @@ pub struct TupleType<'db> {
#[cfg(test)]
mod tests {
use super::{builtins_symbol_ty, BytesLiteralType, StringLiteralType, Type, UnionType};
use super::{
builtins_symbol_ty, BytesLiteralType, StringLiteralType, Truthiness, TupleType, Type,
UnionType,
};
use crate::db::tests::TestDb;
use crate::program::{Program, SearchPathSettings};
use crate::python_version::PythonVersion;
@@ -1102,17 +1381,19 @@ mod tests {
/// A test representation of a type that can be transformed unambiguously into a real Type,
/// given a db.
#[derive(Debug)]
#[derive(Debug, Clone)]
enum Ty {
Never,
Unknown,
Any,
IntLiteral(i64),
BoolLiteral(bool),
StringLiteral(&'static str),
LiteralString,
BytesLiteral(&'static str),
BuiltinInstance(&'static str),
Union(Vec<Ty>),
Tuple(Vec<Ty>),
}
impl Ty {
@@ -1125,6 +1406,7 @@ mod tests {
Ty::StringLiteral(s) => {
Type::StringLiteral(StringLiteralType::new(db, (*s).into()))
}
Ty::BoolLiteral(b) => Type::BooleanLiteral(b),
Ty::LiteralString => Type::LiteralString,
Ty::BytesLiteral(s) => {
Type::BytesLiteral(BytesLiteralType::new(db, s.as_bytes().into()))
@@ -1133,6 +1415,10 @@ mod tests {
Ty::Union(tys) => {
UnionType::from_elements(db, tys.into_iter().map(|ty| ty.into_type(db)))
}
Ty::Tuple(tys) => {
let elements = tys.into_iter().map(|ty| ty.into_type(db)).collect();
Type::Tuple(TupleType::new(db, elements))
}
}
}
}
@@ -1202,4 +1488,56 @@ mod tests {
assert!(from.into_type(&db).is_equivalent_to(&db, to.into_type(&db)));
}
#[test_case(Ty::IntLiteral(1); "is_int_literal_truthy")]
#[test_case(Ty::IntLiteral(-1))]
#[test_case(Ty::StringLiteral("foo"))]
#[test_case(Ty::Tuple(vec![Ty::IntLiteral(0)]))]
#[test_case(Ty::Union(vec![Ty::IntLiteral(1), Ty::IntLiteral(2)]))]
fn is_truthy(ty: Ty) {
let db = setup_db();
assert_eq!(ty.into_type(&db).bool(&db), Truthiness::AlwaysTrue);
}
#[test_case(Ty::Tuple(vec![]))]
#[test_case(Ty::IntLiteral(0))]
#[test_case(Ty::StringLiteral(""))]
#[test_case(Ty::Union(vec![Ty::IntLiteral(0), Ty::IntLiteral(0)]))]
fn is_falsy(ty: Ty) {
let db = setup_db();
assert_eq!(ty.into_type(&db).bool(&db), Truthiness::AlwaysFalse);
}
#[test_case(Ty::BuiltinInstance("str"))]
#[test_case(Ty::Union(vec![Ty::IntLiteral(1), Ty::IntLiteral(0)]))]
#[test_case(Ty::Union(vec![Ty::BuiltinInstance("str"), Ty::IntLiteral(0)]))]
#[test_case(Ty::Union(vec![Ty::BuiltinInstance("str"), Ty::IntLiteral(1)]))]
fn boolean_value_is_unknown(ty: Ty) {
let db = setup_db();
assert_eq!(ty.into_type(&db).bool(&db), Truthiness::Ambiguous);
}
#[test_case(Ty::IntLiteral(1), Ty::StringLiteral("1"))]
#[test_case(Ty::BoolLiteral(true), Ty::StringLiteral("True"))]
#[test_case(Ty::BoolLiteral(false), Ty::StringLiteral("False"))]
#[test_case(Ty::StringLiteral("ab'cd"), Ty::StringLiteral("ab'cd"))] // no quotes
#[test_case(Ty::LiteralString, Ty::LiteralString)]
#[test_case(Ty::BuiltinInstance("int"), Ty::BuiltinInstance("str"))]
fn has_correct_str(ty: Ty, expected: Ty) {
let db = setup_db();
assert_eq!(ty.into_type(&db).str(&db), expected.into_type(&db));
}
#[test_case(Ty::IntLiteral(1), Ty::StringLiteral("1"))]
#[test_case(Ty::BoolLiteral(true), Ty::StringLiteral("True"))]
#[test_case(Ty::BoolLiteral(false), Ty::StringLiteral("False"))]
#[test_case(Ty::StringLiteral("ab'cd"), Ty::StringLiteral("'ab\\'cd'"))] // single quotes
#[test_case(Ty::LiteralString, Ty::LiteralString)]
#[test_case(Ty::BuiltinInstance("int"), Ty::BuiltinInstance("str"))]
fn has_correct_repr(ty: Ty, expected: Ty) {
let db = setup_db();
assert_eq!(ty.into_type(&db).repr(&db), expected.into_type(&db));
}
}

View File

@@ -215,6 +215,7 @@ impl<'db> InnerIntersectionBuilder<'db> {
/// Adds a positive type to this intersection.
fn add_positive(&mut self, db: &'db dyn Db, ty: Type<'db>) {
// TODO `Any`/`Unknown`/`Todo` actually should not self-cancel
match ty {
Type::Intersection(inter) => {
let pos = inter.positive(db);
@@ -234,7 +235,7 @@ impl<'db> InnerIntersectionBuilder<'db> {
/// Adds a negative type to this intersection.
fn add_negative(&mut self, db: &'db dyn Db, ty: Type<'db>) {
// TODO Any/Unknown actually should not self-cancel
// TODO `Any`/`Unknown`/`Todo` actually should not self-cancel
match ty {
Type::Intersection(intersection) => {
let pos = intersection.negative(db);
@@ -388,7 +389,7 @@ mod tests {
#[test]
fn build_union_simplify_subtype() {
let db = setup_db();
let t0 = builtins_symbol_ty(&db, "str").to_instance(&db);
let t0 = Type::builtin_str_instance(&db);
let t1 = Type::LiteralString;
let u0 = UnionType::from_elements(&db, [t0, t1]);
let u1 = UnionType::from_elements(&db, [t1, t0]);
@@ -400,7 +401,7 @@ mod tests {
#[test]
fn build_union_no_simplify_unknown() {
let db = setup_db();
let t0 = builtins_symbol_ty(&db, "str").to_instance(&db);
let t0 = Type::builtin_str_instance(&db);
let t1 = Type::Unknown;
let u0 = UnionType::from_elements(&db, [t0, t1]);
let u1 = UnionType::from_elements(&db, [t1, t0]);
@@ -412,8 +413,8 @@ mod tests {
#[test]
fn build_union_subsume_multiple() {
let db = setup_db();
let str_ty = builtins_symbol_ty(&db, "str").to_instance(&db);
let int_ty = builtins_symbol_ty(&db, "int").to_instance(&db);
let str_ty = Type::builtin_str_instance(&db);
let int_ty = Type::builtin_int_instance(&db);
let object_ty = builtins_symbol_ty(&db, "object").to_instance(&db);
let unknown_ty = Type::Unknown;

View File

@@ -36,9 +36,8 @@ impl Display for DisplayType<'_> {
| Type::BytesLiteral(_)
| Type::Class(_)
| Type::Function(_)
| Type::RevealTypeFunction(_)
) {
write!(f, "Literal[{representation}]",)
write!(f, "Literal[{representation}]")
} else {
representation.fmt(f)
}
@@ -67,15 +66,16 @@ impl Display for DisplayRepresentation<'_> {
Type::Unknown => f.write_str("Unknown"),
Type::Unbound => f.write_str("Unbound"),
Type::None => f.write_str("None"),
// `[Type::Todo]`'s display should be explicit that is not a valid display of
// any other type
Type::Todo => f.write_str("@Todo"),
Type::Module(file) => {
write!(f, "<module '{:?}'>", file.path(self.db))
}
// TODO functions and classes should display using a fully qualified name
Type::Class(class) => f.write_str(class.name(self.db)),
Type::Instance(class) => f.write_str(class.name(self.db)),
Type::Function(function) | Type::RevealTypeFunction(function) => {
f.write_str(function.name(self.db))
}
Type::Function(function) => f.write_str(function.name(self.db)),
Type::Union(union) => union.display(self.db).fmt(f),
Type::Intersection(intersection) => intersection.display(self.db).fmt(f),
Type::IntLiteral(n) => n.fmt(f),
@@ -194,7 +194,7 @@ impl TryFrom<Type<'_>> for LiteralTypeKind {
fn try_from(value: Type<'_>) -> Result<Self, Self::Error> {
match value {
Type::Class(_) => Ok(Self::Class),
Type::Function(_) | Type::RevealTypeFunction(_) => Ok(Self::Function),
Type::Function(_) => Ok(Self::Function),
Type::IntLiteral(_) => Ok(Self::IntLiteral),
Type::StringLiteral(_) => Ok(Self::StringLiteral),
Type::BytesLiteral(_) => Ok(Self::BytesLiteral),
@@ -335,7 +335,7 @@ mod tests {
class B: ...
",
)?;
let mod_file = system_path_to_file(&db, "src/main.py").expect("Expected file to exist.");
let mod_file = system_path_to_file(&db, "src/main.py").expect("file to exist");
let union_elements = &[
Type::Unknown,

File diff suppressed because it is too large Load Diff

View File

@@ -1 +0,0 @@
9e506eb5e8fc2823db8c60ad561b1145ff114947

View File

@@ -1,46 +0,0 @@
import os
import sys
from collections.abc import Iterator
from contextlib import AbstractContextManager
from pathlib import Path
from types import ModuleType
from typing import Any, BinaryIO, TextIO
from typing_extensions import TypeAlias
if sys.version_info >= (3, 9):
from importlib.abc import Traversable
__all__ = ["Package", "Resource", "contents", "is_resource", "open_binary", "open_text", "path", "read_binary", "read_text"]
if sys.version_info >= (3, 9):
__all__ += ["as_file", "files"]
if sys.version_info >= (3, 10):
__all__ += ["ResourceReader"]
Package: TypeAlias = str | ModuleType
if sys.version_info >= (3, 11):
Resource: TypeAlias = str
else:
Resource: TypeAlias = str | os.PathLike[Any]
def open_binary(package: Package, resource: Resource) -> BinaryIO: ...
def open_text(package: Package, resource: Resource, encoding: str = "utf-8", errors: str = "strict") -> TextIO: ...
def read_binary(package: Package, resource: Resource) -> bytes: ...
def read_text(package: Package, resource: Resource, encoding: str = "utf-8", errors: str = "strict") -> str: ...
def path(package: Package, resource: Resource) -> AbstractContextManager[Path]: ...
def is_resource(package: Package, name: str) -> bool: ...
def contents(package: Package) -> Iterator[str]: ...
if sys.version_info >= (3, 9):
def as_file(path: Traversable) -> AbstractContextManager[Path]: ...
if sys.version_info >= (3, 12):
def files(anchor: Package | None = ...) -> Traversable: ...
elif sys.version_info >= (3, 9):
def files(package: Package) -> Traversable: ...
if sys.version_info >= (3, 10):
from importlib.abc import ResourceReader as ResourceReader

View File

@@ -47,7 +47,7 @@ impl BackgroundDocumentRequestHandler for DocumentDiagnosticRequestHandler {
fn compute_diagnostics(snapshot: &DocumentSnapshot, db: &RootDatabase) -> Vec<Diagnostic> {
let Some(file) = snapshot.file(db) else {
tracing::info!(
"No file found for snapshot for '{}'",
"No file found for snapshot for `{}`",
snapshot.query().file_url()
);
return vec![];

View File

@@ -0,0 +1,32 @@
[package]
name = "red_knot_vendored"
version = "0.0.0"
publish = false
authors = { workspace = true }
edition = { workspace = true }
rust-version = { workspace = true }
homepage = { workspace = true }
documentation = { workspace = true }
repository = { workspace = true }
license = { workspace = true }
[dependencies]
ruff_db = { workspace = true }
once_cell = { workspace = true }
zip = { workspace = true }
[build-dependencies]
path-slash = { workspace = true }
walkdir = { workspace = true }
zip = { workspace = true, features = ["zstd", "deflate"] }
[dev-dependencies]
walkdir = { workspace = true }
[features]
zstd = ["zip/zstd"]
deflate = ["zip/deflate"]
[lints]
workspace = true

View File

@@ -1,9 +1,5 @@
# Red Knot
# Vendored types for the stdlib
Semantic analysis for the red-knot project.
## Vendored types for the stdlib
This crate vendors [typeshed](https://github.com/python/typeshed)'s stubs for the standard library. The vendored stubs can be found in `crates/red_knot_python_semantic/vendor/typeshed`. The file `crates/red_knot_python_semantic/vendor/typeshed/source_commit.txt` tells you the typeshed commit that our vendored stdlib stubs currently correspond to.
This crate vendors [typeshed](https://github.com/python/typeshed)'s stubs for the standard library. The vendored stubs can be found in `crates/red_knot_vendored/vendor/typeshed`. The file `crates/red_knot_vendored/vendor/typeshed/source_commit.txt` tells you the typeshed commit that our vendored stdlib stubs currently correspond to.
The typeshed stubs are updated every two weeks via an automated PR using the `sync_typeshed.yaml` workflow in the `.github/workflows` directory. This workflow can also be triggered at any time via [workflow dispatch](https://docs.github.com/en/actions/using-workflows/manually-running-a-workflow#running-a-workflow).

View File

@@ -3,7 +3,7 @@
//!
//! This script should be automatically run at build time
//! whenever the script itself changes, or whenever any files
//! in `crates/red_knot_python_semantic/vendor/typeshed` change.
//! in `crates/red_knot_vendored/vendor/typeshed` change.
use std::fs::File;
use std::path::Path;
@@ -30,17 +30,12 @@ fn zip_dir(directory_path: &str, writer: File) -> ZipResult<File> {
// We can't use `#[cfg(...)]` here because the target-arch in a build script is the
// architecture of the system running the build script and not the architecture of the build-target.
// That's why we use the `TARGET` environment variable here.
#[cfg(target_arch = "powerpc64")]
let method = CompressionMethod::Deflated;
#[cfg(not(target_arch = "powerpc64"))]
let method = {
let target = std::env::var("TARGET").unwrap();
if target.contains("wasm32") || target.contains("powerpc64") {
CompressionMethod::Deflated
} else {
CompressionMethod::Zstd
}
let method = if cfg!(feature = "zstd") {
CompressionMethod::Zstd
} else if cfg!(feature = "deflate") {
CompressionMethod::Deflated
} else {
CompressionMethod::Stored
};
let options = FileOptions::default()

View File

@@ -6,7 +6,7 @@ use ruff_db::vendored::VendoredFileSystem;
// Luckily this crate will fail to build if this file isn't available at build time.
static TYPESHED_ZIP_BYTES: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/zipped_typeshed.zip"));
pub fn vendored_typeshed_stubs() -> &'static VendoredFileSystem {
pub fn file_system() -> &'static VendoredFileSystem {
static VENDORED_TYPESHED_STUBS: Lazy<VendoredFileSystem> =
Lazy::new(|| VendoredFileSystem::new_static(TYPESHED_ZIP_BYTES).unwrap());
&VENDORED_TYPESHED_STUBS
@@ -42,7 +42,7 @@ mod tests {
#[test]
fn typeshed_vfs_consistent_with_vendored_stubs() {
let vendored_typeshed_dir = Path::new("vendor/typeshed").canonicalize().unwrap();
let vendored_typeshed_stubs = vendored_typeshed_stubs();
let vendored_typeshed_stubs = file_system();
let mut empty_iterator = true;
for entry in walkdir::WalkDir::new(&vendored_typeshed_dir).min_depth(1) {

View File

@@ -0,0 +1 @@
91a58b07cdd807b1d965e04ba85af2adab8bf924

View File

@@ -161,6 +161,8 @@ importlib.metadata._meta: 3.10-
importlib.metadata.diagnose: 3.13-
importlib.readers: 3.10-
importlib.resources: 3.7-
importlib.resources._common: 3.11-
importlib.resources._functional: 3.13-
importlib.resources.abc: 3.11-
importlib.resources.readers: 3.11-
importlib.resources.simple: 3.11-

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