Compare commits
3 Commits
micha/fixp
...
dcreager/f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c976dc570 | ||
|
|
b44fb47f25 | ||
|
|
0a4dec0323 |
6
.gitattributes
vendored
6
.gitattributes
vendored
@@ -12,12 +12,6 @@ crates/ruff_python_parser/resources/invalid/re_lexing/line_continuation_windows_
|
||||
crates/ruff_python_parser/resources/invalid/re_lex_logical_token_windows_eol.py text eol=crlf
|
||||
crates/ruff_python_parser/resources/invalid/re_lex_logical_token_mac_eol.py text eol=cr
|
||||
|
||||
crates/ruff_linter/resources/test/fixtures/ruff/RUF046_CR.py text eol=cr
|
||||
crates/ruff_linter/resources/test/fixtures/ruff/RUF046_LF.py text eol=lf
|
||||
|
||||
crates/ruff_linter/resources/test/fixtures/pyupgrade/UP018_CR.py text eol=cr
|
||||
crates/ruff_linter/resources/test/fixtures/pyupgrade/UP018_LF.py text eol=lf
|
||||
|
||||
crates/ruff_python_parser/resources/inline linguist-generated=true
|
||||
|
||||
ruff.schema.json -diff linguist-generated=true text=auto eol=lf
|
||||
|
||||
10
.github/CODEOWNERS
vendored
10
.github/CODEOWNERS
vendored
@@ -14,11 +14,11 @@
|
||||
# flake8-pyi
|
||||
/crates/ruff_linter/src/rules/flake8_pyi/ @AlexWaygood
|
||||
|
||||
# Script for fuzzing the parser/ty etc.
|
||||
# Script for fuzzing the parser/red-knot etc.
|
||||
/python/py-fuzzer/ @AlexWaygood
|
||||
|
||||
# ty
|
||||
/crates/ty* @carljm @MichaReiser @AlexWaygood @sharkdp @dcreager
|
||||
# red-knot
|
||||
/crates/red_knot* @carljm @MichaReiser @AlexWaygood @sharkdp @dcreager
|
||||
/crates/ruff_db/ @carljm @MichaReiser @AlexWaygood @sharkdp @dcreager
|
||||
/scripts/ty_benchmark/ @carljm @MichaReiser @AlexWaygood @sharkdp @dcreager
|
||||
/crates/ty_python_semantic @carljm @AlexWaygood @sharkdp @dcreager
|
||||
/scripts/knot_benchmark/ @carljm @MichaReiser @AlexWaygood @sharkdp @dcreager
|
||||
/crates/red_knot_python_semantic @carljm @AlexWaygood @sharkdp @dcreager
|
||||
|
||||
3
.github/ISSUE_TEMPLATE/config.yml
vendored
3
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,8 +1,5 @@
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: Report an issue with ty
|
||||
url: https://github.com/astral-sh/ty/issues/new/choose
|
||||
about: Please report issues for our type checker ty in the ty repository.
|
||||
- name: Documentation
|
||||
url: https://docs.astral.sh/ruff
|
||||
about: Please consult the documentation before creating an issue.
|
||||
|
||||
1
.github/actionlint.yaml
vendored
1
.github/actionlint.yaml
vendored
@@ -6,6 +6,5 @@ self-hosted-runner:
|
||||
labels:
|
||||
- depot-ubuntu-latest-8
|
||||
- depot-ubuntu-22.04-16
|
||||
- depot-ubuntu-22.04-32
|
||||
- github-windows-2025-x86_64-8
|
||||
- github-windows-2025-x86_64-16
|
||||
|
||||
16
.github/workflows/build-binaries.yml
vendored
16
.github/workflows/build-binaries.yml
vendored
@@ -43,7 +43,7 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
- name: "Prep README.md"
|
||||
@@ -72,7 +72,7 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: x64
|
||||
@@ -114,7 +114,7 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: arm64
|
||||
@@ -170,7 +170,7 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: ${{ matrix.platform.arch }}
|
||||
@@ -223,7 +223,7 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: x64
|
||||
@@ -298,7 +298,7 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
- name: "Prep README.md"
|
||||
@@ -363,7 +363,7 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: x64
|
||||
@@ -429,7 +429,7 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
- name: "Prep README.md"
|
||||
|
||||
8
.github/workflows/build-docker.yml
vendored
8
.github/workflows/build-docker.yml
vendored
@@ -79,7 +79,7 @@ jobs:
|
||||
# Adapted from https://docs.docker.com/build/ci/github-actions/multi-platform/
|
||||
- name: Build and push by digest
|
||||
id: build
|
||||
uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6
|
||||
uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6
|
||||
with:
|
||||
context: .
|
||||
platforms: ${{ matrix.platform }}
|
||||
@@ -113,7 +113,7 @@ jobs:
|
||||
if: ${{ inputs.plan != '' && !fromJson(inputs.plan).announcement_tag_is_implicit }}
|
||||
steps:
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
|
||||
with:
|
||||
path: /tmp/digests
|
||||
pattern: digests-*
|
||||
@@ -231,7 +231,7 @@ jobs:
|
||||
${{ env.TAG_PATTERNS }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6
|
||||
uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
@@ -256,7 +256,7 @@ jobs:
|
||||
if: ${{ inputs.plan != '' && !fromJson(inputs.plan).announcement_tag_is_implicit }}
|
||||
steps:
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
|
||||
with:
|
||||
path: /tmp/digests
|
||||
pattern: digests-*
|
||||
|
||||
113
.github/workflows/ci.yaml
vendored
113
.github/workflows/ci.yaml
vendored
@@ -36,8 +36,8 @@ jobs:
|
||||
code: ${{ steps.check_code.outputs.changed }}
|
||||
# Flag that is raised when any code that affects the fuzzer is changed
|
||||
fuzz: ${{ steps.check_fuzzer.outputs.changed }}
|
||||
# Flag that is set to "true" when code related to ty changes.
|
||||
ty: ${{ steps.check_ty.outputs.changed }}
|
||||
# Flag that is set to "true" when code related to red-knot changes.
|
||||
red_knot: ${{ steps.check_red_knot.outputs.changed }}
|
||||
|
||||
# Flag that is set to "true" when code related to the playground changes.
|
||||
playground: ${{ steps.check_playground.outputs.changed }}
|
||||
@@ -84,7 +84,7 @@ jobs:
|
||||
if git diff --quiet "${MERGE_BASE}...HEAD" -- ':Cargo.toml' \
|
||||
':Cargo.lock' \
|
||||
':crates/**' \
|
||||
':!crates/ty*/**' \
|
||||
':!crates/red_knot*/**' \
|
||||
':!crates/ruff_python_formatter/**' \
|
||||
':!crates/ruff_formatter/**' \
|
||||
':!crates/ruff_dev/**' \
|
||||
@@ -145,7 +145,7 @@ jobs:
|
||||
run: |
|
||||
if git diff --quiet "${MERGE_BASE}...HEAD" -- ':**' \
|
||||
':!**/*.md' \
|
||||
':crates/ty_python_semantic/resources/mdtest/**/*.md' \
|
||||
':crates/red_knot_python_semantic/resources/mdtest/**/*.md' \
|
||||
':!docs/**' \
|
||||
':!assets/**' \
|
||||
':.github/workflows/ci.yaml' \
|
||||
@@ -168,15 +168,15 @@ jobs:
|
||||
echo "changed=true" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Check if the ty code changed
|
||||
id: check_ty
|
||||
- name: Check if the red-knot code changed
|
||||
id: check_red_knot
|
||||
env:
|
||||
MERGE_BASE: ${{ steps.merge_base.outputs.sha }}
|
||||
run: |
|
||||
if git diff --quiet "${MERGE_BASE}...HEAD" -- \
|
||||
':Cargo.toml' \
|
||||
':Cargo.lock' \
|
||||
':crates/ty*/**' \
|
||||
':crates/red_knot*/**' \
|
||||
':crates/ruff_db/**' \
|
||||
':crates/ruff_annotate_snippets/**' \
|
||||
':crates/ruff_python_ast/**' \
|
||||
@@ -221,7 +221,7 @@ jobs:
|
||||
- name: "Clippy"
|
||||
run: cargo clippy --workspace --all-targets --all-features --locked -- -D warnings
|
||||
- name: "Clippy (wasm)"
|
||||
run: cargo clippy -p ruff_wasm -p ty_wasm --target wasm32-unknown-unknown --all-features --locked -- -D warnings
|
||||
run: cargo clippy -p ruff_wasm -p red_knot_wasm --target wasm32-unknown-unknown --all-features --locked -- -D warnings
|
||||
|
||||
cargo-test-linux:
|
||||
name: "cargo test (linux)"
|
||||
@@ -239,21 +239,21 @@ jobs:
|
||||
- name: "Install mold"
|
||||
uses: rui314/setup-mold@e16410e7f8d9e167b74ad5697a9089a35126eb50 # v1
|
||||
- name: "Install cargo nextest"
|
||||
uses: taiki-e/install-action@86c23eed46c17b80677df6d8151545ce3e236c61 # v2
|
||||
uses: taiki-e/install-action@09dc018eee06ae1c9e0409786563f534210ceb83 # v2
|
||||
with:
|
||||
tool: cargo-nextest
|
||||
- name: "Install cargo insta"
|
||||
uses: taiki-e/install-action@86c23eed46c17b80677df6d8151545ce3e236c61 # v2
|
||||
uses: taiki-e/install-action@09dc018eee06ae1c9e0409786563f534210ceb83 # v2
|
||||
with:
|
||||
tool: cargo-insta
|
||||
- name: ty mdtests (GitHub annotations)
|
||||
if: ${{ needs.determine_changes.outputs.ty == 'true' }}
|
||||
- name: Red-knot mdtests (GitHub annotations)
|
||||
if: ${{ needs.determine_changes.outputs.red_knot == 'true' }}
|
||||
env:
|
||||
NO_COLOR: 1
|
||||
MDTEST_GITHUB_ANNOTATIONS_FORMAT: 1
|
||||
# Ignore errors if this step fails; we want to continue to later steps in the workflow anyway.
|
||||
# This step is just to get nice GitHub annotations on the PR diff in the files-changed tab.
|
||||
run: cargo test -p ty_python_semantic --test mdtest || true
|
||||
run: cargo test -p red_knot_python_semantic --test mdtest || true
|
||||
- name: "Run tests"
|
||||
shell: bash
|
||||
env:
|
||||
@@ -268,7 +268,7 @@ jobs:
|
||||
# sync, not just public items. Eventually we should do this for all
|
||||
# crates; for now add crates here as they are warning-clean to prevent
|
||||
# regression.
|
||||
- run: cargo doc --no-deps -p ty_python_semantic -p ty -p ty_test -p ruff_db --document-private-items
|
||||
- run: cargo doc --no-deps -p red_knot_python_semantic -p red_knot -p red_knot_test -p ruff_db --document-private-items
|
||||
env:
|
||||
# Setting RUSTDOCFLAGS because `cargo doc --check` isn't yet implemented (https://github.com/rust-lang/cargo/issues/10025).
|
||||
RUSTDOCFLAGS: "-D warnings"
|
||||
@@ -276,10 +276,6 @@ jobs:
|
||||
with:
|
||||
name: ruff
|
||||
path: target/debug/ruff
|
||||
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: ty
|
||||
path: target/debug/ty
|
||||
|
||||
cargo-test-linux-release:
|
||||
name: "cargo test (linux, release)"
|
||||
@@ -297,11 +293,11 @@ jobs:
|
||||
- name: "Install mold"
|
||||
uses: rui314/setup-mold@e16410e7f8d9e167b74ad5697a9089a35126eb50 # v1
|
||||
- name: "Install cargo nextest"
|
||||
uses: taiki-e/install-action@86c23eed46c17b80677df6d8151545ce3e236c61 # v2
|
||||
uses: taiki-e/install-action@09dc018eee06ae1c9e0409786563f534210ceb83 # v2
|
||||
with:
|
||||
tool: cargo-nextest
|
||||
- name: "Install cargo insta"
|
||||
uses: taiki-e/install-action@86c23eed46c17b80677df6d8151545ce3e236c61 # v2
|
||||
uses: taiki-e/install-action@09dc018eee06ae1c9e0409786563f534210ceb83 # v2
|
||||
with:
|
||||
tool: cargo-insta
|
||||
- name: "Run tests"
|
||||
@@ -324,7 +320,7 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Install cargo nextest"
|
||||
uses: taiki-e/install-action@86c23eed46c17b80677df6d8151545ce3e236c61 # v2
|
||||
uses: taiki-e/install-action@09dc018eee06ae1c9e0409786563f534210ceb83 # v2
|
||||
with:
|
||||
tool: cargo-nextest
|
||||
- name: "Run tests"
|
||||
@@ -350,7 +346,7 @@ jobs:
|
||||
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup target add wasm32-unknown-unknown
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
- uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0
|
||||
with:
|
||||
node-version: 20
|
||||
cache: "npm"
|
||||
@@ -362,9 +358,9 @@ jobs:
|
||||
run: |
|
||||
cd crates/ruff_wasm
|
||||
wasm-pack test --node
|
||||
- name: "Test ty_wasm"
|
||||
- name: "Test red_knot_wasm"
|
||||
run: |
|
||||
cd crates/ty_wasm
|
||||
cd crates/red_knot_wasm
|
||||
wasm-pack test --node
|
||||
|
||||
cargo-build-release:
|
||||
@@ -407,11 +403,11 @@ jobs:
|
||||
- name: "Install mold"
|
||||
uses: rui314/setup-mold@e16410e7f8d9e167b74ad5697a9089a35126eb50 # v1
|
||||
- name: "Install cargo nextest"
|
||||
uses: taiki-e/install-action@86c23eed46c17b80677df6d8151545ce3e236c61 # v2
|
||||
uses: taiki-e/install-action@09dc018eee06ae1c9e0409786563f534210ceb83 # v2
|
||||
with:
|
||||
tool: cargo-nextest
|
||||
- name: "Install cargo insta"
|
||||
uses: taiki-e/install-action@86c23eed46c17b80677df6d8151545ce3e236c61 # v2
|
||||
uses: taiki-e/install-action@09dc018eee06ae1c9e0409786563f534210ceb83 # v2
|
||||
with:
|
||||
tool: cargo-insta
|
||||
- name: "Run tests"
|
||||
@@ -460,7 +456,7 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
|
||||
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
- uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
|
||||
name: Download Ruff binary to test
|
||||
id: download-cached-binary
|
||||
with:
|
||||
@@ -525,11 +521,11 @@ jobs:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
|
||||
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
- uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
|
||||
name: Download comparison Ruff binary
|
||||
id: ruff-target
|
||||
with:
|
||||
@@ -636,53 +632,6 @@ jobs:
|
||||
name: ecosystem-result
|
||||
path: ecosystem-result
|
||||
|
||||
fuzz-ty:
|
||||
name: "Fuzz for new ty panics"
|
||||
runs-on: depot-ubuntu-22.04-16
|
||||
needs:
|
||||
- cargo-test-linux
|
||||
- determine_changes
|
||||
# Only runs on pull requests, since that is the only we way we can find the base version for comparison.
|
||||
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && github.event_name == 'pull_request' && needs.determine_changes.outputs.ty == 'true' }}
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
name: Download new ty binary
|
||||
id: ty-new
|
||||
with:
|
||||
name: ty
|
||||
path: target/debug
|
||||
- uses: dawidd6/action-download-artifact@20319c5641d495c8a52e688b7dc5fada6c3a9fbc # v8
|
||||
name: Download baseline ty binary
|
||||
with:
|
||||
name: ty
|
||||
branch: ${{ github.event.pull_request.base.ref }}
|
||||
workflow: "ci.yaml"
|
||||
check_artifacts: true
|
||||
- uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
|
||||
- name: Fuzz
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
NEW_TY: ${{ steps.ty-new.outputs.download-path }}
|
||||
run: |
|
||||
# Make executable, since artifact download doesn't preserve this
|
||||
chmod +x "${PWD}/ty" "${NEW_TY}/ty"
|
||||
|
||||
(
|
||||
uvx \
|
||||
--python="${PYTHON_VERSION}" \
|
||||
--from=./python/py-fuzzer \
|
||||
fuzz \
|
||||
--test-executable="${NEW_TY}/ty" \
|
||||
--baseline-executable="${PWD}/ty" \
|
||||
--only-new-bugs \
|
||||
--bin=ty \
|
||||
0-500
|
||||
)
|
||||
|
||||
cargo-shear:
|
||||
name: "cargo shear"
|
||||
runs-on: ubuntu-latest
|
||||
@@ -705,7 +654,7 @@ jobs:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: x64
|
||||
@@ -759,7 +708,7 @@ jobs:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0
|
||||
with:
|
||||
python-version: "3.13"
|
||||
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
|
||||
@@ -830,12 +779,12 @@ jobs:
|
||||
persist-credentials: false
|
||||
repository: "astral-sh/ruff-lsp"
|
||||
|
||||
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0
|
||||
with:
|
||||
# installation fails on 3.13 and newer
|
||||
python-version: "3.12"
|
||||
|
||||
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
- uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
|
||||
name: Download development ruff binary
|
||||
id: ruff-target
|
||||
with:
|
||||
@@ -872,7 +821,7 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup target add wasm32-unknown-unknown
|
||||
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
- uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0
|
||||
with:
|
||||
node-version: 22
|
||||
cache: "npm"
|
||||
@@ -908,7 +857,7 @@ jobs:
|
||||
run: rustup show
|
||||
|
||||
- name: "Install codspeed"
|
||||
uses: taiki-e/install-action@86c23eed46c17b80677df6d8151545ce3e236c61 # v2
|
||||
uses: taiki-e/install-action@09dc018eee06ae1c9e0409786563f534210ceb83 # v2
|
||||
with:
|
||||
tool: cargo-codspeed
|
||||
|
||||
|
||||
10
.github/workflows/daily_property_tests.yaml
vendored
10
.github/workflows/daily_property_tests.yaml
vendored
@@ -38,17 +38,17 @@ jobs:
|
||||
- name: "Install mold"
|
||||
uses: rui314/setup-mold@e16410e7f8d9e167b74ad5697a9089a35126eb50 # v1
|
||||
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
|
||||
- name: Build ty
|
||||
- name: Build Red Knot
|
||||
# A release build takes longer (2 min vs 1 min), but the property tests run much faster in release
|
||||
# mode (1.5 min vs 14 min), so the overall time is shorter with a release build.
|
||||
run: cargo build --locked --release --package ty_python_semantic --tests
|
||||
run: cargo build --locked --release --package red_knot_python_semantic --tests
|
||||
- name: Run property tests
|
||||
shell: bash
|
||||
run: |
|
||||
export QUICKCHECK_TESTS=100000
|
||||
for _ in {1..5}; do
|
||||
cargo test --locked --release --package ty_python_semantic -- --ignored list::property_tests
|
||||
cargo test --locked --release --package ty_python_semantic -- --ignored types::property_tests::stable
|
||||
cargo test --locked --release --package red_knot_python_semantic -- --ignored list::property_tests
|
||||
cargo test --locked --release --package red_knot_python_semantic -- --ignored types::property_tests::stable
|
||||
done
|
||||
|
||||
create-issue-on-failure:
|
||||
@@ -68,5 +68,5 @@ jobs:
|
||||
repo: "ruff",
|
||||
title: `Daily property test run failed on ${new Date().toDateString()}`,
|
||||
body: "Run listed here: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
|
||||
labels: ["bug", "ty", "testing"],
|
||||
labels: ["bug", "red-knot", "testing"],
|
||||
})
|
||||
|
||||
18
.github/workflows/mypy_primer.yaml
vendored
18
.github/workflows/mypy_primer.yaml
vendored
@@ -5,7 +5,7 @@ permissions: {}
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "crates/ty*/**"
|
||||
- "crates/red_knot*/**"
|
||||
- "crates/ruff_db"
|
||||
- "crates/ruff_python_ast"
|
||||
- "crates/ruff_python_parser"
|
||||
@@ -21,12 +21,11 @@ env:
|
||||
CARGO_NET_RETRY: 10
|
||||
CARGO_TERM_COLOR: always
|
||||
RUSTUP_MAX_RETRIES: 10
|
||||
RUST_BACKTRACE: 1
|
||||
|
||||
jobs:
|
||||
mypy_primer:
|
||||
name: Run mypy_primer
|
||||
runs-on: depot-ubuntu-22.04-32
|
||||
runs-on: depot-ubuntu-22.04-16
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
@@ -41,16 +40,19 @@ jobs:
|
||||
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
|
||||
with:
|
||||
workspaces: "ruff"
|
||||
|
||||
- name: Install Rust toolchain
|
||||
run: rustup show
|
||||
|
||||
- name: Install mypy_primer
|
||||
run: |
|
||||
uv tool install "git+https://github.com/astral-sh/mypy_primer.git@add-red-knot-support-v5"
|
||||
|
||||
- name: Run mypy_primer
|
||||
shell: bash
|
||||
run: |
|
||||
cd ruff
|
||||
|
||||
PRIMER_SELECTOR="$(paste -s -d'|' crates/ty_python_semantic/resources/primer/good.txt)"
|
||||
PRIMER_SELECTOR="$(paste -s -d'|' crates/red_knot_python_semantic/resources/primer/good.txt)"
|
||||
|
||||
echo "new commit"
|
||||
git rev-list --format=%s --max-count=1 "$GITHUB_SHA"
|
||||
@@ -64,11 +66,9 @@ jobs:
|
||||
|
||||
echo "Project selector: $PRIMER_SELECTOR"
|
||||
# Allow the exit code to be 0 or 1, only fail for actual mypy_primer crashes/bugs
|
||||
uvx \
|
||||
--from="git+https://github.com/hauntsaninja/mypy_primer@4b15cf3b07db69db67bbfaebfffb2a8a28040933" \
|
||||
mypy_primer \
|
||||
uvx mypy_primer \
|
||||
--repo ruff \
|
||||
--type-checker ty \
|
||||
--type-checker knot \
|
||||
--old base_commit \
|
||||
--new "$GITHUB_SHA" \
|
||||
--project-selector "/($PRIMER_SELECTOR)\$" \
|
||||
|
||||
2
.github/workflows/publish-docs.yml
vendored
2
.github/workflows/publish-docs.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
||||
ref: ${{ inputs.ref }}
|
||||
persist-credentials: true
|
||||
|
||||
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0
|
||||
with:
|
||||
python-version: 3.12
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Publish the ty playground.
|
||||
name: "[ty Playground] Release"
|
||||
# Publish the Red Knot playground.
|
||||
name: "[Knot Playground] Release"
|
||||
|
||||
permissions: {}
|
||||
|
||||
@@ -7,12 +7,12 @@ on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- "crates/ty*/**"
|
||||
- "crates/red_knot*/**"
|
||||
- "crates/ruff_db/**"
|
||||
- "crates/ruff_python_ast/**"
|
||||
- "crates/ruff_python_parser/**"
|
||||
- "playground/**"
|
||||
- ".github/workflows/publish-ty-playground.yml"
|
||||
- ".github/workflows/publish-knot-playground.yml"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref_name }}
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup target add wasm32-unknown-unknown
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
- uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0
|
||||
with:
|
||||
node-version: 22
|
||||
- uses: jetli/wasm-bindgen-action@20b33e20595891ab1a0ed73145d8a21fc96e7c29 # v0.2.0
|
||||
@@ -45,8 +45,8 @@ jobs:
|
||||
- name: "Run TypeScript checks"
|
||||
run: npm run check
|
||||
working-directory: playground
|
||||
- name: "Build ty playground"
|
||||
run: npm run build --workspace ty-playground
|
||||
- name: "Build Knot playground"
|
||||
run: npm run build --workspace knot-playground
|
||||
working-directory: playground
|
||||
- name: "Deploy to Cloudflare Pages"
|
||||
if: ${{ env.CF_API_TOKEN_EXISTS == 'true' }}
|
||||
@@ -55,4 +55,4 @@ jobs:
|
||||
apiToken: ${{ secrets.CF_API_TOKEN }}
|
||||
accountId: ${{ secrets.CF_ACCOUNT_ID }}
|
||||
# `github.head_ref` is only set during pull requests and for manual runs or tags we use `main` to deploy to production
|
||||
command: pages deploy playground/ty/dist --project-name=ty-playground --branch ${{ github.head_ref || 'main' }} --commit-hash ${GITHUB_SHA}
|
||||
command: pages deploy playground/knot/dist --project-name=knot-playground --branch ${{ github.head_ref || 'main' }} --commit-hash ${GITHUB_SHA}
|
||||
2
.github/workflows/publish-playground.yml
vendored
2
.github/workflows/publish-playground.yml
vendored
@@ -29,7 +29,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup target add wasm32-unknown-unknown
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
- uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0
|
||||
with:
|
||||
node-version: 22
|
||||
cache: "npm"
|
||||
|
||||
2
.github/workflows/publish-pypi.yml
vendored
2
.github/workflows/publish-pypi.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
steps:
|
||||
- name: "Install uv"
|
||||
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
|
||||
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
- uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
|
||||
with:
|
||||
pattern: wheels-*
|
||||
path: wheels
|
||||
|
||||
2
.github/workflows/publish-wasm.yml
vendored
2
.github/workflows/publish-wasm.yml
vendored
@@ -45,7 +45,7 @@ jobs:
|
||||
jq '.name="@astral-sh/ruff-wasm-${{ matrix.target }}"' crates/ruff_wasm/pkg/package.json > /tmp/package.json
|
||||
mv /tmp/package.json crates/ruff_wasm/pkg
|
||||
- run: cp LICENSE crates/ruff_wasm/pkg # wasm-pack does not put the LICENSE file in the pkg
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
- uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0
|
||||
with:
|
||||
node-version: 20
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
|
||||
29
.github/workflows/release.yml
vendored
29
.github/workflows/release.yml
vendored
@@ -40,7 +40,6 @@ permissions:
|
||||
# If there's a prerelease-style suffix to the version, then the release(s)
|
||||
# will be marked as a prerelease.
|
||||
on:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
@@ -61,7 +60,7 @@ jobs:
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@85e6279cec87321a52edac9c87bce653a07cf6c2
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
@@ -69,9 +68,9 @@ jobs:
|
||||
# we specify bash to get pipefail; it guards against the `curl` command
|
||||
# failing. otherwise `sh` won't catch that `curl` returned non-0
|
||||
shell: bash
|
||||
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/astral-sh/cargo-dist/releases/download/v0.28.5-prerelease.1/cargo-dist-installer.sh | sh"
|
||||
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/astral-sh/cargo-dist/releases/download/v0.28.4-prerelease.1/cargo-dist-installer.sh | sh"
|
||||
- name: Cache dist
|
||||
uses: actions/upload-artifact@6027e3dd177782cd8ab9af838c04fd81a07f1d47
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
|
||||
with:
|
||||
name: cargo-dist-cache
|
||||
path: ~/.cargo/bin/dist
|
||||
@@ -87,7 +86,7 @@ jobs:
|
||||
cat plan-dist-manifest.json
|
||||
echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT"
|
||||
- name: "Upload dist-manifest.json"
|
||||
uses: actions/upload-artifact@6027e3dd177782cd8ab9af838c04fd81a07f1d47
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
|
||||
with:
|
||||
name: artifacts-plan-dist-manifest
|
||||
path: plan-dist-manifest.json
|
||||
@@ -124,19 +123,19 @@ jobs:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json
|
||||
steps:
|
||||
- uses: actions/checkout@85e6279cec87321a52edac9c87bce653a07cf6c2
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
- name: Install cached dist
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093
|
||||
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e
|
||||
with:
|
||||
name: cargo-dist-cache
|
||||
path: ~/.cargo/bin/
|
||||
- run: chmod +x ~/.cargo/bin/dist
|
||||
# Get all the local artifacts for the global tasks to use (for e.g. checksums)
|
||||
- name: Fetch local artifacts
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093
|
||||
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e
|
||||
with:
|
||||
pattern: artifacts-*
|
||||
path: target/distrib/
|
||||
@@ -154,7 +153,7 @@ jobs:
|
||||
|
||||
cp dist-manifest.json "$BUILD_MANIFEST_NAME"
|
||||
- name: "Upload artifacts"
|
||||
uses: actions/upload-artifact@6027e3dd177782cd8ab9af838c04fd81a07f1d47
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
|
||||
with:
|
||||
name: artifacts-build-global
|
||||
path: |
|
||||
@@ -175,19 +174,19 @@ jobs:
|
||||
outputs:
|
||||
val: ${{ steps.host.outputs.manifest }}
|
||||
steps:
|
||||
- uses: actions/checkout@85e6279cec87321a52edac9c87bce653a07cf6c2
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
- name: Install cached dist
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093
|
||||
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e
|
||||
with:
|
||||
name: cargo-dist-cache
|
||||
path: ~/.cargo/bin/
|
||||
- run: chmod +x ~/.cargo/bin/dist
|
||||
# Fetch artifacts from scratch-storage
|
||||
- name: Fetch artifacts
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093
|
||||
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e
|
||||
with:
|
||||
pattern: artifacts-*
|
||||
path: target/distrib/
|
||||
@@ -201,7 +200,7 @@ jobs:
|
||||
cat dist-manifest.json
|
||||
echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT"
|
||||
- name: "Upload dist-manifest.json"
|
||||
uses: actions/upload-artifact@6027e3dd177782cd8ab9af838c04fd81a07f1d47
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
|
||||
with:
|
||||
# Overwrite the previous copy
|
||||
name: artifacts-dist-manifest
|
||||
@@ -251,13 +250,13 @@ jobs:
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@85e6279cec87321a52edac9c87bce653a07cf6c2
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
# Create a GitHub Release while uploading all files to it
|
||||
- name: "Download GitHub Artifacts"
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093
|
||||
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e
|
||||
with:
|
||||
pattern: artifacts-*
|
||||
path: artifacts
|
||||
|
||||
16
.github/workflows/sync_typeshed.yaml
vendored
16
.github/workflows/sync_typeshed.yaml
vendored
@@ -39,13 +39,13 @@ jobs:
|
||||
- name: Sync typeshed
|
||||
id: sync
|
||||
run: |
|
||||
rm -rf ruff/crates/ty_vendored/vendor/typeshed
|
||||
mkdir ruff/crates/ty_vendored/vendor/typeshed
|
||||
cp typeshed/README.md ruff/crates/ty_vendored/vendor/typeshed
|
||||
cp typeshed/LICENSE ruff/crates/ty_vendored/vendor/typeshed
|
||||
cp -r typeshed/stdlib ruff/crates/ty_vendored/vendor/typeshed/stdlib
|
||||
rm -rf ruff/crates/ty_vendored/vendor/typeshed/stdlib/@tests
|
||||
git -C typeshed rev-parse HEAD > ruff/crates/ty_vendored/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' }}
|
||||
@@ -79,5 +79,5 @@ jobs:
|
||||
repo: "ruff",
|
||||
title: `Automated typeshed sync failed on ${new Date().toDateString()}`,
|
||||
body: "Run listed here: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
|
||||
labels: ["bug", "ty"],
|
||||
labels: ["bug", "red-knot"],
|
||||
})
|
||||
|
||||
@@ -3,8 +3,8 @@ fail_fast: false
|
||||
exclude: |
|
||||
(?x)^(
|
||||
.github/workflows/release.yml|
|
||||
crates/ty_vendored/vendor/.*|
|
||||
crates/ty_project/resources/.*|
|
||||
crates/red_knot_vendored/vendor/.*|
|
||||
crates/red_knot_project/resources/.*|
|
||||
crates/ruff_benchmark/resources/.*|
|
||||
crates/ruff_linter/resources/.*|
|
||||
crates/ruff_linter/src/rules/.*/snapshots/.*|
|
||||
@@ -65,7 +65,7 @@ repos:
|
||||
- black==25.1.0
|
||||
|
||||
- repo: https://github.com/crate-ci/typos
|
||||
rev: v1.32.0
|
||||
rev: v1.31.1
|
||||
hooks:
|
||||
- id: typos
|
||||
|
||||
@@ -79,7 +79,7 @@ repos:
|
||||
pass_filenames: false # This makes it a lot faster
|
||||
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.11.8
|
||||
rev: v0.11.6
|
||||
hooks:
|
||||
- id: ruff-format
|
||||
- id: ruff
|
||||
|
||||
61
CHANGELOG.md
61
CHANGELOG.md
@@ -1,66 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
## 0.11.8
|
||||
|
||||
### Preview features
|
||||
|
||||
- \[`airflow`\] Apply auto fixes to cases where the names have changed in Airflow 3 (`AIR302`, `AIR311`) ([#17553](https://github.com/astral-sh/ruff/pull/17553), [#17570](https://github.com/astral-sh/ruff/pull/17570), [#17571](https://github.com/astral-sh/ruff/pull/17571))
|
||||
- \[`airflow`\] Extend `AIR301` rule ([#17598](https://github.com/astral-sh/ruff/pull/17598))
|
||||
- \[`airflow`\] Update existing `AIR302` rules with better suggestions ([#17542](https://github.com/astral-sh/ruff/pull/17542))
|
||||
- \[`refurb`\] Mark fix as safe for `readlines-in-for` (`FURB129`) ([#17644](https://github.com/astral-sh/ruff/pull/17644))
|
||||
- [syntax-errors] `nonlocal` declaration at module level ([#17559](https://github.com/astral-sh/ruff/pull/17559))
|
||||
- [syntax-errors] Detect single starred expression assignment `x = *y` ([#17624](https://github.com/astral-sh/ruff/pull/17624))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- \[`flake8-pyi`\] Ensure `Literal[None,] | Literal[None,]` is not autofixed to `None | None` (`PYI061`) ([#17659](https://github.com/astral-sh/ruff/pull/17659))
|
||||
- \[`flake8-use-pathlib`\] Avoid suggesting `Path.iterdir()` for `os.listdir` with file descriptor (`PTH208`) ([#17715](https://github.com/astral-sh/ruff/pull/17715))
|
||||
- \[`flake8-use-pathlib`\] Fix `PTH104` false positive when `rename` is passed a file descriptor ([#17712](https://github.com/astral-sh/ruff/pull/17712))
|
||||
- \[`flake8-use-pathlib`\] Fix `PTH116` false positive when `stat` is passed a file descriptor ([#17709](https://github.com/astral-sh/ruff/pull/17709))
|
||||
- \[`flake8-use-pathlib`\] Fix `PTH123` false positive when `open` is passed a file descriptor from a function call ([#17705](https://github.com/astral-sh/ruff/pull/17705))
|
||||
- \[`pycodestyle`\] Fix duplicated diagnostic in `E712` ([#17651](https://github.com/astral-sh/ruff/pull/17651))
|
||||
- \[`pylint`\] Detect `global` declarations in module scope (`PLE0118`) ([#17411](https://github.com/astral-sh/ruff/pull/17411))
|
||||
- [syntax-errors] Make `async-comprehension-in-sync-comprehension` more specific ([#17460](https://github.com/astral-sh/ruff/pull/17460))
|
||||
|
||||
### Configuration
|
||||
|
||||
- Add option to disable `typing_extensions` imports ([#17611](https://github.com/astral-sh/ruff/pull/17611))
|
||||
|
||||
### Documentation
|
||||
|
||||
- Fix example syntax for the `lint.pydocstyle.ignore-var-parameters` option ([#17740](https://github.com/astral-sh/ruff/pull/17740))
|
||||
- Add fix safety sections (`ASYNC116`, `FLY002`, `D200`, `RUF005`, `RUF017`, `RUF027`, `RUF028`, `RUF057`) ([#17497](https://github.com/astral-sh/ruff/pull/17497), [#17496](https://github.com/astral-sh/ruff/pull/17496), [#17502](https://github.com/astral-sh/ruff/pull/17502), [#17484](https://github.com/astral-sh/ruff/pull/17484), [#17480](https://github.com/astral-sh/ruff/pull/17480), [#17485](https://github.com/astral-sh/ruff/pull/17485), [#17722](https://github.com/astral-sh/ruff/pull/17722), [#17483](https://github.com/astral-sh/ruff/pull/17483))
|
||||
|
||||
### Other changes
|
||||
|
||||
- Add Python 3.14 to configuration options ([#17647](https://github.com/astral-sh/ruff/pull/17647))
|
||||
- Make syntax error for unparenthesized except tuples version specific to before 3.14 ([#17660](https://github.com/astral-sh/ruff/pull/17660))
|
||||
|
||||
## 0.11.7
|
||||
|
||||
### Preview features
|
||||
|
||||
- \[`airflow`\] Apply auto fixes to cases where the names have changed in Airflow 3 (`AIR301`) ([#17355](https://github.com/astral-sh/ruff/pull/17355))
|
||||
- \[`perflint`\] Implement fix for `manual-dict-comprehension` (`PERF403`) ([#16719](https://github.com/astral-sh/ruff/pull/16719))
|
||||
- [syntax-errors] Make duplicate parameter names a semantic error ([#17131](https://github.com/astral-sh/ruff/pull/17131))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- \[`airflow`\] Fix typos in provider package names (`AIR302`, `AIR312`) ([#17574](https://github.com/astral-sh/ruff/pull/17574))
|
||||
- \[`flake8-type-checking`\] Visit keyword arguments in checks involving `typing.cast`/`typing.NewType` arguments ([#17538](https://github.com/astral-sh/ruff/pull/17538))
|
||||
- \[`pyupgrade`\] Preserve parenthesis when fixing native literals containing newlines (`UP018`) ([#17220](https://github.com/astral-sh/ruff/pull/17220))
|
||||
- \[`refurb`\] Mark the `FURB161` fix unsafe except for integers and booleans ([#17240](https://github.com/astral-sh/ruff/pull/17240))
|
||||
|
||||
### Rule changes
|
||||
|
||||
- \[`perflint`\] Allow list function calls to be replaced with a comprehension (`PERF401`) ([#17519](https://github.com/astral-sh/ruff/pull/17519))
|
||||
- \[`pycodestyle`\] Auto-fix redundant boolean comparison (`E712`) ([#17090](https://github.com/astral-sh/ruff/pull/17090))
|
||||
- \[`pylint`\] make fix unsafe if delete comments (`PLR1730`) ([#17459](https://github.com/astral-sh/ruff/pull/17459))
|
||||
|
||||
### Documentation
|
||||
|
||||
- Add fix safety sections to docs for several rules ([#17410](https://github.com/astral-sh/ruff/pull/17410),[#17440](https://github.com/astral-sh/ruff/pull/17440),[#17441](https://github.com/astral-sh/ruff/pull/17441),[#17443](https://github.com/astral-sh/ruff/pull/17443),[#17444](https://github.com/astral-sh/ruff/pull/17444))
|
||||
|
||||
## 0.11.6
|
||||
|
||||
### Preview features
|
||||
|
||||
@@ -71,7 +71,8 @@ representative at an online or offline event.
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at <hey@astral.sh>.
|
||||
reported to the community leaders responsible for enforcement at
|
||||
<charlie.r.marsh@gmail.com>.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
|
||||
@@ -366,15 +366,6 @@ uvx --from ./python/ruff-ecosystem ruff-ecosystem format ruff "./target/debug/ru
|
||||
|
||||
See the [ruff-ecosystem package](https://github.com/astral-sh/ruff/tree/main/python/ruff-ecosystem) for more details.
|
||||
|
||||
## Upgrading Rust
|
||||
|
||||
1. Change the `channel` in `./rust-toolchain.toml` to the new Rust version (`<latest>`)
|
||||
1. Change the `rust-version` in the `./Cargo.toml` to `<latest> - 2` (e.g. 1.84 if the latest is 1.86)
|
||||
1. Run `cargo clippy --fix --allow-dirty --allow-staged` to fix new clippy warnings
|
||||
1. Create and merge the PR
|
||||
1. Bump the Rust version in Ruff's conda forge recipe. See [this PR](https://github.com/conda-forge/ruff-feedstock/pull/266) for an example.
|
||||
1. Enjoy the new Rust version!
|
||||
|
||||
## Benchmarking and Profiling
|
||||
|
||||
We have several ways of benchmarking and profiling Ruff:
|
||||
|
||||
603
Cargo.lock
generated
603
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
84
Cargo.toml
84
Cargo.toml
@@ -35,14 +35,14 @@ 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" }
|
||||
|
||||
ty_ide = { path = "crates/ty_ide" }
|
||||
ty_project = { path = "crates/ty_project", default-features = false }
|
||||
ty_python_semantic = { path = "crates/ty_python_semantic" }
|
||||
ty_server = { path = "crates/ty_server" }
|
||||
ty_test = { path = "crates/ty_test" }
|
||||
ty_vendored = { path = "crates/ty_vendored" }
|
||||
red_knot_ide = { path = "crates/red_knot_ide" }
|
||||
red_knot_project = { path = "crates/red_knot_project", default-features = false }
|
||||
red_knot_python_semantic = { path = "crates/red_knot_python_semantic" }
|
||||
red_knot_server = { path = "crates/red_knot_server" }
|
||||
red_knot_test = { path = "crates/red_knot_test" }
|
||||
|
||||
aho-corasick = { version = "1.1.3" }
|
||||
anstream = { version = "0.6.18" }
|
||||
@@ -124,7 +124,7 @@ rayon = { version = "1.10.0" }
|
||||
regex = { version = "1.10.2" }
|
||||
rustc-hash = { version = "2.0.0" }
|
||||
# When updating salsa, make sure to also update the revision in `fuzz/Cargo.toml`
|
||||
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "ef93d3830ffb8dc621fef1ba7b234ccef9dc3639" }
|
||||
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "87bf6b6c2d5f6479741271da73bd9d30c2580c26" }
|
||||
schemars = { version = "0.8.16" }
|
||||
seahash = { version = "4.1.0" }
|
||||
serde = { version = "1.0.197", features = ["derive"] }
|
||||
@@ -268,3 +268,73 @@ debug = 1
|
||||
# The profile that 'cargo dist' will build with.
|
||||
[profile.dist]
|
||||
inherits = "release"
|
||||
|
||||
# Config for 'dist'
|
||||
[workspace.metadata.dist]
|
||||
# The preferred dist version to use in CI (Cargo.toml SemVer syntax)
|
||||
cargo-dist-version = "0.28.4-prerelease.1"
|
||||
# CI backends to support
|
||||
ci = "github"
|
||||
# The installers to generate for each app
|
||||
installers = ["shell", "powershell"]
|
||||
# The archive format to use for windows builds (defaults .zip)
|
||||
windows-archive = ".zip"
|
||||
# The archive format to use for non-windows builds (defaults .tar.xz)
|
||||
unix-archive = ".tar.gz"
|
||||
# Target platforms to build apps for (Rust target-triple syntax)
|
||||
targets = [
|
||||
"aarch64-apple-darwin",
|
||||
"aarch64-pc-windows-msvc",
|
||||
"aarch64-unknown-linux-gnu",
|
||||
"aarch64-unknown-linux-musl",
|
||||
"arm-unknown-linux-musleabihf",
|
||||
"armv7-unknown-linux-gnueabihf",
|
||||
"armv7-unknown-linux-musleabihf",
|
||||
"i686-pc-windows-msvc",
|
||||
"i686-unknown-linux-gnu",
|
||||
"i686-unknown-linux-musl",
|
||||
"powerpc64-unknown-linux-gnu",
|
||||
"powerpc64le-unknown-linux-gnu",
|
||||
"s390x-unknown-linux-gnu",
|
||||
"x86_64-apple-darwin",
|
||||
"x86_64-pc-windows-msvc",
|
||||
"x86_64-unknown-linux-gnu",
|
||||
"x86_64-unknown-linux-musl",
|
||||
]
|
||||
# Whether to auto-include files like READMEs, LICENSEs, and CHANGELOGs (default true)
|
||||
auto-includes = false
|
||||
# Whether dist should create a Github Release or use an existing draft
|
||||
create-release = true
|
||||
# Which actions to run on pull requests
|
||||
pr-run-mode = "skip"
|
||||
# Whether CI should trigger releases with dispatches instead of tag pushes
|
||||
dispatch-releases = true
|
||||
# Which phase dist should use to create the GitHub release
|
||||
github-release = "announce"
|
||||
# Whether CI should include auto-generated code to build local artifacts
|
||||
build-local-artifacts = false
|
||||
# Local artifacts jobs to run in CI
|
||||
local-artifacts-jobs = ["./build-binaries", "./build-docker"]
|
||||
# Publish jobs to run in CI
|
||||
publish-jobs = ["./publish-pypi", "./publish-wasm"]
|
||||
# Post-announce jobs to run in CI
|
||||
post-announce-jobs = [
|
||||
"./notify-dependents",
|
||||
"./publish-docs",
|
||||
"./publish-playground",
|
||||
]
|
||||
# Custom permissions for GitHub Jobs
|
||||
github-custom-job-permissions = { "build-docker" = { packages = "write", contents = "read" }, "publish-wasm" = { contents = "read", id-token = "write", packages = "write" } }
|
||||
# Whether to install an updater program
|
||||
install-updater = false
|
||||
# Path that installers should place binaries in
|
||||
install-path = ["$XDG_BIN_HOME/", "$XDG_DATA_HOME/../bin", "~/.local/bin"]
|
||||
|
||||
[workspace.metadata.dist.github-custom-runners]
|
||||
global = "depot-ubuntu-latest-4"
|
||||
|
||||
[workspace.metadata.dist.github-action-commits]
|
||||
"actions/checkout" = "11bd71901bbe5b1630ceea73d27597364c9af683" # v4
|
||||
"actions/upload-artifact" = "ea165f8d65b6e75b540449e92b4886f43607fa02" # v4.6.2
|
||||
"actions/download-artifact" = "95815c38cf2ff2164869cbab79da8d1f422bc89e" # v4.2.1
|
||||
"actions/attest-build-provenance" = "c074443f1aee8d4aeeae555aebba3282517141b2" #v2.2.3
|
||||
|
||||
@@ -149,8 +149,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.11.8/install.sh | sh
|
||||
powershell -c "irm https://astral.sh/ruff/0.11.8/install.ps1 | iex"
|
||||
curl -LsSf https://astral.sh/ruff/0.11.6/install.sh | sh
|
||||
powershell -c "irm https://astral.sh/ruff/0.11.6/install.ps1 | iex"
|
||||
```
|
||||
|
||||
You can also install Ruff via [Homebrew](https://formulae.brew.sh/formula/ruff), [Conda](https://anaconda.org/conda-forge/ruff),
|
||||
@@ -183,7 +183,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.11.8
|
||||
rev: v0.11.6
|
||||
hooks:
|
||||
# Run the linter.
|
||||
- id: ruff
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[files]
|
||||
# https://github.com/crate-ci/typos/issues/868
|
||||
extend-exclude = [
|
||||
"crates/ty_vendored/vendor/**/*",
|
||||
"crates/red_knot_vendored/vendor/**/*",
|
||||
"**/resources/**/*",
|
||||
"**/snapshots/**/*",
|
||||
]
|
||||
|
||||
@@ -1,28 +1,26 @@
|
||||
[package]
|
||||
name = "ty"
|
||||
name = "red_knot"
|
||||
version = "0.0.0"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
# Releases occur in this other repository!
|
||||
repository = "https://github.com/astral-sh/ty/"
|
||||
repository.workspace = true
|
||||
authors.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
red_knot_python_semantic = { workspace = true }
|
||||
red_knot_project = { workspace = true, features = ["zstd"] }
|
||||
red_knot_server = { workspace = true }
|
||||
ruff_db = { workspace = true, features = ["os", "cache"] }
|
||||
ruff_python_ast = { workspace = true }
|
||||
ty_python_semantic = { workspace = true }
|
||||
ty_project = { workspace = true, features = ["zstd"] }
|
||||
ty_server = { workspace = true }
|
||||
|
||||
anyhow = { workspace = true }
|
||||
argfile = { workspace = true }
|
||||
clap = { workspace = true, features = ["wrap_help", "string"] }
|
||||
clap_complete_command = { workspace = true }
|
||||
clap = { workspace = true, features = ["wrap_help"] }
|
||||
colored = { workspace = true }
|
||||
countme = { workspace = true, features = ["enable"] }
|
||||
crossbeam = { workspace = true }
|
||||
25
crates/red_knot/README.md
Normal file
25
crates/red_knot/README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Red Knot
|
||||
|
||||
Red Knot is an extremely fast type checker.
|
||||
Currently, it is a work-in-progress and not ready for user testing.
|
||||
|
||||
Red Knot is designed to prioritize good type inference, even in unannotated code,
|
||||
and aims to avoid false positives.
|
||||
|
||||
While Red Knot will produce similar results to mypy and pyright on many codebases,
|
||||
100% compatibility with these tools is a non-goal.
|
||||
On some codebases, Red Knot's design decisions lead to different outcomes
|
||||
than you would get from running one of these more established tools.
|
||||
|
||||
## Contributing
|
||||
|
||||
Core type checking tests are written as Markdown code blocks.
|
||||
They can be found in [`red_knot_python_semantic/resources/mdtest`][resources-mdtest].
|
||||
See [`red_knot_test/README.md`][mdtest-readme] for more information
|
||||
on the test framework itself.
|
||||
|
||||
The list of open issues can be found [here][open-issues].
|
||||
|
||||
[mdtest-readme]: ../red_knot_test/README.md
|
||||
[open-issues]: https://github.com/astral-sh/ruff/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20label%3Ared-knot
|
||||
[resources-mdtest]: ../red_knot_python_semantic/resources/mdtest
|
||||
@@ -8,43 +8,16 @@ fn main() {
|
||||
// The workspace root directory is not available without walking up the tree
|
||||
// https://github.com/rust-lang/cargo/issues/3946
|
||||
let workspace_root = Path::new(&std::env::var("CARGO_MANIFEST_DIR").unwrap())
|
||||
.join("..")
|
||||
.join("..")
|
||||
.join("..");
|
||||
|
||||
version_info(&workspace_root);
|
||||
commit_info(&workspace_root);
|
||||
|
||||
#[allow(clippy::disallowed_methods)]
|
||||
let target = std::env::var("TARGET").unwrap();
|
||||
println!("cargo::rustc-env=RUST_HOST_TARGET={target}");
|
||||
}
|
||||
|
||||
/// Retrieve the version from the `dist-workspace.toml` file and set `TY_VERSION`.
|
||||
fn version_info(workspace_root: &Path) {
|
||||
let dist_file = workspace_root.join("dist-workspace.toml");
|
||||
if !dist_file.exists() {
|
||||
return;
|
||||
}
|
||||
|
||||
println!("cargo:rerun-if-changed={}", dist_file.display());
|
||||
|
||||
let dist_file = fs::read_to_string(dist_file);
|
||||
if let Ok(dist_file) = dist_file {
|
||||
let lines = dist_file.lines();
|
||||
for line in lines {
|
||||
if line.starts_with("version =") {
|
||||
let (_key, version) = line.split_once('=').unwrap();
|
||||
println!(
|
||||
"cargo::rustc-env=TY_VERSION={}",
|
||||
version.trim().trim_matches('"')
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve commit information from the Git repository.
|
||||
fn commit_info(workspace_root: &Path) {
|
||||
// If not in a git repository, do not attempt to retrieve commit information
|
||||
let git_dir = workspace_root.join(".git");
|
||||
@@ -76,8 +49,7 @@ fn commit_info(workspace_root: &Path) {
|
||||
.arg("-1")
|
||||
.arg("--date=short")
|
||||
.arg("--abbrev=9")
|
||||
.arg("--format=%H %h %cd %(describe:tags)")
|
||||
.current_dir(workspace_root)
|
||||
.arg("--format=%H %h %cd %(describe)")
|
||||
.output()
|
||||
{
|
||||
Ok(output) if output.status.success() => output,
|
||||
@@ -87,20 +59,18 @@ fn commit_info(workspace_root: &Path) {
|
||||
let mut parts = stdout.split_whitespace();
|
||||
let mut next = || parts.next().unwrap();
|
||||
let _commit_hash = next();
|
||||
println!("cargo::rustc-env=TY_COMMIT_SHORT_HASH={}", next());
|
||||
println!("cargo::rustc-env=TY_COMMIT_DATE={}", next());
|
||||
println!("cargo::rustc-env=RED_KNOT_COMMIT_SHORT_HASH={}", next());
|
||||
println!("cargo::rustc-env=RED_KNOT_COMMIT_DATE={}", next());
|
||||
|
||||
// Describe can fail for some commits
|
||||
// https://git-scm.com/docs/pretty-formats#Documentation/pretty-formats.txt-emdescribeoptionsem
|
||||
if let Some(describe) = parts.next() {
|
||||
let mut describe_parts = describe.split('-');
|
||||
let last_tag = describe_parts.next().unwrap();
|
||||
|
||||
println!("cargo::rustc-env=TY_LAST_TAG={last_tag}");
|
||||
let _last_tag = describe_parts.next().unwrap();
|
||||
|
||||
// If this is the tagged commit, this component will be missing
|
||||
println!(
|
||||
"cargo::rustc-env=TY_LAST_TAG_DISTANCE={}",
|
||||
"cargo::rustc-env=RED_KNOT_LAST_TAG_DISTANCE={}",
|
||||
describe_parts.next().unwrap_or("0")
|
||||
);
|
||||
}
|
||||
@@ -2,16 +2,16 @@
|
||||
|
||||
## Basics
|
||||
|
||||
`mypy_primer` can be run using `uvx --from "…" mypy_primer`. For example, to see the help message, run:
|
||||
For now, we use our own [fork of mypy primer]. It can be run using `uvx --from "…" mypy_primer`. For example, to see the help message, run:
|
||||
|
||||
```sh
|
||||
uvx --from "git+https://github.com/hauntsaninja/mypy_primer" mypy_primer -h
|
||||
uvx --from "git+https://github.com/astral-sh/mypy_primer.git@add-red-knot-support" mypy_primer -h
|
||||
```
|
||||
|
||||
Alternatively, you can install the forked version of `mypy_primer` using:
|
||||
|
||||
```sh
|
||||
uv tool install "git+https://github.com/hauntsaninja/mypy_primer"
|
||||
uv tool install "git+https://github.com/astral-sh/mypy_primer.git@add-red-knot-support"
|
||||
```
|
||||
|
||||
and then run it using `uvx mypy_primer` or just `mypy_primer`, if your `PATH` is set up accordingly (see: [Tool executables]).
|
||||
@@ -22,7 +22,7 @@ To show the diagnostics diff between two Git revisions (e.g. your feature branch
|
||||
|
||||
```sh
|
||||
mypy_primer \
|
||||
--type-checker ty \
|
||||
--type-checker knot \
|
||||
--old origin/main \
|
||||
--new my/feature \
|
||||
--debug \
|
||||
@@ -31,15 +31,15 @@ mypy_primer \
|
||||
```
|
||||
|
||||
This will show the diagnostics diff for the `black` project between the `main` branch and your `my/feature` branch. To run the
|
||||
diff for all projects we currently enable in CI, use `--project-selector "/($(paste -s -d'|' crates/ty_python_semantic/resources/primer/good.txt))\$"`.
|
||||
diff for all projects we currently enable in CI, use `--project-selector "/($(paste -s -d'|' crates/red_knot_python_semantic/resources/primer/good.txt))\$"`.
|
||||
|
||||
You can also take a look at the [full list of ecosystem projects]. Note that some of them might still need a `ty_paths` configuration
|
||||
You can also take a look at the [full list of ecosystem projects]. Note that some of them might still need a `knot_paths` configuration
|
||||
option to work correctly.
|
||||
|
||||
## Avoiding recompilation
|
||||
|
||||
If you want to run `mypy_primer` repeatedly, e.g. for different projects, but for the same combination of `--old` and `--new`, you
|
||||
can use set the `MYPY_PRIMER_NO_REBUILD` environment variable to avoid recompilation of ty:
|
||||
can use set the `MYPY_PRIMER_NO_REBUILD` environment variable to avoid recompilation of Red Knot:
|
||||
|
||||
```sh
|
||||
MYPY_PRIMER_NO_REBUILD=1 mypy_primer …
|
||||
@@ -56,5 +56,6 @@ mypy_primer --repo /path/to/ruff --old origin/main --new my/local-branch …
|
||||
|
||||
Note that you might need to clean up `/tmp/mypy_primer` in order for this to work correctly.
|
||||
|
||||
[full list of ecosystem projects]: https://github.com/hauntsaninja/mypy_primer/blob/master/mypy_primer/projects.py
|
||||
[fork of mypy primer]: https://github.com/astral-sh/mypy_primer/tree/add-red-knot-support
|
||||
[full list of ecosystem projects]: https://github.com/astral-sh/mypy_primer/blob/add-red-knot-support/mypy_primer/projects.py
|
||||
[tool executables]: https://docs.astral.sh/uv/concepts/tools/#tool-executables
|
||||
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
@@ -1,7 +1,6 @@
|
||||
# Tracing
|
||||
|
||||
Traces are a useful tool to narrow down the location of a bug or, at least, to understand why the compiler is doing a
|
||||
particular thing.
|
||||
Traces are a useful tool to narrow down the location of a bug or, at least, to understand why the compiler is doing a particular thing.
|
||||
Note, tracing messages with severity `debug` or greater are user-facing. They should be phrased accordingly.
|
||||
Tracing spans are only shown when using `-vvv`.
|
||||
|
||||
@@ -10,28 +9,20 @@ Tracing spans are only shown when using `-vvv`.
|
||||
The CLI supports different verbosity levels.
|
||||
|
||||
- default: Only show errors and warnings.
|
||||
- `-v` activates `info!`: Show generally useful information such as paths of configuration files, detected platform,
|
||||
etc., but it's not a lot of messages, it's something you'll activate in CI by default. cargo build e.g. shows you
|
||||
which packages are fresh.
|
||||
- `-vv` activates `debug!` and timestamps: This should be enough information to get to the bottom of bug reports. When
|
||||
you're processing many packages or files, you'll get pages and pages of output, but each line is link to a specific
|
||||
action or state change.
|
||||
- `-vvv` activates `trace!` (only in debug builds) and shows tracing-spans: At this level, you're logging everything.
|
||||
Most of this is wasted, it's really slow, we dump e.g. the entire resolution graph. Only useful to developers, and you
|
||||
almost certainly want to use `TY_LOG` to filter it down to the area your investigating.
|
||||
- `-v` activates `info!`: Show generally useful information such as paths of configuration files, detected platform, etc., but it's not a lot of messages, it's something you'll activate in CI by default. cargo build e.g. shows you which packages are fresh.
|
||||
- `-vv` activates `debug!` and timestamps: This should be enough information to get to the bottom of bug reports. When you're processing many packages or files, you'll get pages and pages of output, but each line is link to a specific action or state change.
|
||||
- `-vvv` activates `trace!` (only in debug builds) and shows tracing-spans: At this level, you're logging everything. Most of this is wasted, it's really slow, we dump e.g. the entire resolution graph. Only useful to developers, and you almost certainly want to use `RED_KNOT_LOG` to filter it down to the area your investigating.
|
||||
|
||||
## Better logging with `TY_LOG` and `TY_MAX_PARALLELISM`
|
||||
## Better logging with `RED_KNOT_LOG` and `RAYON_NUM_THREADS`
|
||||
|
||||
By default, the CLI shows messages from the `ruff` and `ty` crates. Tracing messages from other crates are not shown.
|
||||
The `TY_LOG` environment variable allows you to customize which messages are shown by specifying one
|
||||
or
|
||||
more [filter directives](https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives).
|
||||
By default, the CLI shows messages from the `ruff` and `red_knot` crates. Tracing messages from other crates are not shown.
|
||||
The `RED_KNOT_LOG` environment variable allows you to customize which messages are shown by specifying one
|
||||
or more [filter directives](https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives).
|
||||
|
||||
The `TY_MAX_PARALLELISM` environment variable, meanwhile, can be used to control the level of parallelism ty uses.
|
||||
By default, ty will attempt to parallelize its work so that multiple files are checked simultaneously,
|
||||
but this can result in a confused logging output where messages from different threads are intertwined and non
|
||||
determinism.
|
||||
To switch off parallelism entirely and have more readable logs, use `TY_MAX_PARALLELISM=1` (or `RAYON_NUM_THREADS=1`).
|
||||
The `RAYON_NUM_THREADS` environment variable, meanwhile, can be used to control the level of concurrency red-knot uses.
|
||||
By default, red-knot will attempt to parallelize its work so that multiple files are checked simultaneously,
|
||||
but this can result in a confused logging output where messages from different threads are intertwined.
|
||||
To switch off concurrency entirely and have more readable logs, use `RAYON_NUM_THREADS=1`.
|
||||
|
||||
### Examples
|
||||
|
||||
@@ -40,23 +31,23 @@ To switch off parallelism entirely and have more readable logs, use `TY_MAX_PARA
|
||||
Shows debug messages from all crates.
|
||||
|
||||
```bash
|
||||
TY_LOG=debug
|
||||
RED_KNOT_LOG=debug
|
||||
```
|
||||
|
||||
#### Show salsa query execution messages
|
||||
|
||||
Show the salsa `execute: my_query` messages in addition to all ty messages.
|
||||
Show the salsa `execute: my_query` messages in addition to all red knot messages.
|
||||
|
||||
```bash
|
||||
TY_LOG=ruff=trace,ty=trace,salsa=info
|
||||
RED_KNOT_LOG=ruff=trace,red_knot=trace,salsa=info
|
||||
```
|
||||
|
||||
#### Show typing traces
|
||||
|
||||
Only show traces for the `ty_python_semantic::types` module.
|
||||
Only show traces for the `red_knot_python_semantic::types` module.
|
||||
|
||||
```bash
|
||||
TY_LOG="ty_python_semantic::types"
|
||||
RED_KNOT_LOG="red_knot_python_semantic::types"
|
||||
```
|
||||
|
||||
Note: Ensure that you use `-vvv` to see tracing spans.
|
||||
@@ -66,7 +57,7 @@ Note: Ensure that you use `-vvv` to see tracing spans.
|
||||
Shows all messages that are inside of a span for a specific file.
|
||||
|
||||
```bash
|
||||
TY_LOG=ty[{file=/home/micha/astral/test/x.py}]=trace
|
||||
RED_KNOT_LOG=red_knot[{file=/home/micha/astral/test/x.py}]=trace
|
||||
```
|
||||
|
||||
**Note**: Tracing still shows all spans because tracing can't know at the time of entering the span
|
||||
@@ -88,24 +79,22 @@ query to return the failure as part of the query's result or use a Salsa accumul
|
||||
|
||||
## Tracing in tests
|
||||
|
||||
You can use `ruff_db::testing::setup_logging` or `ruff_db::testing::setup_logging_with_filter` to set up logging in
|
||||
tests.
|
||||
You can use `ruff_db::testing::setup_logging` or `ruff_db::testing::setup_logging_with_filter` to set up logging in tests.
|
||||
|
||||
```rust
|
||||
use ruff_db::testing::setup_logging;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let _logging = setup_logging();
|
||||
let _logging = setup_logging();
|
||||
|
||||
tracing::info!("This message will be printed to stderr");
|
||||
tracing::info!("This message will be printed to stderr");
|
||||
}
|
||||
```
|
||||
|
||||
Note: Most test runners capture stderr and only show its output when a test fails.
|
||||
|
||||
Note also that `setup_logging` only sets up logging for the current thread because
|
||||
[`set_global_default`](https://docs.rs/tracing/latest/tracing/subscriber/fn.set_global_default.html) can only be
|
||||
Note also that `setup_logging` only sets up logging for the current thread because [`set_global_default`](https://docs.rs/tracing/latest/tracing/subscriber/fn.set_global_default.html) can only be
|
||||
called **once**.
|
||||
|
||||
## Release builds
|
||||
@@ -114,11 +103,10 @@ called **once**.
|
||||
|
||||
## Profiling
|
||||
|
||||
ty generates a folded stack trace to the current directory named `tracing.folded` when setting the environment variable
|
||||
`TY_LOG_PROFILE` to `1` or `true`.
|
||||
Red Knot generates a folded stack trace to the current directory named `tracing.folded` when setting the environment variable `RED_KNOT_LOG_PROFILE` to `1` or `true`.
|
||||
|
||||
```bash
|
||||
TY_LOG_PROFILE=1 ty -- --current-directory=../test -vvv
|
||||
RED_KNOT_LOG_PROFILE=1 red_knot -- --current-directory=../test -vvv
|
||||
```
|
||||
|
||||
You can convert the textual representation into a visual one using `inferno`.
|
||||
@@ -1,14 +1,18 @@
|
||||
use crate::logging::Verbosity;
|
||||
use crate::python_version::PythonVersion;
|
||||
use clap::{ArgAction, ArgMatches, Error, Parser};
|
||||
use red_knot_project::metadata::options::{EnvironmentOptions, Options, TerminalOptions};
|
||||
use red_knot_project::metadata::value::{RangedValue, RelativePathBuf};
|
||||
use red_knot_python_semantic::lint;
|
||||
use ruff_db::system::SystemPathBuf;
|
||||
use ty_project::metadata::options::{EnvironmentOptions, Options, TerminalOptions};
|
||||
use ty_project::metadata::value::{RangedValue, RelativePathBuf};
|
||||
use ty_python_semantic::lint;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(author, name = "ty", about = "An extremely fast Python type checker.")]
|
||||
#[command(long_version = crate::version::version())]
|
||||
#[command(
|
||||
author,
|
||||
name = "red-knot",
|
||||
about = "An extremely fast Python type checker."
|
||||
)]
|
||||
#[command(version)]
|
||||
pub(crate) struct Args {
|
||||
#[command(subcommand)]
|
||||
pub(crate) command: Command,
|
||||
@@ -22,12 +26,8 @@ pub(crate) enum Command {
|
||||
/// Start the language server
|
||||
Server,
|
||||
|
||||
/// Display ty's version
|
||||
/// Display Red Knot's version
|
||||
Version,
|
||||
|
||||
/// Generate shell completion
|
||||
#[clap(hide = true)]
|
||||
GenerateShellCompletion { shell: clap_complete_command::Shell },
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
@@ -48,11 +48,11 @@ pub(crate) struct CheckCommand {
|
||||
#[arg(long, value_name = "PROJECT")]
|
||||
pub(crate) project: Option<SystemPathBuf>,
|
||||
|
||||
/// Path to the Python installation from which ty resolves type information and third-party dependencies.
|
||||
/// Path to the Python installation from which Red Knot resolves type information and third-party dependencies.
|
||||
///
|
||||
/// If not specified, ty will look at the `VIRTUAL_ENV` environment variable.
|
||||
/// If not specified, Red Knot will look at the `VIRTUAL_ENV` environment variable.
|
||||
///
|
||||
/// ty will search in the path's `site-packages` directories for type information and
|
||||
/// Red Knot will search in the path's `site-packages` directories for type information and
|
||||
/// third-party imports.
|
||||
///
|
||||
/// This option is commonly used to specify the path to a virtual environment.
|
||||
@@ -105,19 +105,6 @@ pub(crate) struct CheckCommand {
|
||||
/// Watch files for changes and recheck files related to the changed files.
|
||||
#[arg(long, short = 'W')]
|
||||
pub(crate) watch: bool,
|
||||
|
||||
/// Respect file exclusions via `.gitignore` and other standard ignore files.
|
||||
/// Use `--no-respect-gitignore` to disable.
|
||||
#[arg(
|
||||
long,
|
||||
overrides_with("no_respect_ignore_files"),
|
||||
help_heading = "File selection",
|
||||
default_missing_value = "true",
|
||||
num_args = 0..1
|
||||
)]
|
||||
respect_ignore_files: Option<bool>,
|
||||
#[clap(long, overrides_with("respect_ignore_files"), hide = true)]
|
||||
no_respect_ignore_files: bool,
|
||||
}
|
||||
|
||||
impl CheckCommand {
|
||||
@@ -133,13 +120,6 @@ impl CheckCommand {
|
||||
)
|
||||
};
|
||||
|
||||
// --no-respect-gitignore defaults to false and is set true by CLI flag. If passed, override config file
|
||||
// Otherwise, only pass this through if explicitly set (don't default to anything here to
|
||||
// make sure that doesn't take precedence over an explicitly-set config file value)
|
||||
let respect_ignore_files = self
|
||||
.no_respect_ignore_files
|
||||
.then_some(false)
|
||||
.or(self.respect_ignore_files);
|
||||
Options {
|
||||
environment: Some(EnvironmentOptions {
|
||||
python_version: self
|
||||
@@ -164,7 +144,6 @@ impl CheckCommand {
|
||||
error_on_warning: self.error_on_warning,
|
||||
}),
|
||||
rules,
|
||||
respect_ignore_files,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Sets up logging for ty
|
||||
//! Sets up logging for Red Knot
|
||||
|
||||
use anyhow::Context;
|
||||
use colored::Colorize;
|
||||
@@ -42,14 +42,14 @@ impl Verbosity {
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub(crate) enum VerbosityLevel {
|
||||
/// Default output level. Only shows Ruff and ty events up to the [`WARN`](tracing::Level::WARN).
|
||||
/// Default output level. Only shows Ruff and Red Knot events up to the [`WARN`](tracing::Level::WARN).
|
||||
Default,
|
||||
|
||||
/// Enables verbose output. Emits Ruff and ty events up to the [`INFO`](tracing::Level::INFO).
|
||||
/// Enables verbose output. Emits Ruff and Red Knot events up to the [`INFO`](tracing::Level::INFO).
|
||||
/// Corresponds to `-v`.
|
||||
Verbose,
|
||||
|
||||
/// Enables a more verbose tracing format and emits Ruff and ty events up to [`DEBUG`](tracing::Level::DEBUG).
|
||||
/// Enables a more verbose tracing format and emits Ruff and Red Knot events up to [`DEBUG`](tracing::Level::DEBUG).
|
||||
/// Corresponds to `-vv`
|
||||
ExtraVerbose,
|
||||
|
||||
@@ -79,11 +79,11 @@ impl VerbosityLevel {
|
||||
pub(crate) fn setup_tracing(level: VerbosityLevel) -> anyhow::Result<TracingGuard> {
|
||||
use tracing_subscriber::prelude::*;
|
||||
|
||||
// The `TY_LOG` environment variable overrides the default log level.
|
||||
let filter = if let Ok(log_env_variable) = std::env::var("TY_LOG") {
|
||||
// The `RED_KNOT_LOG` environment variable overrides the default log level.
|
||||
let filter = if let Ok(log_env_variable) = std::env::var("RED_KNOT_LOG") {
|
||||
EnvFilter::builder()
|
||||
.parse(log_env_variable)
|
||||
.context("Failed to parse directives specified in TY_LOG environment variable.")?
|
||||
.context("Failed to parse directives specified in RED_KNOT_LOG environment variable.")?
|
||||
} else {
|
||||
match level {
|
||||
VerbosityLevel::Default => {
|
||||
@@ -93,9 +93,9 @@ pub(crate) fn setup_tracing(level: VerbosityLevel) -> anyhow::Result<TracingGuar
|
||||
level => {
|
||||
let level_filter = level.level_filter();
|
||||
|
||||
// Show info|debug|trace events, but allow `TY_LOG` to override
|
||||
// Show info|debug|trace events, but allow `RED_KNOT_LOG` to override
|
||||
let filter = EnvFilter::default().add_directive(
|
||||
format!("ty={level_filter}")
|
||||
format!("red_knot={level_filter}")
|
||||
.parse()
|
||||
.expect("Hardcoded directive to be valid"),
|
||||
);
|
||||
@@ -131,7 +131,7 @@ pub(crate) fn setup_tracing(level: VerbosityLevel) -> anyhow::Result<TracingGuar
|
||||
} else {
|
||||
let subscriber = registry.with(
|
||||
tracing_subscriber::fmt::layer()
|
||||
.event_format(TyFormat {
|
||||
.event_format(RedKnotFormat {
|
||||
display_level: true,
|
||||
display_timestamp: level.is_extra_verbose(),
|
||||
show_spans: false,
|
||||
@@ -147,7 +147,7 @@ pub(crate) fn setup_tracing(level: VerbosityLevel) -> anyhow::Result<TracingGuar
|
||||
})
|
||||
}
|
||||
|
||||
#[expect(clippy::type_complexity)]
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn setup_profile<S>() -> (
|
||||
Option<tracing_flame::FlameLayer<S, BufWriter<File>>>,
|
||||
Option<tracing_flame::FlushGuard<BufWriter<File>>>,
|
||||
@@ -155,7 +155,7 @@ fn setup_profile<S>() -> (
|
||||
where
|
||||
S: Subscriber + for<'span> LookupSpan<'span>,
|
||||
{
|
||||
if let Ok("1" | "true") = std::env::var("TY_LOG_PROFILE").as_deref() {
|
||||
if let Ok("1" | "true") = std::env::var("RED_KNOT_LOG_PROFILE").as_deref() {
|
||||
let (layer, guard) = tracing_flame::FlameLayer::with_file("tracing.folded")
|
||||
.expect("Flame layer to be created");
|
||||
(Some(layer), Some(guard))
|
||||
@@ -168,14 +168,14 @@ pub(crate) struct TracingGuard {
|
||||
_flame_guard: Option<tracing_flame::FlushGuard<BufWriter<File>>>,
|
||||
}
|
||||
|
||||
struct TyFormat {
|
||||
struct RedKnotFormat {
|
||||
display_timestamp: bool,
|
||||
display_level: bool,
|
||||
show_spans: bool,
|
||||
}
|
||||
|
||||
/// See <https://docs.rs/tracing-subscriber/0.3.18/src/tracing_subscriber/fmt/format/mod.rs.html#1026-1156>
|
||||
impl<S, N> FormatEvent<S, N> for TyFormat
|
||||
impl<S, N> FormatEvent<S, N> for RedKnotFormat
|
||||
where
|
||||
S: Subscriber + for<'a> LookupSpan<'a>,
|
||||
N: for<'a> FormatFields<'a> + 'static,
|
||||
@@ -7,37 +7,34 @@ use std::sync::Mutex;
|
||||
use crate::args::{Args, CheckCommand, Command, TerminalColor};
|
||||
use crate::logging::setup_tracing;
|
||||
use anyhow::{anyhow, Context};
|
||||
use clap::{CommandFactory, Parser};
|
||||
use clap::Parser;
|
||||
use colored::Colorize;
|
||||
use crossbeam::channel as crossbeam_channel;
|
||||
use rayon::ThreadPoolBuilder;
|
||||
use red_knot_project::metadata::options::Options;
|
||||
use red_knot_project::watch::ProjectWatcher;
|
||||
use red_knot_project::{watch, Db};
|
||||
use red_knot_project::{ProjectDatabase, ProjectMetadata};
|
||||
use red_knot_server::run_server;
|
||||
use ruff_db::diagnostic::{Diagnostic, DisplayDiagnosticConfig, Severity};
|
||||
use ruff_db::max_parallelism;
|
||||
use ruff_db::system::{OsSystem, SystemPath, SystemPathBuf};
|
||||
use salsa::plumbing::ZalsaDatabase;
|
||||
use ty_project::metadata::options::Options;
|
||||
use ty_project::watch::ProjectWatcher;
|
||||
use ty_project::{watch, Db};
|
||||
use ty_project::{ProjectDatabase, ProjectMetadata};
|
||||
use ty_server::run_server;
|
||||
|
||||
mod args;
|
||||
mod logging;
|
||||
mod python_version;
|
||||
mod version;
|
||||
|
||||
#[allow(clippy::print_stdout, clippy::unnecessary_wraps, clippy::print_stderr)]
|
||||
pub fn main() -> ExitStatus {
|
||||
setup_rayon();
|
||||
|
||||
run().unwrap_or_else(|error| {
|
||||
use std::io::Write;
|
||||
|
||||
// Use `writeln` instead of `eprintln` to avoid panicking when the stderr pipe is broken.
|
||||
let mut stderr = std::io::stderr().lock();
|
||||
|
||||
// This communicates that this isn't a linter error but ty itself hard-errored for
|
||||
// This communicates that this isn't a linter error but Red Knot itself hard-errored for
|
||||
// some reason (e.g. failed to resolve the configuration)
|
||||
writeln!(stderr, "{}", "ty failed".red().bold()).ok();
|
||||
writeln!(stderr, "{}", "Red Knot failed".red().bold()).ok();
|
||||
// Currently we generally only see one error, but e.g. with io errors when resolving
|
||||
// the configuration it is help to chain errors ("resolving configuration failed" ->
|
||||
// "failed to read file: subdir/pyproject.toml")
|
||||
@@ -68,17 +65,13 @@ fn run() -> anyhow::Result<ExitStatus> {
|
||||
Command::Server => run_server().map(|()| ExitStatus::Success),
|
||||
Command::Check(check_args) => run_check(check_args),
|
||||
Command::Version => version().map(|()| ExitStatus::Success),
|
||||
Command::GenerateShellCompletion { shell } => {
|
||||
shell.generate(&mut Args::command(), &mut stdout());
|
||||
Ok(ExitStatus::Success)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn version() -> Result<()> {
|
||||
let mut stdout = BufWriter::new(io::stdout().lock());
|
||||
let version_info = crate::version::version();
|
||||
writeln!(stdout, "ty {}", &version_info)?;
|
||||
writeln!(stdout, "red knot {}", &version_info)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -97,7 +90,7 @@ fn run_check(args: CheckCommand) -> anyhow::Result<ExitStatus> {
|
||||
SystemPathBuf::from_path_buf(cwd)
|
||||
.map_err(|path| {
|
||||
anyhow!(
|
||||
"The current working directory `{}` contains non-Unicode characters. ty only supports Unicode paths.",
|
||||
"The current working directory `{}` contains non-Unicode characters. Red Knot only supports Unicode paths.",
|
||||
path.display()
|
||||
)
|
||||
})?
|
||||
@@ -176,12 +169,8 @@ pub enum ExitStatus {
|
||||
/// Checking was successful but there were errors.
|
||||
Failure = 1,
|
||||
|
||||
/// Checking failed due to an invocation error (e.g. the current directory no longer exists, incorrect CLI arguments, ...)
|
||||
/// Checking failed.
|
||||
Error = 2,
|
||||
|
||||
/// Internal ty error (panic, or any other error that isn't due to the user using the
|
||||
/// program incorrectly or transient environment errors).
|
||||
InternalError = 101,
|
||||
}
|
||||
|
||||
impl Termination for ExitStatus {
|
||||
@@ -257,16 +246,11 @@ impl MainLoop {
|
||||
// Spawn a new task that checks the project. This needs to be done in a separate thread
|
||||
// to prevent blocking the main loop here.
|
||||
rayon::spawn(move || {
|
||||
match db.check() {
|
||||
Ok(result) => {
|
||||
// Send the result back to the main loop for printing.
|
||||
sender
|
||||
.send(MainLoopMessage::CheckCompleted { result, revision })
|
||||
.unwrap();
|
||||
}
|
||||
Err(cancelled) => {
|
||||
tracing::debug!("Check has been cancelled: {cancelled:?}");
|
||||
}
|
||||
if let Ok(result) = db.check() {
|
||||
// Send the result back to the main loop for printing.
|
||||
sender
|
||||
.send(MainLoopMessage::CheckCompleted { result, revision })
|
||||
.unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -280,6 +264,12 @@ impl MainLoop {
|
||||
.format(terminal_settings.output_format)
|
||||
.color(colored::control::SHOULD_COLORIZE.should_colorize());
|
||||
|
||||
let min_error_severity = if terminal_settings.error_on_warning {
|
||||
Severity::Warning
|
||||
} else {
|
||||
Severity::Error
|
||||
};
|
||||
|
||||
if check_revision == revision {
|
||||
if db.project().files(db).is_empty() {
|
||||
tracing::warn!("No python files found under the given path(s)");
|
||||
@@ -294,13 +284,13 @@ impl MainLoop {
|
||||
return Ok(ExitStatus::Success);
|
||||
}
|
||||
} else {
|
||||
let mut max_severity = Severity::Info;
|
||||
let mut failed = false;
|
||||
let diagnostics_count = result.len();
|
||||
|
||||
for diagnostic in result {
|
||||
write!(stdout, "{}", diagnostic.display(db, &display_config))?;
|
||||
|
||||
max_severity = max_severity.max(diagnostic.severity());
|
||||
failed |= diagnostic.severity() >= min_error_severity;
|
||||
}
|
||||
|
||||
writeln!(
|
||||
@@ -310,22 +300,11 @@ impl MainLoop {
|
||||
if diagnostics_count > 1 { "s" } else { "" }
|
||||
)?;
|
||||
|
||||
if max_severity.is_fatal() {
|
||||
tracing::warn!("A fatal error occurred while checking some files. Not all project files were analyzed. See the diagnostics list above for details.");
|
||||
}
|
||||
|
||||
if self.watcher.is_none() {
|
||||
return Ok(match max_severity {
|
||||
Severity::Info => ExitStatus::Success,
|
||||
Severity::Warning => {
|
||||
if terminal_settings.error_on_warning {
|
||||
ExitStatus::Failure
|
||||
} else {
|
||||
ExitStatus::Success
|
||||
}
|
||||
}
|
||||
Severity::Error => ExitStatus::Failure,
|
||||
Severity::Fatal => ExitStatus::InternalError,
|
||||
return Ok(if failed {
|
||||
ExitStatus::Failure
|
||||
} else {
|
||||
ExitStatus::Success
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -404,17 +383,3 @@ fn set_colored_override(color: Option<TerminalColor>) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes the global rayon thread pool to never use more than `TY_MAX_PARALLELISM` threads.
|
||||
fn setup_rayon() {
|
||||
ThreadPoolBuilder::default()
|
||||
.num_threads(max_parallelism().get())
|
||||
// Use a reasonably large stack size to avoid running into stack overflows too easily. The
|
||||
// size was chosen in such a way as to still be able to handle large expressions involving
|
||||
// binary operators (x + x + … + x) both during the AST walk in semantic index building as
|
||||
// well as during type checking. Using this stack size, we can handle handle expressions
|
||||
// that are several times larger than the corresponding limits in existing type checkers.
|
||||
.stack_size(16 * 1024 * 1024)
|
||||
.build_global()
|
||||
.unwrap();
|
||||
}
|
||||
@@ -1,17 +1,16 @@
|
||||
//! Code for representing ty's release version number.
|
||||
//! Code for representing Red Knot's release version number.
|
||||
use std::fmt;
|
||||
|
||||
/// Information about the git repository where ty was built from.
|
||||
/// Information about the git repository where Red Knot was built from.
|
||||
pub(crate) struct CommitInfo {
|
||||
short_commit_hash: String,
|
||||
commit_date: String,
|
||||
commits_since_last_tag: u32,
|
||||
last_tag: Option<String>,
|
||||
}
|
||||
|
||||
/// ty's version.
|
||||
/// Red Knot's version.
|
||||
pub(crate) struct VersionInfo {
|
||||
/// ty's version, such as "0.5.1"
|
||||
/// Red Knot's version, such as "0.5.1"
|
||||
version: String,
|
||||
/// Information about the git commit we may have been built from.
|
||||
///
|
||||
@@ -35,13 +34,7 @@ impl fmt::Display for VersionInfo {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<VersionInfo> for clap::builder::Str {
|
||||
fn from(val: VersionInfo) -> Self {
|
||||
val.to_string().into()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns information about ty's version.
|
||||
/// Returns information about Red Knot's version.
|
||||
pub(crate) fn version() -> VersionInfo {
|
||||
// Environment variables are only read at compile-time
|
||||
macro_rules! option_env_str {
|
||||
@@ -50,30 +43,18 @@ pub(crate) fn version() -> VersionInfo {
|
||||
};
|
||||
}
|
||||
|
||||
// Commit info is pulled from git and set by `build.rs`
|
||||
let commit_info = option_env_str!("TY_COMMIT_SHORT_HASH").map(|short_commit_hash| CommitInfo {
|
||||
short_commit_hash,
|
||||
commit_date: option_env_str!("TY_COMMIT_DATE").unwrap(),
|
||||
commits_since_last_tag: option_env_str!("TY_LAST_TAG_DISTANCE")
|
||||
.as_deref()
|
||||
.map_or(0, |value| value.parse::<u32>().unwrap_or(0)),
|
||||
last_tag: option_env_str!("TY_LAST_TAG"),
|
||||
});
|
||||
// This version is pulled from Cargo.toml and set by Cargo
|
||||
let version = option_env_str!("CARGO_PKG_VERSION").unwrap();
|
||||
|
||||
// The version is pulled from `dist-workspace.toml` and set by `build.rs`
|
||||
let version = option_env_str!("TY_VERSION").unwrap_or_else(|| {
|
||||
// If missing, using the last tag
|
||||
commit_info
|
||||
.as_ref()
|
||||
.and_then(|info| {
|
||||
info.last_tag.as_ref().map(|tag| {
|
||||
tag.strip_prefix("v")
|
||||
.map(std::string::ToString::to_string)
|
||||
.unwrap_or(tag.clone())
|
||||
})
|
||||
})
|
||||
.unwrap_or("unknown".to_string())
|
||||
});
|
||||
// Commit info is pulled from git and set by `build.rs`
|
||||
let commit_info =
|
||||
option_env_str!("RED_KNOT_COMMIT_SHORT_HASH").map(|short_commit_hash| CommitInfo {
|
||||
short_commit_hash,
|
||||
commit_date: option_env_str!("RED_KNOT_COMMIT_DATE").unwrap(),
|
||||
commits_since_last_tag: option_env_str!("RED_KNOT_LAST_TAG_DISTANCE")
|
||||
.as_deref()
|
||||
.map_or(0, |value| value.parse::<u32>().unwrap_or(0)),
|
||||
});
|
||||
|
||||
VersionInfo {
|
||||
version,
|
||||
@@ -104,7 +85,6 @@ mod tests {
|
||||
short_commit_hash: "53b0f5d92".to_string(),
|
||||
commit_date: "2023-10-19".to_string(),
|
||||
commits_since_last_tag: 0,
|
||||
last_tag: None,
|
||||
}),
|
||||
};
|
||||
assert_snapshot!(version, @"0.0.0 (53b0f5d92 2023-10-19)");
|
||||
@@ -118,7 +98,6 @@ mod tests {
|
||||
short_commit_hash: "53b0f5d92".to_string(),
|
||||
commit_date: "2023-10-19".to_string(),
|
||||
commits_since_last_tag: 24,
|
||||
last_tag: None,
|
||||
}),
|
||||
};
|
||||
assert_snapshot!(version, @"0.0.0+24 (53b0f5d92 2023-10-19)");
|
||||
@@ -1,121 +1,10 @@
|
||||
use anyhow::Context;
|
||||
use insta::internals::SettingsBindDropGuard;
|
||||
use insta_cmd::{assert_cmd_snapshot, get_cargo_bin};
|
||||
use std::fmt::Write;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
use tempfile::TempDir;
|
||||
|
||||
#[test]
|
||||
fn test_run_in_sub_directory() -> anyhow::Result<()> {
|
||||
let case = TestCase::with_files([("test.py", "~"), ("subdir/nothing", "")])?;
|
||||
assert_cmd_snapshot!(case.command().current_dir(case.root().join("subdir")).arg(".."), @r"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
error: invalid-syntax
|
||||
--> <temp_dir>/test.py:1:2
|
||||
|
|
||||
1 | ~
|
||||
| ^ Expected an expression
|
||||
|
|
||||
|
||||
Found 1 diagnostic
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_include_hidden_files_by_default() -> anyhow::Result<()> {
|
||||
let case = TestCase::with_files([(".test.py", "~")])?;
|
||||
assert_cmd_snapshot!(case.command(), @r"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
error: invalid-syntax
|
||||
--> .test.py:1:2
|
||||
|
|
||||
1 | ~
|
||||
| ^ Expected an expression
|
||||
|
|
||||
|
||||
Found 1 diagnostic
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_respect_ignore_files() -> anyhow::Result<()> {
|
||||
// First test that the default option works correctly (the file is skipped)
|
||||
let case = TestCase::with_files([(".ignore", "test.py"), ("test.py", "~")])?;
|
||||
assert_cmd_snapshot!(case.command(), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
All checks passed!
|
||||
|
||||
----- stderr -----
|
||||
WARN No python files found under the given path(s)
|
||||
");
|
||||
|
||||
// Test that we can set to false via CLI
|
||||
assert_cmd_snapshot!(case.command().arg("--no-respect-ignore-files"), @r"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
error: invalid-syntax
|
||||
--> test.py:1:2
|
||||
|
|
||||
1 | ~
|
||||
| ^ Expected an expression
|
||||
|
|
||||
|
||||
Found 1 diagnostic
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
|
||||
// Test that we can set to false via config file
|
||||
case.write_file("ty.toml", "respect-ignore-files = false")?;
|
||||
assert_cmd_snapshot!(case.command(), @r"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
error: invalid-syntax
|
||||
--> test.py:1:2
|
||||
|
|
||||
1 | ~
|
||||
| ^ Expected an expression
|
||||
|
|
||||
|
||||
Found 1 diagnostic
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
|
||||
// Ensure CLI takes precedence
|
||||
case.write_file("ty.toml", "respect-ignore-files = true")?;
|
||||
assert_cmd_snapshot!(case.command().arg("--no-respect-ignore-files"), @r"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
error: invalid-syntax
|
||||
--> test.py:1:2
|
||||
|
|
||||
1 | ~
|
||||
| ^ Expected an expression
|
||||
|
|
||||
|
||||
Found 1 diagnostic
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
Ok(())
|
||||
}
|
||||
/// Specifying an option on the CLI should take precedence over the same setting in the
|
||||
/// project's configuration. Here, this is tested for the Python version.
|
||||
#[test]
|
||||
@@ -124,7 +13,7 @@ fn config_override_python_version() -> anyhow::Result<()> {
|
||||
(
|
||||
"pyproject.toml",
|
||||
r#"
|
||||
[tool.ty.environment]
|
||||
[tool.knot.environment]
|
||||
python-version = "3.11"
|
||||
"#,
|
||||
),
|
||||
@@ -143,14 +32,13 @@ fn config_override_python_version() -> anyhow::Result<()> {
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
error: lint:unresolved-attribute: Type `<module 'sys'>` has no attribute `last_exc`
|
||||
--> test.py:5:7
|
||||
error: lint:unresolved-attribute
|
||||
--> <temp_dir>/test.py:5:7
|
||||
|
|
||||
4 | # Access `sys.last_exc` that was only added in Python 3.12
|
||||
5 | print(sys.last_exc)
|
||||
| ^^^^^^^^^^^^
|
||||
| ^^^^^^^^^^^^ Type `<module 'sys'>` has no attribute `last_exc`
|
||||
|
|
||||
info: `lint:unresolved-attribute` is enabled by default
|
||||
|
||||
Found 1 diagnostic
|
||||
|
||||
@@ -176,7 +64,7 @@ fn config_override_python_platform() -> anyhow::Result<()> {
|
||||
(
|
||||
"pyproject.toml",
|
||||
r#"
|
||||
[tool.ty.environment]
|
||||
[tool.knot.environment]
|
||||
python-platform = "linux"
|
||||
"#,
|
||||
),
|
||||
@@ -196,7 +84,7 @@ fn config_override_python_platform() -> anyhow::Result<()> {
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
info: revealed-type: Revealed type
|
||||
--> test.py:5:1
|
||||
--> <temp_dir>/test.py:5:1
|
||||
|
|
||||
3 | from typing_extensions import reveal_type
|
||||
4 |
|
||||
@@ -214,7 +102,7 @@ fn config_override_python_platform() -> anyhow::Result<()> {
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
info: revealed-type: Revealed type
|
||||
--> test.py:5:1
|
||||
--> <temp_dir>/test.py:5:1
|
||||
|
|
||||
3 | from typing_extensions import reveal_type
|
||||
4 |
|
||||
@@ -251,7 +139,7 @@ fn cli_arguments_are_relative_to_the_current_directory() -> anyhow::Result<()> {
|
||||
(
|
||||
"pyproject.toml",
|
||||
r#"
|
||||
[tool.ty.environment]
|
||||
[tool.knot.environment]
|
||||
python-version = "3.11"
|
||||
"#,
|
||||
),
|
||||
@@ -277,15 +165,14 @@ fn cli_arguments_are_relative_to_the_current_directory() -> anyhow::Result<()> {
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
error: lint:unresolved-import: Cannot resolve imported module `utils`
|
||||
--> test.py:2:6
|
||||
error: lint:unresolved-import
|
||||
--> <temp_dir>/child/test.py:2:6
|
||||
|
|
||||
2 | from utils import add
|
||||
| ^^^^^
|
||||
| ^^^^^ Cannot resolve import `utils`
|
||||
3 |
|
||||
4 | stat = add(10, 15)
|
||||
|
|
||||
info: `lint:unresolved-import` is enabled by default
|
||||
|
||||
Found 1 diagnostic
|
||||
|
||||
@@ -323,7 +210,7 @@ fn paths_in_configuration_files_are_relative_to_the_project_root() -> anyhow::Re
|
||||
(
|
||||
"pyproject.toml",
|
||||
r#"
|
||||
[tool.ty.environment]
|
||||
[tool.knot.environment]
|
||||
python-version = "3.11"
|
||||
extra-paths = ["libs"]
|
||||
"#,
|
||||
@@ -378,25 +265,23 @@ fn configuration_rule_severity() -> anyhow::Result<()> {
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
error: lint:division-by-zero: Cannot divide object of type `Literal[4]` by zero
|
||||
--> test.py:2:5
|
||||
error: lint:division-by-zero
|
||||
--> <temp_dir>/test.py:2:5
|
||||
|
|
||||
2 | y = 4 / 0
|
||||
| ^^^^^
|
||||
| ^^^^^ Cannot divide object of type `Literal[4]` by zero
|
||||
3 |
|
||||
4 | for a in range(0, int(y)):
|
||||
|
|
||||
info: `lint:division-by-zero` is enabled by default
|
||||
|
||||
warning: lint:possibly-unresolved-reference: Name `x` used when possibly not defined
|
||||
--> test.py:7:7
|
||||
warning: lint:possibly-unresolved-reference
|
||||
--> <temp_dir>/test.py:7:7
|
||||
|
|
||||
5 | x = a
|
||||
6 |
|
||||
7 | print(x) # possibly-unresolved-reference
|
||||
| ^
|
||||
| ^ Name `x` used when possibly not defined
|
||||
|
|
||||
info: `lint:possibly-unresolved-reference` is enabled by default
|
||||
|
||||
Found 2 diagnostics
|
||||
|
||||
@@ -406,7 +291,7 @@ fn configuration_rule_severity() -> anyhow::Result<()> {
|
||||
case.write_file(
|
||||
"pyproject.toml",
|
||||
r#"
|
||||
[tool.ty.rules]
|
||||
[tool.knot.rules]
|
||||
division-by-zero = "warn" # demote to warn
|
||||
possibly-unresolved-reference = "ignore"
|
||||
"#,
|
||||
@@ -416,15 +301,14 @@ fn configuration_rule_severity() -> anyhow::Result<()> {
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
warning: lint:division-by-zero: Cannot divide object of type `Literal[4]` by zero
|
||||
--> test.py:2:5
|
||||
warning: lint:division-by-zero
|
||||
--> <temp_dir>/test.py:2:5
|
||||
|
|
||||
2 | y = 4 / 0
|
||||
| ^^^^^
|
||||
| ^^^^^ Cannot divide object of type `Literal[4]` by zero
|
||||
3 |
|
||||
4 | for a in range(0, int(y)):
|
||||
|
|
||||
info: `lint:division-by-zero` was selected in the configuration file
|
||||
|
||||
Found 1 diagnostic
|
||||
|
||||
@@ -457,37 +341,34 @@ fn cli_rule_severity() -> anyhow::Result<()> {
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
error: lint:unresolved-import: Cannot resolve imported module `does_not_exit`
|
||||
--> test.py:2:8
|
||||
error: lint:unresolved-import
|
||||
--> <temp_dir>/test.py:2:8
|
||||
|
|
||||
2 | import does_not_exit
|
||||
| ^^^^^^^^^^^^^
|
||||
| ^^^^^^^^^^^^^ Cannot resolve import `does_not_exit`
|
||||
3 |
|
||||
4 | y = 4 / 0
|
||||
|
|
||||
info: `lint:unresolved-import` is enabled by default
|
||||
|
||||
error: lint:division-by-zero: Cannot divide object of type `Literal[4]` by zero
|
||||
--> test.py:4:5
|
||||
error: lint:division-by-zero
|
||||
--> <temp_dir>/test.py:4:5
|
||||
|
|
||||
2 | import does_not_exit
|
||||
3 |
|
||||
4 | y = 4 / 0
|
||||
| ^^^^^
|
||||
| ^^^^^ Cannot divide object of type `Literal[4]` by zero
|
||||
5 |
|
||||
6 | for a in range(0, int(y)):
|
||||
|
|
||||
info: `lint:division-by-zero` is enabled by default
|
||||
|
||||
warning: lint:possibly-unresolved-reference: Name `x` used when possibly not defined
|
||||
--> test.py:9:7
|
||||
warning: lint:possibly-unresolved-reference
|
||||
--> <temp_dir>/test.py:9:7
|
||||
|
|
||||
7 | x = a
|
||||
8 |
|
||||
9 | print(x) # possibly-unresolved-reference
|
||||
| ^
|
||||
| ^ Name `x` used when possibly not defined
|
||||
|
|
||||
info: `lint:possibly-unresolved-reference` is enabled by default
|
||||
|
||||
Found 3 diagnostics
|
||||
|
||||
@@ -507,27 +388,25 @@ fn cli_rule_severity() -> anyhow::Result<()> {
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
warning: lint:unresolved-import: Cannot resolve imported module `does_not_exit`
|
||||
--> test.py:2:8
|
||||
warning: lint:unresolved-import
|
||||
--> <temp_dir>/test.py:2:8
|
||||
|
|
||||
2 | import does_not_exit
|
||||
| ^^^^^^^^^^^^^
|
||||
| ^^^^^^^^^^^^^ Cannot resolve import `does_not_exit`
|
||||
3 |
|
||||
4 | y = 4 / 0
|
||||
|
|
||||
info: `lint:unresolved-import` was selected on the command line
|
||||
|
||||
warning: lint:division-by-zero: Cannot divide object of type `Literal[4]` by zero
|
||||
--> test.py:4:5
|
||||
warning: lint:division-by-zero
|
||||
--> <temp_dir>/test.py:4:5
|
||||
|
|
||||
2 | import does_not_exit
|
||||
3 |
|
||||
4 | y = 4 / 0
|
||||
| ^^^^^
|
||||
| ^^^^^ Cannot divide object of type `Literal[4]` by zero
|
||||
5 |
|
||||
6 | for a in range(0, int(y)):
|
||||
|
|
||||
info: `lint:division-by-zero` was selected on the command line
|
||||
|
||||
Found 2 diagnostics
|
||||
|
||||
@@ -560,25 +439,23 @@ fn cli_rule_severity_precedence() -> anyhow::Result<()> {
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
error: lint:division-by-zero: Cannot divide object of type `Literal[4]` by zero
|
||||
--> test.py:2:5
|
||||
error: lint:division-by-zero
|
||||
--> <temp_dir>/test.py:2:5
|
||||
|
|
||||
2 | y = 4 / 0
|
||||
| ^^^^^
|
||||
| ^^^^^ Cannot divide object of type `Literal[4]` by zero
|
||||
3 |
|
||||
4 | for a in range(0, int(y)):
|
||||
|
|
||||
info: `lint:division-by-zero` is enabled by default
|
||||
|
||||
warning: lint:possibly-unresolved-reference: Name `x` used when possibly not defined
|
||||
--> test.py:7:7
|
||||
warning: lint:possibly-unresolved-reference
|
||||
--> <temp_dir>/test.py:7:7
|
||||
|
|
||||
5 | x = a
|
||||
6 |
|
||||
7 | print(x) # possibly-unresolved-reference
|
||||
| ^
|
||||
| ^ Name `x` used when possibly not defined
|
||||
|
|
||||
info: `lint:possibly-unresolved-reference` is enabled by default
|
||||
|
||||
Found 2 diagnostics
|
||||
|
||||
@@ -599,15 +476,14 @@ fn cli_rule_severity_precedence() -> anyhow::Result<()> {
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
warning: lint:division-by-zero: Cannot divide object of type `Literal[4]` by zero
|
||||
--> test.py:2:5
|
||||
warning: lint:division-by-zero
|
||||
--> <temp_dir>/test.py:2:5
|
||||
|
|
||||
2 | y = 4 / 0
|
||||
| ^^^^^
|
||||
| ^^^^^ Cannot divide object of type `Literal[4]` by zero
|
||||
3 |
|
||||
4 | for a in range(0, int(y)):
|
||||
|
|
||||
info: `lint:division-by-zero` was selected on the command line
|
||||
|
||||
Found 1 diagnostic
|
||||
|
||||
@@ -618,14 +494,14 @@ fn cli_rule_severity_precedence() -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// ty warns about unknown rules specified in a configuration file
|
||||
/// Red Knot warns about unknown rules specified in a configuration file
|
||||
#[test]
|
||||
fn configuration_unknown_rules() -> anyhow::Result<()> {
|
||||
let case = TestCase::with_files([
|
||||
(
|
||||
"pyproject.toml",
|
||||
r#"
|
||||
[tool.ty.rules]
|
||||
[tool.knot.rules]
|
||||
division-by-zer = "warn" # incorrect rule name
|
||||
"#,
|
||||
),
|
||||
@@ -637,9 +513,9 @@ fn configuration_unknown_rules() -> anyhow::Result<()> {
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
warning: unknown-rule
|
||||
--> pyproject.toml:3:1
|
||||
--> <temp_dir>/pyproject.toml:3:1
|
||||
|
|
||||
2 | [tool.ty.rules]
|
||||
2 | [tool.knot.rules]
|
||||
3 | division-by-zer = "warn" # incorrect rule name
|
||||
| ^^^^^^^^^^^^^^^ Unknown lint rule `division-by-zer`
|
||||
|
|
||||
@@ -652,7 +528,7 @@ fn configuration_unknown_rules() -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// ty warns about unknown rules specified in a CLI argument
|
||||
/// Red Knot warns about unknown rules specified in a CLI argument
|
||||
#[test]
|
||||
fn cli_unknown_rules() -> anyhow::Result<()> {
|
||||
let case = TestCase::with_file("test.py", "print(10)")?;
|
||||
@@ -679,13 +555,12 @@ fn exit_code_only_warnings() -> anyhow::Result<()> {
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
warning: lint:unresolved-reference: Name `x` used when not defined
|
||||
--> test.py:1:7
|
||||
warning: lint:unresolved-reference
|
||||
--> <temp_dir>/test.py:1:7
|
||||
|
|
||||
1 | print(x) # [unresolved-reference]
|
||||
| ^
|
||||
| ^ Name `x` used when not defined
|
||||
|
|
||||
info: `lint:unresolved-reference` is enabled by default
|
||||
|
||||
Found 1 diagnostic
|
||||
|
||||
@@ -710,7 +585,7 @@ fn exit_code_only_info() -> anyhow::Result<()> {
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
info: revealed-type: Revealed type
|
||||
--> test.py:3:1
|
||||
--> <temp_dir>/test.py:3:1
|
||||
|
|
||||
2 | from typing_extensions import reveal_type
|
||||
3 | reveal_type(1)
|
||||
@@ -740,7 +615,7 @@ fn exit_code_only_info_and_error_on_warning_is_true() -> anyhow::Result<()> {
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
info: revealed-type: Revealed type
|
||||
--> test.py:3:1
|
||||
--> <temp_dir>/test.py:3:1
|
||||
|
|
||||
2 | from typing_extensions import reveal_type
|
||||
3 | reveal_type(1)
|
||||
@@ -763,13 +638,12 @@ fn exit_code_no_errors_but_error_on_warning_is_true() -> anyhow::Result<()> {
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
warning: lint:unresolved-reference: Name `x` used when not defined
|
||||
--> test.py:1:7
|
||||
warning: lint:unresolved-reference
|
||||
--> <temp_dir>/test.py:1:7
|
||||
|
|
||||
1 | print(x) # [unresolved-reference]
|
||||
| ^
|
||||
| ^ Name `x` used when not defined
|
||||
|
|
||||
info: `lint:unresolved-reference` is enabled by default
|
||||
|
||||
Found 1 diagnostic
|
||||
|
||||
@@ -784,7 +658,7 @@ fn exit_code_no_errors_but_error_on_warning_is_enabled_in_configuration() -> any
|
||||
let case = TestCase::with_files([
|
||||
("test.py", r"print(x) # [unresolved-reference]"),
|
||||
(
|
||||
"ty.toml",
|
||||
"knot.toml",
|
||||
r#"
|
||||
[terminal]
|
||||
error-on-warning = true
|
||||
@@ -796,13 +670,12 @@ fn exit_code_no_errors_but_error_on_warning_is_enabled_in_configuration() -> any
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
warning: lint:unresolved-reference: Name `x` used when not defined
|
||||
--> test.py:1:7
|
||||
warning: lint:unresolved-reference
|
||||
--> <temp_dir>/test.py:1:7
|
||||
|
|
||||
1 | print(x) # [unresolved-reference]
|
||||
| ^
|
||||
| ^ Name `x` used when not defined
|
||||
|
|
||||
info: `lint:unresolved-reference` is enabled by default
|
||||
|
||||
Found 1 diagnostic
|
||||
|
||||
@@ -826,23 +699,21 @@ fn exit_code_both_warnings_and_errors() -> anyhow::Result<()> {
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
warning: lint:unresolved-reference: Name `x` used when not defined
|
||||
--> test.py:2:7
|
||||
warning: lint:unresolved-reference
|
||||
--> <temp_dir>/test.py:2:7
|
||||
|
|
||||
2 | print(x) # [unresolved-reference]
|
||||
| ^
|
||||
| ^ Name `x` used when not defined
|
||||
3 | print(4[1]) # [non-subscriptable]
|
||||
|
|
||||
info: `lint:unresolved-reference` is enabled by default
|
||||
|
||||
error: lint:non-subscriptable: Cannot subscript object of type `Literal[4]` with no `__getitem__` method
|
||||
--> test.py:3:7
|
||||
error: lint:non-subscriptable
|
||||
--> <temp_dir>/test.py:3:7
|
||||
|
|
||||
2 | print(x) # [unresolved-reference]
|
||||
3 | print(4[1]) # [non-subscriptable]
|
||||
| ^
|
||||
| ^ Cannot subscript object of type `Literal[4]` with no `__getitem__` method
|
||||
|
|
||||
info: `lint:non-subscriptable` is enabled by default
|
||||
|
||||
Found 2 diagnostics
|
||||
|
||||
@@ -866,23 +737,21 @@ fn exit_code_both_warnings_and_errors_and_error_on_warning_is_true() -> anyhow::
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
warning: lint:unresolved-reference: Name `x` used when not defined
|
||||
--> test.py:2:7
|
||||
warning: lint:unresolved-reference
|
||||
--> <temp_dir>/test.py:2:7
|
||||
|
|
||||
2 | print(x) # [unresolved-reference]
|
||||
| ^
|
||||
| ^ Name `x` used when not defined
|
||||
3 | print(4[1]) # [non-subscriptable]
|
||||
|
|
||||
info: `lint:unresolved-reference` is enabled by default
|
||||
|
||||
error: lint:non-subscriptable: Cannot subscript object of type `Literal[4]` with no `__getitem__` method
|
||||
--> test.py:3:7
|
||||
error: lint:non-subscriptable
|
||||
--> <temp_dir>/test.py:3:7
|
||||
|
|
||||
2 | print(x) # [unresolved-reference]
|
||||
3 | print(4[1]) # [non-subscriptable]
|
||||
| ^
|
||||
| ^ Cannot subscript object of type `Literal[4]` with no `__getitem__` method
|
||||
|
|
||||
info: `lint:non-subscriptable` is enabled by default
|
||||
|
||||
Found 2 diagnostics
|
||||
|
||||
@@ -906,23 +775,21 @@ fn exit_code_exit_zero_is_true() -> anyhow::Result<()> {
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
warning: lint:unresolved-reference: Name `x` used when not defined
|
||||
--> test.py:2:7
|
||||
warning: lint:unresolved-reference
|
||||
--> <temp_dir>/test.py:2:7
|
||||
|
|
||||
2 | print(x) # [unresolved-reference]
|
||||
| ^
|
||||
| ^ Name `x` used when not defined
|
||||
3 | print(4[1]) # [non-subscriptable]
|
||||
|
|
||||
info: `lint:unresolved-reference` is enabled by default
|
||||
|
||||
error: lint:non-subscriptable: Cannot subscript object of type `Literal[4]` with no `__getitem__` method
|
||||
--> test.py:3:7
|
||||
error: lint:non-subscriptable
|
||||
--> <temp_dir>/test.py:3:7
|
||||
|
|
||||
2 | print(x) # [unresolved-reference]
|
||||
3 | print(4[1]) # [non-subscriptable]
|
||||
| ^
|
||||
| ^ Cannot subscript object of type `Literal[4]` with no `__getitem__` method
|
||||
|
|
||||
info: `lint:non-subscriptable` is enabled by default
|
||||
|
||||
Found 2 diagnostics
|
||||
|
||||
@@ -936,7 +803,7 @@ fn exit_code_exit_zero_is_true() -> anyhow::Result<()> {
|
||||
fn user_configuration() -> anyhow::Result<()> {
|
||||
let case = TestCase::with_files([
|
||||
(
|
||||
"project/ty.toml",
|
||||
"project/knot.toml",
|
||||
r#"
|
||||
[rules]
|
||||
division-by-zero = "warn"
|
||||
@@ -968,25 +835,23 @@ fn user_configuration() -> anyhow::Result<()> {
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
warning: lint:division-by-zero: Cannot divide object of type `Literal[4]` by zero
|
||||
--> main.py:2:5
|
||||
warning: lint:division-by-zero
|
||||
--> <temp_dir>/project/main.py:2:5
|
||||
|
|
||||
2 | y = 4 / 0
|
||||
| ^^^^^
|
||||
| ^^^^^ Cannot divide object of type `Literal[4]` by zero
|
||||
3 |
|
||||
4 | for a in range(0, int(y)):
|
||||
|
|
||||
info: `lint:division-by-zero` was selected in the configuration file
|
||||
|
||||
warning: lint:possibly-unresolved-reference: Name `x` used when possibly not defined
|
||||
--> main.py:7:7
|
||||
warning: lint:possibly-unresolved-reference
|
||||
--> <temp_dir>/project/main.py:7:7
|
||||
|
|
||||
5 | x = a
|
||||
6 |
|
||||
7 | print(x)
|
||||
| ^
|
||||
| ^ Name `x` used when possibly not defined
|
||||
|
|
||||
info: `lint:possibly-unresolved-reference` is enabled by default
|
||||
|
||||
Found 2 diagnostics
|
||||
|
||||
@@ -998,7 +863,7 @@ fn user_configuration() -> anyhow::Result<()> {
|
||||
// Changing the level for `division-by-zero` has no effect, because the project-level configuration
|
||||
// has higher precedence.
|
||||
case.write_file(
|
||||
config_directory.join("ty/ty.toml"),
|
||||
config_directory.join("knot/knot.toml"),
|
||||
r#"
|
||||
[rules]
|
||||
division-by-zero = "error"
|
||||
@@ -1012,25 +877,23 @@ fn user_configuration() -> anyhow::Result<()> {
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
warning: lint:division-by-zero: Cannot divide object of type `Literal[4]` by zero
|
||||
--> main.py:2:5
|
||||
warning: lint:division-by-zero
|
||||
--> <temp_dir>/project/main.py:2:5
|
||||
|
|
||||
2 | y = 4 / 0
|
||||
| ^^^^^
|
||||
| ^^^^^ Cannot divide object of type `Literal[4]` by zero
|
||||
3 |
|
||||
4 | for a in range(0, int(y)):
|
||||
|
|
||||
info: `lint:division-by-zero` was selected in the configuration file
|
||||
|
||||
error: lint:possibly-unresolved-reference: Name `x` used when possibly not defined
|
||||
--> main.py:7:7
|
||||
error: lint:possibly-unresolved-reference
|
||||
--> <temp_dir>/project/main.py:7:7
|
||||
|
|
||||
5 | x = a
|
||||
6 |
|
||||
7 | print(x)
|
||||
| ^
|
||||
| ^ Name `x` used when possibly not defined
|
||||
|
|
||||
info: `lint:possibly-unresolved-reference` was selected in the configuration file
|
||||
|
||||
Found 2 diagnostics
|
||||
|
||||
@@ -1072,31 +935,28 @@ fn check_specific_paths() -> anyhow::Result<()> {
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
error: lint:division-by-zero: Cannot divide object of type `Literal[4]` by zero
|
||||
--> project/main.py:2:5
|
||||
error: lint:unresolved-import
|
||||
--> <temp_dir>/project/tests/test_main.py:2:8
|
||||
|
|
||||
2 | import does_not_exist # error: unresolved-import
|
||||
| ^^^^^^^^^^^^^^ Cannot resolve import `does_not_exist`
|
||||
|
|
||||
|
||||
error: lint:division-by-zero
|
||||
--> <temp_dir>/project/main.py:2:5
|
||||
|
|
||||
2 | y = 4 / 0 # error: division-by-zero
|
||||
| ^^^^^
|
||||
| ^^^^^ Cannot divide object of type `Literal[4]` by zero
|
||||
|
|
||||
info: `lint:division-by-zero` is enabled by default
|
||||
|
||||
error: lint:unresolved-import: Cannot resolve imported module `main2`
|
||||
--> project/other.py:2:6
|
||||
error: lint:unresolved-import
|
||||
--> <temp_dir>/project/other.py:2:6
|
||||
|
|
||||
2 | from main2 import z # error: unresolved-import
|
||||
| ^^^^^
|
||||
| ^^^^^ Cannot resolve import `main2`
|
||||
3 |
|
||||
4 | print(z)
|
||||
|
|
||||
info: `lint:unresolved-import` is enabled by default
|
||||
|
||||
error: lint:unresolved-import: Cannot resolve imported module `does_not_exist`
|
||||
--> project/tests/test_main.py:2:8
|
||||
|
|
||||
2 | import does_not_exist # error: unresolved-import
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
||||
info: `lint:unresolved-import` is enabled by default
|
||||
|
||||
Found 3 diagnostics
|
||||
|
||||
@@ -1112,23 +972,21 @@ fn check_specific_paths() -> anyhow::Result<()> {
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
error: lint:unresolved-import: Cannot resolve imported module `main2`
|
||||
--> project/other.py:2:6
|
||||
error: lint:unresolved-import
|
||||
--> <temp_dir>/project/tests/test_main.py:2:8
|
||||
|
|
||||
2 | import does_not_exist # error: unresolved-import
|
||||
| ^^^^^^^^^^^^^^ Cannot resolve import `does_not_exist`
|
||||
|
|
||||
|
||||
error: lint:unresolved-import
|
||||
--> <temp_dir>/project/other.py:2:6
|
||||
|
|
||||
2 | from main2 import z # error: unresolved-import
|
||||
| ^^^^^
|
||||
| ^^^^^ Cannot resolve import `main2`
|
||||
3 |
|
||||
4 | print(z)
|
||||
|
|
||||
info: `lint:unresolved-import` is enabled by default
|
||||
|
||||
error: lint:unresolved-import: Cannot resolve imported module `does_not_exist`
|
||||
--> project/tests/test_main.py:2:8
|
||||
|
|
||||
2 | import does_not_exist # error: unresolved-import
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
||||
info: `lint:unresolved-import` is enabled by default
|
||||
|
||||
Found 2 diagnostics
|
||||
|
||||
@@ -1184,8 +1042,8 @@ fn concise_diagnostics() -> anyhow::Result<()> {
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
warning[lint:unresolved-reference] test.py:2:7: Name `x` used when not defined
|
||||
error[lint:non-subscriptable] test.py:3:7: Cannot subscript object of type `Literal[4]` with no `__getitem__` method
|
||||
warning[lint:unresolved-reference] <temp_dir>/test.py:2:7: Name `x` used when not defined
|
||||
error[lint:non-subscriptable] <temp_dir>/test.py:3:7: Cannot subscript object of type `Literal[4]` with no `__getitem__` method
|
||||
Found 2 diagnostics
|
||||
|
||||
----- stderr -----
|
||||
@@ -1218,7 +1076,7 @@ fn concise_revealed_type() -> anyhow::Result<()> {
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
info[revealed-type] test.py:5:1: Revealed type: `Literal["hello"]`
|
||||
info[revealed-type] <temp_dir>/test.py:5:1: Revealed type: `Literal["hello"]`
|
||||
Found 1 diagnostic
|
||||
|
||||
----- stderr -----
|
||||
@@ -1227,42 +1085,6 @@ fn concise_revealed_type() -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_handle_large_binop_expressions() -> anyhow::Result<()> {
|
||||
let mut content = String::new();
|
||||
writeln!(
|
||||
&mut content,
|
||||
"
|
||||
from typing_extensions import reveal_type
|
||||
total = 1{plus_one_repeated}
|
||||
reveal_type(total)
|
||||
",
|
||||
plus_one_repeated = " + 1".repeat(2000 - 1)
|
||||
)?;
|
||||
|
||||
let case = TestCase::with_file("test.py", &ruff_python_trivia::textwrap::dedent(&content))?;
|
||||
|
||||
assert_cmd_snapshot!(case.command(), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
info: revealed-type: Revealed type
|
||||
--> test.py:4:1
|
||||
|
|
||||
2 | from typing_extensions import reveal_type
|
||||
3 | total = 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1...
|
||||
4 | reveal_type(total)
|
||||
| ^^^^^^^^^^^^^^^^^^ `Literal[2000]`
|
||||
|
|
||||
|
||||
Found 1 diagnostic
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct TestCase {
|
||||
_temp_dir: TempDir,
|
||||
_settings_scope: SettingsBindDropGuard,
|
||||
@@ -1335,7 +1157,7 @@ impl TestCase {
|
||||
}
|
||||
|
||||
fn command(&self) -> Command {
|
||||
let mut command = Command::new(get_cargo_bin("ty"));
|
||||
let mut command = Command::new(get_cargo_bin("red_knot"));
|
||||
command.current_dir(&self.project_dir).arg("check");
|
||||
command
|
||||
}
|
||||
@@ -3,6 +3,12 @@ use std::io::Write;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use red_knot_project::metadata::options::{EnvironmentOptions, Options};
|
||||
use red_knot_project::metadata::pyproject::{PyProject, Tool};
|
||||
use red_knot_project::metadata::value::{RangedValue, RelativePathBuf};
|
||||
use red_knot_project::watch::{directory_watcher, ChangeEvent, ProjectWatcher};
|
||||
use red_knot_project::{Db, ProjectDatabase, ProjectMetadata};
|
||||
use red_knot_python_semantic::{resolve_module, ModuleName, PythonPlatform};
|
||||
use ruff_db::files::{system_path_to_file, File, FileError};
|
||||
use ruff_db::source::source_text;
|
||||
use ruff_db::system::{
|
||||
@@ -10,12 +16,6 @@ use ruff_db::system::{
|
||||
};
|
||||
use ruff_db::{Db as _, Upcast};
|
||||
use ruff_python_ast::PythonVersion;
|
||||
use ty_project::metadata::options::{EnvironmentOptions, Options};
|
||||
use ty_project::metadata::pyproject::{PyProject, Tool};
|
||||
use ty_project::metadata::value::{RangedValue, RelativePathBuf};
|
||||
use ty_project::watch::{directory_watcher, ChangeEvent, ProjectWatcher};
|
||||
use ty_project::{Db, ProjectDatabase, ProjectMetadata};
|
||||
use ty_python_semantic::{resolve_module, ModuleName, PythonPlatform};
|
||||
|
||||
struct TestCase {
|
||||
db: ProjectDatabase,
|
||||
@@ -173,7 +173,9 @@ impl TestCase {
|
||||
self.project_path("pyproject.toml").as_std_path(),
|
||||
toml::to_string(&PyProject {
|
||||
project: None,
|
||||
tool: Some(Tool { ty: Some(options) }),
|
||||
tool: Some(Tool {
|
||||
knot: Some(options),
|
||||
}),
|
||||
})
|
||||
.context("Failed to serialize options")?,
|
||||
)
|
||||
@@ -380,7 +382,9 @@ where
|
||||
project_path.join("pyproject.toml").as_std_path(),
|
||||
toml::to_string(&PyProject {
|
||||
project: None,
|
||||
tool: Some(Tool { ty: Some(options) }),
|
||||
tool: Some(Tool {
|
||||
knot: Some(options),
|
||||
}),
|
||||
})
|
||||
.context("Failed to serialize options")?,
|
||||
)
|
||||
@@ -1542,7 +1546,7 @@ mod unix {
|
||||
//
|
||||
// I further tested how good editor support is for symlinked files and it is not good ;)
|
||||
// * VS Code doesn't update the file content if a file gets changed through a symlink
|
||||
// * PyCharm doesn't update diagnostics if a symlinked module is changed (same as ty).
|
||||
// * PyCharm doesn't update diagnostics if a symlinked module is changed (same as red knot).
|
||||
//
|
||||
// That's why I think it's fine to not support this case for now.
|
||||
|
||||
@@ -1654,7 +1658,7 @@ mod unix {
|
||||
// It would be nice if this is supported but the underlying file system watchers
|
||||
// only emit a single event. For reference
|
||||
// * VS Code doesn't update the file content if a file gets changed through a symlink
|
||||
// * PyCharm doesn't update diagnostics if a symlinked module is changed (same as ty).
|
||||
// * PyCharm doesn't update diagnostics if a symlinked module is changed (same as red knot).
|
||||
// We could add support for it by keeping a reverse map from `real_path` to symlinked path but
|
||||
// it doesn't seem worth doing considering that as prominent tools like PyCharm don't support it.
|
||||
// Pyright does support it, thanks to chokidar.
|
||||
@@ -1678,7 +1682,7 @@ fn nested_projects_delete_root() -> anyhow::Result<()> {
|
||||
[project]
|
||||
name = "inner"
|
||||
|
||||
[tool.ty]
|
||||
[tool.knot]
|
||||
"#,
|
||||
)?;
|
||||
|
||||
@@ -1688,7 +1692,7 @@ fn nested_projects_delete_root() -> anyhow::Result<()> {
|
||||
[project]
|
||||
name = "outer"
|
||||
|
||||
[tool.ty]
|
||||
[tool.knot]
|
||||
"#,
|
||||
)?;
|
||||
|
||||
@@ -1728,9 +1732,9 @@ fn changes_to_user_configuration() -> anyhow::Result<()> {
|
||||
)?;
|
||||
|
||||
let config_directory = context.join_root_path("home/.config");
|
||||
std::fs::create_dir_all(config_directory.join("ty").as_std_path())?;
|
||||
std::fs::create_dir_all(config_directory.join("knot").as_std_path())?;
|
||||
std::fs::write(
|
||||
config_directory.join("ty/ty.toml").as_std_path(),
|
||||
config_directory.join("knot/knot.toml").as_std_path(),
|
||||
r#"
|
||||
[rules]
|
||||
division-by-zero = "ignore"
|
||||
@@ -1761,14 +1765,14 @@ fn changes_to_user_configuration() -> anyhow::Result<()> {
|
||||
|
||||
// Enable division-by-zero in the user configuration with warning severity
|
||||
update_file(
|
||||
case.root_path().join("home/.config/ty/ty.toml"),
|
||||
case.root_path().join("home/.config/knot/knot.toml"),
|
||||
r#"
|
||||
[rules]
|
||||
division-by-zero = "warn"
|
||||
"#,
|
||||
)?;
|
||||
|
||||
let changes = case.stop_watch(event_for_file("ty.toml"));
|
||||
let changes = case.stop_watch(event_for_file("knot.toml"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -1789,7 +1793,7 @@ fn changes_to_user_configuration() -> anyhow::Result<()> {
|
||||
///
|
||||
/// This test currently fails on case-insensitive systems because `Files` is case-sensitive
|
||||
/// but the `System::metadata` call isn't. This means that
|
||||
/// ty considers both `Lib.py` and `lib.py` to exist when only `lib.py` does
|
||||
/// Red Knot considers both `Lib.py` and `lib.py` to exist when only `lib.py` does
|
||||
///
|
||||
/// The incoming change events then are no-ops because they don't change either file's
|
||||
/// status nor does it update their last modified time (renaming a file doesn't bump it's
|
||||
@@ -1801,7 +1805,7 @@ fn changes_to_user_configuration() -> anyhow::Result<()> {
|
||||
/// `System` calls should be case sensitive. This would be the most consistent
|
||||
/// but might be hard to pull off.
|
||||
///
|
||||
/// What the right solution is also depends on if ty itself should be case
|
||||
/// What the right solution is also depends on if Red Knot itself should be case
|
||||
/// sensitive or not. E.g. should `include="src"` be case sensitive on all systems
|
||||
/// or only on case-sensitive systems?
|
||||
///
|
||||
@@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "ty_ide"
|
||||
name = "red_knot_ide"
|
||||
version = "0.0.0"
|
||||
publish = false
|
||||
authors = { workspace = true }
|
||||
@@ -15,7 +15,7 @@ ruff_db = { workspace = true }
|
||||
ruff_python_ast = { workspace = true }
|
||||
ruff_python_parser = { workspace = true }
|
||||
ruff_text_size = { workspace = true }
|
||||
ty_python_semantic = { workspace = true }
|
||||
red_knot_python_semantic = { workspace = true }
|
||||
|
||||
rustc-hash = { workspace = true }
|
||||
salsa = { workspace = true }
|
||||
@@ -23,7 +23,7 @@ smallvec = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
ty_vendored = { workspace = true }
|
||||
red_knot_vendored = { workspace = true }
|
||||
|
||||
insta = { workspace = true, features = ["filters"] }
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
use red_knot_python_semantic::Db as SemanticDb;
|
||||
use ruff_db::{Db as SourceDb, Upcast};
|
||||
use ty_python_semantic::Db as SemanticDb;
|
||||
|
||||
#[salsa::db]
|
||||
pub trait Db: SemanticDb + Upcast<dyn SemanticDb> + Upcast<dyn SourceDb> {}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::Db;
|
||||
use red_knot_python_semantic::lint::{LintRegistry, RuleSelection};
|
||||
use red_knot_python_semantic::{default_lint_registry, Db as SemanticDb, Program};
|
||||
use ruff_db::files::{File, Files};
|
||||
use ruff_db::system::{DbWithTestSystem, System, TestSystem};
|
||||
use ruff_db::vendored::VendoredFileSystem;
|
||||
use ruff_db::{Db as SourceDb, Upcast};
|
||||
use ty_python_semantic::lint::{LintRegistry, RuleSelection};
|
||||
use ty_python_semantic::{default_lint_registry, Db as SemanticDb, Program};
|
||||
|
||||
type Events = Arc<Mutex<Vec<salsa::Event>>>;
|
||||
|
||||
#[salsa::db]
|
||||
#[derive(Clone)]
|
||||
@@ -25,35 +23,31 @@ pub(crate) mod tests {
|
||||
files: Files,
|
||||
system: TestSystem,
|
||||
vendored: VendoredFileSystem,
|
||||
events: Events,
|
||||
events: Arc<std::sync::Mutex<Vec<salsa::Event>>>,
|
||||
rule_selection: Arc<RuleSelection>,
|
||||
}
|
||||
|
||||
#[expect(dead_code)]
|
||||
#[allow(dead_code)]
|
||||
impl TestDb {
|
||||
pub(crate) fn new() -> Self {
|
||||
let events = Events::default();
|
||||
Self {
|
||||
storage: salsa::Storage::new(Some(Box::new({
|
||||
let events = events.clone();
|
||||
move |event| {
|
||||
tracing::trace!("event: {event:?}");
|
||||
let mut events = events.lock().unwrap();
|
||||
events.push(event);
|
||||
}
|
||||
}))),
|
||||
storage: salsa::Storage::default(),
|
||||
system: TestSystem::default(),
|
||||
vendored: ty_vendored::file_system().clone(),
|
||||
events,
|
||||
vendored: red_knot_vendored::file_system().clone(),
|
||||
events: Arc::default(),
|
||||
files: Files::default(),
|
||||
rule_selection: Arc::new(RuleSelection::from_registry(default_lint_registry())),
|
||||
}
|
||||
}
|
||||
|
||||
/// Takes the salsa events.
|
||||
///
|
||||
/// ## Panics
|
||||
/// If there are any pending salsa snapshots.
|
||||
pub(crate) fn take_salsa_events(&mut self) -> Vec<salsa::Event> {
|
||||
let mut events = self.events.lock().unwrap();
|
||||
let inner = Arc::get_mut(&mut self.events).expect("no pending salsa snapshots");
|
||||
|
||||
let events = inner.get_mut().unwrap();
|
||||
std::mem::take(&mut *events)
|
||||
}
|
||||
|
||||
@@ -133,5 +127,12 @@ pub(crate) mod tests {
|
||||
impl Db for TestDb {}
|
||||
|
||||
#[salsa::db]
|
||||
impl salsa::Database for TestDb {}
|
||||
impl salsa::Database for TestDb {
|
||||
fn salsa_event(&self, event: &dyn Fn() -> salsa::Event) {
|
||||
let event = event();
|
||||
tracing::trace!("event: {event:?}");
|
||||
let mut events = self.events.lock().unwrap();
|
||||
events.push(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
use crate::find_node::covering_node;
|
||||
use crate::{Db, HasNavigationTargets, NavigationTargets, RangedValue};
|
||||
use red_knot_python_semantic::types::Type;
|
||||
use red_knot_python_semantic::{HasType, SemanticModel};
|
||||
use ruff_db::files::{File, FileRange};
|
||||
use ruff_db::parsed::{parsed_module, ParsedModule};
|
||||
use ruff_python_ast::{self as ast, AnyNodeRef};
|
||||
use ruff_python_parser::TokenKind;
|
||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||
use ty_python_semantic::types::Type;
|
||||
use ty_python_semantic::{HasType, SemanticModel};
|
||||
|
||||
pub fn goto_type_definition(
|
||||
db: &dyn Db,
|
||||
@@ -272,9 +272,9 @@ mod tests {
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_type_definition(), @r"
|
||||
assert_snapshot!(test.goto_type_definition(), @r###"
|
||||
info: lint:goto-type-definition: Type definition
|
||||
--> main.py:2:19
|
||||
--> /main.py:2:19
|
||||
|
|
||||
2 | class Test: ...
|
||||
| ^^^^
|
||||
@@ -282,14 +282,14 @@ mod tests {
|
||||
4 | ab = Test()
|
||||
|
|
||||
info: Source
|
||||
--> main.py:4:13
|
||||
--> /main.py:4:13
|
||||
|
|
||||
2 | class Test: ...
|
||||
3 |
|
||||
4 | ab = Test()
|
||||
| ^^
|
||||
|
|
||||
");
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -304,9 +304,9 @@ mod tests {
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_type_definition(), @r"
|
||||
assert_snapshot!(test.goto_type_definition(), @r###"
|
||||
info: lint:goto-type-definition: Type definition
|
||||
--> main.py:2:17
|
||||
--> /main.py:2:17
|
||||
|
|
||||
2 | def foo(a, b): ...
|
||||
| ^^^
|
||||
@@ -314,14 +314,14 @@ mod tests {
|
||||
4 | ab = foo
|
||||
|
|
||||
info: Source
|
||||
--> main.py:6:13
|
||||
--> /main.py:6:13
|
||||
|
|
||||
4 | ab = foo
|
||||
5 |
|
||||
6 | ab
|
||||
| ^^
|
||||
|
|
||||
");
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -344,7 +344,7 @@ mod tests {
|
||||
|
||||
assert_snapshot!(test.goto_type_definition(), @r"
|
||||
info: lint:goto-type-definition: Type definition
|
||||
--> main.py:3:17
|
||||
--> /main.py:3:17
|
||||
|
|
||||
3 | def foo(a, b): ...
|
||||
| ^^^
|
||||
@@ -352,7 +352,7 @@ mod tests {
|
||||
5 | def bar(a, b): ...
|
||||
|
|
||||
info: Source
|
||||
--> main.py:12:13
|
||||
--> /main.py:12:13
|
||||
|
|
||||
10 | a = bar
|
||||
11 |
|
||||
@@ -361,7 +361,7 @@ mod tests {
|
||||
|
|
||||
|
||||
info: lint:goto-type-definition: Type definition
|
||||
--> main.py:5:17
|
||||
--> /main.py:5:17
|
||||
|
|
||||
3 | def foo(a, b): ...
|
||||
4 |
|
||||
@@ -371,7 +371,7 @@ mod tests {
|
||||
7 | if random.choice():
|
||||
|
|
||||
info: Source
|
||||
--> main.py:12:13
|
||||
--> /main.py:12:13
|
||||
|
|
||||
10 | a = bar
|
||||
11 |
|
||||
@@ -395,13 +395,13 @@ mod tests {
|
||||
|
||||
assert_snapshot!(test.goto_type_definition(), @r"
|
||||
info: lint:goto-type-definition: Type definition
|
||||
--> lib.py:1:1
|
||||
--> /lib.py:1:1
|
||||
|
|
||||
1 | a = 10
|
||||
| ^^^^^^
|
||||
|
|
||||
info: Source
|
||||
--> main.py:4:13
|
||||
--> /main.py:4:13
|
||||
|
|
||||
2 | import lib
|
||||
3 |
|
||||
@@ -433,7 +433,7 @@ mod tests {
|
||||
440 | def __new__(cls, object: object = ...) -> Self: ...
|
||||
|
|
||||
info: Source
|
||||
--> main.py:4:13
|
||||
--> /main.py:4:13
|
||||
|
|
||||
2 | a: str = "test"
|
||||
3 |
|
||||
@@ -462,7 +462,7 @@ mod tests {
|
||||
440 | def __new__(cls, object: object = ...) -> Self: ...
|
||||
|
|
||||
info: Source
|
||||
--> main.py:2:22
|
||||
--> /main.py:2:22
|
||||
|
|
||||
2 | a: str = "test"
|
||||
| ^^^^^^
|
||||
@@ -478,20 +478,20 @@ mod tests {
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_type_definition(), @r"
|
||||
assert_snapshot!(test.goto_type_definition(), @r###"
|
||||
info: lint:goto-type-definition: Type definition
|
||||
--> main.py:2:24
|
||||
--> /main.py:2:24
|
||||
|
|
||||
2 | type Alias[T: int = bool] = list[T]
|
||||
| ^
|
||||
|
|
||||
info: Source
|
||||
--> main.py:2:46
|
||||
--> /main.py:2:46
|
||||
|
|
||||
2 | type Alias[T: int = bool] = list[T]
|
||||
| ^
|
||||
|
|
||||
");
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -544,7 +544,7 @@ mod tests {
|
||||
440 | def __new__(cls, object: object = ...) -> Self: ...
|
||||
|
|
||||
info: Source
|
||||
--> main.py:4:18
|
||||
--> /main.py:4:18
|
||||
|
|
||||
2 | def test(a: str): ...
|
||||
3 |
|
||||
@@ -579,7 +579,7 @@ mod tests {
|
||||
233 | def __new__(cls, x: ConvertibleToInt = ..., /) -> Self: ...
|
||||
|
|
||||
info: Source
|
||||
--> main.py:4:18
|
||||
--> /main.py:4:18
|
||||
|
|
||||
2 | def test(a: str): ...
|
||||
3 |
|
||||
@@ -613,7 +613,7 @@ f(**kwargs<CURSOR>)
|
||||
1088 | # Also multiprocessing.managers.SyncManager.dict()
|
||||
|
|
||||
info: Source
|
||||
--> main.py:6:5
|
||||
--> /main.py:6:5
|
||||
|
|
||||
4 | kwargs = { "name": "test"}
|
||||
5 |
|
||||
@@ -644,7 +644,7 @@ f(**kwargs<CURSOR>)
|
||||
440 | def __new__(cls, object: object = ...) -> Self: ...
|
||||
|
|
||||
info: Source
|
||||
--> main.py:3:17
|
||||
--> /main.py:3:17
|
||||
|
|
||||
2 | def foo(a: str):
|
||||
3 | a
|
||||
@@ -666,23 +666,23 @@ f(**kwargs<CURSOR>)
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_type_definition(), @r"
|
||||
assert_snapshot!(test.goto_type_definition(), @r###"
|
||||
info: lint:goto-type-definition: Type definition
|
||||
--> main.py:2:19
|
||||
--> /main.py:2:19
|
||||
|
|
||||
2 | class X:
|
||||
| ^
|
||||
3 | def foo(a, b): ...
|
||||
|
|
||||
info: Source
|
||||
--> main.py:7:13
|
||||
--> /main.py:7:13
|
||||
|
|
||||
5 | x = X()
|
||||
6 |
|
||||
7 | x.foo()
|
||||
| ^
|
||||
|
|
||||
");
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -695,9 +695,9 @@ f(**kwargs<CURSOR>)
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_type_definition(), @r"
|
||||
assert_snapshot!(test.goto_type_definition(), @r###"
|
||||
info: lint:goto-type-definition: Type definition
|
||||
--> main.py:2:17
|
||||
--> /main.py:2:17
|
||||
|
|
||||
2 | def foo(a, b): ...
|
||||
| ^^^
|
||||
@@ -705,14 +705,14 @@ f(**kwargs<CURSOR>)
|
||||
4 | foo()
|
||||
|
|
||||
info: Source
|
||||
--> main.py:4:13
|
||||
--> /main.py:4:13
|
||||
|
|
||||
2 | def foo(a, b): ...
|
||||
3 |
|
||||
4 | foo()
|
||||
| ^^^
|
||||
|
|
||||
");
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -737,7 +737,7 @@ f(**kwargs<CURSOR>)
|
||||
440 | def __new__(cls, object: object = ...) -> Self: ...
|
||||
|
|
||||
info: Source
|
||||
--> main.py:4:27
|
||||
--> /main.py:4:27
|
||||
|
|
||||
2 | def foo(a: str | None, b):
|
||||
3 | if a is not None:
|
||||
@@ -767,7 +767,7 @@ f(**kwargs<CURSOR>)
|
||||
672 | def __bool__(self) -> Literal[False]: ...
|
||||
|
|
||||
info: Source
|
||||
--> main.py:3:17
|
||||
--> /main.py:3:17
|
||||
|
|
||||
2 | def foo(a: str | None, b):
|
||||
3 | a
|
||||
@@ -785,7 +785,7 @@ f(**kwargs<CURSOR>)
|
||||
440 | def __new__(cls, object: object = ...) -> Self: ...
|
||||
|
|
||||
info: Source
|
||||
--> main.py:3:17
|
||||
--> /main.py:3:17
|
||||
|
|
||||
2 | def foo(a: str | None, b):
|
||||
3 | a
|
||||
@@ -1,12 +1,12 @@
|
||||
use crate::goto::{find_goto_target, GotoTarget};
|
||||
use crate::{Db, MarkupKind, RangedValue};
|
||||
use red_knot_python_semantic::types::Type;
|
||||
use red_knot_python_semantic::SemanticModel;
|
||||
use ruff_db::files::{File, FileRange};
|
||||
use ruff_db::parsed::parsed_module;
|
||||
use ruff_text_size::{Ranged, TextSize};
|
||||
use std::fmt;
|
||||
use std::fmt::Formatter;
|
||||
use ty_python_semantic::types::Type;
|
||||
use ty_python_semantic::SemanticModel;
|
||||
|
||||
pub fn hover(db: &dyn Db, file: File, offset: TextSize) -> Option<RangedValue<Hover>> {
|
||||
let parsed = parsed_module(db.upcast(), file);
|
||||
@@ -156,7 +156,7 @@ mod tests {
|
||||
```
|
||||
---------------------------------------------
|
||||
info: lint:hover: Hovered content is
|
||||
--> main.py:4:9
|
||||
--> /main.py:4:9
|
||||
|
|
||||
2 | a = 10
|
||||
3 |
|
||||
@@ -192,7 +192,7 @@ mod tests {
|
||||
```
|
||||
---------------------------------------------
|
||||
info: lint:hover: Hovered content is
|
||||
--> main.py:10:9
|
||||
--> /main.py:10:9
|
||||
|
|
||||
9 | foo = Foo()
|
||||
10 | foo.a
|
||||
@@ -214,7 +214,7 @@ mod tests {
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.hover(), @r"
|
||||
assert_snapshot!(test.hover(), @r###"
|
||||
def foo(a, b) -> Unknown
|
||||
---------------------------------------------
|
||||
```text
|
||||
@@ -222,7 +222,7 @@ mod tests {
|
||||
```
|
||||
---------------------------------------------
|
||||
info: lint:hover: Hovered content is
|
||||
--> main.py:4:13
|
||||
--> /main.py:4:13
|
||||
|
|
||||
2 | def foo(a, b): ...
|
||||
3 |
|
||||
@@ -231,7 +231,7 @@ mod tests {
|
||||
| |
|
||||
| source
|
||||
|
|
||||
");
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -251,7 +251,7 @@ mod tests {
|
||||
```
|
||||
---------------------------------------------
|
||||
info: lint:hover: Hovered content is
|
||||
--> main.py:3:17
|
||||
--> /main.py:3:17
|
||||
|
|
||||
2 | def foo(a: int, b: int, c: int):
|
||||
3 | a + b == c
|
||||
@@ -282,7 +282,7 @@ mod tests {
|
||||
```
|
||||
---------------------------------------------
|
||||
info: lint:hover: Hovered content is
|
||||
--> main.py:4:18
|
||||
--> /main.py:4:18
|
||||
|
|
||||
2 | def test(a: int): ...
|
||||
3 |
|
||||
@@ -312,7 +312,7 @@ mod tests {
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.hover(), @r"
|
||||
assert_snapshot!(test.hover(), @r###"
|
||||
(def foo(a, b) -> Unknown) | (def bar(a, b) -> Unknown)
|
||||
---------------------------------------------
|
||||
```text
|
||||
@@ -320,7 +320,7 @@ mod tests {
|
||||
```
|
||||
---------------------------------------------
|
||||
info: lint:hover: Hovered content is
|
||||
--> main.py:12:13
|
||||
--> /main.py:12:13
|
||||
|
|
||||
10 | a = bar
|
||||
11 |
|
||||
@@ -329,7 +329,7 @@ mod tests {
|
||||
| |
|
||||
| source
|
||||
|
|
||||
");
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -352,7 +352,7 @@ mod tests {
|
||||
```
|
||||
---------------------------------------------
|
||||
info: lint:hover: Hovered content is
|
||||
--> main.py:4:13
|
||||
--> /main.py:4:13
|
||||
|
|
||||
2 | import lib
|
||||
3 |
|
||||
@@ -381,7 +381,7 @@ mod tests {
|
||||
```
|
||||
---------------------------------------------
|
||||
info: lint:hover: Hovered content is
|
||||
--> main.py:2:46
|
||||
--> /main.py:2:46
|
||||
|
|
||||
2 | type Alias[T: int = bool] = list[T]
|
||||
| ^- Cursor offset
|
||||
@@ -407,7 +407,7 @@ mod tests {
|
||||
```
|
||||
---------------------------------------------
|
||||
info: lint:hover: Hovered content is
|
||||
--> main.py:2:53
|
||||
--> /main.py:2:53
|
||||
|
|
||||
2 | type Alias[**P = [int, str]] = Callable[P, int]
|
||||
| ^- Cursor offset
|
||||
@@ -433,7 +433,7 @@ mod tests {
|
||||
```
|
||||
---------------------------------------------
|
||||
info: lint:hover: Hovered content is
|
||||
--> main.py:2:43
|
||||
--> /main.py:2:43
|
||||
|
|
||||
2 | type Alias[*Ts = ()] = tuple[*Ts]
|
||||
| ^^- Cursor offset
|
||||
@@ -444,129 +444,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_variable_assignment() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
value<CURSOR> = 1
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.hover(), @r"
|
||||
Literal[1]
|
||||
---------------------------------------------
|
||||
```text
|
||||
Literal[1]
|
||||
```
|
||||
---------------------------------------------
|
||||
info: lint:hover: Hovered content is
|
||||
--> main.py:2:13
|
||||
|
|
||||
2 | value = 1
|
||||
| ^^^^^- Cursor offset
|
||||
| |
|
||||
| source
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_augmented_assignment() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
value = 1
|
||||
value<CURSOR> += 2
|
||||
"#,
|
||||
);
|
||||
|
||||
// We currently show the *previous* value of the variable (1), not the new one (3).
|
||||
// Showing the new value might be more intuitive for some users, but the actual 'use'
|
||||
// of the `value` symbol here in read-context is `1`. This comment mainly exists to
|
||||
// signal that it might be okay to revisit this in the future and reveal 3 instead.
|
||||
assert_snapshot!(test.hover(), @r"
|
||||
Literal[1]
|
||||
---------------------------------------------
|
||||
```text
|
||||
Literal[1]
|
||||
```
|
||||
---------------------------------------------
|
||||
info: lint:hover: Hovered content is
|
||||
--> main.py:3:13
|
||||
|
|
||||
2 | value = 1
|
||||
3 | value += 2
|
||||
| ^^^^^- Cursor offset
|
||||
| |
|
||||
| source
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_attribute_assignment() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
class C:
|
||||
attr: int = 1
|
||||
|
||||
C.attr<CURSOR> = 2
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.hover(), @r"
|
||||
Literal[2]
|
||||
---------------------------------------------
|
||||
```text
|
||||
Literal[2]
|
||||
```
|
||||
---------------------------------------------
|
||||
info: lint:hover: Hovered content is
|
||||
--> main.py:5:13
|
||||
|
|
||||
3 | attr: int = 1
|
||||
4 |
|
||||
5 | C.attr = 2
|
||||
| ^^^^^^- Cursor offset
|
||||
| |
|
||||
| source
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_augmented_attribute_assignment() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
class C:
|
||||
attr = 1
|
||||
|
||||
C.attr<CURSOR> += 2
|
||||
"#,
|
||||
);
|
||||
|
||||
// See the comment in the `hover_augmented_assignment` test above. The same
|
||||
// reasoning applies here.
|
||||
assert_snapshot!(test.hover(), @r"
|
||||
Unknown | Literal[1]
|
||||
---------------------------------------------
|
||||
```text
|
||||
Unknown | Literal[1]
|
||||
```
|
||||
---------------------------------------------
|
||||
info: lint:hover: Hovered content is
|
||||
--> main.py:5:13
|
||||
|
|
||||
3 | attr = 1
|
||||
4 |
|
||||
5 | C.attr += 2
|
||||
| ^^^^^^- Cursor offset
|
||||
| |
|
||||
| source
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_annotated_assignment() {
|
||||
fn hover_class_member_declaration() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
class Foo:
|
||||
@@ -574,15 +452,16 @@ mod tests {
|
||||
"#,
|
||||
);
|
||||
|
||||
// TODO: This should be int and not `Never`, https://github.com/astral-sh/ruff/issues/17122
|
||||
assert_snapshot!(test.hover(), @r"
|
||||
int
|
||||
Never
|
||||
---------------------------------------------
|
||||
```text
|
||||
int
|
||||
Never
|
||||
```
|
||||
---------------------------------------------
|
||||
info: lint:hover: Hovered content is
|
||||
--> main.py:3:13
|
||||
--> /main.py:3:13
|
||||
|
|
||||
2 | class Foo:
|
||||
3 | a: int
|
||||
@@ -593,64 +472,6 @@ mod tests {
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_annotated_assignment_with_rhs() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
class Foo:
|
||||
a<CURSOR>: int = 1
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.hover(), @r"
|
||||
Literal[1]
|
||||
---------------------------------------------
|
||||
```text
|
||||
Literal[1]
|
||||
```
|
||||
---------------------------------------------
|
||||
info: lint:hover: Hovered content is
|
||||
--> main.py:3:13
|
||||
|
|
||||
2 | class Foo:
|
||||
3 | a: int = 1
|
||||
| ^- Cursor offset
|
||||
| |
|
||||
| source
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_annotated_attribute_assignment() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
class Foo:
|
||||
def __init__(self, a: int):
|
||||
self.a<CURSOR>: int = a
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.hover(), @r"
|
||||
int
|
||||
---------------------------------------------
|
||||
```text
|
||||
int
|
||||
```
|
||||
---------------------------------------------
|
||||
info: lint:hover: Hovered content is
|
||||
--> main.py:4:17
|
||||
|
|
||||
2 | class Foo:
|
||||
3 | def __init__(self, a: int):
|
||||
4 | self.a: int = a
|
||||
| ^^^^^^- Cursor offset
|
||||
| |
|
||||
| source
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_type_narrowing() {
|
||||
let test = cursor_test(
|
||||
@@ -669,7 +490,7 @@ mod tests {
|
||||
```
|
||||
---------------------------------------------
|
||||
info: lint:hover: Hovered content is
|
||||
--> main.py:4:27
|
||||
--> /main.py:4:27
|
||||
|
|
||||
2 | def foo(a: str | None, b):
|
||||
3 | if a is not None:
|
||||
@@ -1,4 +1,6 @@
|
||||
use crate::Db;
|
||||
use red_knot_python_semantic::types::Type;
|
||||
use red_knot_python_semantic::{HasType, SemanticModel};
|
||||
use ruff_db::files::File;
|
||||
use ruff_db::parsed::parsed_module;
|
||||
use ruff_python_ast::visitor::source_order::{self, SourceOrderVisitor, TraversalSignal};
|
||||
@@ -6,8 +8,6 @@ use ruff_python_ast::{AnyNodeRef, Expr, Stmt};
|
||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||
use std::fmt;
|
||||
use std::fmt::Formatter;
|
||||
use ty_python_semantic::types::Type;
|
||||
use ty_python_semantic::{HasType, SemanticModel};
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct InlayHint<'db> {
|
||||
@@ -157,11 +157,11 @@ mod tests {
|
||||
|
||||
use crate::db::tests::TestDb;
|
||||
|
||||
use ruff_db::system::{DbWithWritableSystem, SystemPathBuf};
|
||||
use ruff_python_ast::PythonVersion;
|
||||
use ty_python_semantic::{
|
||||
use red_knot_python_semantic::{
|
||||
Program, ProgramSettings, PythonPath, PythonPlatform, SearchPathSettings,
|
||||
};
|
||||
use ruff_db::system::{DbWithWritableSystem, SystemPathBuf};
|
||||
use ruff_python_ast::PythonVersion;
|
||||
|
||||
pub(super) fn inlay_hint_test(source: &str) -> InlayHintTest {
|
||||
const START: &str = "<START>";
|
||||
@@ -1,4 +1,3 @@
|
||||
mod completion;
|
||||
mod db;
|
||||
mod find_node;
|
||||
mod goto;
|
||||
@@ -6,7 +5,6 @@ mod hover;
|
||||
mod inlay_hints;
|
||||
mod markup;
|
||||
|
||||
pub use completion::completion;
|
||||
pub use db::Db;
|
||||
pub use goto::goto_type_definition;
|
||||
pub use hover::hover;
|
||||
@@ -16,9 +14,9 @@ pub use markup::MarkupKind;
|
||||
use rustc_hash::FxHashSet;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use red_knot_python_semantic::types::{Type, TypeDefinition};
|
||||
use ruff_db::files::{File, FileRange};
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
use ty_python_semantic::types::{Type, TypeDefinition};
|
||||
|
||||
/// Information associated with a text range.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
@@ -201,14 +199,14 @@ impl HasNavigationTargets for TypeDefinition<'_> {
|
||||
mod tests {
|
||||
use crate::db::tests::TestDb;
|
||||
use insta::internals::SettingsBindDropGuard;
|
||||
use red_knot_python_semantic::{
|
||||
Program, ProgramSettings, PythonPath, PythonPlatform, SearchPathSettings,
|
||||
};
|
||||
use ruff_db::diagnostic::{Diagnostic, DiagnosticFormat, DisplayDiagnosticConfig};
|
||||
use ruff_db::files::{system_path_to_file, File};
|
||||
use ruff_db::system::{DbWithWritableSystem, SystemPath, SystemPathBuf};
|
||||
use ruff_python_ast::PythonVersion;
|
||||
use ruff_text_size::TextSize;
|
||||
use ty_python_semantic::{
|
||||
Program, ProgramSettings, PythonPath, PythonPlatform, SearchPathSettings,
|
||||
};
|
||||
|
||||
pub(super) fn cursor_test(source: &str) -> CursorTest {
|
||||
let mut db = TestDb::new();
|
||||
@@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "ty_project"
|
||||
name = "red_knot_project"
|
||||
version = "0.0.0"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
@@ -18,9 +18,9 @@ ruff_macros = { workspace = true }
|
||||
ruff_python_ast = { workspace = true, features = ["serde"] }
|
||||
ruff_python_formatter = { workspace = true, optional = true }
|
||||
ruff_text_size = { workspace = true }
|
||||
ty_ide = { workspace = true }
|
||||
ty_python_semantic = { workspace = true, features = ["serde"] }
|
||||
ty_vendored = { workspace = true }
|
||||
red_knot_ide = { workspace = true }
|
||||
red_knot_python_semantic = { workspace = true, features = ["serde"] }
|
||||
red_knot_vendored = { workspace = true }
|
||||
|
||||
anyhow = { workspace = true }
|
||||
crossbeam = { workspace = true }
|
||||
@@ -43,13 +43,13 @@ insta = { workspace = true, features = ["redactions", "ron"] }
|
||||
|
||||
[features]
|
||||
default = ["zstd"]
|
||||
deflate = ["ty_vendored/deflate"]
|
||||
deflate = ["red_knot_vendored/deflate"]
|
||||
schemars = [
|
||||
"dep:schemars",
|
||||
"ruff_db/schemars",
|
||||
"ty_python_semantic/schemars",
|
||||
"red_knot_python_semantic/schemars",
|
||||
]
|
||||
zstd = ["ty_vendored/zstd"]
|
||||
zstd = ["red_knot_vendored/zstd"]
|
||||
format = ["ruff_python_formatter"]
|
||||
|
||||
[lints]
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user