]> git.lizzy.rs Git - rust.git/commitdiff
:arrow_up: rust-analyzer
authorLaurențiu Nicola <lnicola@dend.ro>
Wed, 9 Nov 2022 19:49:10 +0000 (21:49 +0200)
committerLaurențiu Nicola <lnicola@dend.ro>
Wed, 9 Nov 2022 19:49:10 +0000 (21:49 +0200)
74 files changed:
1  2 
src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/bug_report.md
src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/critical_nightly_regression.md
src/tools/rust-analyzer/.github/workflows/release.yaml
src/tools/rust-analyzer/crates/hir-def/src/pretty.rs
src/tools/rust-analyzer/crates/hir-def/src/type_ref.rs
src/tools/rust-analyzer/crates/hir-ty/src/display.rs
src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs
src/tools/rust-analyzer/crates/hir/src/lib.rs
src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_block.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_integer_literal.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_projection_method.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/number_representation.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_or_with_or_else.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/unnecessary_async.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_tuple.rs
src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs
src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
src/tools/rust-analyzer/crates/ide-assists/src/tests.rs
src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs
src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
src/tools/rust-analyzer/crates/ide-db/src/search.rs
src/tools/rust-analyzer/crates/ide/src/moniker.rs
src/tools/rust-analyzer/crates/ide/src/rename.rs
src/tools/rust-analyzer/crates/ide/src/signature_help.rs
src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs
src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
src/tools/rust-analyzer/docs/dev/guide.md
src/tools/rust-analyzer/docs/user/generated_config.adoc
src/tools/rust-analyzer/docs/user/manual.adoc
src/tools/rust-analyzer/editors/code/package-lock.json
src/tools/rust-analyzer/editors/code/package.json
src/tools/rust-analyzer/triagebot.toml

index a038dce3248aa41fb5ede1be651a3cf7fce1c078,0000000000000000000000000000000000000000..c2e21933c9a6811bcd47a61e2378c88e79a37fb3
mode 100644,000000..100644
--- /dev/null
@@@ -1,26 -1,0 +1,25 @@@
-  1. extension doesn't load in VSCodium: #11080
-  2. on-the-fly diagnostics are mostly unimplemented (`cargo check` diagnostics will be shown when saving a file): #3107
 +---
 +name: Bug report
 +about: Create a bug report for rust-analyzer.
 +title: ''
 +labels: ''
 +assignees: ''
 +
 +---
 +
 +<!--
 +Troubleshooting guide: https://rust-analyzer.github.io/manual.html#troubleshooting
 +Forum for questions: https://users.rust-lang.org/c/ide/14
 +
 +Before submitting, please make sure that you're not running into one of these known issues:
 +
++ 1. on-the-fly diagnostics are mostly unimplemented (`cargo check` diagnostics will be shown when saving a file): #3107
 +
 +Otherwise please try to provide information which will help us to fix the issue faster. Minimal reproducible examples with few dependencies are especially lovely <3.
 +-->
 +
 +**rust-analyzer version**: (eg. output of "rust-analyzer: Show RA Version" command, accessible in VSCode via <kbd>Ctrl/⌘</kbd>+<kbd>Shift</kbd>+<kbd>P</kbd>)
 +
 +**rustc version**: (eg. output of `rustc -V`)
 +
 +**relevant settings**: (eg. client settings, or environment variables like `CARGO`, `RUSTUP_HOME` or `CARGO_HOME`)
index a0b1627d7e2efba51625f4332849c3ee6f02aefb,0000000000000000000000000000000000000000..ad220ff65ca14997564c2d3186beb826fd1482b3
mode 100644,000000..100644
--- /dev/null
@@@ -1,17 -1,0 +1,16 @@@
- labels: ''
- assignees: 'matklad'
 +---
 +name: Critical Nightly Regression
 +about: You are using nightly rust-analyzer and the latest version is unusable.
 +title: ''
- @matklad, please take a look.
++labels: 'Broken Window'
++assignees: ''
 +
 +---
 +
 +<!--
 +Troubleshooting guide: https://rust-analyzer.github.io/manual.html#troubleshooting
 +
 +Please try to provide information which will help us to fix the issue faster. Minimal reproducible examples with few dependencies are especially lovely <3.
 +-->
 +
 +This is a serious regression in nightly and it's important to fix it before the next release.
index 422fe29f9d5c37576060b0698aaa0c6c60aab603,0000000000000000000000000000000000000000..b070dd3406f203877c2528f4e6a8805cb344737b
mode 100644,000000..100644
--- /dev/null
@@@ -1,273 -1,0 +1,272 @@@
-         # token from https://dev.azure.com/rust-analyzer/
-         run: npx ovsx publish --pat ${{ secrets.OPENVSX_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix || true
 +name: release
 +on:
 +  schedule:
 +    - cron: "0 0 * * *" # midnight UTC
 +
 +  workflow_dispatch:
 +
 +  push:
 +    branches:
 +      - release
 +      - trigger-nightly
 +
 +env:
 +  CARGO_INCREMENTAL: 0
 +  CARGO_NET_RETRY: 10
 +  RUSTFLAGS: "-D warnings -W unreachable-pub"
 +  RUSTUP_MAX_RETRIES: 10
 +  FETCH_DEPTH: 0 # pull in the tags for the version string
 +  MACOSX_DEPLOYMENT_TARGET: 10.15
 +  CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc
 +  CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_LINKER: arm-linux-gnueabihf-gcc
 +
 +jobs:
 +  dist:
 +    strategy:
 +      matrix:
 +        include:
 +          - os: windows-latest
 +            target: x86_64-pc-windows-msvc
 +            code-target: win32-x64
 +          - os: windows-latest
 +            target: aarch64-pc-windows-msvc
 +            code-target: win32-arm64
 +          - os: ubuntu-20.04
 +            target: x86_64-unknown-linux-gnu
 +            code-target: linux-x64
 +            container: ubuntu:18.04
 +          - os: ubuntu-20.04
 +            target: aarch64-unknown-linux-gnu
 +            code-target: linux-arm64
 +          - os: ubuntu-20.04
 +            target: arm-unknown-linux-gnueabihf
 +            code-target: linux-armhf
 +          - os: macos-11
 +            target: x86_64-apple-darwin
 +            code-target: darwin-x64
 +          - os: macos-11
 +            target: aarch64-apple-darwin
 +            code-target: darwin-arm64
 +
 +    name: dist (${{ matrix.target }})
 +    runs-on: ${{ matrix.os }}
 +    container: ${{ matrix.container }}
 +
 +    env:
 +      RA_TARGET: ${{ matrix.target }}
 +
 +    steps:
 +      - name: Checkout repository
 +        uses: actions/checkout@v3
 +        with:
 +          fetch-depth: ${{ env.FETCH_DEPTH }}
 +
 +      - name: Install toolchain dependencies
 +        if: matrix.container == 'ubuntu:18.04'
 +        shell: bash
 +        run: |
 +          apt-get update && apt-get install -y build-essential curl
 +          curl --proto '=https' --tlsv1.2 --retry 10 --retry-connrefused -fsSL "https://sh.rustup.rs" | sh -s -- --profile minimal --default-toolchain none -y
 +          echo "${CARGO_HOME:-$HOME/.cargo}/bin" >> $GITHUB_PATH
 +
 +      - name: Install Rust toolchain
 +        run: |
 +          rustup update --no-self-update stable
 +          rustup target add ${{ matrix.target }}
 +          rustup component add rust-src
 +
 +      - name: Install Node.js
 +        uses: actions/setup-node@v3
 +        with:
 +          node-version: 16
 +
 +      - name: Update apt repositories
 +        if: matrix.target == 'aarch64-unknown-linux-gnu' || matrix.target == 'arm-unknown-linux-gnueabihf'
 +        run: sudo apt-get update
 +
 +      - name: Install AArch64 target toolchain
 +        if: matrix.target == 'aarch64-unknown-linux-gnu'
 +        run: sudo apt-get install gcc-aarch64-linux-gnu
 +
 +      - name: Install ARM target toolchain
 +        if: matrix.target == 'arm-unknown-linux-gnueabihf'
 +        run: sudo apt-get install gcc-arm-linux-gnueabihf
 +
 +      - name: Dist
 +        run: cargo xtask dist --client-patch-version ${{ github.run_number }}
 +
 +      - run: npm ci
 +        working-directory: editors/code
 +
 +      - name: Package Extension (release)
 +        if: github.ref == 'refs/heads/release'
 +        run: npx vsce package -o "../../dist/rust-analyzer-${{ matrix.code-target }}.vsix" --target ${{ matrix.code-target }}
 +        working-directory: editors/code
 +
 +      - name: Package Extension (nightly)
 +        if: github.ref != 'refs/heads/release'
 +        run: npx vsce package -o "../../dist/rust-analyzer-${{ matrix.code-target }}.vsix" --target ${{ matrix.code-target }} --pre-release
 +        working-directory: editors/code
 +
 +      - if: matrix.target == 'x86_64-unknown-linux-gnu'
 +        run: rm -rf editors/code/server
 +
 +      - if: matrix.target == 'x86_64-unknown-linux-gnu' && github.ref == 'refs/heads/release'
 +        run: npx vsce package -o ../../dist/rust-analyzer-no-server.vsix
 +        working-directory: editors/code
 +
 +      - if: matrix.target == 'x86_64-unknown-linux-gnu' && github.ref != 'refs/heads/release'
 +        run: npx vsce package -o ../../dist/rust-analyzer-no-server.vsix --pre-release
 +        working-directory: editors/code
 +
 +      - name: Run analysis-stats on rust-analyzer
 +        if: matrix.target == 'x86_64-unknown-linux-gnu'
 +        run: target/${{ matrix.target }}/release/rust-analyzer analysis-stats .
 +
 +      - name: Run analysis-stats on rust std library
 +        if: matrix.target == 'x86_64-unknown-linux-gnu'
 +        run: target/${{ matrix.target }}/release/rust-analyzer analysis-stats --with-deps $(rustc --print sysroot)/lib/rustlib/src/rust/library/std
 +
 +      - name: Upload artifacts
 +        uses: actions/upload-artifact@v1
 +        with:
 +          name: dist-${{ matrix.target }}
 +          path: ./dist
 +
 +  dist-x86_64-unknown-linux-musl:
 +    name: dist (x86_64-unknown-linux-musl)
 +    runs-on: ubuntu-latest
 +    env:
 +      RA_TARGET: x86_64-unknown-linux-musl
 +      # For some reason `-crt-static` is not working for clang without lld
 +      RUSTFLAGS: "-C link-arg=-fuse-ld=lld -C target-feature=-crt-static"
 +    container:
 +      image: rust:alpine
 +      volumes:
 +        - /usr/local/cargo/registry:/usr/local/cargo/registry
 +
 +    steps:
 +      - name: Install dependencies
 +        run: apk add --no-cache git clang lld musl-dev nodejs npm
 +
 +      - name: Checkout repository
 +        uses: actions/checkout@v3
 +        with:
 +          fetch-depth: ${{ env.FETCH_DEPTH }}
 +
 +      - name: Dist
 +        run: cargo xtask dist --client-patch-version ${{ github.run_number }}
 +
 +      - run: npm ci
 +        working-directory: editors/code
 +
 +      - name: Package Extension (release)
 +        if: github.ref == 'refs/heads/release'
 +        run: npx vsce package -o "../../dist/rust-analyzer-alpine-x64.vsix" --target alpine-x64
 +        working-directory: editors/code
 +
 +      - name: Package Extension (nightly)
 +        if: github.ref != 'refs/heads/release'
 +        run: npx vsce package -o "../../dist/rust-analyzer-alpine-x64.vsix" --target alpine-x64 --pre-release
 +        working-directory: editors/code
 +
 +      - run: rm -rf editors/code/server
 +
 +      - name: Upload artifacts
 +        uses: actions/upload-artifact@v1
 +        with:
 +          name: dist-x86_64-unknown-linux-musl
 +          path: ./dist
 +
 +  publish:
 +    name: publish
 +    runs-on: ubuntu-latest
 +    needs: ["dist", "dist-x86_64-unknown-linux-musl"]
 +    steps:
 +      - name: Install Nodejs
 +        uses: actions/setup-node@v3
 +        with:
 +          node-version: 16
 +
 +      - run: echo "TAG=$(date --iso -u)" >> $GITHUB_ENV
 +        if: github.ref == 'refs/heads/release'
 +      - run: echo "TAG=nightly" >> $GITHUB_ENV
 +        if: github.ref != 'refs/heads/release'
 +      - run: 'echo "TAG: $TAG"'
 +
 +      - name: Checkout repository
 +        uses: actions/checkout@v3
 +        with:
 +          fetch-depth: ${{ env.FETCH_DEPTH }}
 +
 +      - run: echo "HEAD_SHA=$(git rev-parse HEAD)" >> $GITHUB_ENV
 +      - run: 'echo "HEAD_SHA: $HEAD_SHA"'
 +
 +      - uses: actions/download-artifact@v1
 +        with:
 +          name: dist-aarch64-apple-darwin
 +          path: dist
 +      - uses: actions/download-artifact@v1
 +        with:
 +          name: dist-x86_64-apple-darwin
 +          path: dist
 +      - uses: actions/download-artifact@v1
 +        with:
 +          name: dist-x86_64-unknown-linux-gnu
 +          path: dist
 +      - uses: actions/download-artifact@v1
 +        with:
 +          name: dist-x86_64-unknown-linux-musl
 +          path: dist
 +      - uses: actions/download-artifact@v1
 +        with:
 +          name: dist-aarch64-unknown-linux-gnu
 +          path: dist
 +      - uses: actions/download-artifact@v1
 +        with:
 +          name: dist-arm-unknown-linux-gnueabihf
 +          path: dist
 +      - uses: actions/download-artifact@v1
 +        with:
 +          name: dist-x86_64-pc-windows-msvc
 +          path: dist
 +      - uses: actions/download-artifact@v1
 +        with:
 +          name: dist-aarch64-pc-windows-msvc
 +          path: dist
 +      - run: ls -al ./dist
 +
 +      - name: Publish Release
 +        uses: ./.github/actions/github-release
 +        with:
 +          files: "dist/*"
 +          name: ${{ env.TAG }}
 +          token: ${{ secrets.GITHUB_TOKEN }}
 +
 +      - run: rm dist/rust-analyzer-no-server.vsix
 +
 +      - run: npm ci
 +        working-directory: ./editors/code
 +
 +      - name: Publish Extension (Code Marketplace, release)
 +        if: github.ref == 'refs/heads/release' && (github.repository == 'rust-analyzer/rust-analyzer' || github.repository == 'rust-lang/rust-analyzer')
 +        working-directory: ./editors/code
 +        # token from https://dev.azure.com/rust-analyzer/
 +        run: npx vsce publish --pat ${{ secrets.MARKETPLACE_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix
 +
 +      - name: Publish Extension (OpenVSX, release)
 +        if: github.ref == 'refs/heads/release' && (github.repository == 'rust-analyzer/rust-analyzer' || github.repository == 'rust-lang/rust-analyzer')
 +        working-directory: ./editors/code
-         run: npx ovsx publish --pat ${{ secrets.OPENVSX_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix || true
++        run: npx ovsx publish --pat ${{ secrets.OPENVSX_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix
 +        timeout-minutes: 2
 +
 +      - name: Publish Extension (Code Marketplace, nightly)
 +        if: github.ref != 'refs/heads/release' && (github.repository == 'rust-analyzer/rust-analyzer' || github.repository == 'rust-lang/rust-analyzer')
 +        working-directory: ./editors/code
 +        run: npx vsce publish --pat ${{ secrets.MARKETPLACE_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix
 +
 +      - name: Publish Extension (OpenVSX, nightly)
 +        if: github.ref != 'refs/heads/release' && (github.repository == 'rust-analyzer/rust-analyzer' || github.repository == 'rust-lang/rust-analyzer')
 +        working-directory: ./editors/code
++        run: npx ovsx publish --pat ${{ secrets.OPENVSX_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix
 +        timeout-minutes: 2
index 6636c8a23ca5fea4e31eef52f1d9d86ab0292359,0000000000000000000000000000000000000000..933970d10e47282924e6496cbac5a33a2d17d76d
mode 100644,000000..100644
--- /dev/null
@@@ -1,209 -1,0 +1,212 @@@
-         TypeRef::Fn(args_and_ret, varargs) => {
 +//! Display and pretty printing routines.
 +
 +use std::fmt::{self, Write};
 +
 +use hir_expand::mod_path::PathKind;
 +use itertools::Itertools;
 +
 +use crate::{
 +    intern::Interned,
 +    path::{GenericArg, GenericArgs, Path},
 +    type_ref::{Mutability, TraitBoundModifier, TypeBound, TypeRef},
 +};
 +
 +pub(crate) fn print_path(path: &Path, buf: &mut dyn Write) -> fmt::Result {
 +    match path.type_anchor() {
 +        Some(anchor) => {
 +            write!(buf, "<")?;
 +            print_type_ref(anchor, buf)?;
 +            write!(buf, ">::")?;
 +        }
 +        None => match path.kind() {
 +            PathKind::Plain => {}
 +            PathKind::Super(0) => write!(buf, "self")?,
 +            PathKind::Super(n) => {
 +                for i in 0..*n {
 +                    if i == 0 {
 +                        buf.write_str("super")?;
 +                    } else {
 +                        buf.write_str("::super")?;
 +                    }
 +                }
 +            }
 +            PathKind::Crate => write!(buf, "crate")?,
 +            PathKind::Abs => {}
 +            PathKind::DollarCrate(_) => write!(buf, "$crate")?,
 +        },
 +    }
 +
 +    for (i, segment) in path.segments().iter().enumerate() {
 +        if i != 0 || !matches!(path.kind(), PathKind::Plain) {
 +            write!(buf, "::")?;
 +        }
 +
 +        write!(buf, "{}", segment.name)?;
 +        if let Some(generics) = segment.args_and_bindings {
 +            write!(buf, "::<")?;
 +            print_generic_args(generics, buf)?;
 +
 +            write!(buf, ">")?;
 +        }
 +    }
 +
 +    Ok(())
 +}
 +
 +pub(crate) fn print_generic_args(generics: &GenericArgs, buf: &mut dyn Write) -> fmt::Result {
 +    let mut first = true;
 +    let args = if generics.has_self_type {
 +        let (self_ty, args) = generics.args.split_first().unwrap();
 +        write!(buf, "Self=")?;
 +        print_generic_arg(self_ty, buf)?;
 +        first = false;
 +        args
 +    } else {
 +        &generics.args
 +    };
 +    for arg in args {
 +        if !first {
 +            write!(buf, ", ")?;
 +        }
 +        first = false;
 +        print_generic_arg(arg, buf)?;
 +    }
 +    for binding in &generics.bindings {
 +        if !first {
 +            write!(buf, ", ")?;
 +        }
 +        first = false;
 +        write!(buf, "{}", binding.name)?;
 +        if !binding.bounds.is_empty() {
 +            write!(buf, ": ")?;
 +            print_type_bounds(&binding.bounds, buf)?;
 +        }
 +        if let Some(ty) = &binding.type_ref {
 +            write!(buf, " = ")?;
 +            print_type_ref(ty, buf)?;
 +        }
 +    }
 +    Ok(())
 +}
 +
 +pub(crate) fn print_generic_arg(arg: &GenericArg, buf: &mut dyn Write) -> fmt::Result {
 +    match arg {
 +        GenericArg::Type(ty) => print_type_ref(ty, buf),
 +        GenericArg::Const(c) => write!(buf, "{}", c),
 +        GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name),
 +    }
 +}
 +
 +pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Result {
 +    // FIXME: deduplicate with `HirDisplay` impl
 +    match type_ref {
 +        TypeRef::Never => write!(buf, "!")?,
 +        TypeRef::Placeholder => write!(buf, "_")?,
 +        TypeRef::Tuple(fields) => {
 +            write!(buf, "(")?;
 +            for (i, field) in fields.iter().enumerate() {
 +                if i != 0 {
 +                    write!(buf, ", ")?;
 +                }
 +                print_type_ref(field, buf)?;
 +            }
 +            write!(buf, ")")?;
 +        }
 +        TypeRef::Path(path) => print_path(path, buf)?,
 +        TypeRef::RawPtr(pointee, mtbl) => {
 +            let mtbl = match mtbl {
 +                Mutability::Shared => "*const",
 +                Mutability::Mut => "*mut",
 +            };
 +            write!(buf, "{} ", mtbl)?;
 +            print_type_ref(pointee, buf)?;
 +        }
 +        TypeRef::Reference(pointee, lt, mtbl) => {
 +            let mtbl = match mtbl {
 +                Mutability::Shared => "",
 +                Mutability::Mut => "mut ",
 +            };
 +            write!(buf, "&")?;
 +            if let Some(lt) = lt {
 +                write!(buf, "{} ", lt.name)?;
 +            }
 +            write!(buf, "{}", mtbl)?;
 +            print_type_ref(pointee, buf)?;
 +        }
 +        TypeRef::Array(elem, len) => {
 +            write!(buf, "[")?;
 +            print_type_ref(elem, buf)?;
 +            write!(buf, "; {}]", len)?;
 +        }
 +        TypeRef::Slice(elem) => {
 +            write!(buf, "[")?;
 +            print_type_ref(elem, buf)?;
 +            write!(buf, "]")?;
 +        }
++        TypeRef::Fn(args_and_ret, varargs, is_unsafe) => {
 +            let ((_, return_type), args) =
 +                args_and_ret.split_last().expect("TypeRef::Fn is missing return type");
++            if *is_unsafe {
++                write!(buf, "unsafe ")?;
++            }
 +            write!(buf, "fn(")?;
 +            for (i, (_, typeref)) in args.iter().enumerate() {
 +                if i != 0 {
 +                    write!(buf, ", ")?;
 +                }
 +                print_type_ref(typeref, buf)?;
 +            }
 +            if *varargs {
 +                if !args.is_empty() {
 +                    write!(buf, ", ")?;
 +                }
 +                write!(buf, "...")?;
 +            }
 +            write!(buf, ") -> ")?;
 +            print_type_ref(return_type, buf)?;
 +        }
 +        TypeRef::Macro(_ast_id) => {
 +            write!(buf, "<macro>")?;
 +        }
 +        TypeRef::Error => write!(buf, "{{unknown}}")?,
 +        TypeRef::ImplTrait(bounds) => {
 +            write!(buf, "impl ")?;
 +            print_type_bounds(bounds, buf)?;
 +        }
 +        TypeRef::DynTrait(bounds) => {
 +            write!(buf, "dyn ")?;
 +            print_type_bounds(bounds, buf)?;
 +        }
 +    }
 +
 +    Ok(())
 +}
 +
 +pub(crate) fn print_type_bounds(
 +    bounds: &[Interned<TypeBound>],
 +    buf: &mut dyn Write,
 +) -> fmt::Result {
 +    for (i, bound) in bounds.iter().enumerate() {
 +        if i != 0 {
 +            write!(buf, " + ")?;
 +        }
 +
 +        match bound.as_ref() {
 +            TypeBound::Path(path, modifier) => {
 +                match modifier {
 +                    TraitBoundModifier::None => (),
 +                    TraitBoundModifier::Maybe => write!(buf, "?")?,
 +                }
 +                print_path(path, buf)?;
 +            }
 +            TypeBound::ForLifetime(lifetimes, path) => {
 +                write!(buf, "for<{}> ", lifetimes.iter().format(", "))?;
 +                print_path(path, buf)?;
 +            }
 +            TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name)?,
 +            TypeBound::Error => write!(buf, "{{unknown}}")?,
 +        }
 +    }
 +
 +    Ok(())
 +}
index 5b4c71be7fb837eb688529d7d6ab976445655979,0000000000000000000000000000000000000000..f8bb78ddcfe02d26b790869308108f7454e6509a
mode 100644,000000..100644
--- /dev/null
@@@ -1,490 -1,0 +1,490 @@@
-     Fn(Vec<(Option<Name>, TypeRef)>, bool /*varargs*/),
 +//! HIR for references to types. Paths in these are not yet resolved. They can
 +//! be directly created from an ast::TypeRef, without further queries.
 +
 +use std::fmt::Write;
 +
 +use hir_expand::{
 +    name::{AsName, Name},
 +    AstId,
 +};
 +use syntax::ast::{self, HasName};
 +
 +use crate::{
 +    body::LowerCtx,
 +    builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
 +    expr::Literal,
 +    intern::Interned,
 +    path::Path,
 +};
 +
 +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
 +pub enum Mutability {
 +    Shared,
 +    Mut,
 +}
 +
 +impl Mutability {
 +    pub fn from_mutable(mutable: bool) -> Mutability {
 +        if mutable {
 +            Mutability::Mut
 +        } else {
 +            Mutability::Shared
 +        }
 +    }
 +
 +    pub fn as_keyword_for_ref(self) -> &'static str {
 +        match self {
 +            Mutability::Shared => "",
 +            Mutability::Mut => "mut ",
 +        }
 +    }
 +
 +    pub fn as_keyword_for_ptr(self) -> &'static str {
 +        match self {
 +            Mutability::Shared => "const ",
 +            Mutability::Mut => "mut ",
 +        }
 +    }
 +
 +    /// Returns `true` if the mutability is [`Mut`].
 +    ///
 +    /// [`Mut`]: Mutability::Mut
 +    #[must_use]
 +    pub fn is_mut(&self) -> bool {
 +        matches!(self, Self::Mut)
 +    }
 +
 +    /// Returns `true` if the mutability is [`Shared`].
 +    ///
 +    /// [`Shared`]: Mutability::Shared
 +    #[must_use]
 +    pub fn is_shared(&self) -> bool {
 +        matches!(self, Self::Shared)
 +    }
 +}
 +
 +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
 +pub enum Rawness {
 +    RawPtr,
 +    Ref,
 +}
 +
 +impl Rawness {
 +    pub fn from_raw(is_raw: bool) -> Rawness {
 +        if is_raw {
 +            Rawness::RawPtr
 +        } else {
 +            Rawness::Ref
 +        }
 +    }
 +
 +    pub fn is_raw(&self) -> bool {
 +        matches!(self, Self::RawPtr)
 +    }
 +}
 +
 +#[derive(Clone, PartialEq, Eq, Hash, Debug)]
 +pub struct TraitRef {
 +    pub path: Path,
 +}
 +
 +impl TraitRef {
 +    /// Converts an `ast::PathType` to a `hir::TraitRef`.
 +    pub(crate) fn from_ast(ctx: &LowerCtx<'_>, node: ast::Type) -> Option<Self> {
 +        // FIXME: Use `Path::from_src`
 +        match node {
 +            ast::Type::PathType(path) => {
 +                path.path().and_then(|it| ctx.lower_path(it)).map(|path| TraitRef { path })
 +            }
 +            _ => None,
 +        }
 +    }
 +}
 +
 +/// Compare ty::Ty
 +///
 +/// Note: Most users of `TypeRef` that end up in the salsa database intern it using
 +/// `Interned<TypeRef>` to save space. But notably, nested `TypeRef`s are not interned, since that
 +/// does not seem to save any noticeable amount of memory.
 +#[derive(Clone, PartialEq, Eq, Hash, Debug)]
 +pub enum TypeRef {
 +    Never,
 +    Placeholder,
 +    Tuple(Vec<TypeRef>),
 +    Path(Path),
 +    RawPtr(Box<TypeRef>, Mutability),
 +    Reference(Box<TypeRef>, Option<LifetimeRef>, Mutability),
 +    // FIXME: for full const generics, the latter element (length) here is going to have to be an
 +    // expression that is further lowered later in hir_ty.
 +    Array(Box<TypeRef>, ConstScalarOrPath),
 +    Slice(Box<TypeRef>),
 +    /// A fn pointer. Last element of the vector is the return type.
-                 TypeRef::Fn(params, is_varargs)
++    Fn(Vec<(Option<Name>, TypeRef)>, bool /*varargs*/, bool /*is_unsafe*/),
 +    ImplTrait(Vec<Interned<TypeBound>>),
 +    DynTrait(Vec<Interned<TypeBound>>),
 +    Macro(AstId<ast::MacroCall>),
 +    Error,
 +}
 +
 +#[derive(Clone, PartialEq, Eq, Hash, Debug)]
 +pub struct LifetimeRef {
 +    pub name: Name,
 +}
 +
 +impl LifetimeRef {
 +    pub(crate) fn new_name(name: Name) -> Self {
 +        LifetimeRef { name }
 +    }
 +
 +    pub(crate) fn new(lifetime: &ast::Lifetime) -> Self {
 +        LifetimeRef { name: Name::new_lifetime(lifetime) }
 +    }
 +
 +    pub fn missing() -> LifetimeRef {
 +        LifetimeRef { name: Name::missing() }
 +    }
 +}
 +
 +#[derive(Clone, PartialEq, Eq, Hash, Debug)]
 +pub enum TypeBound {
 +    Path(Path, TraitBoundModifier),
 +    ForLifetime(Box<[Name]>, Path),
 +    Lifetime(LifetimeRef),
 +    Error,
 +}
 +
 +/// A modifier on a bound, currently this is only used for `?Sized`, where the
 +/// modifier is `Maybe`.
 +#[derive(Clone, PartialEq, Eq, Hash, Debug)]
 +pub enum TraitBoundModifier {
 +    None,
 +    Maybe,
 +}
 +
 +impl TypeRef {
 +    /// Converts an `ast::TypeRef` to a `hir::TypeRef`.
 +    pub fn from_ast(ctx: &LowerCtx<'_>, node: ast::Type) -> Self {
 +        match node {
 +            ast::Type::ParenType(inner) => TypeRef::from_ast_opt(ctx, inner.ty()),
 +            ast::Type::TupleType(inner) => {
 +                TypeRef::Tuple(inner.fields().map(|it| TypeRef::from_ast(ctx, it)).collect())
 +            }
 +            ast::Type::NeverType(..) => TypeRef::Never,
 +            ast::Type::PathType(inner) => {
 +                // FIXME: Use `Path::from_src`
 +                inner
 +                    .path()
 +                    .and_then(|it| ctx.lower_path(it))
 +                    .map(TypeRef::Path)
 +                    .unwrap_or(TypeRef::Error)
 +            }
 +            ast::Type::PtrType(inner) => {
 +                let inner_ty = TypeRef::from_ast_opt(ctx, inner.ty());
 +                let mutability = Mutability::from_mutable(inner.mut_token().is_some());
 +                TypeRef::RawPtr(Box::new(inner_ty), mutability)
 +            }
 +            ast::Type::ArrayType(inner) => {
 +                // FIXME: This is a hack. We should probably reuse the machinery of
 +                // `hir_def::body::lower` to lower this into an `Expr` and then evaluate it at the
 +                // `hir_ty` level, which would allow knowing the type of:
 +                // let v: [u8; 2 + 2] = [0u8; 4];
 +                let len = ConstScalarOrPath::from_expr_opt(inner.expr());
 +                TypeRef::Array(Box::new(TypeRef::from_ast_opt(ctx, inner.ty())), len)
 +            }
 +            ast::Type::SliceType(inner) => {
 +                TypeRef::Slice(Box::new(TypeRef::from_ast_opt(ctx, inner.ty())))
 +            }
 +            ast::Type::RefType(inner) => {
 +                let inner_ty = TypeRef::from_ast_opt(ctx, inner.ty());
 +                let lifetime = inner.lifetime().map(|lt| LifetimeRef::new(&lt));
 +                let mutability = Mutability::from_mutable(inner.mut_token().is_some());
 +                TypeRef::Reference(Box::new(inner_ty), lifetime, mutability)
 +            }
 +            ast::Type::InferType(_inner) => TypeRef::Placeholder,
 +            ast::Type::FnPtrType(inner) => {
 +                let ret_ty = inner
 +                    .ret_type()
 +                    .and_then(|rt| rt.ty())
 +                    .map(|it| TypeRef::from_ast(ctx, it))
 +                    .unwrap_or_else(|| TypeRef::Tuple(Vec::new()));
 +                let mut is_varargs = false;
 +                let mut params = if let Some(pl) = inner.param_list() {
 +                    if let Some(param) = pl.params().last() {
 +                        is_varargs = param.dotdotdot_token().is_some();
 +                    }
 +
 +                    pl.params()
 +                        .map(|it| {
 +                            let type_ref = TypeRef::from_ast_opt(ctx, it.ty());
 +                            let name = match it.pat() {
 +                                Some(ast::Pat::IdentPat(it)) => Some(
 +                                    it.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing),
 +                                ),
 +                                _ => None,
 +                            };
 +                            (name, type_ref)
 +                        })
 +                        .collect()
 +                } else {
 +                    Vec::new()
 +                };
 +                params.push((None, ret_ty));
-                 TypeRef::Fn(params, _) => {
++                TypeRef::Fn(params, is_varargs, inner.unsafe_token().is_some())
 +            }
 +            // for types are close enough for our purposes to the inner type for now...
 +            ast::Type::ForType(inner) => TypeRef::from_ast_opt(ctx, inner.ty()),
 +            ast::Type::ImplTraitType(inner) => {
 +                TypeRef::ImplTrait(type_bounds_from_ast(ctx, inner.type_bound_list()))
 +            }
 +            ast::Type::DynTraitType(inner) => {
 +                TypeRef::DynTrait(type_bounds_from_ast(ctx, inner.type_bound_list()))
 +            }
 +            ast::Type::MacroType(mt) => match mt.macro_call() {
 +                Some(mc) => ctx.ast_id(ctx.db, &mc).map(TypeRef::Macro).unwrap_or(TypeRef::Error),
 +                None => TypeRef::Error,
 +            },
 +        }
 +    }
 +
 +    pub(crate) fn from_ast_opt(ctx: &LowerCtx<'_>, node: Option<ast::Type>) -> Self {
 +        match node {
 +            Some(node) => TypeRef::from_ast(ctx, node),
 +            None => TypeRef::Error,
 +        }
 +    }
 +
 +    pub(crate) fn unit() -> TypeRef {
 +        TypeRef::Tuple(Vec::new())
 +    }
 +
 +    pub fn walk(&self, f: &mut impl FnMut(&TypeRef)) {
 +        go(self, f);
 +
 +        fn go(type_ref: &TypeRef, f: &mut impl FnMut(&TypeRef)) {
 +            f(type_ref);
 +            match type_ref {
++                TypeRef::Fn(params, _, _) => {
 +                    params.iter().for_each(|(_, param_type)| go(param_type, f))
 +                }
 +                TypeRef::Tuple(types) => types.iter().for_each(|t| go(t, f)),
 +                TypeRef::RawPtr(type_ref, _)
 +                | TypeRef::Reference(type_ref, ..)
 +                | TypeRef::Array(type_ref, _)
 +                | TypeRef::Slice(type_ref) => go(type_ref, f),
 +                TypeRef::ImplTrait(bounds) | TypeRef::DynTrait(bounds) => {
 +                    for bound in bounds {
 +                        match bound.as_ref() {
 +                            TypeBound::Path(path, _) | TypeBound::ForLifetime(_, path) => {
 +                                go_path(path, f)
 +                            }
 +                            TypeBound::Lifetime(_) | TypeBound::Error => (),
 +                        }
 +                    }
 +                }
 +                TypeRef::Path(path) => go_path(path, f),
 +                TypeRef::Never | TypeRef::Placeholder | TypeRef::Macro(_) | TypeRef::Error => {}
 +            };
 +        }
 +
 +        fn go_path(path: &Path, f: &mut impl FnMut(&TypeRef)) {
 +            if let Some(type_ref) = path.type_anchor() {
 +                go(type_ref, f);
 +            }
 +            for segment in path.segments().iter() {
 +                if let Some(args_and_bindings) = segment.args_and_bindings {
 +                    for arg in &args_and_bindings.args {
 +                        match arg {
 +                            crate::path::GenericArg::Type(type_ref) => {
 +                                go(type_ref, f);
 +                            }
 +                            crate::path::GenericArg::Const(_)
 +                            | crate::path::GenericArg::Lifetime(_) => {}
 +                        }
 +                    }
 +                    for binding in &args_and_bindings.bindings {
 +                        if let Some(type_ref) = &binding.type_ref {
 +                            go(type_ref, f);
 +                        }
 +                        for bound in &binding.bounds {
 +                            match bound.as_ref() {
 +                                TypeBound::Path(path, _) | TypeBound::ForLifetime(_, path) => {
 +                                    go_path(path, f)
 +                                }
 +                                TypeBound::Lifetime(_) | TypeBound::Error => (),
 +                            }
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +pub(crate) fn type_bounds_from_ast(
 +    lower_ctx: &LowerCtx<'_>,
 +    type_bounds_opt: Option<ast::TypeBoundList>,
 +) -> Vec<Interned<TypeBound>> {
 +    if let Some(type_bounds) = type_bounds_opt {
 +        type_bounds.bounds().map(|it| Interned::new(TypeBound::from_ast(lower_ctx, it))).collect()
 +    } else {
 +        vec![]
 +    }
 +}
 +
 +impl TypeBound {
 +    pub(crate) fn from_ast(ctx: &LowerCtx<'_>, node: ast::TypeBound) -> Self {
 +        let lower_path_type = |path_type: ast::PathType| ctx.lower_path(path_type.path()?);
 +
 +        match node.kind() {
 +            ast::TypeBoundKind::PathType(path_type) => {
 +                let m = match node.question_mark_token() {
 +                    Some(_) => TraitBoundModifier::Maybe,
 +                    None => TraitBoundModifier::None,
 +                };
 +                lower_path_type(path_type)
 +                    .map(|p| TypeBound::Path(p, m))
 +                    .unwrap_or(TypeBound::Error)
 +            }
 +            ast::TypeBoundKind::ForType(for_type) => {
 +                let lt_refs = match for_type.generic_param_list() {
 +                    Some(gpl) => gpl
 +                        .lifetime_params()
 +                        .flat_map(|lp| lp.lifetime().map(|lt| Name::new_lifetime(&lt)))
 +                        .collect(),
 +                    None => Box::default(),
 +                };
 +                let path = for_type.ty().and_then(|ty| match ty {
 +                    ast::Type::PathType(path_type) => lower_path_type(path_type),
 +                    _ => None,
 +                });
 +                match path {
 +                    Some(p) => TypeBound::ForLifetime(lt_refs, p),
 +                    None => TypeBound::Error,
 +                }
 +            }
 +            ast::TypeBoundKind::Lifetime(lifetime) => {
 +                TypeBound::Lifetime(LifetimeRef::new(&lifetime))
 +            }
 +        }
 +    }
 +
 +    pub fn as_path(&self) -> Option<(&Path, &TraitBoundModifier)> {
 +        match self {
 +            TypeBound::Path(p, m) => Some((p, m)),
 +            TypeBound::ForLifetime(_, p) => Some((p, &TraitBoundModifier::None)),
 +            TypeBound::Lifetime(_) | TypeBound::Error => None,
 +        }
 +    }
 +}
 +
 +#[derive(Debug, Clone, PartialEq, Eq, Hash)]
 +pub enum ConstScalarOrPath {
 +    Scalar(ConstScalar),
 +    Path(Name),
 +}
 +
 +impl std::fmt::Display for ConstScalarOrPath {
 +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 +        match self {
 +            ConstScalarOrPath::Scalar(s) => s.fmt(f),
 +            ConstScalarOrPath::Path(n) => n.fmt(f),
 +        }
 +    }
 +}
 +
 +impl ConstScalarOrPath {
 +    pub(crate) fn from_expr_opt(expr: Option<ast::Expr>) -> Self {
 +        match expr {
 +            Some(x) => Self::from_expr(x),
 +            None => Self::Scalar(ConstScalar::Unknown),
 +        }
 +    }
 +
 +    // FIXME: as per the comments on `TypeRef::Array`, this evaluation should not happen at this
 +    // parse stage.
 +    fn from_expr(expr: ast::Expr) -> Self {
 +        match expr {
 +            ast::Expr::PathExpr(p) => {
 +                match p.path().and_then(|x| x.segment()).and_then(|x| x.name_ref()) {
 +                    Some(x) => Self::Path(x.as_name()),
 +                    None => Self::Scalar(ConstScalar::Unknown),
 +                }
 +            }
 +            ast::Expr::PrefixExpr(prefix_expr) => match prefix_expr.op_kind() {
 +                Some(ast::UnaryOp::Neg) => {
 +                    let unsigned = Self::from_expr_opt(prefix_expr.expr());
 +                    // Add sign
 +                    match unsigned {
 +                        Self::Scalar(ConstScalar::UInt(num)) => {
 +                            Self::Scalar(ConstScalar::Int(-(num as i128)))
 +                        }
 +                        other => other,
 +                    }
 +                }
 +                _ => Self::from_expr_opt(prefix_expr.expr()),
 +            },
 +            ast::Expr::Literal(literal) => Self::Scalar(match literal.kind() {
 +                ast::LiteralKind::IntNumber(num) => {
 +                    num.value().map(ConstScalar::UInt).unwrap_or(ConstScalar::Unknown)
 +                }
 +                ast::LiteralKind::Char(c) => {
 +                    c.value().map(ConstScalar::Char).unwrap_or(ConstScalar::Unknown)
 +                }
 +                ast::LiteralKind::Bool(f) => ConstScalar::Bool(f),
 +                _ => ConstScalar::Unknown,
 +            }),
 +            _ => Self::Scalar(ConstScalar::Unknown),
 +        }
 +    }
 +}
 +
 +/// A concrete constant value
 +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 +pub enum ConstScalar {
 +    Int(i128),
 +    UInt(u128),
 +    Bool(bool),
 +    Char(char),
 +
 +    /// Case of an unknown value that rustc might know but we don't
 +    // FIXME: this is a hack to get around chalk not being able to represent unevaluatable
 +    // constants
 +    // https://github.com/rust-lang/rust-analyzer/pull/8813#issuecomment-840679177
 +    // https://rust-lang.zulipchat.com/#narrow/stream/144729-wg-traits/topic/Handling.20non.20evaluatable.20constants'.20equality/near/238386348
 +    Unknown,
 +}
 +
 +impl ConstScalar {
 +    pub fn builtin_type(&self) -> BuiltinType {
 +        match self {
 +            ConstScalar::UInt(_) | ConstScalar::Unknown => BuiltinType::Uint(BuiltinUint::U128),
 +            ConstScalar::Int(_) => BuiltinType::Int(BuiltinInt::I128),
 +            ConstScalar::Char(_) => BuiltinType::Char,
 +            ConstScalar::Bool(_) => BuiltinType::Bool,
 +        }
 +    }
 +}
 +
 +impl From<Literal> for ConstScalar {
 +    fn from(literal: Literal) -> Self {
 +        match literal {
 +            Literal::Char(c) => Self::Char(c),
 +            Literal::Bool(flag) => Self::Bool(flag),
 +            Literal::Int(num, _) => Self::Int(num),
 +            Literal::Uint(num, _) => Self::UInt(num),
 +            _ => Self::Unknown,
 +        }
 +    }
 +}
 +
 +impl std::fmt::Display for ConstScalar {
 +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
 +        match self {
 +            ConstScalar::Int(num) => num.fmt(f),
 +            ConstScalar::UInt(num) => num.fmt(f),
 +            ConstScalar::Bool(flag) => flag.fmt(f),
 +            ConstScalar::Char(c) => write!(f, "'{c}'"),
 +            ConstScalar::Unknown => f.write_char('_'),
 +        }
 +    }
 +}
index 5ad66132635340c410488f425529be1d4397720f,0000000000000000000000000000000000000000..a22a4b170f61c749e68e929368ebb6233060ffd1
mode 100644,000000..100644
--- /dev/null
@@@ -1,1377 -1,0 +1,1380 @@@
-             TypeRef::Fn(parameters, is_varargs) => {
 +//! The `HirDisplay` trait, which serves two purposes: Turning various bits from
 +//! HIR back into source code, and just displaying them for debugging/testing
 +//! purposes.
 +
 +use std::fmt::{self, Debug};
 +
 +use base_db::CrateId;
 +use chalk_ir::BoundVar;
 +use hir_def::{
 +    body,
 +    db::DefDatabase,
 +    find_path,
 +    generics::{TypeOrConstParamData, TypeParamProvenance},
 +    intern::{Internable, Interned},
 +    item_scope::ItemInNs,
 +    path::{Path, PathKind},
 +    type_ref::{ConstScalar, TraitBoundModifier, TypeBound, TypeRef},
 +    visibility::Visibility,
 +    HasModule, ItemContainerId, Lookup, ModuleId, TraitId,
 +};
 +use hir_expand::{hygiene::Hygiene, name::Name};
 +use itertools::Itertools;
 +use smallvec::SmallVec;
 +use syntax::SmolStr;
 +
 +use crate::{
 +    db::HirDatabase,
 +    from_assoc_type_id, from_foreign_def_id, from_placeholder_idx, lt_from_placeholder_idx,
 +    mapping::from_chalk,
 +    primitive, to_assoc_type_id,
 +    utils::{self, generics},
 +    AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Const, ConstValue, DomainGoal,
 +    GenericArg, ImplTraitId, Interner, Lifetime, LifetimeData, LifetimeOutlives, Mutability,
 +    OpaqueTy, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Scalar, Substitution, TraitRef,
 +    TraitRefExt, Ty, TyExt, TyKind, WhereClause,
 +};
 +
 +pub struct HirFormatter<'a> {
 +    pub db: &'a dyn HirDatabase,
 +    fmt: &'a mut dyn fmt::Write,
 +    buf: String,
 +    curr_size: usize,
 +    pub(crate) max_size: Option<usize>,
 +    omit_verbose_types: bool,
 +    display_target: DisplayTarget,
 +}
 +
 +pub trait HirDisplay {
 +    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError>;
 +
 +    /// Returns a `Display`able type that is human-readable.
 +    fn into_displayable<'a>(
 +        &'a self,
 +        db: &'a dyn HirDatabase,
 +        max_size: Option<usize>,
 +        omit_verbose_types: bool,
 +        display_target: DisplayTarget,
 +    ) -> HirDisplayWrapper<'a, Self>
 +    where
 +        Self: Sized,
 +    {
 +        assert!(
 +            !matches!(display_target, DisplayTarget::SourceCode { .. }),
 +            "HirDisplayWrapper cannot fail with DisplaySourceCodeError, use HirDisplay::hir_fmt directly instead"
 +        );
 +        HirDisplayWrapper { db, t: self, max_size, omit_verbose_types, display_target }
 +    }
 +
 +    /// Returns a `Display`able type that is human-readable.
 +    /// Use this for showing types to the user (e.g. diagnostics)
 +    fn display<'a>(&'a self, db: &'a dyn HirDatabase) -> HirDisplayWrapper<'a, Self>
 +    where
 +        Self: Sized,
 +    {
 +        HirDisplayWrapper {
 +            db,
 +            t: self,
 +            max_size: None,
 +            omit_verbose_types: false,
 +            display_target: DisplayTarget::Diagnostics,
 +        }
 +    }
 +
 +    /// Returns a `Display`able type that is human-readable and tries to be succinct.
 +    /// Use this for showing types to the user where space is constrained (e.g. doc popups)
 +    fn display_truncated<'a>(
 +        &'a self,
 +        db: &'a dyn HirDatabase,
 +        max_size: Option<usize>,
 +    ) -> HirDisplayWrapper<'a, Self>
 +    where
 +        Self: Sized,
 +    {
 +        HirDisplayWrapper {
 +            db,
 +            t: self,
 +            max_size,
 +            omit_verbose_types: true,
 +            display_target: DisplayTarget::Diagnostics,
 +        }
 +    }
 +
 +    /// Returns a String representation of `self` that can be inserted into the given module.
 +    /// Use this when generating code (e.g. assists)
 +    fn display_source_code<'a>(
 +        &'a self,
 +        db: &'a dyn HirDatabase,
 +        module_id: ModuleId,
 +    ) -> Result<String, DisplaySourceCodeError> {
 +        let mut result = String::new();
 +        match self.hir_fmt(&mut HirFormatter {
 +            db,
 +            fmt: &mut result,
 +            buf: String::with_capacity(20),
 +            curr_size: 0,
 +            max_size: None,
 +            omit_verbose_types: false,
 +            display_target: DisplayTarget::SourceCode { module_id },
 +        }) {
 +            Ok(()) => {}
 +            Err(HirDisplayError::FmtError) => panic!("Writing to String can't fail!"),
 +            Err(HirDisplayError::DisplaySourceCodeError(e)) => return Err(e),
 +        };
 +        Ok(result)
 +    }
 +
 +    /// Returns a String representation of `self` for test purposes
 +    fn display_test<'a>(&'a self, db: &'a dyn HirDatabase) -> HirDisplayWrapper<'a, Self>
 +    where
 +        Self: Sized,
 +    {
 +        HirDisplayWrapper {
 +            db,
 +            t: self,
 +            max_size: None,
 +            omit_verbose_types: false,
 +            display_target: DisplayTarget::Test,
 +        }
 +    }
 +}
 +
 +impl<'a> HirFormatter<'a> {
 +    pub fn write_joined<T: HirDisplay>(
 +        &mut self,
 +        iter: impl IntoIterator<Item = T>,
 +        sep: &str,
 +    ) -> Result<(), HirDisplayError> {
 +        let mut first = true;
 +        for e in iter {
 +            if !first {
 +                write!(self, "{}", sep)?;
 +            }
 +            first = false;
 +
 +            // Abbreviate multiple omitted types with a single ellipsis.
 +            if self.should_truncate() {
 +                return write!(self, "{}", TYPE_HINT_TRUNCATION);
 +            }
 +
 +            e.hir_fmt(self)?;
 +        }
 +        Ok(())
 +    }
 +
 +    /// This allows using the `write!` macro directly with a `HirFormatter`.
 +    pub fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> Result<(), HirDisplayError> {
 +        // We write to a buffer first to track output size
 +        self.buf.clear();
 +        fmt::write(&mut self.buf, args)?;
 +        self.curr_size += self.buf.len();
 +
 +        // Then we write to the internal formatter from the buffer
 +        self.fmt.write_str(&self.buf).map_err(HirDisplayError::from)
 +    }
 +
 +    pub fn write_str(&mut self, s: &str) -> Result<(), HirDisplayError> {
 +        self.fmt.write_str(s)?;
 +        Ok(())
 +    }
 +
 +    pub fn write_char(&mut self, c: char) -> Result<(), HirDisplayError> {
 +        self.fmt.write_char(c)?;
 +        Ok(())
 +    }
 +
 +    pub fn should_truncate(&self) -> bool {
 +        match self.max_size {
 +            Some(max_size) => self.curr_size >= max_size,
 +            None => false,
 +        }
 +    }
 +
 +    pub fn omit_verbose_types(&self) -> bool {
 +        self.omit_verbose_types
 +    }
 +}
 +
 +#[derive(Clone, Copy)]
 +pub enum DisplayTarget {
 +    /// Display types for inlays, doc popups, autocompletion, etc...
 +    /// Showing `{unknown}` or not qualifying paths is fine here.
 +    /// There's no reason for this to fail.
 +    Diagnostics,
 +    /// Display types for inserting them in source files.
 +    /// The generated code should compile, so paths need to be qualified.
 +    SourceCode { module_id: ModuleId },
 +    /// Only for test purpose to keep real types
 +    Test,
 +}
 +
 +impl DisplayTarget {
 +    fn is_source_code(&self) -> bool {
 +        matches!(self, Self::SourceCode { .. })
 +    }
 +    fn is_test(&self) -> bool {
 +        matches!(self, Self::Test)
 +    }
 +}
 +
 +#[derive(Debug)]
 +pub enum DisplaySourceCodeError {
 +    PathNotFound,
 +    UnknownType,
 +    Closure,
 +    Generator,
 +}
 +
 +pub enum HirDisplayError {
 +    /// Errors that can occur when generating source code
 +    DisplaySourceCodeError(DisplaySourceCodeError),
 +    /// `FmtError` is required to be compatible with std::fmt::Display
 +    FmtError,
 +}
 +impl From<fmt::Error> for HirDisplayError {
 +    fn from(_: fmt::Error) -> Self {
 +        Self::FmtError
 +    }
 +}
 +
 +pub struct HirDisplayWrapper<'a, T> {
 +    db: &'a dyn HirDatabase,
 +    t: &'a T,
 +    max_size: Option<usize>,
 +    omit_verbose_types: bool,
 +    display_target: DisplayTarget,
 +}
 +
 +impl<'a, T> fmt::Display for HirDisplayWrapper<'a, T>
 +where
 +    T: HirDisplay,
 +{
 +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +        match self.t.hir_fmt(&mut HirFormatter {
 +            db: self.db,
 +            fmt: f,
 +            buf: String::with_capacity(20),
 +            curr_size: 0,
 +            max_size: self.max_size,
 +            omit_verbose_types: self.omit_verbose_types,
 +            display_target: self.display_target,
 +        }) {
 +            Ok(()) => Ok(()),
 +            Err(HirDisplayError::FmtError) => Err(fmt::Error),
 +            Err(HirDisplayError::DisplaySourceCodeError(_)) => {
 +                // This should never happen
 +                panic!("HirDisplay::hir_fmt failed with DisplaySourceCodeError when calling Display::fmt!")
 +            }
 +        }
 +    }
 +}
 +
 +const TYPE_HINT_TRUNCATION: &str = "…";
 +
 +impl<T: HirDisplay> HirDisplay for &'_ T {
 +    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
 +        HirDisplay::hir_fmt(*self, f)
 +    }
 +}
 +
 +impl<T: HirDisplay + Internable> HirDisplay for Interned<T> {
 +    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
 +        HirDisplay::hir_fmt(self.as_ref(), f)
 +    }
 +}
 +
 +impl HirDisplay for ProjectionTy {
 +    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
 +        if f.should_truncate() {
 +            return write!(f, "{}", TYPE_HINT_TRUNCATION);
 +        }
 +
 +        let trait_ref = self.trait_ref(f.db);
 +        write!(f, "<")?;
 +        fmt_trait_ref(&trait_ref, f, true)?;
 +        write!(f, ">::{}", f.db.type_alias_data(from_assoc_type_id(self.associated_ty_id)).name)?;
 +        let proj_params_count =
 +            self.substitution.len(Interner) - trait_ref.substitution.len(Interner);
 +        let proj_params = &self.substitution.as_slice(Interner)[..proj_params_count];
 +        if !proj_params.is_empty() {
 +            write!(f, "<")?;
 +            f.write_joined(proj_params, ", ")?;
 +            write!(f, ">")?;
 +        }
 +        Ok(())
 +    }
 +}
 +
 +impl HirDisplay for OpaqueTy {
 +    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
 +        if f.should_truncate() {
 +            return write!(f, "{}", TYPE_HINT_TRUNCATION);
 +        }
 +
 +        self.substitution.at(Interner, 0).hir_fmt(f)
 +    }
 +}
 +
 +impl HirDisplay for GenericArg {
 +    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
 +        match self.interned() {
 +            crate::GenericArgData::Ty(ty) => ty.hir_fmt(f),
 +            crate::GenericArgData::Lifetime(lt) => lt.hir_fmt(f),
 +            crate::GenericArgData::Const(c) => c.hir_fmt(f),
 +        }
 +    }
 +}
 +
 +impl HirDisplay for Const {
 +    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
 +        let data = self.interned();
 +        match data.value {
 +            ConstValue::BoundVar(idx) => idx.hir_fmt(f),
 +            ConstValue::InferenceVar(..) => write!(f, "#c#"),
 +            ConstValue::Placeholder(idx) => {
 +                let id = from_placeholder_idx(f.db, idx);
 +                let generics = generics(f.db.upcast(), id.parent);
 +                let param_data = &generics.params.type_or_consts[id.local_id];
 +                write!(f, "{}", param_data.name().unwrap())
 +            }
 +            ConstValue::Concrete(c) => write!(f, "{}", c.interned),
 +        }
 +    }
 +}
 +
 +impl HirDisplay for BoundVar {
 +    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
 +        write!(f, "?{}.{}", self.debruijn.depth(), self.index)
 +    }
 +}
 +
 +impl HirDisplay for Ty {
 +    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
 +        if f.should_truncate() {
 +            return write!(f, "{}", TYPE_HINT_TRUNCATION);
 +        }
 +
 +        match self.kind(Interner) {
 +            TyKind::Never => write!(f, "!")?,
 +            TyKind::Str => write!(f, "str")?,
 +            TyKind::Scalar(Scalar::Bool) => write!(f, "bool")?,
 +            TyKind::Scalar(Scalar::Char) => write!(f, "char")?,
 +            &TyKind::Scalar(Scalar::Float(t)) => write!(f, "{}", primitive::float_ty_to_string(t))?,
 +            &TyKind::Scalar(Scalar::Int(t)) => write!(f, "{}", primitive::int_ty_to_string(t))?,
 +            &TyKind::Scalar(Scalar::Uint(t)) => write!(f, "{}", primitive::uint_ty_to_string(t))?,
 +            TyKind::Slice(t) => {
 +                write!(f, "[")?;
 +                t.hir_fmt(f)?;
 +                write!(f, "]")?;
 +            }
 +            TyKind::Array(t, c) => {
 +                write!(f, "[")?;
 +                t.hir_fmt(f)?;
 +                write!(f, "; ")?;
 +                c.hir_fmt(f)?;
 +                write!(f, "]")?;
 +            }
 +            TyKind::Raw(m, t) | TyKind::Ref(m, _, t) => {
 +                if matches!(self.kind(Interner), TyKind::Raw(..)) {
 +                    write!(
 +                        f,
 +                        "*{}",
 +                        match m {
 +                            Mutability::Not => "const ",
 +                            Mutability::Mut => "mut ",
 +                        }
 +                    )?;
 +                } else {
 +                    write!(
 +                        f,
 +                        "&{}",
 +                        match m {
 +                            Mutability::Not => "",
 +                            Mutability::Mut => "mut ",
 +                        }
 +                    )?;
 +                }
 +
 +                // FIXME: all this just to decide whether to use parentheses...
 +                let contains_impl_fn = |bounds: &[QuantifiedWhereClause]| {
 +                    bounds.iter().any(|bound| {
 +                        if let WhereClause::Implemented(trait_ref) = bound.skip_binders() {
 +                            let trait_ = trait_ref.hir_trait_id();
 +                            fn_traits(f.db.upcast(), trait_).any(|it| it == trait_)
 +                        } else {
 +                            false
 +                        }
 +                    })
 +                };
 +                let (preds_to_print, has_impl_fn_pred) = match t.kind(Interner) {
 +                    TyKind::Dyn(dyn_ty) if dyn_ty.bounds.skip_binders().interned().len() > 1 => {
 +                        let bounds = dyn_ty.bounds.skip_binders().interned();
 +                        (bounds.len(), contains_impl_fn(bounds))
 +                    }
 +                    TyKind::Alias(AliasTy::Opaque(OpaqueTy {
 +                        opaque_ty_id,
 +                        substitution: parameters,
 +                    }))
 +                    | TyKind::OpaqueType(opaque_ty_id, parameters) => {
 +                        let impl_trait_id =
 +                            f.db.lookup_intern_impl_trait_id((*opaque_ty_id).into());
 +                        if let ImplTraitId::ReturnTypeImplTrait(func, idx) = impl_trait_id {
 +                            let datas =
 +                                f.db.return_type_impl_traits(func)
 +                                    .expect("impl trait id without data");
 +                            let data = (*datas)
 +                                .as_ref()
 +                                .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
 +                            let bounds = data.substitute(Interner, parameters);
 +                            let mut len = bounds.skip_binders().len();
 +
 +                            // Don't count Sized but count when it absent
 +                            // (i.e. when explicit ?Sized bound is set).
 +                            let default_sized = SizedByDefault::Sized {
 +                                anchor: func.lookup(f.db.upcast()).module(f.db.upcast()).krate(),
 +                            };
 +                            let sized_bounds = bounds
 +                                .skip_binders()
 +                                .iter()
 +                                .filter(|b| {
 +                                    matches!(
 +                                        b.skip_binders(),
 +                                        WhereClause::Implemented(trait_ref)
 +                                            if default_sized.is_sized_trait(
 +                                                trait_ref.hir_trait_id(),
 +                                                f.db.upcast(),
 +                                            ),
 +                                    )
 +                                })
 +                                .count();
 +                            match sized_bounds {
 +                                0 => len += 1,
 +                                _ => {
 +                                    len = len.saturating_sub(sized_bounds);
 +                                }
 +                            }
 +
 +                            (len, contains_impl_fn(bounds.skip_binders()))
 +                        } else {
 +                            (0, false)
 +                        }
 +                    }
 +                    _ => (0, false),
 +                };
 +
 +                if has_impl_fn_pred && preds_to_print <= 2 {
 +                    return t.hir_fmt(f);
 +                }
 +
 +                if preds_to_print > 1 {
 +                    write!(f, "(")?;
 +                    t.hir_fmt(f)?;
 +                    write!(f, ")")?;
 +                } else {
 +                    t.hir_fmt(f)?;
 +                }
 +            }
 +            TyKind::Tuple(_, substs) => {
 +                if substs.len(Interner) == 1 {
 +                    write!(f, "(")?;
 +                    substs.at(Interner, 0).hir_fmt(f)?;
 +                    write!(f, ",)")?;
 +                } else {
 +                    write!(f, "(")?;
 +                    f.write_joined(&*substs.as_slice(Interner), ", ")?;
 +                    write!(f, ")")?;
 +                }
 +            }
 +            TyKind::Function(fn_ptr) => {
 +                let sig = CallableSig::from_fn_ptr(fn_ptr);
 +                sig.hir_fmt(f)?;
 +            }
 +            TyKind::FnDef(def, parameters) => {
 +                let def = from_chalk(f.db, *def);
 +                let sig = f.db.callable_item_signature(def).substitute(Interner, parameters);
 +                match def {
 +                    CallableDefId::FunctionId(ff) => {
 +                        write!(f, "fn {}", f.db.function_data(ff).name)?
 +                    }
 +                    CallableDefId::StructId(s) => write!(f, "{}", f.db.struct_data(s).name)?,
 +                    CallableDefId::EnumVariantId(e) => {
 +                        write!(f, "{}", f.db.enum_data(e.parent).variants[e.local_id].name)?
 +                    }
 +                };
 +                if parameters.len(Interner) > 0 {
 +                    let generics = generics(f.db.upcast(), def.into());
 +                    let (parent_params, self_param, type_params, const_params, _impl_trait_params) =
 +                        generics.provenance_split();
 +                    let total_len = parent_params + self_param + type_params + const_params;
 +                    // We print all params except implicit impl Trait params. Still a bit weird; should we leave out parent and self?
 +                    if total_len > 0 {
 +                        // `parameters` are in the order of fn's params (including impl traits),
 +                        // parent's params (those from enclosing impl or trait, if any).
 +                        let parameters = parameters.as_slice(Interner);
 +                        let fn_params_len = self_param + type_params + const_params;
 +                        let fn_params = parameters.get(..fn_params_len);
 +                        let parent_params = parameters.get(parameters.len() - parent_params..);
 +                        let params = parent_params.into_iter().chain(fn_params).flatten();
 +                        write!(f, "<")?;
 +                        f.write_joined(params, ", ")?;
 +                        write!(f, ">")?;
 +                    }
 +                }
 +                write!(f, "(")?;
 +                f.write_joined(sig.params(), ", ")?;
 +                write!(f, ")")?;
 +                let ret = sig.ret();
 +                if !ret.is_unit() {
 +                    write!(f, " -> ")?;
 +                    ret.hir_fmt(f)?;
 +                }
 +            }
 +            TyKind::Adt(AdtId(def_id), parameters) => {
 +                match f.display_target {
 +                    DisplayTarget::Diagnostics | DisplayTarget::Test => {
 +                        let name = match *def_id {
 +                            hir_def::AdtId::StructId(it) => f.db.struct_data(it).name.clone(),
 +                            hir_def::AdtId::UnionId(it) => f.db.union_data(it).name.clone(),
 +                            hir_def::AdtId::EnumId(it) => f.db.enum_data(it).name.clone(),
 +                        };
 +                        write!(f, "{}", name)?;
 +                    }
 +                    DisplayTarget::SourceCode { module_id } => {
 +                        if let Some(path) = find_path::find_path(
 +                            f.db.upcast(),
 +                            ItemInNs::Types((*def_id).into()),
 +                            module_id,
 +                            false,
 +                        ) {
 +                            write!(f, "{}", path)?;
 +                        } else {
 +                            return Err(HirDisplayError::DisplaySourceCodeError(
 +                                DisplaySourceCodeError::PathNotFound,
 +                            ));
 +                        }
 +                    }
 +                }
 +
 +                if parameters.len(Interner) > 0 {
 +                    let parameters_to_write = if f.display_target.is_source_code()
 +                        || f.omit_verbose_types()
 +                    {
 +                        match self
 +                            .as_generic_def(f.db)
 +                            .map(|generic_def_id| f.db.generic_defaults(generic_def_id))
 +                            .filter(|defaults| !defaults.is_empty())
 +                        {
 +                            None => parameters.as_slice(Interner),
 +                            Some(default_parameters) => {
 +                                fn should_show(
 +                                    parameter: &GenericArg,
 +                                    default_parameters: &[Binders<GenericArg>],
 +                                    i: usize,
 +                                    parameters: &Substitution,
 +                                ) -> bool {
 +                                    if parameter.ty(Interner).map(|x| x.kind(Interner))
 +                                        == Some(&TyKind::Error)
 +                                    {
 +                                        return true;
 +                                    }
 +                                    if let Some(ConstValue::Concrete(c)) =
 +                                        parameter.constant(Interner).map(|x| x.data(Interner).value)
 +                                    {
 +                                        if c.interned == ConstScalar::Unknown {
 +                                            return true;
 +                                        }
 +                                    }
 +                                    let default_parameter = match default_parameters.get(i) {
 +                                        Some(x) => x,
 +                                        None => return true,
 +                                    };
 +                                    let actual_default =
 +                                        default_parameter.clone().substitute(Interner, &parameters);
 +                                    parameter != &actual_default
 +                                }
 +                                let mut default_from = 0;
 +                                for (i, parameter) in parameters.iter(Interner).enumerate() {
 +                                    if should_show(parameter, &default_parameters, i, parameters) {
 +                                        default_from = i + 1;
 +                                    }
 +                                }
 +                                &parameters.as_slice(Interner)[0..default_from]
 +                            }
 +                        }
 +                    } else {
 +                        parameters.as_slice(Interner)
 +                    };
 +                    if !parameters_to_write.is_empty() {
 +                        write!(f, "<")?;
 +
 +                        if f.display_target.is_source_code() {
 +                            let mut first = true;
 +                            for generic_arg in parameters_to_write {
 +                                if !first {
 +                                    write!(f, ", ")?;
 +                                }
 +                                first = false;
 +
 +                                if generic_arg.ty(Interner).map(|ty| ty.kind(Interner))
 +                                    == Some(&TyKind::Error)
 +                                {
 +                                    write!(f, "_")?;
 +                                } else {
 +                                    generic_arg.hir_fmt(f)?;
 +                                }
 +                            }
 +                        } else {
 +                            f.write_joined(parameters_to_write, ", ")?;
 +                        }
 +
 +                        write!(f, ">")?;
 +                    }
 +                }
 +            }
 +            TyKind::AssociatedType(assoc_type_id, parameters) => {
 +                let type_alias = from_assoc_type_id(*assoc_type_id);
 +                let trait_ = match type_alias.lookup(f.db.upcast()).container {
 +                    ItemContainerId::TraitId(it) => it,
 +                    _ => panic!("not an associated type"),
 +                };
 +                let trait_ = f.db.trait_data(trait_);
 +                let type_alias_data = f.db.type_alias_data(type_alias);
 +
 +                // Use placeholder associated types when the target is test (https://rust-lang.github.io/chalk/book/clauses/type_equality.html#placeholder-associated-types)
 +                if f.display_target.is_test() {
 +                    write!(f, "{}::{}", trait_.name, type_alias_data.name)?;
 +                    // Note that the generic args for the associated type come before those for the
 +                    // trait (including the self type).
 +                    // FIXME: reconsider the generic args order upon formatting?
 +                    if parameters.len(Interner) > 0 {
 +                        write!(f, "<")?;
 +                        f.write_joined(parameters.as_slice(Interner), ", ")?;
 +                        write!(f, ">")?;
 +                    }
 +                } else {
 +                    let projection_ty = ProjectionTy {
 +                        associated_ty_id: to_assoc_type_id(type_alias),
 +                        substitution: parameters.clone(),
 +                    };
 +
 +                    projection_ty.hir_fmt(f)?;
 +                }
 +            }
 +            TyKind::Foreign(type_alias) => {
 +                let type_alias = f.db.type_alias_data(from_foreign_def_id(*type_alias));
 +                write!(f, "{}", type_alias.name)?;
 +            }
 +            TyKind::OpaqueType(opaque_ty_id, parameters) => {
 +                let impl_trait_id = f.db.lookup_intern_impl_trait_id((*opaque_ty_id).into());
 +                match impl_trait_id {
 +                    ImplTraitId::ReturnTypeImplTrait(func, idx) => {
 +                        let datas =
 +                            f.db.return_type_impl_traits(func).expect("impl trait id without data");
 +                        let data = (*datas)
 +                            .as_ref()
 +                            .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
 +                        let bounds = data.substitute(Interner, &parameters);
 +                        let krate = func.lookup(f.db.upcast()).module(f.db.upcast()).krate();
 +                        write_bounds_like_dyn_trait_with_prefix(
 +                            "impl",
 +                            bounds.skip_binders(),
 +                            SizedByDefault::Sized { anchor: krate },
 +                            f,
 +                        )?;
 +                        // FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution
 +                    }
 +                    ImplTraitId::AsyncBlockTypeImplTrait(..) => {
 +                        write!(f, "impl Future<Output = ")?;
 +                        parameters.at(Interner, 0).hir_fmt(f)?;
 +                        write!(f, ">")?;
 +                    }
 +                }
 +            }
 +            TyKind::Closure(.., substs) => {
 +                if f.display_target.is_source_code() {
 +                    return Err(HirDisplayError::DisplaySourceCodeError(
 +                        DisplaySourceCodeError::Closure,
 +                    ));
 +                }
 +                let sig = substs.at(Interner, 0).assert_ty_ref(Interner).callable_sig(f.db);
 +                if let Some(sig) = sig {
 +                    if sig.params().is_empty() {
 +                        write!(f, "||")?;
 +                    } else if f.should_truncate() {
 +                        write!(f, "|{}|", TYPE_HINT_TRUNCATION)?;
 +                    } else {
 +                        write!(f, "|")?;
 +                        f.write_joined(sig.params(), ", ")?;
 +                        write!(f, "|")?;
 +                    };
 +
 +                    write!(f, " -> ")?;
 +                    sig.ret().hir_fmt(f)?;
 +                } else {
 +                    write!(f, "{{closure}}")?;
 +                }
 +            }
 +            TyKind::Placeholder(idx) => {
 +                let id = from_placeholder_idx(f.db, *idx);
 +                let generics = generics(f.db.upcast(), id.parent);
 +                let param_data = &generics.params.type_or_consts[id.local_id];
 +                match param_data {
 +                    TypeOrConstParamData::TypeParamData(p) => match p.provenance {
 +                        TypeParamProvenance::TypeParamList | TypeParamProvenance::TraitSelf => {
 +                            write!(f, "{}", p.name.clone().unwrap_or_else(Name::missing))?
 +                        }
 +                        TypeParamProvenance::ArgumentImplTrait => {
 +                            let substs = generics.placeholder_subst(f.db);
 +                            let bounds =
 +                                f.db.generic_predicates(id.parent)
 +                                    .iter()
 +                                    .map(|pred| pred.clone().substitute(Interner, &substs))
 +                                    .filter(|wc| match &wc.skip_binders() {
 +                                        WhereClause::Implemented(tr) => {
 +                                            &tr.self_type_parameter(Interner) == self
 +                                        }
 +                                        WhereClause::AliasEq(AliasEq {
 +                                            alias: AliasTy::Projection(proj),
 +                                            ty: _,
 +                                        }) => &proj.self_type_parameter(f.db) == self,
 +                                        _ => false,
 +                                    })
 +                                    .collect::<Vec<_>>();
 +                            let krate = id.parent.module(f.db.upcast()).krate();
 +                            write_bounds_like_dyn_trait_with_prefix(
 +                                "impl",
 +                                &bounds,
 +                                SizedByDefault::Sized { anchor: krate },
 +                                f,
 +                            )?;
 +                        }
 +                    },
 +                    TypeOrConstParamData::ConstParamData(p) => {
 +                        write!(f, "{}", p.name)?;
 +                    }
 +                }
 +            }
 +            TyKind::BoundVar(idx) => idx.hir_fmt(f)?,
 +            TyKind::Dyn(dyn_ty) => {
 +                // Reorder bounds to satisfy `write_bounds_like_dyn_trait()`'s expectation.
 +                // FIXME: `Iterator::partition_in_place()` or `Vec::drain_filter()` may make it
 +                // more efficient when either of them hits stable.
 +                let mut bounds: SmallVec<[_; 4]> =
 +                    dyn_ty.bounds.skip_binders().iter(Interner).cloned().collect();
 +                let (auto_traits, others): (SmallVec<[_; 4]>, _) =
 +                    bounds.drain(1..).partition(|b| b.skip_binders().trait_id().is_some());
 +                bounds.extend(others);
 +                bounds.extend(auto_traits);
 +
 +                write_bounds_like_dyn_trait_with_prefix(
 +                    "dyn",
 +                    &bounds,
 +                    SizedByDefault::NotSized,
 +                    f,
 +                )?;
 +            }
 +            TyKind::Alias(AliasTy::Projection(p_ty)) => p_ty.hir_fmt(f)?,
 +            TyKind::Alias(AliasTy::Opaque(opaque_ty)) => {
 +                let impl_trait_id = f.db.lookup_intern_impl_trait_id(opaque_ty.opaque_ty_id.into());
 +                match impl_trait_id {
 +                    ImplTraitId::ReturnTypeImplTrait(func, idx) => {
 +                        let datas =
 +                            f.db.return_type_impl_traits(func).expect("impl trait id without data");
 +                        let data = (*datas)
 +                            .as_ref()
 +                            .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
 +                        let bounds = data.substitute(Interner, &opaque_ty.substitution);
 +                        let krate = func.lookup(f.db.upcast()).module(f.db.upcast()).krate();
 +                        write_bounds_like_dyn_trait_with_prefix(
 +                            "impl",
 +                            bounds.skip_binders(),
 +                            SizedByDefault::Sized { anchor: krate },
 +                            f,
 +                        )?;
 +                    }
 +                    ImplTraitId::AsyncBlockTypeImplTrait(..) => {
 +                        write!(f, "{{async block}}")?;
 +                    }
 +                };
 +            }
 +            TyKind::Error => {
 +                if f.display_target.is_source_code() {
 +                    return Err(HirDisplayError::DisplaySourceCodeError(
 +                        DisplaySourceCodeError::UnknownType,
 +                    ));
 +                }
 +                write!(f, "{{unknown}}")?;
 +            }
 +            TyKind::InferenceVar(..) => write!(f, "_")?,
 +            TyKind::Generator(_, subst) => {
 +                if f.display_target.is_source_code() {
 +                    return Err(HirDisplayError::DisplaySourceCodeError(
 +                        DisplaySourceCodeError::Generator,
 +                    ));
 +                }
 +
 +                let subst = subst.as_slice(Interner);
 +                let a: Option<SmallVec<[&Ty; 3]>> = subst
 +                    .get(subst.len() - 3..)
 +                    .map(|args| args.iter().map(|arg| arg.ty(Interner)).collect())
 +                    .flatten();
 +
 +                if let Some([resume_ty, yield_ty, ret_ty]) = a.as_deref() {
 +                    write!(f, "|")?;
 +                    resume_ty.hir_fmt(f)?;
 +                    write!(f, "|")?;
 +
 +                    write!(f, " yields ")?;
 +                    yield_ty.hir_fmt(f)?;
 +
 +                    write!(f, " -> ")?;
 +                    ret_ty.hir_fmt(f)?;
 +                } else {
 +                    // This *should* be unreachable, but fallback just in case.
 +                    write!(f, "{{generator}}")?;
 +                }
 +            }
 +            TyKind::GeneratorWitness(..) => write!(f, "{{generator witness}}")?,
 +        }
 +        Ok(())
 +    }
 +}
 +
 +impl HirDisplay for CallableSig {
 +    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
 +        write!(f, "fn(")?;
 +        f.write_joined(self.params(), ", ")?;
 +        if self.is_varargs {
 +            if self.params().is_empty() {
 +                write!(f, "...")?;
 +            } else {
 +                write!(f, ", ...")?;
 +            }
 +        }
 +        write!(f, ")")?;
 +        let ret = self.ret();
 +        if !ret.is_unit() {
 +            write!(f, " -> ")?;
 +            ret.hir_fmt(f)?;
 +        }
 +        Ok(())
 +    }
 +}
 +
 +fn fn_traits(db: &dyn DefDatabase, trait_: TraitId) -> impl Iterator<Item = TraitId> {
 +    let krate = trait_.lookup(db).container.krate();
 +    utils::fn_traits(db, krate)
 +}
 +
 +#[derive(Clone, Copy, PartialEq, Eq)]
 +pub enum SizedByDefault {
 +    NotSized,
 +    Sized { anchor: CrateId },
 +}
 +
 +impl SizedByDefault {
 +    fn is_sized_trait(self, trait_: TraitId, db: &dyn DefDatabase) -> bool {
 +        match self {
 +            Self::NotSized => false,
 +            Self::Sized { anchor } => {
 +                let sized_trait = db
 +                    .lang_item(anchor, SmolStr::new_inline("sized"))
 +                    .and_then(|lang_item| lang_item.as_trait());
 +                Some(trait_) == sized_trait
 +            }
 +        }
 +    }
 +}
 +
 +pub fn write_bounds_like_dyn_trait_with_prefix(
 +    prefix: &str,
 +    predicates: &[QuantifiedWhereClause],
 +    default_sized: SizedByDefault,
 +    f: &mut HirFormatter<'_>,
 +) -> Result<(), HirDisplayError> {
 +    write!(f, "{}", prefix)?;
 +    if !predicates.is_empty()
 +        || predicates.is_empty() && matches!(default_sized, SizedByDefault::Sized { .. })
 +    {
 +        write!(f, " ")?;
 +        write_bounds_like_dyn_trait(predicates, default_sized, f)
 +    } else {
 +        Ok(())
 +    }
 +}
 +
 +fn write_bounds_like_dyn_trait(
 +    predicates: &[QuantifiedWhereClause],
 +    default_sized: SizedByDefault,
 +    f: &mut HirFormatter<'_>,
 +) -> Result<(), HirDisplayError> {
 +    // Note: This code is written to produce nice results (i.e.
 +    // corresponding to surface Rust) for types that can occur in
 +    // actual Rust. It will have weird results if the predicates
 +    // aren't as expected (i.e. self types = $0, projection
 +    // predicates for a certain trait come after the Implemented
 +    // predicate for that trait).
 +    let mut first = true;
 +    let mut angle_open = false;
 +    let mut is_fn_trait = false;
 +    let mut is_sized = false;
 +    for p in predicates.iter() {
 +        match p.skip_binders() {
 +            WhereClause::Implemented(trait_ref) => {
 +                let trait_ = trait_ref.hir_trait_id();
 +                if default_sized.is_sized_trait(trait_, f.db.upcast()) {
 +                    is_sized = true;
 +                    if matches!(default_sized, SizedByDefault::Sized { .. }) {
 +                        // Don't print +Sized, but rather +?Sized if absent.
 +                        continue;
 +                    }
 +                }
 +                if !is_fn_trait {
 +                    is_fn_trait = fn_traits(f.db.upcast(), trait_).any(|it| it == trait_);
 +                }
 +                if !is_fn_trait && angle_open {
 +                    write!(f, ">")?;
 +                    angle_open = false;
 +                }
 +                if !first {
 +                    write!(f, " + ")?;
 +                }
 +                // We assume that the self type is ^0.0 (i.e. the
 +                // existential) here, which is the only thing that's
 +                // possible in actual Rust, and hence don't print it
 +                write!(f, "{}", f.db.trait_data(trait_).name)?;
 +                if let [_, params @ ..] = &*trait_ref.substitution.as_slice(Interner) {
 +                    if is_fn_trait {
 +                        if let Some(args) =
 +                            params.first().and_then(|it| it.assert_ty_ref(Interner).as_tuple())
 +                        {
 +                            write!(f, "(")?;
 +                            f.write_joined(args.as_slice(Interner), ", ")?;
 +                            write!(f, ")")?;
 +                        }
 +                    } else if !params.is_empty() {
 +                        write!(f, "<")?;
 +                        f.write_joined(params, ", ")?;
 +                        // there might be assoc type bindings, so we leave the angle brackets open
 +                        angle_open = true;
 +                    }
 +                }
 +            }
 +            WhereClause::AliasEq(alias_eq) if is_fn_trait => {
 +                is_fn_trait = false;
 +                if !alias_eq.ty.is_unit() {
 +                    write!(f, " -> ")?;
 +                    alias_eq.ty.hir_fmt(f)?;
 +                }
 +            }
 +            WhereClause::AliasEq(AliasEq { ty, alias }) => {
 +                // in types in actual Rust, these will always come
 +                // after the corresponding Implemented predicate
 +                if angle_open {
 +                    write!(f, ", ")?;
 +                } else {
 +                    write!(f, "<")?;
 +                    angle_open = true;
 +                }
 +                if let AliasTy::Projection(proj) = alias {
 +                    let assoc_ty_id = from_assoc_type_id(proj.associated_ty_id);
 +                    let type_alias = f.db.type_alias_data(assoc_ty_id);
 +                    write!(f, "{}", type_alias.name)?;
 +
 +                    let proj_arg_count = generics(f.db.upcast(), assoc_ty_id.into()).len_self();
 +                    if proj_arg_count > 0 {
 +                        write!(f, "<")?;
 +                        f.write_joined(
 +                            &proj.substitution.as_slice(Interner)[..proj_arg_count],
 +                            ", ",
 +                        )?;
 +                        write!(f, ">")?;
 +                    }
 +                    write!(f, " = ")?;
 +                }
 +                ty.hir_fmt(f)?;
 +            }
 +
 +            // FIXME implement these
 +            WhereClause::LifetimeOutlives(_) => {}
 +            WhereClause::TypeOutlives(_) => {}
 +        }
 +        first = false;
 +    }
 +    if angle_open {
 +        write!(f, ">")?;
 +    }
 +    if matches!(default_sized, SizedByDefault::Sized { .. }) {
 +        if !is_sized {
 +            write!(f, "{}?Sized", if first { "" } else { " + " })?;
 +        } else if first {
 +            write!(f, "Sized")?;
 +        }
 +    }
 +    Ok(())
 +}
 +
 +fn fmt_trait_ref(
 +    tr: &TraitRef,
 +    f: &mut HirFormatter<'_>,
 +    use_as: bool,
 +) -> Result<(), HirDisplayError> {
 +    if f.should_truncate() {
 +        return write!(f, "{}", TYPE_HINT_TRUNCATION);
 +    }
 +
 +    tr.self_type_parameter(Interner).hir_fmt(f)?;
 +    if use_as {
 +        write!(f, " as ")?;
 +    } else {
 +        write!(f, ": ")?;
 +    }
 +    write!(f, "{}", f.db.trait_data(tr.hir_trait_id()).name)?;
 +    if tr.substitution.len(Interner) > 1 {
 +        write!(f, "<")?;
 +        f.write_joined(&tr.substitution.as_slice(Interner)[1..], ", ")?;
 +        write!(f, ">")?;
 +    }
 +    Ok(())
 +}
 +
 +impl HirDisplay for TraitRef {
 +    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
 +        fmt_trait_ref(self, f, false)
 +    }
 +}
 +
 +impl HirDisplay for WhereClause {
 +    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
 +        if f.should_truncate() {
 +            return write!(f, "{}", TYPE_HINT_TRUNCATION);
 +        }
 +
 +        match self {
 +            WhereClause::Implemented(trait_ref) => trait_ref.hir_fmt(f)?,
 +            WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(projection_ty), ty }) => {
 +                write!(f, "<")?;
 +                fmt_trait_ref(&projection_ty.trait_ref(f.db), f, true)?;
 +                write!(
 +                    f,
 +                    ">::{} = ",
 +                    f.db.type_alias_data(from_assoc_type_id(projection_ty.associated_ty_id)).name,
 +                )?;
 +                ty.hir_fmt(f)?;
 +            }
 +            WhereClause::AliasEq(_) => write!(f, "{{error}}")?,
 +
 +            // FIXME implement these
 +            WhereClause::TypeOutlives(..) => {}
 +            WhereClause::LifetimeOutlives(..) => {}
 +        }
 +        Ok(())
 +    }
 +}
 +
 +impl HirDisplay for LifetimeOutlives {
 +    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
 +        self.a.hir_fmt(f)?;
 +        write!(f, ": ")?;
 +        self.b.hir_fmt(f)
 +    }
 +}
 +
 +impl HirDisplay for Lifetime {
 +    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
 +        self.interned().hir_fmt(f)
 +    }
 +}
 +
 +impl HirDisplay for LifetimeData {
 +    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
 +        match self {
 +            LifetimeData::BoundVar(idx) => idx.hir_fmt(f),
 +            LifetimeData::InferenceVar(_) => write!(f, "_"),
 +            LifetimeData::Placeholder(idx) => {
 +                let id = lt_from_placeholder_idx(f.db, *idx);
 +                let generics = generics(f.db.upcast(), id.parent);
 +                let param_data = &generics.params.lifetimes[id.local_id];
 +                write!(f, "{}", param_data.name)
 +            }
 +            LifetimeData::Static => write!(f, "'static"),
 +            LifetimeData::Empty(_) => Ok(()),
 +            LifetimeData::Erased => Ok(()),
 +            LifetimeData::Phantom(_, _) => Ok(()),
 +        }
 +    }
 +}
 +
 +impl HirDisplay for DomainGoal {
 +    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
 +        match self {
 +            DomainGoal::Holds(wc) => {
 +                write!(f, "Holds(")?;
 +                wc.hir_fmt(f)?;
 +                write!(f, ")")?;
 +            }
 +            _ => write!(f, "?")?,
 +        }
 +        Ok(())
 +    }
 +}
 +
 +pub fn write_visibility(
 +    module_id: ModuleId,
 +    vis: Visibility,
 +    f: &mut HirFormatter<'_>,
 +) -> Result<(), HirDisplayError> {
 +    match vis {
 +        Visibility::Public => write!(f, "pub "),
 +        Visibility::Module(vis_id) => {
 +            let def_map = module_id.def_map(f.db.upcast());
 +            let root_module_id = def_map.module_id(def_map.root());
 +            if vis_id == module_id {
 +                // pub(self) or omitted
 +                Ok(())
 +            } else if root_module_id == vis_id {
 +                write!(f, "pub(crate) ")
 +            } else if module_id.containing_module(f.db.upcast()) == Some(vis_id) {
 +                write!(f, "pub(super) ")
 +            } else {
 +                write!(f, "pub(in ...) ")
 +            }
 +        }
 +    }
 +}
 +
 +impl HirDisplay for TypeRef {
 +    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
 +        match self {
 +            TypeRef::Never => write!(f, "!")?,
 +            TypeRef::Placeholder => write!(f, "_")?,
 +            TypeRef::Tuple(elems) => {
 +                write!(f, "(")?;
 +                f.write_joined(elems, ", ")?;
 +                if elems.len() == 1 {
 +                    write!(f, ",")?;
 +                }
 +                write!(f, ")")?;
 +            }
 +            TypeRef::Path(path) => path.hir_fmt(f)?,
 +            TypeRef::RawPtr(inner, mutability) => {
 +                let mutability = match mutability {
 +                    hir_def::type_ref::Mutability::Shared => "*const ",
 +                    hir_def::type_ref::Mutability::Mut => "*mut ",
 +                };
 +                write!(f, "{}", mutability)?;
 +                inner.hir_fmt(f)?;
 +            }
 +            TypeRef::Reference(inner, lifetime, mutability) => {
 +                let mutability = match mutability {
 +                    hir_def::type_ref::Mutability::Shared => "",
 +                    hir_def::type_ref::Mutability::Mut => "mut ",
 +                };
 +                write!(f, "&")?;
 +                if let Some(lifetime) = lifetime {
 +                    write!(f, "{} ", lifetime.name)?;
 +                }
 +                write!(f, "{}", mutability)?;
 +                inner.hir_fmt(f)?;
 +            }
 +            TypeRef::Array(inner, len) => {
 +                write!(f, "[")?;
 +                inner.hir_fmt(f)?;
 +                write!(f, "; {}]", len)?;
 +            }
 +            TypeRef::Slice(inner) => {
 +                write!(f, "[")?;
 +                inner.hir_fmt(f)?;
 +                write!(f, "]")?;
 +            }
-                     if *is_varargs {
++            &TypeRef::Fn(ref parameters, is_varargs, is_unsafe) => {
 +                // FIXME: Function pointer qualifiers.
++                if is_unsafe {
++                    write!(f, "unsafe ")?;
++                }
 +                write!(f, "fn(")?;
 +                if let Some(((_, return_type), function_parameters)) = parameters.split_last() {
 +                    for index in 0..function_parameters.len() {
 +                        let (param_name, param_type) = &function_parameters[index];
 +                        if let Some(name) = param_name {
 +                            write!(f, "{}: ", name)?;
 +                        }
 +
 +                        param_type.hir_fmt(f)?;
 +
 +                        if index != function_parameters.len() - 1 {
 +                            write!(f, ", ")?;
 +                        }
 +                    }
++                    if is_varargs {
 +                        write!(f, "{}...", if parameters.len() == 1 { "" } else { ", " })?;
 +                    }
 +                    write!(f, ")")?;
 +                    match &return_type {
 +                        TypeRef::Tuple(tup) if tup.is_empty() => {}
 +                        _ => {
 +                            write!(f, " -> ")?;
 +                            return_type.hir_fmt(f)?;
 +                        }
 +                    }
 +                }
 +            }
 +            TypeRef::ImplTrait(bounds) => {
 +                write!(f, "impl ")?;
 +                f.write_joined(bounds, " + ")?;
 +            }
 +            TypeRef::DynTrait(bounds) => {
 +                write!(f, "dyn ")?;
 +                f.write_joined(bounds, " + ")?;
 +            }
 +            TypeRef::Macro(macro_call) => {
 +                let macro_call = macro_call.to_node(f.db.upcast());
 +                let ctx = body::LowerCtx::with_hygiene(f.db.upcast(), &Hygiene::new_unhygienic());
 +                match macro_call.path() {
 +                    Some(path) => match Path::from_src(path, &ctx) {
 +                        Some(path) => path.hir_fmt(f)?,
 +                        None => write!(f, "{{macro}}")?,
 +                    },
 +                    None => write!(f, "{{macro}}")?,
 +                }
 +                write!(f, "!(..)")?;
 +            }
 +            TypeRef::Error => write!(f, "{{error}}")?,
 +        }
 +        Ok(())
 +    }
 +}
 +
 +impl HirDisplay for TypeBound {
 +    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
 +        match self {
 +            TypeBound::Path(path, modifier) => {
 +                match modifier {
 +                    TraitBoundModifier::None => (),
 +                    TraitBoundModifier::Maybe => write!(f, "?")?,
 +                }
 +                path.hir_fmt(f)
 +            }
 +            TypeBound::Lifetime(lifetime) => write!(f, "{}", lifetime.name),
 +            TypeBound::ForLifetime(lifetimes, path) => {
 +                write!(f, "for<{}> ", lifetimes.iter().format(", "))?;
 +                path.hir_fmt(f)
 +            }
 +            TypeBound::Error => write!(f, "{{error}}"),
 +        }
 +    }
 +}
 +
 +impl HirDisplay for Path {
 +    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
 +        match (self.type_anchor(), self.kind()) {
 +            (Some(anchor), _) => {
 +                write!(f, "<")?;
 +                anchor.hir_fmt(f)?;
 +                write!(f, ">")?;
 +            }
 +            (_, PathKind::Plain) => {}
 +            (_, PathKind::Abs) => {}
 +            (_, PathKind::Crate) => write!(f, "crate")?,
 +            (_, PathKind::Super(0)) => write!(f, "self")?,
 +            (_, PathKind::Super(n)) => {
 +                for i in 0..*n {
 +                    if i > 0 {
 +                        write!(f, "::")?;
 +                    }
 +                    write!(f, "super")?;
 +                }
 +            }
 +            (_, PathKind::DollarCrate(id)) => {
 +                // Resolve `$crate` to the crate's display name.
 +                // FIXME: should use the dependency name instead if available, but that depends on
 +                // the crate invoking `HirDisplay`
 +                let crate_graph = f.db.crate_graph();
 +                let name = crate_graph[*id]
 +                    .display_name
 +                    .as_ref()
 +                    .map(|name| name.canonical_name())
 +                    .unwrap_or("$crate");
 +                write!(f, "{name}")?
 +            }
 +        }
 +
 +        for (seg_idx, segment) in self.segments().iter().enumerate() {
 +            if !matches!(self.kind(), PathKind::Plain) || seg_idx > 0 {
 +                write!(f, "::")?;
 +            }
 +            write!(f, "{}", segment.name)?;
 +            if let Some(generic_args) = segment.args_and_bindings {
 +                // We should be in type context, so format as `Foo<Bar>` instead of `Foo::<Bar>`.
 +                // Do we actually format expressions?
 +                if generic_args.desugared_from_fn {
 +                    // First argument will be a tuple, which already includes the parentheses.
 +                    // If the tuple only contains 1 item, write it manually to avoid the trailing `,`.
 +                    if let hir_def::path::GenericArg::Type(TypeRef::Tuple(v)) =
 +                        &generic_args.args[0]
 +                    {
 +                        if v.len() == 1 {
 +                            write!(f, "(")?;
 +                            v[0].hir_fmt(f)?;
 +                            write!(f, ")")?;
 +                        } else {
 +                            generic_args.args[0].hir_fmt(f)?;
 +                        }
 +                    }
 +                    if let Some(ret) = &generic_args.bindings[0].type_ref {
 +                        if !matches!(ret, TypeRef::Tuple(v) if v.is_empty()) {
 +                            write!(f, " -> ")?;
 +                            ret.hir_fmt(f)?;
 +                        }
 +                    }
 +                    return Ok(());
 +                }
 +
 +                write!(f, "<")?;
 +                let mut first = true;
 +                for arg in &generic_args.args {
 +                    if first {
 +                        first = false;
 +                        if generic_args.has_self_type {
 +                            // FIXME: Convert to `<Ty as Trait>` form.
 +                            write!(f, "Self = ")?;
 +                        }
 +                    } else {
 +                        write!(f, ", ")?;
 +                    }
 +                    arg.hir_fmt(f)?;
 +                }
 +                for binding in &generic_args.bindings {
 +                    if first {
 +                        first = false;
 +                    } else {
 +                        write!(f, ", ")?;
 +                    }
 +                    write!(f, "{}", binding.name)?;
 +                    match &binding.type_ref {
 +                        Some(ty) => {
 +                            write!(f, " = ")?;
 +                            ty.hir_fmt(f)?
 +                        }
 +                        None => {
 +                            write!(f, ": ")?;
 +                            f.write_joined(&binding.bounds, " + ")?;
 +                        }
 +                    }
 +                }
 +                write!(f, ">")?;
 +            }
 +        }
 +        Ok(())
 +    }
 +}
 +
 +impl HirDisplay for hir_def::path::GenericArg {
 +    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
 +        match self {
 +            hir_def::path::GenericArg::Type(ty) => ty.hir_fmt(f),
 +            hir_def::path::GenericArg::Const(c) => write!(f, "{}", c),
 +            hir_def::path::GenericArg::Lifetime(lifetime) => write!(f, "{}", lifetime.name),
 +        }
 +    }
 +}
index 0efff651cc174f91fcdcd5af56d4ab6c10ec07ae,0000000000000000000000000000000000000000..0b3c23f5747adc303cf7b66edf8f5ba3a0c636b0
mode 100644,000000..100644
--- /dev/null
@@@ -1,1143 -1,0 +1,1143 @@@
-     ///    let x: &[isize] = &[1, 2, 3];
 +//! Type inference, i.e. the process of walking through the code and determining
 +//! the type of each expression and pattern.
 +//!
 +//! For type inference, compare the implementations in rustc (the various
 +//! check_* methods in rustc_hir_analysis/check/mod.rs are a good entry point) and
 +//! IntelliJ-Rust (org.rust.lang.core.types.infer). Our entry point for
 +//! inference here is the `infer` function, which infers the types of all
 +//! expressions in a given function.
 +//!
 +//! During inference, types (i.e. the `Ty` struct) can contain type 'variables'
 +//! which represent currently unknown types; as we walk through the expressions,
 +//! we might determine that certain variables need to be equal to each other, or
 +//! to certain types. To record this, we use the union-find implementation from
 +//! the `ena` crate, which is extracted from rustc.
 +
 +use std::ops::Index;
 +use std::sync::Arc;
 +
 +use chalk_ir::{cast::Cast, ConstValue, DebruijnIndex, Mutability, Safety, Scalar, TypeFlags};
 +use hir_def::{
 +    body::Body,
 +    builtin_type::BuiltinType,
 +    data::{ConstData, StaticData},
 +    expr::{BindingAnnotation, ExprId, PatId},
 +    lang_item::LangItemTarget,
 +    path::{path, Path},
 +    resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs},
 +    type_ref::TypeRef,
 +    AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, HasModule,
 +    ItemContainerId, Lookup, TraitId, TypeAliasId, VariantId,
 +};
 +use hir_expand::name::{name, Name};
 +use itertools::Either;
 +use la_arena::ArenaMap;
 +use rustc_hash::FxHashMap;
 +use stdx::{always, impl_from};
 +
 +use crate::{
 +    db::HirDatabase, fold_tys, fold_tys_and_consts, infer::coerce::CoerceMany,
 +    lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Const, DomainGoal,
 +    GenericArg, Goal, ImplTraitId, InEnvironment, Interner, ProjectionTy, Substitution,
 +    TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind,
 +};
 +
 +// This lint has a false positive here. See the link below for details.
 +//
 +// https://github.com/rust-lang/rust/issues/57411
 +#[allow(unreachable_pub)]
 +pub use coerce::could_coerce;
 +#[allow(unreachable_pub)]
 +pub use unify::could_unify;
 +
 +pub(crate) mod unify;
 +mod path;
 +mod expr;
 +mod pat;
 +mod coerce;
 +mod closure;
 +
 +/// The entry point of type inference.
 +pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult> {
 +    let _p = profile::span("infer_query");
 +    let resolver = def.resolver(db.upcast());
 +    let body = db.body(def);
 +    let mut ctx = InferenceContext::new(db, def, &body, resolver);
 +
 +    match def {
 +        DefWithBodyId::ConstId(c) => ctx.collect_const(&db.const_data(c)),
 +        DefWithBodyId::FunctionId(f) => ctx.collect_fn(f),
 +        DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_data(s)),
 +        DefWithBodyId::VariantId(v) => {
 +            ctx.return_ty = TyBuilder::builtin(match db.enum_data(v.parent).variant_body_type() {
 +                Either::Left(builtin) => BuiltinType::Int(builtin),
 +                Either::Right(builtin) => BuiltinType::Uint(builtin),
 +            });
 +        }
 +    }
 +
 +    ctx.infer_body();
 +
 +    Arc::new(ctx.resolve_all())
 +}
 +
 +/// Fully normalize all the types found within `ty` in context of `owner` body definition.
 +///
 +/// This is appropriate to use only after type-check: it assumes
 +/// that normalization will succeed, for example.
 +pub(crate) fn normalize(db: &dyn HirDatabase, owner: DefWithBodyId, ty: Ty) -> Ty {
 +    if !ty.data(Interner).flags.intersects(TypeFlags::HAS_PROJECTION) {
 +        return ty;
 +    }
 +    let krate = owner.module(db.upcast()).krate();
 +    let trait_env = owner
 +        .as_generic_def_id()
 +        .map_or_else(|| Arc::new(TraitEnvironment::empty(krate)), |d| db.trait_environment(d));
 +    let mut table = unify::InferenceTable::new(db, trait_env);
 +
 +    let ty_with_vars = table.normalize_associated_types_in(ty);
 +    table.resolve_obligations_as_possible();
 +    table.propagate_diverging_flag();
 +    table.resolve_completely(ty_with_vars)
 +}
 +
 +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
 +enum ExprOrPatId {
 +    ExprId(ExprId),
 +    PatId(PatId),
 +}
 +impl_from!(ExprId, PatId for ExprOrPatId);
 +
 +/// Binding modes inferred for patterns.
 +/// <https://doc.rust-lang.org/reference/patterns.html#binding-modes>
 +#[derive(Copy, Clone, Debug, Eq, PartialEq)]
 +pub enum BindingMode {
 +    Move,
 +    Ref(Mutability),
 +}
 +
 +impl BindingMode {
 +    fn convert(annotation: BindingAnnotation) -> BindingMode {
 +        match annotation {
 +            BindingAnnotation::Unannotated | BindingAnnotation::Mutable => BindingMode::Move,
 +            BindingAnnotation::Ref => BindingMode::Ref(Mutability::Not),
 +            BindingAnnotation::RefMut => BindingMode::Ref(Mutability::Mut),
 +        }
 +    }
 +}
 +
 +impl Default for BindingMode {
 +    fn default() -> Self {
 +        BindingMode::Move
 +    }
 +}
 +
 +/// Used to generalize patterns and assignee expressions.
 +trait PatLike: Into<ExprOrPatId> + Copy {
 +    type BindingMode: Copy;
 +
 +    fn infer(
 +        this: &mut InferenceContext<'_>,
 +        id: Self,
 +        expected_ty: &Ty,
 +        default_bm: Self::BindingMode,
 +    ) -> Ty;
 +}
 +
 +impl PatLike for ExprId {
 +    type BindingMode = ();
 +
 +    fn infer(
 +        this: &mut InferenceContext<'_>,
 +        id: Self,
 +        expected_ty: &Ty,
 +        _: Self::BindingMode,
 +    ) -> Ty {
 +        this.infer_assignee_expr(id, expected_ty)
 +    }
 +}
 +
 +impl PatLike for PatId {
 +    type BindingMode = BindingMode;
 +
 +    fn infer(
 +        this: &mut InferenceContext<'_>,
 +        id: Self,
 +        expected_ty: &Ty,
 +        default_bm: Self::BindingMode,
 +    ) -> Ty {
 +        this.infer_pat(id, expected_ty, default_bm)
 +    }
 +}
 +
 +#[derive(Debug)]
 +pub(crate) struct InferOk<T> {
 +    value: T,
 +    goals: Vec<InEnvironment<Goal>>,
 +}
 +
 +impl<T> InferOk<T> {
 +    fn map<U>(self, f: impl FnOnce(T) -> U) -> InferOk<U> {
 +        InferOk { value: f(self.value), goals: self.goals }
 +    }
 +}
 +
 +#[derive(Debug)]
 +pub(crate) struct TypeError;
 +pub(crate) type InferResult<T> = Result<InferOk<T>, TypeError>;
 +
 +#[derive(Debug, PartialEq, Eq, Clone)]
 +pub enum InferenceDiagnostic {
 +    NoSuchField { expr: ExprId },
 +    BreakOutsideOfLoop { expr: ExprId, is_break: bool },
 +    MismatchedArgCount { call_expr: ExprId, expected: usize, found: usize },
 +}
 +
 +/// A mismatch between an expected and an inferred type.
 +#[derive(Clone, PartialEq, Eq, Debug, Hash)]
 +pub struct TypeMismatch {
 +    pub expected: Ty,
 +    pub actual: Ty,
 +}
 +
 +#[derive(Clone, PartialEq, Eq, Debug)]
 +struct InternedStandardTypes {
 +    unknown: Ty,
 +    bool_: Ty,
 +    unit: Ty,
 +}
 +
 +impl Default for InternedStandardTypes {
 +    fn default() -> Self {
 +        InternedStandardTypes {
 +            unknown: TyKind::Error.intern(Interner),
 +            bool_: TyKind::Scalar(Scalar::Bool).intern(Interner),
 +            unit: TyKind::Tuple(0, Substitution::empty(Interner)).intern(Interner),
 +        }
 +    }
 +}
 +/// Represents coercing a value to a different type of value.
 +///
 +/// We transform values by following a number of `Adjust` steps in order.
 +/// See the documentation on variants of `Adjust` for more details.
 +///
 +/// Here are some common scenarios:
 +///
 +/// 1. The simplest cases are where a pointer is not adjusted fat vs thin.
 +///    Here the pointer will be dereferenced N times (where a dereference can
 +///    happen to raw or borrowed pointers or any smart pointer which implements
 +///    Deref, including Box<_>). The types of dereferences is given by
 +///    `autoderefs`. It can then be auto-referenced zero or one times, indicated
 +///    by `autoref`, to either a raw or borrowed pointer. In these cases unsize is
 +///    `false`.
 +///
 +/// 2. A thin-to-fat coercion involves unsizing the underlying data. We start
 +///    with a thin pointer, deref a number of times, unsize the underlying data,
 +///    then autoref. The 'unsize' phase may change a fixed length array to a
 +///    dynamically sized one, a concrete object to a trait object, or statically
 +///    sized struct to a dynamically sized one. E.g., &[i32; 4] -> &[i32] is
 +///    represented by:
 +///
 +///    ```
 +///    Deref(None) -> [i32; 4],
 +///    Borrow(AutoBorrow::Ref) -> &[i32; 4],
 +///    Unsize -> &[i32],
 +///    ```
 +///
 +///    Note that for a struct, the 'deep' unsizing of the struct is not recorded.
 +///    E.g., `struct Foo<T> { x: T }` we can coerce &Foo<[i32; 4]> to &Foo<[i32]>
 +///    The autoderef and -ref are the same as in the above example, but the type
 +///    stored in `unsize` is `Foo<[i32]>`, we don't store any further detail about
 +///    the underlying conversions from `[i32; 4]` to `[i32]`.
 +///
 +/// 3. Coercing a `Box<T>` to `Box<dyn Trait>` is an interesting special case. In
 +///    that case, we have the pointer we need coming in, so there are no
 +///    autoderefs, and no autoref. Instead we just do the `Unsize` transformation.
 +///    At some point, of course, `Box` should move out of the compiler, in which
 +///    case this is analogous to transforming a struct. E.g., Box<[i32; 4]> ->
 +///    Box<[i32]> is an `Adjust::Unsize` with the target `Box<[i32]>`.
 +#[derive(Clone, Debug, PartialEq, Eq, Hash)]
 +pub struct Adjustment {
 +    pub kind: Adjust,
 +    pub target: Ty,
 +}
 +
 +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 +pub enum Adjust {
 +    /// Go from ! to any type.
 +    NeverToAny,
 +    /// Dereference once, producing a place.
 +    Deref(Option<OverloadedDeref>),
 +    /// Take the address and produce either a `&` or `*` pointer.
 +    Borrow(AutoBorrow),
 +    Pointer(PointerCast),
 +}
 +
 +/// An overloaded autoderef step, representing a `Deref(Mut)::deref(_mut)`
 +/// call, with the signature `&'a T -> &'a U` or `&'a mut T -> &'a mut U`.
 +/// The target type is `U` in both cases, with the region and mutability
 +/// being those shared by both the receiver and the returned reference.
 +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 +pub struct OverloadedDeref(pub Mutability);
 +
 +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 +pub enum AutoBorrow {
 +    /// Converts from T to &T.
 +    Ref(Mutability),
 +    /// Converts from T to *T.
 +    RawPtr(Mutability),
 +}
 +
 +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 +pub enum PointerCast {
 +    /// Go from a fn-item type to a fn-pointer type.
 +    ReifyFnPointer,
 +
 +    /// Go from a safe fn pointer to an unsafe fn pointer.
 +    UnsafeFnPointer,
 +
 +    /// Go from a non-capturing closure to an fn pointer or an unsafe fn pointer.
 +    /// It cannot convert a closure that requires unsafe.
 +    ClosureFnPointer(Safety),
 +
 +    /// Go from a mut raw pointer to a const raw pointer.
 +    MutToConstPointer,
 +
 +    #[allow(dead_code)]
 +    /// Go from `*const [T; N]` to `*const T`
 +    ArrayToPointer,
 +
 +    /// Unsize a pointer/reference value, e.g., `&[T; n]` to
 +    /// `&[T]`. Note that the source could be a thin or fat pointer.
 +    /// This will do things like convert thin pointers to fat
 +    /// pointers, or convert structs containing thin pointers to
 +    /// structs containing fat pointers, or convert between fat
 +    /// pointers. We don't store the details of how the transform is
 +    /// done (in fact, we don't know that, because it might depend on
 +    /// the precise type parameters). We just store the target
 +    /// type. Codegen backends and miri figure out what has to be done
 +    /// based on the precise source/target type at hand.
 +    Unsize,
 +}
 +
 +/// The result of type inference: A mapping from expressions and patterns to types.
 +#[derive(Clone, PartialEq, Eq, Debug, Default)]
 +pub struct InferenceResult {
 +    /// For each method call expr, records the function it resolves to.
 +    method_resolutions: FxHashMap<ExprId, (FunctionId, Substitution)>,
 +    /// For each field access expr, records the field it resolves to.
 +    field_resolutions: FxHashMap<ExprId, FieldId>,
 +    /// For each struct literal or pattern, records the variant it resolves to.
 +    variant_resolutions: FxHashMap<ExprOrPatId, VariantId>,
 +    /// For each associated item record what it resolves to
 +    assoc_resolutions: FxHashMap<ExprOrPatId, AssocItemId>,
 +    pub diagnostics: Vec<InferenceDiagnostic>,
 +    pub type_of_expr: ArenaMap<ExprId, Ty>,
 +    /// For each pattern record the type it resolves to.
 +    ///
 +    /// **Note**: When a pattern type is resolved it may still contain
 +    /// unresolved or missing subpatterns or subpatterns of mismatched types.
 +    pub type_of_pat: ArenaMap<PatId, Ty>,
 +    type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>,
 +    /// Interned common types to return references to.
 +    standard_types: InternedStandardTypes,
 +    /// Stores the types which were implicitly dereferenced in pattern binding modes.
 +    pub pat_adjustments: FxHashMap<PatId, Vec<Ty>>,
 +    pub pat_binding_modes: FxHashMap<PatId, BindingMode>,
 +    pub expr_adjustments: FxHashMap<ExprId, Vec<Adjustment>>,
 +}
 +
 +impl InferenceResult {
 +    pub fn method_resolution(&self, expr: ExprId) -> Option<(FunctionId, Substitution)> {
 +        self.method_resolutions.get(&expr).cloned()
 +    }
 +    pub fn field_resolution(&self, expr: ExprId) -> Option<FieldId> {
 +        self.field_resolutions.get(&expr).copied()
 +    }
 +    pub fn variant_resolution_for_expr(&self, id: ExprId) -> Option<VariantId> {
 +        self.variant_resolutions.get(&id.into()).copied()
 +    }
 +    pub fn variant_resolution_for_pat(&self, id: PatId) -> Option<VariantId> {
 +        self.variant_resolutions.get(&id.into()).copied()
 +    }
 +    pub fn assoc_resolutions_for_expr(&self, id: ExprId) -> Option<AssocItemId> {
 +        self.assoc_resolutions.get(&id.into()).copied()
 +    }
 +    pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option<AssocItemId> {
 +        self.assoc_resolutions.get(&id.into()).copied()
 +    }
 +    pub fn type_mismatch_for_expr(&self, expr: ExprId) -> Option<&TypeMismatch> {
 +        self.type_mismatches.get(&expr.into())
 +    }
 +    pub fn type_mismatch_for_pat(&self, pat: PatId) -> Option<&TypeMismatch> {
 +        self.type_mismatches.get(&pat.into())
 +    }
 +    pub fn expr_type_mismatches(&self) -> impl Iterator<Item = (ExprId, &TypeMismatch)> {
 +        self.type_mismatches.iter().filter_map(|(expr_or_pat, mismatch)| match *expr_or_pat {
 +            ExprOrPatId::ExprId(expr) => Some((expr, mismatch)),
 +            _ => None,
 +        })
 +    }
 +    pub fn pat_type_mismatches(&self) -> impl Iterator<Item = (PatId, &TypeMismatch)> {
 +        self.type_mismatches.iter().filter_map(|(expr_or_pat, mismatch)| match *expr_or_pat {
 +            ExprOrPatId::PatId(pat) => Some((pat, mismatch)),
 +            _ => None,
 +        })
 +    }
 +}
 +
 +impl Index<ExprId> for InferenceResult {
 +    type Output = Ty;
 +
 +    fn index(&self, expr: ExprId) -> &Ty {
 +        self.type_of_expr.get(expr).unwrap_or(&self.standard_types.unknown)
 +    }
 +}
 +
 +impl Index<PatId> for InferenceResult {
 +    type Output = Ty;
 +
 +    fn index(&self, pat: PatId) -> &Ty {
 +        self.type_of_pat.get(pat).unwrap_or(&self.standard_types.unknown)
 +    }
 +}
 +
 +/// The inference context contains all information needed during type inference.
 +#[derive(Clone, Debug)]
 +pub(crate) struct InferenceContext<'a> {
 +    pub(crate) db: &'a dyn HirDatabase,
 +    pub(crate) owner: DefWithBodyId,
 +    pub(crate) body: &'a Body,
 +    pub(crate) resolver: Resolver,
 +    table: unify::InferenceTable<'a>,
 +    trait_env: Arc<TraitEnvironment>,
 +    pub(crate) result: InferenceResult,
 +    /// The return type of the function being inferred, the closure or async block if we're
 +    /// currently within one.
 +    ///
 +    /// We might consider using a nested inference context for checking
 +    /// closures, but currently this is the only field that will change there,
 +    /// so it doesn't make sense.
 +    return_ty: Ty,
 +    /// The resume type and the yield type, respectively, of the generator being inferred.
 +    resume_yield_tys: Option<(Ty, Ty)>,
 +    diverges: Diverges,
 +    breakables: Vec<BreakableContext>,
 +}
 +
 +#[derive(Clone, Debug)]
 +struct BreakableContext {
 +    /// Whether this context contains at least one break expression.
 +    may_break: bool,
 +    /// The coercion target of the context.
 +    coerce: CoerceMany,
 +    /// The optional label of the context.
 +    label: Option<name::Name>,
 +    kind: BreakableKind,
 +}
 +
 +#[derive(Clone, Debug)]
 +enum BreakableKind {
 +    Block,
 +    Loop,
 +    /// A border is something like an async block, closure etc. Anything that prevents
 +    /// breaking/continuing through
 +    Border,
 +}
 +
 +fn find_breakable<'c>(
 +    ctxs: &'c mut [BreakableContext],
 +    label: Option<&name::Name>,
 +) -> Option<&'c mut BreakableContext> {
 +    let mut ctxs = ctxs
 +        .iter_mut()
 +        .rev()
 +        .take_while(|it| matches!(it.kind, BreakableKind::Block | BreakableKind::Loop));
 +    match label {
 +        Some(_) => ctxs.find(|ctx| ctx.label.as_ref() == label),
 +        None => ctxs.find(|ctx| matches!(ctx.kind, BreakableKind::Loop)),
 +    }
 +}
 +
 +fn find_continuable<'c>(
 +    ctxs: &'c mut [BreakableContext],
 +    label: Option<&name::Name>,
 +) -> Option<&'c mut BreakableContext> {
 +    match label {
 +        Some(_) => find_breakable(ctxs, label).filter(|it| matches!(it.kind, BreakableKind::Loop)),
 +        None => find_breakable(ctxs, label),
 +    }
 +}
 +
 +impl<'a> InferenceContext<'a> {
 +    fn new(
 +        db: &'a dyn HirDatabase,
 +        owner: DefWithBodyId,
 +        body: &'a Body,
 +        resolver: Resolver,
 +    ) -> Self {
 +        let krate = owner.module(db.upcast()).krate();
 +        let trait_env = owner
 +            .as_generic_def_id()
 +            .map_or_else(|| Arc::new(TraitEnvironment::empty(krate)), |d| db.trait_environment(d));
 +        InferenceContext {
 +            result: InferenceResult::default(),
 +            table: unify::InferenceTable::new(db, trait_env.clone()),
 +            trait_env,
 +            return_ty: TyKind::Error.intern(Interner), // set in collect_fn_signature
 +            resume_yield_tys: None,
 +            db,
 +            owner,
 +            body,
 +            resolver,
 +            diverges: Diverges::Maybe,
 +            breakables: Vec::new(),
 +        }
 +    }
 +
 +    fn resolve_all(self) -> InferenceResult {
 +        let InferenceContext { mut table, mut result, .. } = self;
 +
 +        // FIXME resolve obligations as well (use Guidance if necessary)
 +        table.resolve_obligations_as_possible();
 +
 +        // make sure diverging type variables are marked as such
 +        table.propagate_diverging_flag();
 +        for ty in result.type_of_expr.values_mut() {
 +            *ty = table.resolve_completely(ty.clone());
 +        }
 +        for ty in result.type_of_pat.values_mut() {
 +            *ty = table.resolve_completely(ty.clone());
 +        }
 +        for mismatch in result.type_mismatches.values_mut() {
 +            mismatch.expected = table.resolve_completely(mismatch.expected.clone());
 +            mismatch.actual = table.resolve_completely(mismatch.actual.clone());
 +        }
 +        for (_, subst) in result.method_resolutions.values_mut() {
 +            *subst = table.resolve_completely(subst.clone());
 +        }
 +        for adjustment in result.expr_adjustments.values_mut().flatten() {
 +            adjustment.target = table.resolve_completely(adjustment.target.clone());
 +        }
 +        for adjustment in result.pat_adjustments.values_mut().flatten() {
 +            *adjustment = table.resolve_completely(adjustment.clone());
 +        }
 +        result
 +    }
 +
 +    fn collect_const(&mut self, data: &ConstData) {
 +        self.return_ty = self.make_ty(&data.type_ref);
 +    }
 +
 +    fn collect_static(&mut self, data: &StaticData) {
 +        self.return_ty = self.make_ty(&data.type_ref);
 +    }
 +
 +    fn collect_fn(&mut self, func: FunctionId) {
 +        let data = self.db.function_data(func);
 +        let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver)
 +            .with_impl_trait_mode(ImplTraitLoweringMode::Param);
 +        let param_tys =
 +            data.params.iter().map(|(_, type_ref)| ctx.lower_ty(type_ref)).collect::<Vec<_>>();
 +        for (ty, pat) in param_tys.into_iter().zip(self.body.params.iter()) {
 +            let ty = self.insert_type_vars(ty);
 +            let ty = self.normalize_associated_types_in(ty);
 +
 +            self.infer_pat(*pat, &ty, BindingMode::default());
 +        }
 +        let error_ty = &TypeRef::Error;
 +        let return_ty = if data.has_async_kw() {
 +            data.async_ret_type.as_deref().unwrap_or(error_ty)
 +        } else {
 +            &*data.ret_type
 +        };
 +        let return_ty = self.make_ty_with_mode(return_ty, ImplTraitLoweringMode::Opaque);
 +        self.return_ty = return_ty;
 +
 +        if let Some(rpits) = self.db.return_type_impl_traits(func) {
 +            // RPIT opaque types use substitution of their parent function.
 +            let fn_placeholders = TyBuilder::placeholder_subst(self.db, func);
 +            self.return_ty = fold_tys(
 +                self.return_ty.clone(),
 +                |ty, _| {
 +                    let opaque_ty_id = match ty.kind(Interner) {
 +                        TyKind::OpaqueType(opaque_ty_id, _) => *opaque_ty_id,
 +                        _ => return ty,
 +                    };
 +                    let idx = match self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) {
 +                        ImplTraitId::ReturnTypeImplTrait(_, idx) => idx,
 +                        _ => unreachable!(),
 +                    };
 +                    let bounds = (*rpits).map_ref(|rpits| {
 +                        rpits.impl_traits[idx as usize].bounds.map_ref(|it| it.into_iter())
 +                    });
 +                    let var = self.table.new_type_var();
 +                    let var_subst = Substitution::from1(Interner, var.clone());
 +                    for bound in bounds {
 +                        let predicate =
 +                            bound.map(|it| it.cloned()).substitute(Interner, &fn_placeholders);
 +                        let (var_predicate, binders) = predicate
 +                            .substitute(Interner, &var_subst)
 +                            .into_value_and_skipped_binders();
 +                        always!(binders.len(Interner) == 0); // quantified where clauses not yet handled
 +                        self.push_obligation(var_predicate.cast(Interner));
 +                    }
 +                    var
 +                },
 +                DebruijnIndex::INNERMOST,
 +            );
 +        }
 +    }
 +
 +    fn infer_body(&mut self) {
 +        self.infer_expr_coerce(self.body.body_expr, &Expectation::has_type(self.return_ty.clone()));
 +    }
 +
 +    fn write_expr_ty(&mut self, expr: ExprId, ty: Ty) {
 +        self.result.type_of_expr.insert(expr, ty);
 +    }
 +
 +    fn write_expr_adj(&mut self, expr: ExprId, adjustments: Vec<Adjustment>) {
 +        self.result.expr_adjustments.insert(expr, adjustments);
 +    }
 +
 +    fn write_method_resolution(&mut self, expr: ExprId, func: FunctionId, subst: Substitution) {
 +        self.result.method_resolutions.insert(expr, (func, subst));
 +    }
 +
 +    fn write_variant_resolution(&mut self, id: ExprOrPatId, variant: VariantId) {
 +        self.result.variant_resolutions.insert(id, variant);
 +    }
 +
 +    fn write_assoc_resolution(&mut self, id: ExprOrPatId, item: AssocItemId) {
 +        self.result.assoc_resolutions.insert(id, item);
 +    }
 +
 +    fn write_pat_ty(&mut self, pat: PatId, ty: Ty) {
 +        self.result.type_of_pat.insert(pat, ty);
 +    }
 +
 +    fn push_diagnostic(&mut self, diagnostic: InferenceDiagnostic) {
 +        self.result.diagnostics.push(diagnostic);
 +    }
 +
 +    fn make_ty_with_mode(
 +        &mut self,
 +        type_ref: &TypeRef,
 +        impl_trait_mode: ImplTraitLoweringMode,
 +    ) -> Ty {
 +        // FIXME use right resolver for block
 +        let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver)
 +            .with_impl_trait_mode(impl_trait_mode);
 +        let ty = ctx.lower_ty(type_ref);
 +        let ty = self.insert_type_vars(ty);
 +        self.normalize_associated_types_in(ty)
 +    }
 +
 +    fn make_ty(&mut self, type_ref: &TypeRef) -> Ty {
 +        self.make_ty_with_mode(type_ref, ImplTraitLoweringMode::Disallowed)
 +    }
 +
 +    fn err_ty(&self) -> Ty {
 +        self.result.standard_types.unknown.clone()
 +    }
 +
 +    /// Replaces ConstScalar::Unknown by a new type var, so we can maybe still infer it.
 +    fn insert_const_vars_shallow(&mut self, c: Const) -> Const {
 +        let data = c.data(Interner);
 +        match data.value {
 +            ConstValue::Concrete(cc) => match cc.interned {
 +                hir_def::type_ref::ConstScalar::Unknown => {
 +                    self.table.new_const_var(data.ty.clone())
 +                }
 +                _ => c,
 +            },
 +            _ => c,
 +        }
 +    }
 +
 +    /// Replaces Ty::Unknown by a new type var, so we can maybe still infer it.
 +    fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty {
 +        match ty.kind(Interner) {
 +            TyKind::Error => self.table.new_type_var(),
 +            TyKind::InferenceVar(..) => {
 +                let ty_resolved = self.resolve_ty_shallow(&ty);
 +                if ty_resolved.is_unknown() {
 +                    self.table.new_type_var()
 +                } else {
 +                    ty
 +                }
 +            }
 +            _ => ty,
 +        }
 +    }
 +
 +    fn insert_type_vars(&mut self, ty: Ty) -> Ty {
 +        fold_tys_and_consts(
 +            ty,
 +            |x, _| match x {
 +                Either::Left(ty) => Either::Left(self.insert_type_vars_shallow(ty)),
 +                Either::Right(c) => Either::Right(self.insert_const_vars_shallow(c)),
 +            },
 +            DebruijnIndex::INNERMOST,
 +        )
 +    }
 +
 +    fn push_obligation(&mut self, o: DomainGoal) {
 +        self.table.register_obligation(o.cast(Interner));
 +    }
 +
 +    fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool {
 +        self.table.unify(ty1, ty2)
 +    }
 +
 +    /// Recurses through the given type, normalizing associated types mentioned
 +    /// in it by replacing them by type variables and registering obligations to
 +    /// resolve later. This should be done once for every type we get from some
 +    /// type annotation (e.g. from a let type annotation, field type or function
 +    /// call). `make_ty` handles this already, but e.g. for field types we need
 +    /// to do it as well.
 +    fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty {
 +        self.table.normalize_associated_types_in(ty)
 +    }
 +
 +    fn resolve_ty_shallow(&mut self, ty: &Ty) -> Ty {
 +        self.table.resolve_ty_shallow(ty)
 +    }
 +
 +    fn resolve_associated_type(&mut self, inner_ty: Ty, assoc_ty: Option<TypeAliasId>) -> Ty {
 +        self.resolve_associated_type_with_params(inner_ty, assoc_ty, &[])
 +    }
 +
 +    fn resolve_associated_type_with_params(
 +        &mut self,
 +        inner_ty: Ty,
 +        assoc_ty: Option<TypeAliasId>,
 +        // FIXME(GATs): these are args for the trait ref, args for assoc type itself should be
 +        // handled when we support them.
 +        params: &[GenericArg],
 +    ) -> Ty {
 +        match assoc_ty {
 +            Some(res_assoc_ty) => {
 +                let trait_ = match res_assoc_ty.lookup(self.db.upcast()).container {
 +                    hir_def::ItemContainerId::TraitId(trait_) => trait_,
 +                    _ => panic!("resolve_associated_type called with non-associated type"),
 +                };
 +                let ty = self.table.new_type_var();
 +                let mut param_iter = params.iter().cloned();
 +                let trait_ref = TyBuilder::trait_ref(self.db, trait_)
 +                    .push(inner_ty)
 +                    .fill(|_| param_iter.next().unwrap())
 +                    .build();
 +                let alias_eq = AliasEq {
 +                    alias: AliasTy::Projection(ProjectionTy {
 +                        associated_ty_id: to_assoc_type_id(res_assoc_ty),
 +                        substitution: trait_ref.substitution.clone(),
 +                    }),
 +                    ty: ty.clone(),
 +                };
 +                self.push_obligation(trait_ref.cast(Interner));
 +                self.push_obligation(alias_eq.cast(Interner));
 +                ty
 +            }
 +            None => self.err_ty(),
 +        }
 +    }
 +
 +    fn resolve_variant(&mut self, path: Option<&Path>, value_ns: bool) -> (Ty, Option<VariantId>) {
 +        let path = match path {
 +            Some(path) => path,
 +            None => return (self.err_ty(), None),
 +        };
 +        let resolver = &self.resolver;
 +        let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
 +        // FIXME: this should resolve assoc items as well, see this example:
 +        // https://play.rust-lang.org/?gist=087992e9e22495446c01c0d4e2d69521
 +        let (resolution, unresolved) = if value_ns {
 +            match resolver.resolve_path_in_value_ns(self.db.upcast(), path.mod_path()) {
 +                Some(ResolveValueResult::ValueNs(value)) => match value {
 +                    ValueNs::EnumVariantId(var) => {
 +                        let substs = ctx.substs_from_path(path, var.into(), true);
 +                        let ty = self.db.ty(var.parent.into());
 +                        let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
 +                        return (ty, Some(var.into()));
 +                    }
 +                    ValueNs::StructId(strukt) => {
 +                        let substs = ctx.substs_from_path(path, strukt.into(), true);
 +                        let ty = self.db.ty(strukt.into());
 +                        let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
 +                        return (ty, Some(strukt.into()));
 +                    }
 +                    ValueNs::ImplSelf(impl_id) => (TypeNs::SelfType(impl_id), None),
 +                    _ => return (self.err_ty(), None),
 +                },
 +                Some(ResolveValueResult::Partial(typens, unresolved)) => (typens, Some(unresolved)),
 +                None => return (self.err_ty(), None),
 +            }
 +        } else {
 +            match resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) {
 +                Some(it) => it,
 +                None => return (self.err_ty(), None),
 +            }
 +        };
 +        return match resolution {
 +            TypeNs::AdtId(AdtId::StructId(strukt)) => {
 +                let substs = ctx.substs_from_path(path, strukt.into(), true);
 +                let ty = self.db.ty(strukt.into());
 +                let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
 +                forbid_unresolved_segments((ty, Some(strukt.into())), unresolved)
 +            }
 +            TypeNs::AdtId(AdtId::UnionId(u)) => {
 +                let substs = ctx.substs_from_path(path, u.into(), true);
 +                let ty = self.db.ty(u.into());
 +                let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
 +                forbid_unresolved_segments((ty, Some(u.into())), unresolved)
 +            }
 +            TypeNs::EnumVariantId(var) => {
 +                let substs = ctx.substs_from_path(path, var.into(), true);
 +                let ty = self.db.ty(var.parent.into());
 +                let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
 +                forbid_unresolved_segments((ty, Some(var.into())), unresolved)
 +            }
 +            TypeNs::SelfType(impl_id) => {
 +                let generics = crate::utils::generics(self.db.upcast(), impl_id.into());
 +                let substs = generics.placeholder_subst(self.db);
 +                let ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs);
 +                self.resolve_variant_on_alias(ty, unresolved, path)
 +            }
 +            TypeNs::TypeAliasId(it) => {
 +                let container = it.lookup(self.db.upcast()).container;
 +                let parent_subst = match container {
 +                    ItemContainerId::TraitId(id) => {
 +                        let subst = TyBuilder::subst_for_def(self.db, id, None)
 +                            .fill_with_inference_vars(&mut self.table)
 +                            .build();
 +                        Some(subst)
 +                    }
 +                    // Type aliases do not exist in impls.
 +                    _ => None,
 +                };
 +                let ty = TyBuilder::def_ty(self.db, it.into(), parent_subst)
 +                    .fill_with_inference_vars(&mut self.table)
 +                    .build();
 +                self.resolve_variant_on_alias(ty, unresolved, path)
 +            }
 +            TypeNs::AdtSelfType(_) => {
 +                // FIXME this could happen in array size expressions, once we're checking them
 +                (self.err_ty(), None)
 +            }
 +            TypeNs::GenericParam(_) => {
 +                // FIXME potentially resolve assoc type
 +                (self.err_ty(), None)
 +            }
 +            TypeNs::AdtId(AdtId::EnumId(_)) | TypeNs::BuiltinType(_) | TypeNs::TraitId(_) => {
 +                // FIXME diagnostic
 +                (self.err_ty(), None)
 +            }
 +        };
 +
 +        fn forbid_unresolved_segments(
 +            result: (Ty, Option<VariantId>),
 +            unresolved: Option<usize>,
 +        ) -> (Ty, Option<VariantId>) {
 +            if unresolved.is_none() {
 +                result
 +            } else {
 +                // FIXME diagnostic
 +                (TyKind::Error.intern(Interner), None)
 +            }
 +        }
 +    }
 +
 +    fn resolve_variant_on_alias(
 +        &mut self,
 +        ty: Ty,
 +        unresolved: Option<usize>,
 +        path: &Path,
 +    ) -> (Ty, Option<VariantId>) {
 +        let remaining = unresolved.map(|x| path.segments().skip(x).len()).filter(|x| x > &0);
 +        match remaining {
 +            None => {
 +                let variant = ty.as_adt().and_then(|(adt_id, _)| match adt_id {
 +                    AdtId::StructId(s) => Some(VariantId::StructId(s)),
 +                    AdtId::UnionId(u) => Some(VariantId::UnionId(u)),
 +                    AdtId::EnumId(_) => {
 +                        // FIXME Error E0071, expected struct, variant or union type, found enum `Foo`
 +                        None
 +                    }
 +                });
 +                (ty, variant)
 +            }
 +            Some(1) => {
 +                let segment = path.mod_path().segments().last().unwrap();
 +                // this could be an enum variant or associated type
 +                if let Some((AdtId::EnumId(enum_id), _)) = ty.as_adt() {
 +                    let enum_data = self.db.enum_data(enum_id);
 +                    if let Some(local_id) = enum_data.variant(segment) {
 +                        let variant = EnumVariantId { parent: enum_id, local_id };
 +                        return (ty, Some(variant.into()));
 +                    }
 +                }
 +                // FIXME potentially resolve assoc type
 +                (self.err_ty(), None)
 +            }
 +            Some(_) => {
 +                // FIXME diagnostic
 +                (self.err_ty(), None)
 +            }
 +        }
 +    }
 +
 +    fn resolve_lang_item(&self, name: Name) -> Option<LangItemTarget> {
 +        let krate = self.resolver.krate();
 +        self.db.lang_item(krate, name.to_smol_str())
 +    }
 +
 +    fn resolve_into_iter_item(&self) -> Option<TypeAliasId> {
 +        let path = path![core::iter::IntoIterator];
 +        let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?;
 +        self.db.trait_data(trait_).associated_type_by_name(&name![IntoIter])
 +    }
 +
 +    fn resolve_iterator_item(&self) -> Option<TypeAliasId> {
 +        let path = path![core::iter::Iterator];
 +        let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?;
 +        self.db.trait_data(trait_).associated_type_by_name(&name![Item])
 +    }
 +
 +    fn resolve_ops_try_ok(&self) -> Option<TypeAliasId> {
 +        // FIXME resolve via lang_item once try v2 is stable
 +        let path = path![core::ops::Try];
 +        let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?;
 +        let trait_data = self.db.trait_data(trait_);
 +        trait_data
 +            // FIXME remove once try v2 is stable
 +            .associated_type_by_name(&name![Ok])
 +            .or_else(|| trait_data.associated_type_by_name(&name![Output]))
 +    }
 +
 +    fn resolve_ops_neg_output(&self) -> Option<TypeAliasId> {
 +        let trait_ = self.resolve_lang_item(name![neg])?.as_trait()?;
 +        self.db.trait_data(trait_).associated_type_by_name(&name![Output])
 +    }
 +
 +    fn resolve_ops_not_output(&self) -> Option<TypeAliasId> {
 +        let trait_ = self.resolve_lang_item(name![not])?.as_trait()?;
 +        self.db.trait_data(trait_).associated_type_by_name(&name![Output])
 +    }
 +
 +    fn resolve_future_future_output(&self) -> Option<TypeAliasId> {
 +        let trait_ = self
 +            .resolver
 +            .resolve_known_trait(self.db.upcast(), &path![core::future::IntoFuture])
 +            .or_else(|| self.resolve_lang_item(name![future_trait])?.as_trait())?;
 +        self.db.trait_data(trait_).associated_type_by_name(&name![Output])
 +    }
 +
 +    fn resolve_boxed_box(&self) -> Option<AdtId> {
 +        let struct_ = self.resolve_lang_item(name![owned_box])?.as_struct()?;
 +        Some(struct_.into())
 +    }
 +
 +    fn resolve_range_full(&self) -> Option<AdtId> {
 +        let path = path![core::ops::RangeFull];
 +        let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
 +        Some(struct_.into())
 +    }
 +
 +    fn resolve_range(&self) -> Option<AdtId> {
 +        let path = path![core::ops::Range];
 +        let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
 +        Some(struct_.into())
 +    }
 +
 +    fn resolve_range_inclusive(&self) -> Option<AdtId> {
 +        let path = path![core::ops::RangeInclusive];
 +        let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
 +        Some(struct_.into())
 +    }
 +
 +    fn resolve_range_from(&self) -> Option<AdtId> {
 +        let path = path![core::ops::RangeFrom];
 +        let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
 +        Some(struct_.into())
 +    }
 +
 +    fn resolve_range_to(&self) -> Option<AdtId> {
 +        let path = path![core::ops::RangeTo];
 +        let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
 +        Some(struct_.into())
 +    }
 +
 +    fn resolve_range_to_inclusive(&self) -> Option<AdtId> {
 +        let path = path![core::ops::RangeToInclusive];
 +        let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
 +        Some(struct_.into())
 +    }
 +
 +    fn resolve_ops_index(&self) -> Option<TraitId> {
 +        self.resolve_lang_item(name![index])?.as_trait()
 +    }
 +
 +    fn resolve_ops_index_output(&self) -> Option<TypeAliasId> {
 +        let trait_ = self.resolve_ops_index()?;
 +        self.db.trait_data(trait_).associated_type_by_name(&name![Output])
 +    }
 +}
 +
 +/// When inferring an expression, we propagate downward whatever type hint we
 +/// are able in the form of an `Expectation`.
 +#[derive(Clone, PartialEq, Eq, Debug)]
 +pub(crate) enum Expectation {
 +    None,
 +    HasType(Ty),
 +    // Castable(Ty), // rustc has this, we currently just don't propagate an expectation for casts
 +    RValueLikeUnsized(Ty),
 +}
 +
 +impl Expectation {
 +    /// The expectation that the type of the expression needs to equal the given
 +    /// type.
 +    fn has_type(ty: Ty) -> Self {
 +        if ty.is_unknown() {
 +            // FIXME: get rid of this?
 +            Expectation::None
 +        } else {
 +            Expectation::HasType(ty)
 +        }
 +    }
 +
 +    fn from_option(ty: Option<Ty>) -> Self {
 +        ty.map_or(Expectation::None, Expectation::HasType)
 +    }
 +
 +    /// The following explanation is copied straight from rustc:
 +    /// Provides an expectation for an rvalue expression given an *optional*
 +    /// hint, which is not required for type safety (the resulting type might
 +    /// be checked higher up, as is the case with `&expr` and `box expr`), but
 +    /// is useful in determining the concrete type.
 +    ///
 +    /// The primary use case is where the expected type is a fat pointer,
 +    /// like `&[isize]`. For example, consider the following statement:
 +    ///
++    ///     let x: &[isize] = &[1, 2, 3];
 +    ///
 +    /// In this case, the expected type for the `&[1, 2, 3]` expression is
 +    /// `&[isize]`. If however we were to say that `[1, 2, 3]` has the
 +    /// expectation `ExpectHasType([isize])`, that would be too strong --
 +    /// `[1, 2, 3]` does not have the type `[isize]` but rather `[isize; 3]`.
 +    /// It is only the `&[1, 2, 3]` expression as a whole that can be coerced
 +    /// to the type `&[isize]`. Therefore, we propagate this more limited hint,
 +    /// which still is useful, because it informs integer literals and the like.
 +    /// See the test case `test/ui/coerce-expect-unsized.rs` and #20169
 +    /// for examples of where this comes up,.
 +    fn rvalue_hint(table: &mut unify::InferenceTable<'_>, ty: Ty) -> Self {
 +        // FIXME: do struct_tail_without_normalization
 +        match table.resolve_ty_shallow(&ty).kind(Interner) {
 +            TyKind::Slice(_) | TyKind::Str | TyKind::Dyn(_) => Expectation::RValueLikeUnsized(ty),
 +            _ => Expectation::has_type(ty),
 +        }
 +    }
 +
 +    /// This expresses no expectation on the type.
 +    fn none() -> Self {
 +        Expectation::None
 +    }
 +
 +    fn resolve(&self, table: &mut unify::InferenceTable<'_>) -> Expectation {
 +        match self {
 +            Expectation::None => Expectation::None,
 +            Expectation::HasType(t) => Expectation::HasType(table.resolve_ty_shallow(t)),
 +            Expectation::RValueLikeUnsized(t) => {
 +                Expectation::RValueLikeUnsized(table.resolve_ty_shallow(t))
 +            }
 +        }
 +    }
 +
 +    fn to_option(&self, table: &mut unify::InferenceTable<'_>) -> Option<Ty> {
 +        match self.resolve(table) {
 +            Expectation::None => None,
 +            Expectation::HasType(t) |
 +            // Expectation::Castable(t) |
 +            Expectation::RValueLikeUnsized(t) => Some(t),
 +        }
 +    }
 +
 +    fn only_has_type(&self, table: &mut unify::InferenceTable<'_>) -> Option<Ty> {
 +        match self {
 +            Expectation::HasType(t) => Some(table.resolve_ty_shallow(t)),
 +            // Expectation::Castable(_) |
 +            Expectation::RValueLikeUnsized(_) | Expectation::None => None,
 +        }
 +    }
 +
 +    /// Comment copied from rustc:
 +    /// Disregard "castable to" expectations because they
 +    /// can lead us astray. Consider for example `if cond
 +    /// {22} else {c} as u8` -- if we propagate the
 +    /// "castable to u8" constraint to 22, it will pick the
 +    /// type 22u8, which is overly constrained (c might not
 +    /// be a u8). In effect, the problem is that the
 +    /// "castable to" expectation is not the tightest thing
 +    /// we can say, so we want to drop it in this case.
 +    /// The tightest thing we can say is "must unify with
 +    /// else branch". Note that in the case of a "has type"
 +    /// constraint, this limitation does not hold.
 +    ///
 +    /// If the expected type is just a type variable, then don't use
 +    /// an expected type. Otherwise, we might write parts of the type
 +    /// when checking the 'then' block which are incompatible with the
 +    /// 'else' branch.
 +    fn adjust_for_branches(&self, table: &mut unify::InferenceTable<'_>) -> Expectation {
 +        match self {
 +            Expectation::HasType(ety) => {
 +                let ety = table.resolve_ty_shallow(ety);
 +                if !ety.is_ty_var() {
 +                    Expectation::HasType(ety)
 +                } else {
 +                    Expectation::None
 +                }
 +            }
 +            Expectation::RValueLikeUnsized(ety) => Expectation::RValueLikeUnsized(ety.clone()),
 +            _ => Expectation::None,
 +        }
 +    }
 +}
 +
 +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
 +enum Diverges {
 +    Maybe,
 +    Always,
 +}
 +
 +impl Diverges {
 +    fn is_always(self) -> bool {
 +        self == Diverges::Always
 +    }
 +}
 +
 +impl std::ops::BitAnd for Diverges {
 +    type Output = Self;
 +    fn bitand(self, other: Self) -> Self {
 +        std::cmp::min(self, other)
 +    }
 +}
 +
 +impl std::ops::BitOr for Diverges {
 +    type Output = Self;
 +    fn bitor(self, other: Self) -> Self {
 +        std::cmp::max(self, other)
 +    }
 +}
 +
 +impl std::ops::BitAndAssign for Diverges {
 +    fn bitand_assign(&mut self, other: Self) {
 +        *self = *self & other;
 +    }
 +}
 +
 +impl std::ops::BitOrAssign for Diverges {
 +    fn bitor_assign(&mut self, other: Self) {
 +        *self = *self | other;
 +    }
 +}
index f56108b26c45bdaea3096f4616828c28ba260d12,0000000000000000000000000000000000000000..b1f4de826077542ad5b54ceaa1d52fb16b99d992
mode 100644,000000..100644
--- /dev/null
@@@ -1,1533 -1,0 +1,1534 @@@
 +//! Type inference for expressions.
 +
 +use std::{
 +    collections::hash_map::Entry,
 +    iter::{repeat, repeat_with},
 +    mem,
 +};
 +
 +use chalk_ir::{
 +    cast::Cast, fold::Shift, DebruijnIndex, GenericArgData, Mutability, TyVariableKind,
 +};
 +use hir_def::{
 +    expr::{
 +        ArithOp, Array, BinaryOp, ClosureKind, CmpOp, Expr, ExprId, LabelId, Literal, Statement,
 +        UnaryOp,
 +    },
 +    generics::TypeOrConstParamData,
 +    path::{GenericArg, GenericArgs},
 +    resolver::resolver_for_expr,
 +    ConstParamId, FieldId, ItemContainerId, Lookup,
 +};
 +use hir_expand::name::Name;
 +use stdx::always;
 +use syntax::ast::RangeOp;
 +
 +use crate::{
 +    autoderef::{self, Autoderef},
 +    consteval,
 +    infer::{coerce::CoerceMany, find_continuable, BreakableKind},
 +    lower::{
 +        const_or_path_to_chalk, generic_arg_to_chalk, lower_to_chalk_mutability, ParamLoweringMode,
 +    },
 +    mapping::{from_chalk, ToChalk},
 +    method_resolution::{self, lang_names_for_bin_op, VisibleFromModule},
 +    primitive::{self, UintTy},
 +    static_lifetime, to_chalk_trait_id,
 +    utils::{generics, Generics},
 +    AdtId, Binders, CallableDefId, FnPointer, FnSig, FnSubst, Interner, Rawness, Scalar,
 +    Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind,
 +};
 +
 +use super::{
 +    coerce::auto_deref_adjust_steps, find_breakable, BindingMode, BreakableContext, Diverges,
 +    Expectation, InferenceContext, InferenceDiagnostic, TypeMismatch,
 +};
 +
 +impl<'a> InferenceContext<'a> {
 +    pub(crate) fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
 +        let ty = self.infer_expr_inner(tgt_expr, expected);
 +        if let Some(expected_ty) = expected.only_has_type(&mut self.table) {
 +            let could_unify = self.unify(&ty, &expected_ty);
 +            if !could_unify {
 +                self.result.type_mismatches.insert(
 +                    tgt_expr.into(),
 +                    TypeMismatch { expected: expected_ty, actual: ty.clone() },
 +                );
 +            }
 +        }
 +        ty
 +    }
 +
 +    /// Infer type of expression with possibly implicit coerce to the expected type.
 +    /// Return the type after possible coercion.
 +    pub(super) fn infer_expr_coerce(&mut self, expr: ExprId, expected: &Expectation) -> Ty {
 +        let ty = self.infer_expr_inner(expr, expected);
 +        if let Some(target) = expected.only_has_type(&mut self.table) {
 +            match self.coerce(Some(expr), &ty, &target) {
 +                Ok(res) => res,
 +                Err(_) => {
 +                    self.result.type_mismatches.insert(
 +                        expr.into(),
 +                        TypeMismatch { expected: target.clone(), actual: ty.clone() },
 +                    );
 +                    target
 +                }
 +            }
 +        } else {
 +            ty
 +        }
 +    }
 +
 +    fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
 +        self.db.unwind_if_cancelled();
 +
 +        let ty = match &self.body[tgt_expr] {
 +            Expr::Missing => self.err_ty(),
 +            &Expr::If { condition, then_branch, else_branch } => {
++                let expected = &expected.adjust_for_branches(&mut self.table);
 +                self.infer_expr(
 +                    condition,
 +                    &Expectation::has_type(TyKind::Scalar(Scalar::Bool).intern(Interner)),
 +                );
 +
 +                let condition_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
 +                let mut both_arms_diverge = Diverges::Always;
 +
 +                let result_ty = self.table.new_type_var();
 +                let then_ty = self.infer_expr_inner(then_branch, expected);
 +                both_arms_diverge &= mem::replace(&mut self.diverges, Diverges::Maybe);
 +                let mut coerce = CoerceMany::new(result_ty);
 +                coerce.coerce(self, Some(then_branch), &then_ty);
 +                let else_ty = match else_branch {
 +                    Some(else_branch) => self.infer_expr_inner(else_branch, expected),
 +                    None => TyBuilder::unit(),
 +                };
 +                both_arms_diverge &= self.diverges;
 +                // FIXME: create a synthetic `else {}` so we have something to refer to here instead of None?
 +                coerce.coerce(self, else_branch, &else_ty);
 +
 +                self.diverges = condition_diverges | both_arms_diverge;
 +
 +                coerce.complete()
 +            }
 +            &Expr::Let { pat, expr } => {
 +                let input_ty = self.infer_expr(expr, &Expectation::none());
 +                self.infer_pat(pat, &input_ty, BindingMode::default());
 +                TyKind::Scalar(Scalar::Bool).intern(Interner)
 +            }
 +            Expr::Block { statements, tail, label, id: _ } => {
 +                let old_resolver = mem::replace(
 +                    &mut self.resolver,
 +                    resolver_for_expr(self.db.upcast(), self.owner, tgt_expr),
 +                );
 +                let ty = match label {
 +                    Some(_) => {
 +                        let break_ty = self.table.new_type_var();
 +                        let (breaks, ty) = self.with_breakable_ctx(
 +                            BreakableKind::Block,
 +                            break_ty.clone(),
 +                            *label,
 +                            |this| {
 +                                this.infer_block(
 +                                    tgt_expr,
 +                                    statements,
 +                                    *tail,
 +                                    &Expectation::has_type(break_ty),
 +                                )
 +                            },
 +                        );
 +                        breaks.unwrap_or(ty)
 +                    }
 +                    None => self.infer_block(tgt_expr, statements, *tail, expected),
 +                };
 +                self.resolver = old_resolver;
 +                ty
 +            }
 +            Expr::Unsafe { body } => self.infer_expr(*body, expected),
 +            Expr::Const { body } => {
 +                self.with_breakable_ctx(BreakableKind::Border, self.err_ty(), None, |this| {
 +                    this.infer_expr(*body, expected)
 +                })
 +                .1
 +            }
 +            Expr::TryBlock { body } => {
 +                self.with_breakable_ctx(BreakableKind::Border, self.err_ty(), None, |this| {
 +                    let _inner = this.infer_expr(*body, expected);
 +                });
 +                // FIXME should be std::result::Result<{inner}, _>
 +                self.err_ty()
 +            }
 +            Expr::Async { body } => {
 +                let ret_ty = self.table.new_type_var();
 +                let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
 +                let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone());
 +
 +                let (_, inner_ty) =
 +                    self.with_breakable_ctx(BreakableKind::Border, self.err_ty(), None, |this| {
 +                        this.infer_expr_coerce(*body, &Expectation::has_type(ret_ty))
 +                    });
 +
 +                self.diverges = prev_diverges;
 +                self.return_ty = prev_ret_ty;
 +
 +                // Use the first type parameter as the output type of future.
 +                // existential type AsyncBlockImplTrait<InnerType>: Future<Output = InnerType>
 +                let impl_trait_id = crate::ImplTraitId::AsyncBlockTypeImplTrait(self.owner, *body);
 +                let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into();
 +                TyKind::OpaqueType(opaque_ty_id, Substitution::from1(Interner, inner_ty))
 +                    .intern(Interner)
 +            }
 +            &Expr::Loop { body, label } => {
 +                let ty = self.table.new_type_var();
 +                let (breaks, ()) =
 +                    self.with_breakable_ctx(BreakableKind::Loop, ty, label, |this| {
 +                        this.infer_expr(body, &Expectation::has_type(TyBuilder::unit()));
 +                    });
 +
 +                match breaks {
 +                    Some(breaks) => {
 +                        self.diverges = Diverges::Maybe;
 +                        breaks
 +                    }
 +                    None => TyKind::Never.intern(Interner),
 +                }
 +            }
 +            &Expr::While { condition, body, label } => {
 +                self.with_breakable_ctx(BreakableKind::Loop, self.err_ty(), label, |this| {
 +                    this.infer_expr(
 +                        condition,
 +                        &Expectation::has_type(TyKind::Scalar(Scalar::Bool).intern(Interner)),
 +                    );
 +                    this.infer_expr(body, &Expectation::has_type(TyBuilder::unit()));
 +                });
 +
 +                // the body may not run, so it diverging doesn't mean we diverge
 +                self.diverges = Diverges::Maybe;
 +                TyBuilder::unit()
 +            }
 +            &Expr::For { iterable, body, pat, label } => {
 +                let iterable_ty = self.infer_expr(iterable, &Expectation::none());
 +                let into_iter_ty =
 +                    self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item());
 +                let pat_ty =
 +                    self.resolve_associated_type(into_iter_ty, self.resolve_iterator_item());
 +
 +                self.infer_pat(pat, &pat_ty, BindingMode::default());
 +                self.with_breakable_ctx(BreakableKind::Loop, self.err_ty(), label, |this| {
 +                    this.infer_expr(body, &Expectation::has_type(TyBuilder::unit()));
 +                });
 +
 +                // the body may not run, so it diverging doesn't mean we diverge
 +                self.diverges = Diverges::Maybe;
 +                TyBuilder::unit()
 +            }
 +            Expr::Closure { body, args, ret_type, arg_types, closure_kind } => {
 +                assert_eq!(args.len(), arg_types.len());
 +
 +                let mut sig_tys = Vec::new();
 +
 +                // collect explicitly written argument types
 +                for arg_type in arg_types.iter() {
 +                    let arg_ty = match arg_type {
 +                        Some(type_ref) => self.make_ty(type_ref),
 +                        None => self.table.new_type_var(),
 +                    };
 +                    sig_tys.push(arg_ty);
 +                }
 +
 +                // add return type
 +                let ret_ty = match ret_type {
 +                    Some(type_ref) => self.make_ty(type_ref),
 +                    None => self.table.new_type_var(),
 +                };
 +                sig_tys.push(ret_ty.clone());
 +                let sig_ty = TyKind::Function(FnPointer {
 +                    num_binders: 0,
 +                    sig: FnSig { abi: (), safety: chalk_ir::Safety::Safe, variadic: false },
 +                    substitution: FnSubst(
 +                        Substitution::from_iter(Interner, sig_tys.clone()).shifted_in(Interner),
 +                    ),
 +                })
 +                .intern(Interner);
 +
 +                let (ty, resume_yield_tys) = if matches!(closure_kind, ClosureKind::Generator(_)) {
 +                    // FIXME: report error when there are more than 1 parameter.
 +                    let resume_ty = match sig_tys.first() {
 +                        // When `sig_tys.len() == 1` the first type is the return type, not the
 +                        // first parameter type.
 +                        Some(ty) if sig_tys.len() > 1 => ty.clone(),
 +                        _ => self.result.standard_types.unit.clone(),
 +                    };
 +                    let yield_ty = self.table.new_type_var();
 +
 +                    let subst = TyBuilder::subst_for_generator(self.db, self.owner)
 +                        .push(resume_ty.clone())
 +                        .push(yield_ty.clone())
 +                        .push(ret_ty.clone())
 +                        .build();
 +
 +                    let generator_id = self.db.intern_generator((self.owner, tgt_expr)).into();
 +                    let generator_ty = TyKind::Generator(generator_id, subst).intern(Interner);
 +
 +                    (generator_ty, Some((resume_ty, yield_ty)))
 +                } else {
 +                    let closure_id = self.db.intern_closure((self.owner, tgt_expr)).into();
 +                    let closure_ty =
 +                        TyKind::Closure(closure_id, Substitution::from1(Interner, sig_ty.clone()))
 +                            .intern(Interner);
 +
 +                    (closure_ty, None)
 +                };
 +
 +                // Eagerly try to relate the closure type with the expected
 +                // type, otherwise we often won't have enough information to
 +                // infer the body.
 +                self.deduce_closure_type_from_expectations(tgt_expr, &ty, &sig_ty, expected);
 +
 +                // Now go through the argument patterns
 +                for (arg_pat, arg_ty) in args.iter().zip(sig_tys) {
 +                    self.infer_pat(*arg_pat, &arg_ty, BindingMode::default());
 +                }
 +
 +                let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
 +                let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone());
 +                let prev_resume_yield_tys =
 +                    mem::replace(&mut self.resume_yield_tys, resume_yield_tys);
 +
 +                self.with_breakable_ctx(BreakableKind::Border, self.err_ty(), None, |this| {
 +                    this.infer_expr_coerce(*body, &Expectation::has_type(ret_ty));
 +                });
 +
 +                self.diverges = prev_diverges;
 +                self.return_ty = prev_ret_ty;
 +                self.resume_yield_tys = prev_resume_yield_tys;
 +
 +                ty
 +            }
 +            Expr::Call { callee, args, .. } => {
 +                let callee_ty = self.infer_expr(*callee, &Expectation::none());
 +                let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone());
 +                let mut res = None;
 +                let mut derefed_callee = callee_ty.clone();
 +                // manual loop to be able to access `derefs.table`
 +                while let Some((callee_deref_ty, _)) = derefs.next() {
 +                    res = derefs.table.callable_sig(&callee_deref_ty, args.len());
 +                    if res.is_some() {
 +                        derefed_callee = callee_deref_ty;
 +                        break;
 +                    }
 +                }
 +                // if the function is unresolved, we use is_varargs=true to
 +                // suppress the arg count diagnostic here
 +                let is_varargs =
 +                    derefed_callee.callable_sig(self.db).map_or(false, |sig| sig.is_varargs)
 +                        || res.is_none();
 +                let (param_tys, ret_ty) = match res {
 +                    Some(res) => {
 +                        let adjustments = auto_deref_adjust_steps(&derefs);
 +                        self.write_expr_adj(*callee, adjustments);
 +                        res
 +                    }
 +                    None => (Vec::new(), self.err_ty()), // FIXME diagnostic
 +                };
 +                let indices_to_skip = self.check_legacy_const_generics(derefed_callee, args);
 +                self.register_obligations_for_call(&callee_ty);
 +
 +                let expected_inputs = self.expected_inputs_for_expected_output(
 +                    expected,
 +                    ret_ty.clone(),
 +                    param_tys.clone(),
 +                );
 +
 +                self.check_call_arguments(
 +                    tgt_expr,
 +                    args,
 +                    &expected_inputs,
 +                    &param_tys,
 +                    &indices_to_skip,
 +                    is_varargs,
 +                );
 +                self.normalize_associated_types_in(ret_ty)
 +            }
 +            Expr::MethodCall { receiver, args, method_name, generic_args } => self
 +                .infer_method_call(
 +                    tgt_expr,
 +                    *receiver,
 +                    args,
 +                    method_name,
 +                    generic_args.as_deref(),
 +                    expected,
 +                ),
 +            Expr::Match { expr, arms } => {
 +                let input_ty = self.infer_expr(*expr, &Expectation::none());
 +
 +                let expected = expected.adjust_for_branches(&mut self.table);
 +
 +                let result_ty = if arms.is_empty() {
 +                    TyKind::Never.intern(Interner)
 +                } else {
 +                    match &expected {
 +                        Expectation::HasType(ty) => ty.clone(),
 +                        _ => self.table.new_type_var(),
 +                    }
 +                };
 +                let mut coerce = CoerceMany::new(result_ty);
 +
 +                let matchee_diverges = self.diverges;
 +                let mut all_arms_diverge = Diverges::Always;
 +
 +                for arm in arms.iter() {
 +                    self.diverges = Diverges::Maybe;
 +                    let _pat_ty = self.infer_pat(arm.pat, &input_ty, BindingMode::default());
 +                    if let Some(guard_expr) = arm.guard {
 +                        self.infer_expr(
 +                            guard_expr,
 +                            &Expectation::has_type(TyKind::Scalar(Scalar::Bool).intern(Interner)),
 +                        );
 +                    }
 +
 +                    let arm_ty = self.infer_expr_inner(arm.expr, &expected);
 +                    all_arms_diverge &= self.diverges;
 +                    coerce.coerce(self, Some(arm.expr), &arm_ty);
 +                }
 +
 +                self.diverges = matchee_diverges | all_arms_diverge;
 +
 +                coerce.complete()
 +            }
 +            Expr::Path(p) => {
 +                // FIXME this could be more efficient...
 +                let resolver = resolver_for_expr(self.db.upcast(), self.owner, tgt_expr);
 +                self.infer_path(&resolver, p, tgt_expr.into()).unwrap_or_else(|| self.err_ty())
 +            }
 +            Expr::Continue { label } => {
 +                if let None = find_continuable(&mut self.breakables, label.as_ref()) {
 +                    self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
 +                        expr: tgt_expr,
 +                        is_break: false,
 +                    });
 +                };
 +                TyKind::Never.intern(Interner)
 +            }
 +            Expr::Break { expr, label } => {
 +                let val_ty = if let Some(expr) = *expr {
 +                    self.infer_expr(expr, &Expectation::none())
 +                } else {
 +                    TyBuilder::unit()
 +                };
 +
 +                match find_breakable(&mut self.breakables, label.as_ref()) {
 +                    Some(ctxt) => {
 +                        // avoiding the borrowck
 +                        let mut coerce = mem::replace(
 +                            &mut ctxt.coerce,
 +                            CoerceMany::new(self.result.standard_types.unknown.clone()),
 +                        );
 +
 +                        // FIXME: create a synthetic `()` during lowering so we have something to refer to here?
 +                        coerce.coerce(self, *expr, &val_ty);
 +
 +                        let ctxt = find_breakable(&mut self.breakables, label.as_ref())
 +                            .expect("breakable stack changed during coercion");
 +                        ctxt.coerce = coerce;
 +                        ctxt.may_break = true;
 +                    }
 +                    None => {
 +                        self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
 +                            expr: tgt_expr,
 +                            is_break: true,
 +                        });
 +                    }
 +                }
 +                TyKind::Never.intern(Interner)
 +            }
 +            Expr::Return { expr } => {
 +                if let Some(expr) = expr {
 +                    self.infer_expr_coerce(*expr, &Expectation::has_type(self.return_ty.clone()));
 +                } else {
 +                    let unit = TyBuilder::unit();
 +                    let _ = self.coerce(Some(tgt_expr), &unit, &self.return_ty.clone());
 +                }
 +                TyKind::Never.intern(Interner)
 +            }
 +            Expr::Yield { expr } => {
 +                if let Some((resume_ty, yield_ty)) = self.resume_yield_tys.clone() {
 +                    if let Some(expr) = expr {
 +                        self.infer_expr_coerce(*expr, &Expectation::has_type(yield_ty));
 +                    } else {
 +                        let unit = self.result.standard_types.unit.clone();
 +                        let _ = self.coerce(Some(tgt_expr), &unit, &yield_ty);
 +                    }
 +                    resume_ty
 +                } else {
 +                    // FIXME: report error (yield expr in non-generator)
 +                    TyKind::Error.intern(Interner)
 +                }
 +            }
 +            Expr::RecordLit { path, fields, spread, .. } => {
 +                let (ty, def_id) = self.resolve_variant(path.as_deref(), false);
 +                if let Some(variant) = def_id {
 +                    self.write_variant_resolution(tgt_expr.into(), variant);
 +                }
 +
 +                if let Some(t) = expected.only_has_type(&mut self.table) {
 +                    self.unify(&ty, &t);
 +                }
 +
 +                let substs = ty
 +                    .as_adt()
 +                    .map(|(_, s)| s.clone())
 +                    .unwrap_or_else(|| Substitution::empty(Interner));
 +                let field_types = def_id.map(|it| self.db.field_types(it)).unwrap_or_default();
 +                let variant_data = def_id.map(|it| it.variant_data(self.db.upcast()));
 +                for field in fields.iter() {
 +                    let field_def =
 +                        variant_data.as_ref().and_then(|it| match it.field(&field.name) {
 +                            Some(local_id) => Some(FieldId { parent: def_id.unwrap(), local_id }),
 +                            None => {
 +                                self.push_diagnostic(InferenceDiagnostic::NoSuchField {
 +                                    expr: field.expr,
 +                                });
 +                                None
 +                            }
 +                        });
 +                    let field_ty = field_def.map_or(self.err_ty(), |it| {
 +                        field_types[it.local_id].clone().substitute(Interner, &substs)
 +                    });
 +                    self.infer_expr_coerce(field.expr, &Expectation::has_type(field_ty));
 +                }
 +                if let Some(expr) = spread {
 +                    self.infer_expr(*expr, &Expectation::has_type(ty.clone()));
 +                }
 +                ty
 +            }
 +            Expr::Field { expr, name } => {
 +                let receiver_ty = self.infer_expr_inner(*expr, &Expectation::none());
 +
 +                let mut autoderef = Autoderef::new(&mut self.table, receiver_ty);
 +                let ty = autoderef.by_ref().find_map(|(derefed_ty, _)| {
 +                    let (field_id, parameters) = match derefed_ty.kind(Interner) {
 +                        TyKind::Tuple(_, substs) => {
 +                            return name.as_tuple_index().and_then(|idx| {
 +                                substs
 +                                    .as_slice(Interner)
 +                                    .get(idx)
 +                                    .map(|a| a.assert_ty_ref(Interner))
 +                                    .cloned()
 +                            });
 +                        }
 +                        TyKind::Adt(AdtId(hir_def::AdtId::StructId(s)), parameters) => {
 +                            let local_id = self.db.struct_data(*s).variant_data.field(name)?;
 +                            let field = FieldId { parent: (*s).into(), local_id };
 +                            (field, parameters.clone())
 +                        }
 +                        TyKind::Adt(AdtId(hir_def::AdtId::UnionId(u)), parameters) => {
 +                            let local_id = self.db.union_data(*u).variant_data.field(name)?;
 +                            let field = FieldId { parent: (*u).into(), local_id };
 +                            (field, parameters.clone())
 +                        }
 +                        _ => return None,
 +                    };
 +                    let is_visible = self.db.field_visibilities(field_id.parent)[field_id.local_id]
 +                        .is_visible_from(self.db.upcast(), self.resolver.module());
 +                    if !is_visible {
 +                        // Write down the first field resolution even if it is not visible
 +                        // This aids IDE features for private fields like goto def and in
 +                        // case of autoderef finding an applicable field, this will be
 +                        // overwritten in a following cycle
 +                        if let Entry::Vacant(entry) = self.result.field_resolutions.entry(tgt_expr)
 +                        {
 +                            entry.insert(field_id);
 +                        }
 +                        return None;
 +                    }
 +                    // can't have `write_field_resolution` here because `self.table` is borrowed :(
 +                    self.result.field_resolutions.insert(tgt_expr, field_id);
 +                    let ty = self.db.field_types(field_id.parent)[field_id.local_id]
 +                        .clone()
 +                        .substitute(Interner, &parameters);
 +                    Some(ty)
 +                });
 +                let ty = match ty {
 +                    Some(ty) => {
 +                        let adjustments = auto_deref_adjust_steps(&autoderef);
 +                        self.write_expr_adj(*expr, adjustments);
 +                        let ty = self.insert_type_vars(ty);
 +                        let ty = self.normalize_associated_types_in(ty);
 +                        ty
 +                    }
 +                    _ => self.err_ty(),
 +                };
 +                ty
 +            }
 +            Expr::Await { expr } => {
 +                let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
 +                self.resolve_associated_type(inner_ty, self.resolve_future_future_output())
 +            }
 +            Expr::Try { expr } => {
 +                let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
 +                self.resolve_associated_type(inner_ty, self.resolve_ops_try_ok())
 +            }
 +            Expr::Cast { expr, type_ref } => {
 +                // FIXME: propagate the "castable to" expectation (and find a test case that shows this is necessary)
 +                let _inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
 +                let cast_ty = self.make_ty(type_ref);
 +                // FIXME check the cast...
 +                cast_ty
 +            }
 +            Expr::Ref { expr, rawness, mutability } => {
 +                let mutability = lower_to_chalk_mutability(*mutability);
 +                let expectation = if let Some((exp_inner, exp_rawness, exp_mutability)) = expected
 +                    .only_has_type(&mut self.table)
 +                    .as_ref()
 +                    .and_then(|t| t.as_reference_or_ptr())
 +                {
 +                    if exp_mutability == Mutability::Mut && mutability == Mutability::Not {
 +                        // FIXME: record type error - expected mut reference but found shared ref,
 +                        // which cannot be coerced
 +                    }
 +                    if exp_rawness == Rawness::Ref && *rawness == Rawness::RawPtr {
 +                        // FIXME: record type error - expected reference but found ptr,
 +                        // which cannot be coerced
 +                    }
 +                    Expectation::rvalue_hint(&mut self.table, Ty::clone(exp_inner))
 +                } else {
 +                    Expectation::none()
 +                };
 +                let inner_ty = self.infer_expr_inner(*expr, &expectation);
 +                match rawness {
 +                    Rawness::RawPtr => TyKind::Raw(mutability, inner_ty),
 +                    Rawness::Ref => TyKind::Ref(mutability, static_lifetime(), inner_ty),
 +                }
 +                .intern(Interner)
 +            }
 +            &Expr::Box { expr } => self.infer_expr_box(expr, expected),
 +            Expr::UnaryOp { expr, op } => {
 +                let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
 +                let inner_ty = self.resolve_ty_shallow(&inner_ty);
 +                match op {
 +                    UnaryOp::Deref => {
 +                        autoderef::deref(&mut self.table, inner_ty).unwrap_or_else(|| self.err_ty())
 +                    }
 +                    UnaryOp::Neg => {
 +                        match inner_ty.kind(Interner) {
 +                            // Fast path for builtins
 +                            TyKind::Scalar(Scalar::Int(_) | Scalar::Uint(_) | Scalar::Float(_))
 +                            | TyKind::InferenceVar(
 +                                _,
 +                                TyVariableKind::Integer | TyVariableKind::Float,
 +                            ) => inner_ty,
 +                            // Otherwise we resolve via the std::ops::Neg trait
 +                            _ => self
 +                                .resolve_associated_type(inner_ty, self.resolve_ops_neg_output()),
 +                        }
 +                    }
 +                    UnaryOp::Not => {
 +                        match inner_ty.kind(Interner) {
 +                            // Fast path for builtins
 +                            TyKind::Scalar(Scalar::Bool | Scalar::Int(_) | Scalar::Uint(_))
 +                            | TyKind::InferenceVar(_, TyVariableKind::Integer) => inner_ty,
 +                            // Otherwise we resolve via the std::ops::Not trait
 +                            _ => self
 +                                .resolve_associated_type(inner_ty, self.resolve_ops_not_output()),
 +                        }
 +                    }
 +                }
 +            }
 +            Expr::BinaryOp { lhs, rhs, op } => match op {
 +                Some(BinaryOp::Assignment { op: None }) => {
 +                    let lhs = *lhs;
 +                    let is_ordinary = match &self.body[lhs] {
 +                        Expr::Array(_)
 +                        | Expr::RecordLit { .. }
 +                        | Expr::Tuple { .. }
 +                        | Expr::Underscore => false,
 +                        Expr::Call { callee, .. } => !matches!(&self.body[*callee], Expr::Path(_)),
 +                        _ => true,
 +                    };
 +
 +                    // In ordinary (non-destructuring) assignments, the type of
 +                    // `lhs` must be inferred first so that the ADT fields
 +                    // instantiations in RHS can be coerced to it. Note that this
 +                    // cannot happen in destructuring assignments because of how
 +                    // they are desugared.
 +                    if is_ordinary {
 +                        let lhs_ty = self.infer_expr(lhs, &Expectation::none());
 +                        self.infer_expr_coerce(*rhs, &Expectation::has_type(lhs_ty));
 +                    } else {
 +                        let rhs_ty = self.infer_expr(*rhs, &Expectation::none());
 +                        self.infer_assignee_expr(lhs, &rhs_ty);
 +                    }
 +                    self.result.standard_types.unit.clone()
 +                }
 +                Some(BinaryOp::LogicOp(_)) => {
 +                    let bool_ty = self.result.standard_types.bool_.clone();
 +                    self.infer_expr_coerce(*lhs, &Expectation::HasType(bool_ty.clone()));
 +                    let lhs_diverges = self.diverges;
 +                    self.infer_expr_coerce(*rhs, &Expectation::HasType(bool_ty.clone()));
 +                    // Depending on the LHS' value, the RHS can never execute.
 +                    self.diverges = lhs_diverges;
 +                    bool_ty
 +                }
 +                Some(op) => self.infer_overloadable_binop(*lhs, *op, *rhs, tgt_expr),
 +                _ => self.err_ty(),
 +            },
 +            Expr::Range { lhs, rhs, range_type } => {
 +                let lhs_ty = lhs.map(|e| self.infer_expr_inner(e, &Expectation::none()));
 +                let rhs_expect = lhs_ty
 +                    .as_ref()
 +                    .map_or_else(Expectation::none, |ty| Expectation::has_type(ty.clone()));
 +                let rhs_ty = rhs.map(|e| self.infer_expr(e, &rhs_expect));
 +                match (range_type, lhs_ty, rhs_ty) {
 +                    (RangeOp::Exclusive, None, None) => match self.resolve_range_full() {
 +                        Some(adt) => TyBuilder::adt(self.db, adt).build(),
 +                        None => self.err_ty(),
 +                    },
 +                    (RangeOp::Exclusive, None, Some(ty)) => match self.resolve_range_to() {
 +                        Some(adt) => TyBuilder::adt(self.db, adt).push(ty).build(),
 +                        None => self.err_ty(),
 +                    },
 +                    (RangeOp::Inclusive, None, Some(ty)) => {
 +                        match self.resolve_range_to_inclusive() {
 +                            Some(adt) => TyBuilder::adt(self.db, adt).push(ty).build(),
 +                            None => self.err_ty(),
 +                        }
 +                    }
 +                    (RangeOp::Exclusive, Some(_), Some(ty)) => match self.resolve_range() {
 +                        Some(adt) => TyBuilder::adt(self.db, adt).push(ty).build(),
 +                        None => self.err_ty(),
 +                    },
 +                    (RangeOp::Inclusive, Some(_), Some(ty)) => {
 +                        match self.resolve_range_inclusive() {
 +                            Some(adt) => TyBuilder::adt(self.db, adt).push(ty).build(),
 +                            None => self.err_ty(),
 +                        }
 +                    }
 +                    (RangeOp::Exclusive, Some(ty), None) => match self.resolve_range_from() {
 +                        Some(adt) => TyBuilder::adt(self.db, adt).push(ty).build(),
 +                        None => self.err_ty(),
 +                    },
 +                    (RangeOp::Inclusive, _, None) => self.err_ty(),
 +                }
 +            }
 +            Expr::Index { base, index } => {
 +                let base_ty = self.infer_expr_inner(*base, &Expectation::none());
 +                let index_ty = self.infer_expr(*index, &Expectation::none());
 +
 +                if let Some(index_trait) = self.resolve_ops_index() {
 +                    let canonicalized = self.canonicalize(base_ty.clone());
 +                    let receiver_adjustments = method_resolution::resolve_indexing_op(
 +                        self.db,
 +                        self.trait_env.clone(),
 +                        canonicalized.value,
 +                        index_trait,
 +                    );
 +                    let (self_ty, adj) = receiver_adjustments
 +                        .map_or((self.err_ty(), Vec::new()), |adj| {
 +                            adj.apply(&mut self.table, base_ty)
 +                        });
 +                    self.write_expr_adj(*base, adj);
 +                    self.resolve_associated_type_with_params(
 +                        self_ty,
 +                        self.resolve_ops_index_output(),
 +                        &[GenericArgData::Ty(index_ty).intern(Interner)],
 +                    )
 +                } else {
 +                    self.err_ty()
 +                }
 +            }
 +            Expr::Tuple { exprs, .. } => {
 +                let mut tys = match expected
 +                    .only_has_type(&mut self.table)
 +                    .as_ref()
 +                    .map(|t| t.kind(Interner))
 +                {
 +                    Some(TyKind::Tuple(_, substs)) => substs
 +                        .iter(Interner)
 +                        .map(|a| a.assert_ty_ref(Interner).clone())
 +                        .chain(repeat_with(|| self.table.new_type_var()))
 +                        .take(exprs.len())
 +                        .collect::<Vec<_>>(),
 +                    _ => (0..exprs.len()).map(|_| self.table.new_type_var()).collect(),
 +                };
 +
 +                for (expr, ty) in exprs.iter().zip(tys.iter_mut()) {
 +                    self.infer_expr_coerce(*expr, &Expectation::has_type(ty.clone()));
 +                }
 +
 +                TyKind::Tuple(tys.len(), Substitution::from_iter(Interner, tys)).intern(Interner)
 +            }
 +            Expr::Array(array) => {
 +                let elem_ty =
 +                    match expected.to_option(&mut self.table).as_ref().map(|t| t.kind(Interner)) {
 +                        Some(TyKind::Array(st, _) | TyKind::Slice(st)) => st.clone(),
 +                        _ => self.table.new_type_var(),
 +                    };
 +                let mut coerce = CoerceMany::new(elem_ty.clone());
 +
 +                let expected = Expectation::has_type(elem_ty.clone());
 +                let len = match array {
 +                    Array::ElementList { elements, .. } => {
 +                        for &expr in elements.iter() {
 +                            let cur_elem_ty = self.infer_expr_inner(expr, &expected);
 +                            coerce.coerce(self, Some(expr), &cur_elem_ty);
 +                        }
 +                        consteval::usize_const(Some(elements.len() as u128))
 +                    }
 +                    &Array::Repeat { initializer, repeat } => {
 +                        self.infer_expr_coerce(initializer, &Expectation::has_type(elem_ty));
 +                        self.infer_expr(
 +                            repeat,
 +                            &Expectation::has_type(
 +                                TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner),
 +                            ),
 +                        );
 +
 +                        if let Some(g_def) = self.owner.as_generic_def_id() {
 +                            let generics = generics(self.db.upcast(), g_def);
 +                            consteval::eval_to_const(
 +                                repeat,
 +                                ParamLoweringMode::Placeholder,
 +                                self,
 +                                || generics,
 +                                DebruijnIndex::INNERMOST,
 +                            )
 +                        } else {
 +                            consteval::usize_const(None)
 +                        }
 +                    }
 +                };
 +
 +                TyKind::Array(coerce.complete(), len).intern(Interner)
 +            }
 +            Expr::Literal(lit) => match lit {
 +                Literal::Bool(..) => TyKind::Scalar(Scalar::Bool).intern(Interner),
 +                Literal::String(..) => {
 +                    TyKind::Ref(Mutability::Not, static_lifetime(), TyKind::Str.intern(Interner))
 +                        .intern(Interner)
 +                }
 +                Literal::ByteString(bs) => {
 +                    let byte_type = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(Interner);
 +
 +                    let len = consteval::usize_const(Some(bs.len() as u128));
 +
 +                    let array_type = TyKind::Array(byte_type, len).intern(Interner);
 +                    TyKind::Ref(Mutability::Not, static_lifetime(), array_type).intern(Interner)
 +                }
 +                Literal::Char(..) => TyKind::Scalar(Scalar::Char).intern(Interner),
 +                Literal::Int(_v, ty) => match ty {
 +                    Some(int_ty) => {
 +                        TyKind::Scalar(Scalar::Int(primitive::int_ty_from_builtin(*int_ty)))
 +                            .intern(Interner)
 +                    }
 +                    None => self.table.new_integer_var(),
 +                },
 +                Literal::Uint(_v, ty) => match ty {
 +                    Some(int_ty) => {
 +                        TyKind::Scalar(Scalar::Uint(primitive::uint_ty_from_builtin(*int_ty)))
 +                            .intern(Interner)
 +                    }
 +                    None => self.table.new_integer_var(),
 +                },
 +                Literal::Float(_v, ty) => match ty {
 +                    Some(float_ty) => {
 +                        TyKind::Scalar(Scalar::Float(primitive::float_ty_from_builtin(*float_ty)))
 +                            .intern(Interner)
 +                    }
 +                    None => self.table.new_float_var(),
 +                },
 +            },
 +            Expr::Underscore => {
 +                // Underscore expressions may only appear in assignee expressions,
 +                // which are handled by `infer_assignee_expr()`, so any underscore
 +                // expression reaching this branch is an error.
 +                self.err_ty()
 +            }
 +        };
 +        // use a new type variable if we got unknown here
 +        let ty = self.insert_type_vars_shallow(ty);
 +        self.write_expr_ty(tgt_expr, ty.clone());
 +        if self.resolve_ty_shallow(&ty).is_never() {
 +            // Any expression that produces a value of type `!` must have diverged
 +            self.diverges = Diverges::Always;
 +        }
 +        ty
 +    }
 +
 +    fn infer_expr_box(&mut self, inner_expr: ExprId, expected: &Expectation) -> Ty {
 +        if let Some(box_id) = self.resolve_boxed_box() {
 +            let table = &mut self.table;
 +            let inner_exp = expected
 +                .to_option(table)
 +                .as_ref()
 +                .map(|e| e.as_adt())
 +                .flatten()
 +                .filter(|(e_adt, _)| e_adt == &box_id)
 +                .map(|(_, subts)| {
 +                    let g = subts.at(Interner, 0);
 +                    Expectation::rvalue_hint(table, Ty::clone(g.assert_ty_ref(Interner)))
 +                })
 +                .unwrap_or_else(Expectation::none);
 +
 +            let inner_ty = self.infer_expr_inner(inner_expr, &inner_exp);
 +            TyBuilder::adt(self.db, box_id)
 +                .push(inner_ty)
 +                .fill_with_defaults(self.db, || self.table.new_type_var())
 +                .build()
 +        } else {
 +            self.err_ty()
 +        }
 +    }
 +
 +    pub(super) fn infer_assignee_expr(&mut self, lhs: ExprId, rhs_ty: &Ty) -> Ty {
 +        let is_rest_expr = |expr| {
 +            matches!(
 +                &self.body[expr],
 +                Expr::Range { lhs: None, rhs: None, range_type: RangeOp::Exclusive },
 +            )
 +        };
 +
 +        let rhs_ty = self.resolve_ty_shallow(rhs_ty);
 +
 +        let ty = match &self.body[lhs] {
 +            Expr::Tuple { exprs, .. } => {
 +                // We don't consider multiple ellipses. This is analogous to
 +                // `hir_def::body::lower::ExprCollector::collect_tuple_pat()`.
 +                let ellipsis = exprs.iter().position(|e| is_rest_expr(*e));
 +                let exprs: Vec<_> = exprs.iter().filter(|e| !is_rest_expr(**e)).copied().collect();
 +
 +                self.infer_tuple_pat_like(&rhs_ty, (), ellipsis, &exprs)
 +            }
 +            Expr::Call { callee, args, .. } => {
 +                // Tuple structs
 +                let path = match &self.body[*callee] {
 +                    Expr::Path(path) => Some(path),
 +                    _ => None,
 +                };
 +
 +                // We don't consider multiple ellipses. This is analogous to
 +                // `hir_def::body::lower::ExprCollector::collect_tuple_pat()`.
 +                let ellipsis = args.iter().position(|e| is_rest_expr(*e));
 +                let args: Vec<_> = args.iter().filter(|e| !is_rest_expr(**e)).copied().collect();
 +
 +                self.infer_tuple_struct_pat_like(path, &rhs_ty, (), lhs, ellipsis, &args)
 +            }
 +            Expr::Array(Array::ElementList { elements, .. }) => {
 +                let elem_ty = match rhs_ty.kind(Interner) {
 +                    TyKind::Array(st, _) => st.clone(),
 +                    _ => self.err_ty(),
 +                };
 +
 +                // There's no need to handle `..` as it cannot be bound.
 +                let sub_exprs = elements.iter().filter(|e| !is_rest_expr(**e));
 +
 +                for e in sub_exprs {
 +                    self.infer_assignee_expr(*e, &elem_ty);
 +                }
 +
 +                match rhs_ty.kind(Interner) {
 +                    TyKind::Array(_, _) => rhs_ty.clone(),
 +                    // Even when `rhs_ty` is not an array type, this assignee
 +                    // expression is inferred to be an array (of unknown element
 +                    // type and length). This should not be just an error type,
 +                    // because we are to compute the unifiability of this type and
 +                    // `rhs_ty` in the end of this function to issue type mismatches.
 +                    _ => TyKind::Array(self.err_ty(), crate::consteval::usize_const(None))
 +                        .intern(Interner),
 +                }
 +            }
 +            Expr::RecordLit { path, fields, .. } => {
 +                let subs = fields.iter().map(|f| (f.name.clone(), f.expr));
 +
 +                self.infer_record_pat_like(path.as_deref(), &rhs_ty, (), lhs.into(), subs)
 +            }
 +            Expr::Underscore => rhs_ty.clone(),
 +            _ => {
 +                // `lhs` is a place expression, a unit struct, or an enum variant.
 +                let lhs_ty = self.infer_expr(lhs, &Expectation::none());
 +
 +                // This is the only branch where this function may coerce any type.
 +                // We are returning early to avoid the unifiability check below.
 +                let lhs_ty = self.insert_type_vars_shallow(lhs_ty);
 +                let ty = match self.coerce(None, &rhs_ty, &lhs_ty) {
 +                    Ok(ty) => ty,
 +                    Err(_) => {
 +                        self.result.type_mismatches.insert(
 +                            lhs.into(),
 +                            TypeMismatch { expected: rhs_ty.clone(), actual: lhs_ty.clone() },
 +                        );
 +                        // `rhs_ty` is returned so no further type mismatches are
 +                        // reported because of this mismatch.
 +                        rhs_ty
 +                    }
 +                };
 +                self.write_expr_ty(lhs, ty.clone());
 +                return ty;
 +            }
 +        };
 +
 +        let ty = self.insert_type_vars_shallow(ty);
 +        if !self.unify(&ty, &rhs_ty) {
 +            self.result
 +                .type_mismatches
 +                .insert(lhs.into(), TypeMismatch { expected: rhs_ty.clone(), actual: ty.clone() });
 +        }
 +        self.write_expr_ty(lhs, ty.clone());
 +        ty
 +    }
 +
 +    fn infer_overloadable_binop(
 +        &mut self,
 +        lhs: ExprId,
 +        op: BinaryOp,
 +        rhs: ExprId,
 +        tgt_expr: ExprId,
 +    ) -> Ty {
 +        let lhs_expectation = Expectation::none();
 +        let lhs_ty = self.infer_expr(lhs, &lhs_expectation);
 +        let rhs_ty = self.table.new_type_var();
 +
 +        let trait_func = lang_names_for_bin_op(op).and_then(|(name, lang_item)| {
 +            let trait_id = self.resolve_lang_item(lang_item)?.as_trait()?;
 +            let func = self.db.trait_data(trait_id).method_by_name(&name)?;
 +            Some((trait_id, func))
 +        });
 +        let (trait_, func) = match trait_func {
 +            Some(it) => it,
 +            None => {
 +                let rhs_ty = self.builtin_binary_op_rhs_expectation(op, lhs_ty.clone());
 +                let rhs_ty = self.infer_expr_coerce(rhs, &Expectation::from_option(rhs_ty));
 +                return self
 +                    .builtin_binary_op_return_ty(op, lhs_ty, rhs_ty)
 +                    .unwrap_or_else(|| self.err_ty());
 +            }
 +        };
 +
 +        // HACK: We can use this substitution for the function because the function itself doesn't
 +        // have its own generic parameters.
 +        let subst = TyBuilder::subst_for_def(self.db, trait_, None)
 +            .push(lhs_ty.clone())
 +            .push(rhs_ty.clone())
 +            .build();
 +        self.write_method_resolution(tgt_expr, func, subst.clone());
 +
 +        let method_ty = self.db.value_ty(func.into()).substitute(Interner, &subst);
 +        self.register_obligations_for_call(&method_ty);
 +
 +        self.infer_expr_coerce(rhs, &Expectation::has_type(rhs_ty.clone()));
 +
 +        let ret_ty = match method_ty.callable_sig(self.db) {
 +            Some(sig) => sig.ret().clone(),
 +            None => self.err_ty(),
 +        };
 +
 +        let ret_ty = self.normalize_associated_types_in(ret_ty);
 +
 +        // FIXME: record autoref adjustments
 +
 +        // use knowledge of built-in binary ops, which can sometimes help inference
 +        if let Some(builtin_rhs) = self.builtin_binary_op_rhs_expectation(op, lhs_ty.clone()) {
 +            self.unify(&builtin_rhs, &rhs_ty);
 +        }
 +        if let Some(builtin_ret) = self.builtin_binary_op_return_ty(op, lhs_ty, rhs_ty) {
 +            self.unify(&builtin_ret, &ret_ty);
 +        }
 +
 +        ret_ty
 +    }
 +
 +    fn infer_block(
 +        &mut self,
 +        expr: ExprId,
 +        statements: &[Statement],
 +        tail: Option<ExprId>,
 +        expected: &Expectation,
 +    ) -> Ty {
 +        for stmt in statements {
 +            match stmt {
 +                Statement::Let { pat, type_ref, initializer, else_branch } => {
 +                    let decl_ty = type_ref
 +                        .as_ref()
 +                        .map(|tr| self.make_ty(tr))
 +                        .unwrap_or_else(|| self.err_ty());
 +
 +                    // Always use the declared type when specified
 +                    let mut ty = decl_ty.clone();
 +
 +                    if let Some(expr) = initializer {
 +                        let actual_ty =
 +                            self.infer_expr_coerce(*expr, &Expectation::has_type(decl_ty.clone()));
 +                        if decl_ty.is_unknown() {
 +                            ty = actual_ty;
 +                        }
 +                    }
 +
 +                    if let Some(expr) = else_branch {
 +                        self.infer_expr_coerce(
 +                            *expr,
 +                            &Expectation::has_type(Ty::new(Interner, TyKind::Never)),
 +                        );
 +                    }
 +
 +                    self.infer_pat(*pat, &ty, BindingMode::default());
 +                }
 +                Statement::Expr { expr, .. } => {
 +                    self.infer_expr(*expr, &Expectation::none());
 +                }
 +            }
 +        }
 +
 +        if let Some(expr) = tail {
 +            self.infer_expr_coerce(expr, expected)
 +        } else {
 +            // Citing rustc: if there is no explicit tail expression,
 +            // that is typically equivalent to a tail expression
 +            // of `()` -- except if the block diverges. In that
 +            // case, there is no value supplied from the tail
 +            // expression (assuming there are no other breaks,
 +            // this implies that the type of the block will be
 +            // `!`).
 +            if self.diverges.is_always() {
 +                // we don't even make an attempt at coercion
 +                self.table.new_maybe_never_var()
 +            } else {
 +                if let Some(t) = expected.only_has_type(&mut self.table) {
 +                    if self.coerce(Some(expr), &TyBuilder::unit(), &t).is_err() {
 +                        self.result.type_mismatches.insert(
 +                            expr.into(),
 +                            TypeMismatch { expected: t.clone(), actual: TyBuilder::unit() },
 +                        );
 +                    }
 +                    t
 +                } else {
 +                    TyBuilder::unit()
 +                }
 +            }
 +        }
 +    }
 +
 +    fn infer_method_call(
 +        &mut self,
 +        tgt_expr: ExprId,
 +        receiver: ExprId,
 +        args: &[ExprId],
 +        method_name: &Name,
 +        generic_args: Option<&GenericArgs>,
 +        expected: &Expectation,
 +    ) -> Ty {
 +        let receiver_ty = self.infer_expr(receiver, &Expectation::none());
 +        let canonicalized_receiver = self.canonicalize(receiver_ty.clone());
 +
 +        let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast());
 +
 +        let resolved = method_resolution::lookup_method(
 +            &canonicalized_receiver.value,
 +            self.db,
 +            self.trait_env.clone(),
 +            &traits_in_scope,
 +            VisibleFromModule::Filter(self.resolver.module()),
 +            method_name,
 +        );
 +        let (receiver_ty, method_ty, substs) = match resolved {
 +            Some((adjust, func)) => {
 +                let (ty, adjustments) = adjust.apply(&mut self.table, receiver_ty);
 +                let generics = generics(self.db.upcast(), func.into());
 +                let substs = self.substs_for_method_call(generics, generic_args);
 +                self.write_expr_adj(receiver, adjustments);
 +                self.write_method_resolution(tgt_expr, func, substs.clone());
 +                (ty, self.db.value_ty(func.into()), substs)
 +            }
 +            None => (
 +                receiver_ty,
 +                Binders::empty(Interner, self.err_ty()),
 +                Substitution::empty(Interner),
 +            ),
 +        };
 +        let method_ty = method_ty.substitute(Interner, &substs);
 +        self.register_obligations_for_call(&method_ty);
 +        let (formal_receiver_ty, param_tys, ret_ty, is_varargs) =
 +            match method_ty.callable_sig(self.db) {
 +                Some(sig) => {
 +                    if !sig.params().is_empty() {
 +                        (
 +                            sig.params()[0].clone(),
 +                            sig.params()[1..].to_vec(),
 +                            sig.ret().clone(),
 +                            sig.is_varargs,
 +                        )
 +                    } else {
 +                        (self.err_ty(), Vec::new(), sig.ret().clone(), sig.is_varargs)
 +                    }
 +                }
 +                None => (self.err_ty(), Vec::new(), self.err_ty(), true),
 +            };
 +        self.unify(&formal_receiver_ty, &receiver_ty);
 +
 +        let expected_inputs =
 +            self.expected_inputs_for_expected_output(expected, ret_ty.clone(), param_tys.clone());
 +
 +        self.check_call_arguments(tgt_expr, args, &expected_inputs, &param_tys, &[], is_varargs);
 +        self.normalize_associated_types_in(ret_ty)
 +    }
 +
 +    fn expected_inputs_for_expected_output(
 +        &mut self,
 +        expected_output: &Expectation,
 +        output: Ty,
 +        inputs: Vec<Ty>,
 +    ) -> Vec<Ty> {
 +        if let Some(expected_ty) = expected_output.to_option(&mut self.table) {
 +            self.table.fudge_inference(|table| {
 +                if table.try_unify(&expected_ty, &output).is_ok() {
 +                    table.resolve_with_fallback(inputs, &|var, kind, _, _| match kind {
 +                        chalk_ir::VariableKind::Ty(tk) => var.to_ty(Interner, tk).cast(Interner),
 +                        chalk_ir::VariableKind::Lifetime => {
 +                            var.to_lifetime(Interner).cast(Interner)
 +                        }
 +                        chalk_ir::VariableKind::Const(ty) => {
 +                            var.to_const(Interner, ty).cast(Interner)
 +                        }
 +                    })
 +                } else {
 +                    Vec::new()
 +                }
 +            })
 +        } else {
 +            Vec::new()
 +        }
 +    }
 +
 +    fn check_call_arguments(
 +        &mut self,
 +        expr: ExprId,
 +        args: &[ExprId],
 +        expected_inputs: &[Ty],
 +        param_tys: &[Ty],
 +        skip_indices: &[u32],
 +        is_varargs: bool,
 +    ) {
 +        if args.len() != param_tys.len() + skip_indices.len() && !is_varargs {
 +            self.push_diagnostic(InferenceDiagnostic::MismatchedArgCount {
 +                call_expr: expr,
 +                expected: param_tys.len() + skip_indices.len(),
 +                found: args.len(),
 +            });
 +        }
 +
 +        // Quoting https://github.com/rust-lang/rust/blob/6ef275e6c3cb1384ec78128eceeb4963ff788dca/src/librustc_typeck/check/mod.rs#L3325 --
 +        // We do this in a pretty awful way: first we type-check any arguments
 +        // that are not closures, then we type-check the closures. This is so
 +        // that we have more information about the types of arguments when we
 +        // type-check the functions. This isn't really the right way to do this.
 +        for &check_closures in &[false, true] {
 +            let mut skip_indices = skip_indices.into_iter().copied().fuse().peekable();
 +            let param_iter = param_tys.iter().cloned().chain(repeat(self.err_ty()));
 +            let expected_iter = expected_inputs
 +                .iter()
 +                .cloned()
 +                .chain(param_iter.clone().skip(expected_inputs.len()));
 +            for (idx, ((&arg, param_ty), expected_ty)) in
 +                args.iter().zip(param_iter).zip(expected_iter).enumerate()
 +            {
 +                let is_closure = matches!(&self.body[arg], Expr::Closure { .. });
 +                if is_closure != check_closures {
 +                    continue;
 +                }
 +
 +                while skip_indices.peek().map_or(false, |i| *i < idx as u32) {
 +                    skip_indices.next();
 +                }
 +                if skip_indices.peek().copied() == Some(idx as u32) {
 +                    continue;
 +                }
 +
 +                // the difference between param_ty and expected here is that
 +                // expected is the parameter when the expected *return* type is
 +                // taken into account. So in `let _: &[i32] = identity(&[1, 2])`
 +                // the expected type is already `&[i32]`, whereas param_ty is
 +                // still an unbound type variable. We don't always want to force
 +                // the parameter to coerce to the expected type (for example in
 +                // `coerce_unsize_expected_type_4`).
 +                let param_ty = self.normalize_associated_types_in(param_ty);
 +                let expected = Expectation::rvalue_hint(&mut self.table, expected_ty);
 +                // infer with the expected type we have...
 +                let ty = self.infer_expr_inner(arg, &expected);
 +
 +                // then coerce to either the expected type or just the formal parameter type
 +                let coercion_target = if let Some(ty) = expected.only_has_type(&mut self.table) {
 +                    // if we are coercing to the expectation, unify with the
 +                    // formal parameter type to connect everything
 +                    self.unify(&ty, &param_ty);
 +                    ty
 +                } else {
 +                    param_ty
 +                };
 +                if !coercion_target.is_unknown() {
 +                    if self.coerce(Some(arg), &ty, &coercion_target).is_err() {
 +                        self.result.type_mismatches.insert(
 +                            arg.into(),
 +                            TypeMismatch { expected: coercion_target, actual: ty.clone() },
 +                        );
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    fn substs_for_method_call(
 +        &mut self,
 +        def_generics: Generics,
 +        generic_args: Option<&GenericArgs>,
 +    ) -> Substitution {
 +        let (parent_params, self_params, type_params, const_params, impl_trait_params) =
 +            def_generics.provenance_split();
 +        assert_eq!(self_params, 0); // method shouldn't have another Self param
 +        let total_len = parent_params + type_params + const_params + impl_trait_params;
 +        let mut substs = Vec::with_capacity(total_len);
 +
 +        // handle provided arguments
 +        if let Some(generic_args) = generic_args {
 +            // if args are provided, it should be all of them, but we can't rely on that
 +            for (arg, kind_id) in generic_args
 +                .args
 +                .iter()
 +                .filter(|arg| !matches!(arg, GenericArg::Lifetime(_)))
 +                .take(type_params + const_params)
 +                .zip(def_generics.iter_id())
 +            {
 +                if let Some(g) = generic_arg_to_chalk(
 +                    self.db,
 +                    kind_id,
 +                    arg,
 +                    self,
 +                    |this, type_ref| this.make_ty(type_ref),
 +                    |this, c, ty| {
 +                        const_or_path_to_chalk(
 +                            this.db,
 +                            &this.resolver,
 +                            ty,
 +                            c,
 +                            ParamLoweringMode::Placeholder,
 +                            || generics(this.db.upcast(), (&this.resolver).generic_def().unwrap()),
 +                            DebruijnIndex::INNERMOST,
 +                        )
 +                    },
 +                ) {
 +                    substs.push(g);
 +                }
 +            }
 +        };
 +
 +        // Handle everything else as unknown. This also handles generic arguments for the method's
 +        // parent (impl or trait), which should come after those for the method.
 +        for (id, data) in def_generics.iter().skip(substs.len()) {
 +            match data {
 +                TypeOrConstParamData::TypeParamData(_) => {
 +                    substs.push(GenericArgData::Ty(self.table.new_type_var()).intern(Interner))
 +                }
 +                TypeOrConstParamData::ConstParamData(_) => {
 +                    substs.push(
 +                        GenericArgData::Const(self.table.new_const_var(
 +                            self.db.const_param_ty(ConstParamId::from_unchecked(id)),
 +                        ))
 +                        .intern(Interner),
 +                    )
 +                }
 +            }
 +        }
 +        assert_eq!(substs.len(), total_len);
 +        Substitution::from_iter(Interner, substs)
 +    }
 +
 +    fn register_obligations_for_call(&mut self, callable_ty: &Ty) {
 +        let callable_ty = self.resolve_ty_shallow(callable_ty);
 +        if let TyKind::FnDef(fn_def, parameters) = callable_ty.kind(Interner) {
 +            let def: CallableDefId = from_chalk(self.db, *fn_def);
 +            let generic_predicates = self.db.generic_predicates(def.into());
 +            for predicate in generic_predicates.iter() {
 +                let (predicate, binders) = predicate
 +                    .clone()
 +                    .substitute(Interner, parameters)
 +                    .into_value_and_skipped_binders();
 +                always!(binders.len(Interner) == 0); // quantified where clauses not yet handled
 +                self.push_obligation(predicate.cast(Interner));
 +            }
 +            // add obligation for trait implementation, if this is a trait method
 +            match def {
 +                CallableDefId::FunctionId(f) => {
 +                    if let ItemContainerId::TraitId(trait_) = f.lookup(self.db.upcast()).container {
 +                        // construct a TraitRef
 +                        let params_len = parameters.len(Interner);
 +                        let trait_params_len = generics(self.db.upcast(), trait_.into()).len();
 +                        let substs = Substitution::from_iter(
 +                            Interner,
 +                            // The generic parameters for the trait come after those for the
 +                            // function.
 +                            &parameters.as_slice(Interner)[params_len - trait_params_len..],
 +                        );
 +                        self.push_obligation(
 +                            TraitRef { trait_id: to_chalk_trait_id(trait_), substitution: substs }
 +                                .cast(Interner),
 +                        );
 +                    }
 +                }
 +                CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_) => {}
 +            }
 +        }
 +    }
 +
 +    /// Returns the argument indices to skip.
 +    fn check_legacy_const_generics(&mut self, callee: Ty, args: &[ExprId]) -> Box<[u32]> {
 +        let (func, subst) = match callee.kind(Interner) {
 +            TyKind::FnDef(fn_id, subst) => {
 +                let callable = CallableDefId::from_chalk(self.db, *fn_id);
 +                let func = match callable {
 +                    CallableDefId::FunctionId(f) => f,
 +                    _ => return Default::default(),
 +                };
 +                (func, subst)
 +            }
 +            _ => return Default::default(),
 +        };
 +
 +        let data = self.db.function_data(func);
 +        if data.legacy_const_generics_indices.is_empty() {
 +            return Default::default();
 +        }
 +
 +        // only use legacy const generics if the param count matches with them
 +        if data.params.len() + data.legacy_const_generics_indices.len() != args.len() {
 +            if args.len() <= data.params.len() {
 +                return Default::default();
 +            } else {
 +                // there are more parameters than there should be without legacy
 +                // const params; use them
 +                let mut indices = data.legacy_const_generics_indices.clone();
 +                indices.sort();
 +                return indices;
 +            }
 +        }
 +
 +        // check legacy const parameters
 +        for (subst_idx, arg_idx) in data.legacy_const_generics_indices.iter().copied().enumerate() {
 +            let arg = match subst.at(Interner, subst_idx).constant(Interner) {
 +                Some(c) => c,
 +                None => continue, // not a const parameter?
 +            };
 +            if arg_idx >= args.len() as u32 {
 +                continue;
 +            }
 +            let _ty = arg.data(Interner).ty.clone();
 +            let expected = Expectation::none(); // FIXME use actual const ty, when that is lowered correctly
 +            self.infer_expr(args[arg_idx as usize], &expected);
 +            // FIXME: evaluate and unify with the const
 +        }
 +        let mut indices = data.legacy_const_generics_indices.clone();
 +        indices.sort();
 +        indices
 +    }
 +
 +    fn builtin_binary_op_return_ty(&mut self, op: BinaryOp, lhs_ty: Ty, rhs_ty: Ty) -> Option<Ty> {
 +        let lhs_ty = self.resolve_ty_shallow(&lhs_ty);
 +        let rhs_ty = self.resolve_ty_shallow(&rhs_ty);
 +        match op {
 +            BinaryOp::LogicOp(_) | BinaryOp::CmpOp(_) => {
 +                Some(TyKind::Scalar(Scalar::Bool).intern(Interner))
 +            }
 +            BinaryOp::Assignment { .. } => Some(TyBuilder::unit()),
 +            BinaryOp::ArithOp(ArithOp::Shl | ArithOp::Shr) => {
 +                // all integer combinations are valid here
 +                if matches!(
 +                    lhs_ty.kind(Interner),
 +                    TyKind::Scalar(Scalar::Int(_) | Scalar::Uint(_))
 +                        | TyKind::InferenceVar(_, TyVariableKind::Integer)
 +                ) && matches!(
 +                    rhs_ty.kind(Interner),
 +                    TyKind::Scalar(Scalar::Int(_) | Scalar::Uint(_))
 +                        | TyKind::InferenceVar(_, TyVariableKind::Integer)
 +                ) {
 +                    Some(lhs_ty)
 +                } else {
 +                    None
 +                }
 +            }
 +            BinaryOp::ArithOp(_) => match (lhs_ty.kind(Interner), rhs_ty.kind(Interner)) {
 +                // (int, int) | (uint, uint) | (float, float)
 +                (TyKind::Scalar(Scalar::Int(_)), TyKind::Scalar(Scalar::Int(_)))
 +                | (TyKind::Scalar(Scalar::Uint(_)), TyKind::Scalar(Scalar::Uint(_)))
 +                | (TyKind::Scalar(Scalar::Float(_)), TyKind::Scalar(Scalar::Float(_))) => {
 +                    Some(rhs_ty)
 +                }
 +                // ({int}, int) | ({int}, uint)
 +                (
 +                    TyKind::InferenceVar(_, TyVariableKind::Integer),
 +                    TyKind::Scalar(Scalar::Int(_) | Scalar::Uint(_)),
 +                ) => Some(rhs_ty),
 +                // (int, {int}) | (uint, {int})
 +                (
 +                    TyKind::Scalar(Scalar::Int(_) | Scalar::Uint(_)),
 +                    TyKind::InferenceVar(_, TyVariableKind::Integer),
 +                ) => Some(lhs_ty),
 +                // ({float} | float)
 +                (
 +                    TyKind::InferenceVar(_, TyVariableKind::Float),
 +                    TyKind::Scalar(Scalar::Float(_)),
 +                ) => Some(rhs_ty),
 +                // (float, {float})
 +                (
 +                    TyKind::Scalar(Scalar::Float(_)),
 +                    TyKind::InferenceVar(_, TyVariableKind::Float),
 +                ) => Some(lhs_ty),
 +                // ({int}, {int}) | ({float}, {float})
 +                (
 +                    TyKind::InferenceVar(_, TyVariableKind::Integer),
 +                    TyKind::InferenceVar(_, TyVariableKind::Integer),
 +                )
 +                | (
 +                    TyKind::InferenceVar(_, TyVariableKind::Float),
 +                    TyKind::InferenceVar(_, TyVariableKind::Float),
 +                ) => Some(rhs_ty),
 +                _ => None,
 +            },
 +        }
 +    }
 +
 +    fn builtin_binary_op_rhs_expectation(&mut self, op: BinaryOp, lhs_ty: Ty) -> Option<Ty> {
 +        Some(match op {
 +            BinaryOp::LogicOp(..) => TyKind::Scalar(Scalar::Bool).intern(Interner),
 +            BinaryOp::Assignment { op: None } => lhs_ty,
 +            BinaryOp::CmpOp(CmpOp::Eq { .. }) => match self
 +                .resolve_ty_shallow(&lhs_ty)
 +                .kind(Interner)
 +            {
 +                TyKind::Scalar(_) | TyKind::Str => lhs_ty,
 +                TyKind::InferenceVar(_, TyVariableKind::Integer | TyVariableKind::Float) => lhs_ty,
 +                _ => return None,
 +            },
 +            BinaryOp::ArithOp(ArithOp::Shl | ArithOp::Shr) => return None,
 +            BinaryOp::CmpOp(CmpOp::Ord { .. })
 +            | BinaryOp::Assignment { op: Some(_) }
 +            | BinaryOp::ArithOp(_) => match self.resolve_ty_shallow(&lhs_ty).kind(Interner) {
 +                TyKind::Scalar(Scalar::Int(_) | Scalar::Uint(_) | Scalar::Float(_)) => lhs_ty,
 +                TyKind::InferenceVar(_, TyVariableKind::Integer | TyVariableKind::Float) => lhs_ty,
 +                _ => return None,
 +            },
 +        })
 +    }
 +
 +    fn with_breakable_ctx<T>(
 +        &mut self,
 +        kind: BreakableKind,
 +        ty: Ty,
 +        label: Option<LabelId>,
 +        cb: impl FnOnce(&mut Self) -> T,
 +    ) -> (Option<Ty>, T) {
 +        self.breakables.push({
 +            let label = label.map(|label| self.body[label].name.clone());
 +            BreakableContext { kind, may_break: false, coerce: CoerceMany::new(ty), label }
 +        });
 +        let res = cb(self);
 +        let ctx = self.breakables.pop().expect("breakable stack broken");
 +        (ctx.may_break.then(|| ctx.coerce.complete()), res)
 +    }
 +}
index 42c3b58d5ada520ba626c22c5ace408f007b2797,0000000000000000000000000000000000000000..b68c764bdca09fa1637bf3c8a9cb37ea1742ee03
mode 100644,000000..100644
--- /dev/null
@@@ -1,510 -1,0 +1,588 @@@
-     NoSolution,
 +//! The type system. We currently use this to infer types for completion, hover
 +//! information and various assists.
 +
 +#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)]
 +
 +#[allow(unused)]
 +macro_rules! eprintln {
 +    ($($tt:tt)*) => { stdx::eprintln!($($tt)*) };
 +}
 +
 +mod autoderef;
 +mod builder;
 +mod chalk_db;
 +mod chalk_ext;
 +pub mod consteval;
 +mod infer;
 +mod inhabitedness;
 +mod interner;
 +mod lower;
 +mod mapping;
 +mod tls;
 +mod utils;
 +mod walk;
 +pub mod db;
 +pub mod diagnostics;
 +pub mod display;
 +pub mod method_resolution;
 +pub mod primitive;
 +pub mod traits;
 +
 +#[cfg(test)]
 +mod tests;
 +#[cfg(test)]
 +mod test_db;
 +
 +use std::sync::Arc;
 +
 +use chalk_ir::{
 +    fold::{Shift, TypeFoldable},
 +    interner::HasInterner,
-     pub fn from_params_and_return(mut params: Vec<Ty>, ret: Ty, is_varargs: bool) -> CallableSig {
++    NoSolution, UniverseIndex,
 +};
 +use hir_def::{expr::ExprId, type_ref::Rawness, TypeOrConstParamId};
++use hir_expand::name;
 +use itertools::Either;
++use traits::FnTrait;
 +use utils::Generics;
 +
 +use crate::{consteval::unknown_const, db::HirDatabase, utils::generics};
 +
 +pub use autoderef::autoderef;
 +pub use builder::{ParamKind, TyBuilder};
 +pub use chalk_ext::*;
 +pub use infer::{
 +    could_coerce, could_unify, Adjust, Adjustment, AutoBorrow, BindingMode, InferenceDiagnostic,
 +    InferenceResult,
 +};
 +pub use interner::Interner;
 +pub use lower::{
 +    associated_type_shorthand_candidates, CallableDefId, ImplTraitLoweringMode, TyDefId,
 +    TyLoweringContext, ValueTyDefId,
 +};
 +pub use mapping::{
 +    from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx,
 +    lt_from_placeholder_idx, to_assoc_type_id, to_chalk_trait_id, to_foreign_def_id,
 +    to_placeholder_idx,
 +};
 +pub use traits::TraitEnvironment;
 +pub use utils::{all_super_traits, is_fn_unsafe_to_call};
 +pub use walk::TypeWalk;
 +
 +pub use chalk_ir::{
 +    cast::Cast, AdtId, BoundVar, DebruijnIndex, Mutability, Safety, Scalar, TyVariableKind,
 +};
 +
 +pub type ForeignDefId = chalk_ir::ForeignDefId<Interner>;
 +pub type AssocTypeId = chalk_ir::AssocTypeId<Interner>;
 +pub type FnDefId = chalk_ir::FnDefId<Interner>;
 +pub type ClosureId = chalk_ir::ClosureId<Interner>;
 +pub type OpaqueTyId = chalk_ir::OpaqueTyId<Interner>;
 +pub type PlaceholderIndex = chalk_ir::PlaceholderIndex;
 +
 +pub type VariableKind = chalk_ir::VariableKind<Interner>;
 +pub type VariableKinds = chalk_ir::VariableKinds<Interner>;
 +pub type CanonicalVarKinds = chalk_ir::CanonicalVarKinds<Interner>;
 +/// Represents generic parameters and an item bound by them. When the item has parent, the binders
 +/// also contain the generic parameters for its parent. See chalk's documentation for details.
 +///
 +/// One thing to keep in mind when working with `Binders` (and `Substitution`s, which represent
 +/// generic arguments) in rust-analyzer is that the ordering within *is* significant - the generic
 +/// parameters/arguments for an item MUST come before those for its parent. This is to facilitate
 +/// the integration with chalk-solve, which mildly puts constraints as such. See #13335 for its
 +/// motivation in detail.
 +pub type Binders<T> = chalk_ir::Binders<T>;
 +/// Interned list of generic arguments for an item. When an item has parent, the `Substitution` for
 +/// it contains generic arguments for both its parent and itself. See chalk's documentation for
 +/// details.
 +///
 +/// See `Binders` for the constraint on the ordering.
 +pub type Substitution = chalk_ir::Substitution<Interner>;
 +pub type GenericArg = chalk_ir::GenericArg<Interner>;
 +pub type GenericArgData = chalk_ir::GenericArgData<Interner>;
 +
 +pub type Ty = chalk_ir::Ty<Interner>;
 +pub type TyKind = chalk_ir::TyKind<Interner>;
 +pub type DynTy = chalk_ir::DynTy<Interner>;
 +pub type FnPointer = chalk_ir::FnPointer<Interner>;
 +// pub type FnSubst = chalk_ir::FnSubst<Interner>;
 +pub use chalk_ir::FnSubst;
 +pub type ProjectionTy = chalk_ir::ProjectionTy<Interner>;
 +pub type AliasTy = chalk_ir::AliasTy<Interner>;
 +pub type OpaqueTy = chalk_ir::OpaqueTy<Interner>;
 +pub type InferenceVar = chalk_ir::InferenceVar;
 +
 +pub type Lifetime = chalk_ir::Lifetime<Interner>;
 +pub type LifetimeData = chalk_ir::LifetimeData<Interner>;
 +pub type LifetimeOutlives = chalk_ir::LifetimeOutlives<Interner>;
 +
 +pub type Const = chalk_ir::Const<Interner>;
 +pub type ConstData = chalk_ir::ConstData<Interner>;
 +pub type ConstValue = chalk_ir::ConstValue<Interner>;
 +pub type ConcreteConst = chalk_ir::ConcreteConst<Interner>;
 +
 +pub type ChalkTraitId = chalk_ir::TraitId<Interner>;
 +pub type TraitRef = chalk_ir::TraitRef<Interner>;
 +pub type QuantifiedWhereClause = Binders<WhereClause>;
 +pub type QuantifiedWhereClauses = chalk_ir::QuantifiedWhereClauses<Interner>;
 +pub type Canonical<T> = chalk_ir::Canonical<T>;
 +
 +pub type FnSig = chalk_ir::FnSig<Interner>;
 +
 +pub type InEnvironment<T> = chalk_ir::InEnvironment<T>;
 +pub type Environment = chalk_ir::Environment<Interner>;
 +pub type DomainGoal = chalk_ir::DomainGoal<Interner>;
 +pub type Goal = chalk_ir::Goal<Interner>;
 +pub type AliasEq = chalk_ir::AliasEq<Interner>;
 +pub type Solution = chalk_solve::Solution<Interner>;
 +pub type ConstrainedSubst = chalk_ir::ConstrainedSubst<Interner>;
 +pub type Guidance = chalk_solve::Guidance<Interner>;
 +pub type WhereClause = chalk_ir::WhereClause<Interner>;
 +
 +/// Return an index of a parameter in the generic type parameter list by it's id.
 +pub fn param_idx(db: &dyn HirDatabase, id: TypeOrConstParamId) -> Option<usize> {
 +    generics(db.upcast(), id.parent).param_idx(id)
 +}
 +
 +pub(crate) fn wrap_empty_binders<T>(value: T) -> Binders<T>
 +where
 +    T: TypeFoldable<Interner> + HasInterner<Interner = Interner>,
 +{
 +    Binders::empty(Interner, value.shifted_in_from(Interner, DebruijnIndex::ONE))
 +}
 +
 +pub(crate) fn make_type_and_const_binders<T: HasInterner<Interner = Interner>>(
 +    which_is_const: impl Iterator<Item = Option<Ty>>,
 +    value: T,
 +) -> Binders<T> {
 +    Binders::new(
 +        VariableKinds::from_iter(
 +            Interner,
 +            which_is_const.map(|x| {
 +                if let Some(ty) = x {
 +                    chalk_ir::VariableKind::Const(ty)
 +                } else {
 +                    chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General)
 +                }
 +            }),
 +        ),
 +        value,
 +    )
 +}
 +
 +pub(crate) fn make_single_type_binders<T: HasInterner<Interner = Interner>>(
 +    value: T,
 +) -> Binders<T> {
 +    Binders::new(
 +        VariableKinds::from_iter(
 +            Interner,
 +            std::iter::once(chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General)),
 +        ),
 +        value,
 +    )
 +}
 +
 +pub(crate) fn make_binders_with_count<T: HasInterner<Interner = Interner>>(
 +    db: &dyn HirDatabase,
 +    count: usize,
 +    generics: &Generics,
 +    value: T,
 +) -> Binders<T> {
 +    let it = generics.iter_id().take(count).map(|id| match id {
 +        Either::Left(_) => None,
 +        Either::Right(id) => Some(db.const_param_ty(id)),
 +    });
 +    crate::make_type_and_const_binders(it, value)
 +}
 +
 +pub(crate) fn make_binders<T: HasInterner<Interner = Interner>>(
 +    db: &dyn HirDatabase,
 +    generics: &Generics,
 +    value: T,
 +) -> Binders<T> {
 +    make_binders_with_count(db, usize::MAX, generics, value)
 +}
 +
 +// FIXME: get rid of this, just replace it by FnPointer
 +/// A function signature as seen by type inference: Several parameter types and
 +/// one return type.
 +#[derive(Clone, PartialEq, Eq, Debug)]
 +pub struct CallableSig {
 +    params_and_return: Arc<[Ty]>,
 +    is_varargs: bool,
++    safety: Safety,
 +}
 +
 +has_interner!(CallableSig);
 +
 +/// A polymorphic function signature.
 +pub type PolyFnSig = Binders<CallableSig>;
 +
 +impl CallableSig {
-         CallableSig { params_and_return: params.into(), is_varargs }
++    pub fn from_params_and_return(
++        mut params: Vec<Ty>,
++        ret: Ty,
++        is_varargs: bool,
++        safety: Safety,
++    ) -> CallableSig {
 +        params.push(ret);
-             sig: FnSig { abi: (), safety: Safety::Safe, variadic: self.is_varargs },
++        CallableSig { params_and_return: params.into(), is_varargs, safety }
 +    }
 +
 +    pub fn from_fn_ptr(fn_ptr: &FnPointer) -> CallableSig {
 +        CallableSig {
 +            // FIXME: what to do about lifetime params? -> return PolyFnSig
 +            params_and_return: fn_ptr
 +                .substitution
 +                .clone()
 +                .shifted_out_to(Interner, DebruijnIndex::ONE)
 +                .expect("unexpected lifetime vars in fn ptr")
 +                .0
 +                .as_slice(Interner)
 +                .iter()
 +                .map(|arg| arg.assert_ty_ref(Interner).clone())
 +                .collect(),
 +            is_varargs: fn_ptr.sig.variadic,
++            safety: fn_ptr.sig.safety,
 +        }
 +    }
 +
 +    pub fn to_fn_ptr(&self) -> FnPointer {
 +        FnPointer {
 +            num_binders: 0,
-         Ok(CallableSig { params_and_return: folded.into(), is_varargs: self.is_varargs })
++            sig: FnSig { abi: (), safety: self.safety, variadic: self.is_varargs },
 +            substitution: FnSubst(Substitution::from_iter(
 +                Interner,
 +                self.params_and_return.iter().cloned(),
 +            )),
 +        }
 +    }
 +
 +    pub fn params(&self) -> &[Ty] {
 +        &self.params_and_return[0..self.params_and_return.len() - 1]
 +    }
 +
 +    pub fn ret(&self) -> &Ty {
 +        &self.params_and_return[self.params_and_return.len() - 1]
 +    }
 +}
 +
 +impl TypeFoldable<Interner> for CallableSig {
 +    fn try_fold_with<E>(
 +        self,
 +        folder: &mut dyn chalk_ir::fold::FallibleTypeFolder<Interner, Error = E>,
 +        outer_binder: DebruijnIndex,
 +    ) -> Result<Self, E> {
 +        let vec = self.params_and_return.to_vec();
 +        let folded = vec.try_fold_with(folder, outer_binder)?;
++        Ok(CallableSig {
++            params_and_return: folded.into(),
++            is_varargs: self.is_varargs,
++            safety: self.safety,
++        })
 +    }
 +}
 +
 +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
 +pub enum ImplTraitId {
 +    ReturnTypeImplTrait(hir_def::FunctionId, u16),
 +    AsyncBlockTypeImplTrait(hir_def::DefWithBodyId, ExprId),
 +}
 +
 +#[derive(Clone, PartialEq, Eq, Debug, Hash)]
 +pub struct ReturnTypeImplTraits {
 +    pub(crate) impl_traits: Vec<ReturnTypeImplTrait>,
 +}
 +
 +has_interner!(ReturnTypeImplTraits);
 +
 +#[derive(Clone, PartialEq, Eq, Debug, Hash)]
 +pub(crate) struct ReturnTypeImplTrait {
 +    pub(crate) bounds: Binders<Vec<QuantifiedWhereClause>>,
 +}
 +
 +pub fn static_lifetime() -> Lifetime {
 +    LifetimeData::Static.intern(Interner)
 +}
 +
 +pub(crate) fn fold_free_vars<T: HasInterner<Interner = Interner> + TypeFoldable<Interner>>(
 +    t: T,
 +    for_ty: impl FnMut(BoundVar, DebruijnIndex) -> Ty,
 +    for_const: impl FnMut(Ty, BoundVar, DebruijnIndex) -> Const,
 +) -> T {
 +    use chalk_ir::fold::TypeFolder;
 +
 +    #[derive(chalk_derive::FallibleTypeFolder)]
 +    #[has_interner(Interner)]
 +    struct FreeVarFolder<
 +        F1: FnMut(BoundVar, DebruijnIndex) -> Ty,
 +        F2: FnMut(Ty, BoundVar, DebruijnIndex) -> Const,
 +    >(F1, F2);
 +    impl<
 +            F1: FnMut(BoundVar, DebruijnIndex) -> Ty,
 +            F2: FnMut(Ty, BoundVar, DebruijnIndex) -> Const,
 +        > TypeFolder<Interner> for FreeVarFolder<F1, F2>
 +    {
 +        fn as_dyn(&mut self) -> &mut dyn TypeFolder<Interner, Error = Self::Error> {
 +            self
 +        }
 +
 +        fn interner(&self) -> Interner {
 +            Interner
 +        }
 +
 +        fn fold_free_var_ty(&mut self, bound_var: BoundVar, outer_binder: DebruijnIndex) -> Ty {
 +            self.0(bound_var, outer_binder)
 +        }
 +
 +        fn fold_free_var_const(
 +            &mut self,
 +            ty: Ty,
 +            bound_var: BoundVar,
 +            outer_binder: DebruijnIndex,
 +        ) -> Const {
 +            self.1(ty, bound_var, outer_binder)
 +        }
 +    }
 +    t.fold_with(&mut FreeVarFolder(for_ty, for_const), DebruijnIndex::INNERMOST)
 +}
 +
 +pub(crate) fn fold_tys<T: HasInterner<Interner = Interner> + TypeFoldable<Interner>>(
 +    t: T,
 +    mut for_ty: impl FnMut(Ty, DebruijnIndex) -> Ty,
 +    binders: DebruijnIndex,
 +) -> T {
 +    fold_tys_and_consts(
 +        t,
 +        |x, d| match x {
 +            Either::Left(x) => Either::Left(for_ty(x, d)),
 +            Either::Right(x) => Either::Right(x),
 +        },
 +        binders,
 +    )
 +}
 +
 +pub(crate) fn fold_tys_and_consts<T: HasInterner<Interner = Interner> + TypeFoldable<Interner>>(
 +    t: T,
 +    f: impl FnMut(Either<Ty, Const>, DebruijnIndex) -> Either<Ty, Const>,
 +    binders: DebruijnIndex,
 +) -> T {
 +    use chalk_ir::fold::{TypeFolder, TypeSuperFoldable};
 +    #[derive(chalk_derive::FallibleTypeFolder)]
 +    #[has_interner(Interner)]
 +    struct TyFolder<F: FnMut(Either<Ty, Const>, DebruijnIndex) -> Either<Ty, Const>>(F);
 +    impl<F: FnMut(Either<Ty, Const>, DebruijnIndex) -> Either<Ty, Const>> TypeFolder<Interner>
 +        for TyFolder<F>
 +    {
 +        fn as_dyn(&mut self) -> &mut dyn TypeFolder<Interner, Error = Self::Error> {
 +            self
 +        }
 +
 +        fn interner(&self) -> Interner {
 +            Interner
 +        }
 +
 +        fn fold_ty(&mut self, ty: Ty, outer_binder: DebruijnIndex) -> Ty {
 +            let ty = ty.super_fold_with(self.as_dyn(), outer_binder);
 +            self.0(Either::Left(ty), outer_binder).left().unwrap()
 +        }
 +
 +        fn fold_const(&mut self, c: Const, outer_binder: DebruijnIndex) -> Const {
 +            self.0(Either::Right(c), outer_binder).right().unwrap()
 +        }
 +    }
 +    t.fold_with(&mut TyFolder(f), binders)
 +}
 +
 +/// 'Canonicalizes' the `t` by replacing any errors with new variables. Also
 +/// ensures there are no unbound variables or inference variables anywhere in
 +/// the `t`.
 +pub fn replace_errors_with_variables<T>(t: &T) -> Canonical<T>
 +where
 +    T: HasInterner<Interner = Interner> + TypeFoldable<Interner> + Clone,
 +{
 +    use chalk_ir::{
 +        fold::{FallibleTypeFolder, TypeSuperFoldable},
 +        Fallible,
 +    };
 +    struct ErrorReplacer {
 +        vars: usize,
 +    }
 +    impl FallibleTypeFolder<Interner> for ErrorReplacer {
 +        type Error = NoSolution;
 +
 +        fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder<Interner, Error = Self::Error> {
 +            self
 +        }
 +
 +        fn interner(&self) -> Interner {
 +            Interner
 +        }
 +
 +        fn try_fold_ty(&mut self, ty: Ty, outer_binder: DebruijnIndex) -> Fallible<Ty> {
 +            if let TyKind::Error = ty.kind(Interner) {
 +                let index = self.vars;
 +                self.vars += 1;
 +                Ok(TyKind::BoundVar(BoundVar::new(outer_binder, index)).intern(Interner))
 +            } else {
 +                ty.try_super_fold_with(self.as_dyn(), outer_binder)
 +            }
 +        }
 +
 +        fn try_fold_inference_ty(
 +            &mut self,
 +            _var: InferenceVar,
 +            _kind: TyVariableKind,
 +            _outer_binder: DebruijnIndex,
 +        ) -> Fallible<Ty> {
 +            if cfg!(debug_assertions) {
 +                // we don't want to just panic here, because then the error message
 +                // won't contain the whole thing, which would not be very helpful
 +                Err(NoSolution)
 +            } else {
 +                Ok(TyKind::Error.intern(Interner))
 +            }
 +        }
 +
 +        fn try_fold_free_var_ty(
 +            &mut self,
 +            _bound_var: BoundVar,
 +            _outer_binder: DebruijnIndex,
 +        ) -> Fallible<Ty> {
 +            if cfg!(debug_assertions) {
 +                // we don't want to just panic here, because then the error message
 +                // won't contain the whole thing, which would not be very helpful
 +                Err(NoSolution)
 +            } else {
 +                Ok(TyKind::Error.intern(Interner))
 +            }
 +        }
 +
 +        fn try_fold_inference_const(
 +            &mut self,
 +            ty: Ty,
 +            _var: InferenceVar,
 +            _outer_binder: DebruijnIndex,
 +        ) -> Fallible<Const> {
 +            if cfg!(debug_assertions) {
 +                Err(NoSolution)
 +            } else {
 +                Ok(unknown_const(ty))
 +            }
 +        }
 +
 +        fn try_fold_free_var_const(
 +            &mut self,
 +            ty: Ty,
 +            _bound_var: BoundVar,
 +            _outer_binder: DebruijnIndex,
 +        ) -> Fallible<Const> {
 +            if cfg!(debug_assertions) {
 +                Err(NoSolution)
 +            } else {
 +                Ok(unknown_const(ty))
 +            }
 +        }
 +
 +        fn try_fold_inference_lifetime(
 +            &mut self,
 +            _var: InferenceVar,
 +            _outer_binder: DebruijnIndex,
 +        ) -> Fallible<Lifetime> {
 +            if cfg!(debug_assertions) {
 +                Err(NoSolution)
 +            } else {
 +                Ok(static_lifetime())
 +            }
 +        }
 +
 +        fn try_fold_free_var_lifetime(
 +            &mut self,
 +            _bound_var: BoundVar,
 +            _outer_binder: DebruijnIndex,
 +        ) -> Fallible<Lifetime> {
 +            if cfg!(debug_assertions) {
 +                Err(NoSolution)
 +            } else {
 +                Ok(static_lifetime())
 +            }
 +        }
 +    }
 +    let mut error_replacer = ErrorReplacer { vars: 0 };
 +    let value = match t.clone().try_fold_with(&mut error_replacer, DebruijnIndex::INNERMOST) {
 +        Ok(t) => t,
 +        Err(_) => panic!("Encountered unbound or inference vars in {:?}", t),
 +    };
 +    let kinds = (0..error_replacer.vars).map(|_| {
 +        chalk_ir::CanonicalVarKind::new(
 +            chalk_ir::VariableKind::Ty(TyVariableKind::General),
 +            chalk_ir::UniverseIndex::ROOT,
 +        )
 +    });
 +    Canonical { value, binders: chalk_ir::CanonicalVarKinds::from_iter(Interner, kinds) }
 +}
++
++pub fn callable_sig_from_fnonce(
++    self_ty: &Canonical<Ty>,
++    env: Arc<TraitEnvironment>,
++    db: &dyn HirDatabase,
++) -> Option<CallableSig> {
++    let krate = env.krate;
++    let fn_once_trait = FnTrait::FnOnce.get_id(db, krate)?;
++    let output_assoc_type = db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?;
++
++    let mut kinds = self_ty.binders.interned().to_vec();
++    let b = TyBuilder::trait_ref(db, fn_once_trait);
++    if b.remaining() != 2 {
++        return None;
++    }
++    let fn_once = b
++        .push(self_ty.value.clone())
++        .fill_with_bound_vars(DebruijnIndex::INNERMOST, kinds.len())
++        .build();
++    kinds.extend(fn_once.substitution.iter(Interner).skip(1).map(|x| {
++        let vk = match x.data(Interner) {
++            chalk_ir::GenericArgData::Ty(_) => {
++                chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General)
++            }
++            chalk_ir::GenericArgData::Lifetime(_) => chalk_ir::VariableKind::Lifetime,
++            chalk_ir::GenericArgData::Const(c) => {
++                chalk_ir::VariableKind::Const(c.data(Interner).ty.clone())
++            }
++        };
++        chalk_ir::WithKind::new(vk, UniverseIndex::ROOT)
++    }));
++
++    // FIXME: chalk refuses to solve `<Self as FnOnce<^0.0>>::Output == ^0.1`, so we first solve
++    // `<Self as FnOnce<^0.0>>` and then replace `^0.0` with the concrete argument tuple.
++    let trait_env = env.env.clone();
++    let obligation = InEnvironment { goal: fn_once.cast(Interner), environment: trait_env };
++    let canonical =
++        Canonical { binders: CanonicalVarKinds::from_iter(Interner, kinds), value: obligation };
++    let subst = match db.trait_solve(krate, canonical) {
++        Some(Solution::Unique(vars)) => vars.value.subst,
++        _ => return None,
++    };
++    let args = subst.at(Interner, self_ty.binders.interned().len()).ty(Interner)?;
++    let params = match args.kind(Interner) {
++        chalk_ir::TyKind::Tuple(_, subst) => {
++            subst.iter(Interner).filter_map(|arg| arg.ty(Interner).cloned()).collect::<Vec<_>>()
++        }
++        _ => return None,
++    };
++    if params.iter().any(|ty| ty.is_unknown()) {
++        return None;
++    }
++
++    let fn_once = TyBuilder::trait_ref(db, fn_once_trait)
++        .push(self_ty.value.clone())
++        .push(args.clone())
++        .build();
++    let projection =
++        TyBuilder::assoc_type_projection(db, output_assoc_type, Some(fn_once.substitution.clone()))
++            .build();
++
++    let ret_ty = db.normalize_projection(projection, env);
++
++    Some(CallableSig::from_params_and_return(params, ret_ty.clone(), false, Safety::Safe))
++}
index 22a85cf154587fa79e05807f3196391255b6513e,0000000000000000000000000000000000000000..baf9842d5fbf262101c6171f215456f48e31895e
mode 100644,000000..100644
--- /dev/null
@@@ -1,1977 -1,0 +1,1986 @@@
-             TypeRef::Fn(params, is_varargs) => {
 +//! Methods for lowering the HIR to types. There are two main cases here:
 +//!
 +//!  - Lowering a type reference like `&usize` or `Option<foo::bar::Baz>` to a
 +//!    type: The entry point for this is `TyLoweringContext::lower_ty`.
 +//!  - Building the type for an item: This happens through the `ty` query.
 +//!
 +//! This usually involves resolving names, collecting generic arguments etc.
 +use std::{
 +    cell::{Cell, RefCell, RefMut},
 +    iter,
 +    sync::Arc,
 +};
 +
 +use base_db::CrateId;
 +use chalk_ir::{
 +    cast::Cast, fold::Shift, fold::TypeFoldable, interner::HasInterner, Mutability, Safety,
 +};
 +
 +use hir_def::{
 +    adt::StructKind,
 +    body::{Expander, LowerCtx},
 +    builtin_type::BuiltinType,
 +    generics::{
 +        TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget,
 +    },
 +    intern::Interned,
 +    lang_item::lang_attr,
 +    path::{GenericArg, ModPath, Path, PathKind, PathSegment, PathSegments},
 +    resolver::{HasResolver, Resolver, TypeNs},
 +    type_ref::{
 +        ConstScalarOrPath, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, TypeRef,
 +    },
 +    AdtId, AssocItemId, ConstId, ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId,
 +    HasModule, ImplId, ItemContainerId, LocalFieldId, Lookup, StaticId, StructId, TraitId,
 +    TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, VariantId,
 +};
 +use hir_expand::{name::Name, ExpandResult};
 +use itertools::Either;
 +use la_arena::ArenaMap;
 +use rustc_hash::FxHashSet;
 +use smallvec::SmallVec;
 +use stdx::{impl_from, never};
 +use syntax::{ast, SmolStr};
 +
 +use crate::{
 +    all_super_traits,
 +    consteval::{intern_const_scalar, path_to_const, unknown_const, unknown_const_as_generic},
 +    db::HirDatabase,
 +    make_binders,
 +    mapping::{from_chalk_trait_id, ToChalk},
 +    static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx,
 +    utils::Generics,
 +    utils::{all_super_trait_refs, associated_type_by_name_including_super_traits, generics},
 +    AliasEq, AliasTy, Binders, BoundVar, CallableSig, Const, DebruijnIndex, DynTy, FnPointer,
 +    FnSig, FnSubst, GenericArgData, ImplTraitId, Interner, ParamKind, PolyFnSig, ProjectionTy,
 +    QuantifiedWhereClause, QuantifiedWhereClauses, ReturnTypeImplTrait, ReturnTypeImplTraits,
 +    Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause,
 +};
 +
 +#[derive(Debug)]
 +pub struct TyLoweringContext<'a> {
 +    pub db: &'a dyn HirDatabase,
 +    pub resolver: &'a Resolver,
 +    in_binders: DebruijnIndex,
 +    /// Note: Conceptually, it's thinkable that we could be in a location where
 +    /// some type params should be represented as placeholders, and others
 +    /// should be converted to variables. I think in practice, this isn't
 +    /// possible currently, so this should be fine for now.
 +    pub type_param_mode: ParamLoweringMode,
 +    pub impl_trait_mode: ImplTraitLoweringMode,
 +    impl_trait_counter: Cell<u16>,
 +    /// When turning `impl Trait` into opaque types, we have to collect the
 +    /// bounds at the same time to get the IDs correct (without becoming too
 +    /// complicated). I don't like using interior mutability (as for the
 +    /// counter), but I've tried and failed to make the lifetimes work for
 +    /// passing around a `&mut TyLoweringContext`. The core problem is that
 +    /// we're grouping the mutable data (the counter and this field) together
 +    /// with the immutable context (the references to the DB and resolver).
 +    /// Splitting this up would be a possible fix.
 +    opaque_type_data: RefCell<Vec<ReturnTypeImplTrait>>,
 +    expander: RefCell<Option<Expander>>,
 +    /// Tracks types with explicit `?Sized` bounds.
 +    pub(crate) unsized_types: RefCell<FxHashSet<Ty>>,
 +}
 +
 +impl<'a> TyLoweringContext<'a> {
 +    pub fn new(db: &'a dyn HirDatabase, resolver: &'a Resolver) -> Self {
 +        let impl_trait_counter = Cell::new(0);
 +        let impl_trait_mode = ImplTraitLoweringMode::Disallowed;
 +        let type_param_mode = ParamLoweringMode::Placeholder;
 +        let in_binders = DebruijnIndex::INNERMOST;
 +        let opaque_type_data = RefCell::new(Vec::new());
 +        Self {
 +            db,
 +            resolver,
 +            in_binders,
 +            impl_trait_mode,
 +            impl_trait_counter,
 +            type_param_mode,
 +            opaque_type_data,
 +            expander: RefCell::new(None),
 +            unsized_types: RefCell::default(),
 +        }
 +    }
 +
 +    pub fn with_debruijn<T>(
 +        &self,
 +        debruijn: DebruijnIndex,
 +        f: impl FnOnce(&TyLoweringContext<'_>) -> T,
 +    ) -> T {
 +        let opaque_ty_data_vec = self.opaque_type_data.take();
 +        let expander = self.expander.take();
 +        let unsized_types = self.unsized_types.take();
 +        let new_ctx = Self {
 +            in_binders: debruijn,
 +            impl_trait_counter: Cell::new(self.impl_trait_counter.get()),
 +            opaque_type_data: RefCell::new(opaque_ty_data_vec),
 +            expander: RefCell::new(expander),
 +            unsized_types: RefCell::new(unsized_types),
 +            ..*self
 +        };
 +        let result = f(&new_ctx);
 +        self.impl_trait_counter.set(new_ctx.impl_trait_counter.get());
 +        self.opaque_type_data.replace(new_ctx.opaque_type_data.into_inner());
 +        self.expander.replace(new_ctx.expander.into_inner());
 +        self.unsized_types.replace(new_ctx.unsized_types.into_inner());
 +        result
 +    }
 +
 +    pub fn with_shifted_in<T>(
 +        &self,
 +        debruijn: DebruijnIndex,
 +        f: impl FnOnce(&TyLoweringContext<'_>) -> T,
 +    ) -> T {
 +        self.with_debruijn(self.in_binders.shifted_in_from(debruijn), f)
 +    }
 +
 +    pub fn with_impl_trait_mode(self, impl_trait_mode: ImplTraitLoweringMode) -> Self {
 +        Self { impl_trait_mode, ..self }
 +    }
 +
 +    pub fn with_type_param_mode(self, type_param_mode: ParamLoweringMode) -> Self {
 +        Self { type_param_mode, ..self }
 +    }
 +}
 +
 +#[derive(Copy, Clone, Debug, PartialEq, Eq)]
 +pub enum ImplTraitLoweringMode {
 +    /// `impl Trait` gets lowered into an opaque type that doesn't unify with
 +    /// anything except itself. This is used in places where values flow 'out',
 +    /// i.e. for arguments of the function we're currently checking, and return
 +    /// types of functions we're calling.
 +    Opaque,
 +    /// `impl Trait` gets lowered into a type variable. Used for argument
 +    /// position impl Trait when inside the respective function, since it allows
 +    /// us to support that without Chalk.
 +    Param,
 +    /// `impl Trait` gets lowered into a variable that can unify with some
 +    /// type. This is used in places where values flow 'in', i.e. for arguments
 +    /// of functions we're calling, and the return type of the function we're
 +    /// currently checking.
 +    Variable,
 +    /// `impl Trait` is disallowed and will be an error.
 +    Disallowed,
 +}
 +
 +#[derive(Copy, Clone, Debug, PartialEq, Eq)]
 +pub enum ParamLoweringMode {
 +    Placeholder,
 +    Variable,
 +}
 +
 +impl<'a> TyLoweringContext<'a> {
 +    pub fn lower_ty(&self, type_ref: &TypeRef) -> Ty {
 +        self.lower_ty_ext(type_ref).0
 +    }
 +
 +    fn generics(&self) -> Generics {
 +        generics(
 +            self.db.upcast(),
 +            self.resolver
 +                .generic_def()
 +                .expect("there should be generics if there's a generic param"),
 +        )
 +    }
 +
 +    pub fn lower_ty_ext(&self, type_ref: &TypeRef) -> (Ty, Option<TypeNs>) {
 +        let mut res = None;
 +        let ty = match type_ref {
 +            TypeRef::Never => TyKind::Never.intern(Interner),
 +            TypeRef::Tuple(inner) => {
 +                let inner_tys = inner.iter().map(|tr| self.lower_ty(tr));
 +                TyKind::Tuple(inner_tys.len(), Substitution::from_iter(Interner, inner_tys))
 +                    .intern(Interner)
 +            }
 +            TypeRef::Path(path) => {
 +                let (ty, res_) = self.lower_path(path);
 +                res = res_;
 +                ty
 +            }
 +            TypeRef::RawPtr(inner, mutability) => {
 +                let inner_ty = self.lower_ty(inner);
 +                TyKind::Raw(lower_to_chalk_mutability(*mutability), inner_ty).intern(Interner)
 +            }
 +            TypeRef::Array(inner, len) => {
 +                let inner_ty = self.lower_ty(inner);
 +                let const_len = const_or_path_to_chalk(
 +                    self.db,
 +                    self.resolver,
 +                    TyBuilder::usize(),
 +                    len,
 +                    self.type_param_mode,
 +                    || self.generics(),
 +                    self.in_binders,
 +                );
 +
 +                TyKind::Array(inner_ty, const_len).intern(Interner)
 +            }
 +            TypeRef::Slice(inner) => {
 +                let inner_ty = self.lower_ty(inner);
 +                TyKind::Slice(inner_ty).intern(Interner)
 +            }
 +            TypeRef::Reference(inner, _, mutability) => {
 +                let inner_ty = self.lower_ty(inner);
 +                let lifetime = static_lifetime();
 +                TyKind::Ref(lower_to_chalk_mutability(*mutability), lifetime, inner_ty)
 +                    .intern(Interner)
 +            }
 +            TypeRef::Placeholder => TyKind::Error.intern(Interner),
-                     sig: FnSig { abi: (), safety: Safety::Safe, variadic: *is_varargs },
++            &TypeRef::Fn(ref params, variadic, is_unsafe) => {
 +                let substs = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
 +                    Substitution::from_iter(Interner, params.iter().map(|(_, tr)| ctx.lower_ty(tr)))
 +                });
 +                TyKind::Function(FnPointer {
 +                    num_binders: 0, // FIXME lower `for<'a> fn()` correctly
-     let sig = CallableSig::from_params_and_return(params, ret, data.is_varargs());
++                    sig: FnSig {
++                        abi: (),
++                        safety: if is_unsafe { Safety::Unsafe } else { Safety::Safe },
++                        variadic,
++                    },
 +                    substitution: FnSubst(substs),
 +                })
 +                .intern(Interner)
 +            }
 +            TypeRef::DynTrait(bounds) => self.lower_dyn_trait(bounds),
 +            TypeRef::ImplTrait(bounds) => {
 +                match self.impl_trait_mode {
 +                    ImplTraitLoweringMode::Opaque => {
 +                        let idx = self.impl_trait_counter.get();
 +                        self.impl_trait_counter.set(idx + 1);
 +                        let func = match self.resolver.generic_def() {
 +                            Some(GenericDefId::FunctionId(f)) => f,
 +                            _ => panic!("opaque impl trait lowering in non-function"),
 +                        };
 +
 +                        assert!(idx as usize == self.opaque_type_data.borrow().len());
 +                        // this dance is to make sure the data is in the right
 +                        // place even if we encounter more opaque types while
 +                        // lowering the bounds
 +                        self.opaque_type_data.borrow_mut().push(ReturnTypeImplTrait {
 +                            bounds: crate::make_single_type_binders(Vec::new()),
 +                        });
 +                        // We don't want to lower the bounds inside the binders
 +                        // we're currently in, because they don't end up inside
 +                        // those binders. E.g. when we have `impl Trait<impl
 +                        // OtherTrait<T>>`, the `impl OtherTrait<T>` can't refer
 +                        // to the self parameter from `impl Trait`, and the
 +                        // bounds aren't actually stored nested within each
 +                        // other, but separately. So if the `T` refers to a type
 +                        // parameter of the outer function, it's just one binder
 +                        // away instead of two.
 +                        let actual_opaque_type_data = self
 +                            .with_debruijn(DebruijnIndex::INNERMOST, |ctx| {
 +                                ctx.lower_impl_trait(bounds, func)
 +                            });
 +                        self.opaque_type_data.borrow_mut()[idx as usize] = actual_opaque_type_data;
 +
 +                        let impl_trait_id = ImplTraitId::ReturnTypeImplTrait(func, idx);
 +                        let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into();
 +                        let generics = generics(self.db.upcast(), func.into());
 +                        let parameters = generics.bound_vars_subst(self.db, self.in_binders);
 +                        TyKind::OpaqueType(opaque_ty_id, parameters).intern(Interner)
 +                    }
 +                    ImplTraitLoweringMode::Param => {
 +                        let idx = self.impl_trait_counter.get();
 +                        // FIXME we're probably doing something wrong here
 +                        self.impl_trait_counter.set(idx + count_impl_traits(type_ref) as u16);
 +                        if let Some(def) = self.resolver.generic_def() {
 +                            let generics = generics(self.db.upcast(), def);
 +                            let param = generics
 +                                .iter()
 +                                .filter(|(_, data)| {
 +                                    matches!(
 +                                        data,
 +                                        TypeOrConstParamData::TypeParamData(data)
 +                                        if data.provenance == TypeParamProvenance::ArgumentImplTrait
 +                                    )
 +                                })
 +                                .nth(idx as usize)
 +                                .map_or(TyKind::Error, |(id, _)| {
 +                                    TyKind::Placeholder(to_placeholder_idx(self.db, id))
 +                                });
 +                            param.intern(Interner)
 +                        } else {
 +                            TyKind::Error.intern(Interner)
 +                        }
 +                    }
 +                    ImplTraitLoweringMode::Variable => {
 +                        let idx = self.impl_trait_counter.get();
 +                        // FIXME we're probably doing something wrong here
 +                        self.impl_trait_counter.set(idx + count_impl_traits(type_ref) as u16);
 +                        let (
 +                            _parent_params,
 +                            self_params,
 +                            list_params,
 +                            const_params,
 +                            _impl_trait_params,
 +                        ) = if let Some(def) = self.resolver.generic_def() {
 +                            let generics = generics(self.db.upcast(), def);
 +                            generics.provenance_split()
 +                        } else {
 +                            (0, 0, 0, 0, 0)
 +                        };
 +                        TyKind::BoundVar(BoundVar::new(
 +                            self.in_binders,
 +                            idx as usize + self_params + list_params + const_params,
 +                        ))
 +                        .intern(Interner)
 +                    }
 +                    ImplTraitLoweringMode::Disallowed => {
 +                        // FIXME: report error
 +                        TyKind::Error.intern(Interner)
 +                    }
 +                }
 +            }
 +            TypeRef::Macro(macro_call) => {
 +                let (mut expander, recursion_start) = {
 +                    match RefMut::filter_map(self.expander.borrow_mut(), Option::as_mut) {
 +                        // There already is an expander here, this means we are already recursing
 +                        Ok(expander) => (expander, false),
 +                        // No expander was created yet, so we are at the start of the expansion recursion
 +                        // and therefore have to create an expander.
 +                        Err(expander) => (
 +                            RefMut::map(expander, |it| {
 +                                it.insert(Expander::new(
 +                                    self.db.upcast(),
 +                                    macro_call.file_id,
 +                                    self.resolver.module(),
 +                                ))
 +                            }),
 +                            true,
 +                        ),
 +                    }
 +                };
 +                let ty = {
 +                    let macro_call = macro_call.to_node(self.db.upcast());
 +                    match expander.enter_expand::<ast::Type>(self.db.upcast(), macro_call) {
 +                        Ok(ExpandResult { value: Some((mark, expanded)), .. }) => {
 +                            let ctx = LowerCtx::new(self.db.upcast(), expander.current_file_id());
 +                            let type_ref = TypeRef::from_ast(&ctx, expanded);
 +
 +                            drop(expander);
 +                            let ty = self.lower_ty(&type_ref);
 +
 +                            self.expander
 +                                .borrow_mut()
 +                                .as_mut()
 +                                .unwrap()
 +                                .exit(self.db.upcast(), mark);
 +                            Some(ty)
 +                        }
 +                        _ => {
 +                            drop(expander);
 +                            None
 +                        }
 +                    }
 +                };
 +
 +                // drop the expander, resetting it to pre-recursion state
 +                if recursion_start {
 +                    *self.expander.borrow_mut() = None;
 +                }
 +                ty.unwrap_or_else(|| TyKind::Error.intern(Interner))
 +            }
 +            TypeRef::Error => TyKind::Error.intern(Interner),
 +        };
 +        (ty, res)
 +    }
 +
 +    /// This is only for `generic_predicates_for_param`, where we can't just
 +    /// lower the self types of the predicates since that could lead to cycles.
 +    /// So we just check here if the `type_ref` resolves to a generic param, and which.
 +    fn lower_ty_only_param(&self, type_ref: &TypeRef) -> Option<TypeOrConstParamId> {
 +        let path = match type_ref {
 +            TypeRef::Path(path) => path,
 +            _ => return None,
 +        };
 +        if path.type_anchor().is_some() {
 +            return None;
 +        }
 +        if path.segments().len() > 1 {
 +            return None;
 +        }
 +        let resolution =
 +            match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) {
 +                Some((it, None)) => it,
 +                _ => return None,
 +            };
 +        match resolution {
 +            TypeNs::GenericParam(param_id) => Some(param_id.into()),
 +            _ => None,
 +        }
 +    }
 +
 +    pub(crate) fn lower_ty_relative_path(
 +        &self,
 +        ty: Ty,
 +        // We need the original resolution to lower `Self::AssocTy` correctly
 +        res: Option<TypeNs>,
 +        remaining_segments: PathSegments<'_>,
 +    ) -> (Ty, Option<TypeNs>) {
 +        match remaining_segments.len() {
 +            0 => (ty, res),
 +            1 => {
 +                // resolve unselected assoc types
 +                let segment = remaining_segments.first().unwrap();
 +                (self.select_associated_type(res, segment), None)
 +            }
 +            _ => {
 +                // FIXME report error (ambiguous associated type)
 +                (TyKind::Error.intern(Interner), None)
 +            }
 +        }
 +    }
 +
 +    pub(crate) fn lower_partly_resolved_path(
 +        &self,
 +        resolution: TypeNs,
 +        resolved_segment: PathSegment<'_>,
 +        remaining_segments: PathSegments<'_>,
 +        infer_args: bool,
 +    ) -> (Ty, Option<TypeNs>) {
 +        let ty = match resolution {
 +            TypeNs::TraitId(trait_) => {
 +                let ty = match remaining_segments.len() {
 +                    1 => {
 +                        let trait_ref =
 +                            self.lower_trait_ref_from_resolved_path(trait_, resolved_segment, None);
 +                        let segment = remaining_segments.first().unwrap();
 +                        let found = self
 +                            .db
 +                            .trait_data(trait_ref.hir_trait_id())
 +                            .associated_type_by_name(segment.name);
 +
 +                        match found {
 +                            Some(associated_ty) => {
 +                                // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent
 +                                // generic params. It's inefficient to splice the `Substitution`s, so we may want
 +                                // that method to optionally take parent `Substitution` as we already know them at
 +                                // this point (`trait_ref.substitution`).
 +                                let substitution = self.substs_from_path_segment(
 +                                    segment,
 +                                    Some(associated_ty.into()),
 +                                    false,
 +                                    None,
 +                                );
 +                                let len_self =
 +                                    generics(self.db.upcast(), associated_ty.into()).len_self();
 +                                let substitution = Substitution::from_iter(
 +                                    Interner,
 +                                    substitution
 +                                        .iter(Interner)
 +                                        .take(len_self)
 +                                        .chain(trait_ref.substitution.iter(Interner)),
 +                                );
 +                                TyKind::Alias(AliasTy::Projection(ProjectionTy {
 +                                    associated_ty_id: to_assoc_type_id(associated_ty),
 +                                    substitution,
 +                                }))
 +                                .intern(Interner)
 +                            }
 +                            None => {
 +                                // FIXME: report error (associated type not found)
 +                                TyKind::Error.intern(Interner)
 +                            }
 +                        }
 +                    }
 +                    0 => {
 +                        // Trait object type without dyn; this should be handled in upstream. See
 +                        // `lower_path()`.
 +                        stdx::never!("unexpected fully resolved trait path");
 +                        TyKind::Error.intern(Interner)
 +                    }
 +                    _ => {
 +                        // FIXME report error (ambiguous associated type)
 +                        TyKind::Error.intern(Interner)
 +                    }
 +                };
 +                return (ty, None);
 +            }
 +            TypeNs::GenericParam(param_id) => {
 +                let generics = generics(
 +                    self.db.upcast(),
 +                    self.resolver.generic_def().expect("generics in scope"),
 +                );
 +                match self.type_param_mode {
 +                    ParamLoweringMode::Placeholder => {
 +                        TyKind::Placeholder(to_placeholder_idx(self.db, param_id.into()))
 +                    }
 +                    ParamLoweringMode::Variable => {
 +                        let idx = match generics.param_idx(param_id.into()) {
 +                            None => {
 +                                never!("no matching generics");
 +                                return (TyKind::Error.intern(Interner), None);
 +                            }
 +                            Some(idx) => idx,
 +                        };
 +
 +                        TyKind::BoundVar(BoundVar::new(self.in_binders, idx))
 +                    }
 +                }
 +                .intern(Interner)
 +            }
 +            TypeNs::SelfType(impl_id) => {
 +                let def =
 +                    self.resolver.generic_def().expect("impl should have generic param scope");
 +                let generics = generics(self.db.upcast(), def);
 +
 +                match self.type_param_mode {
 +                    ParamLoweringMode::Placeholder => {
 +                        // `def` can be either impl itself or item within, and we need impl itself
 +                        // now.
 +                        let generics = generics.parent_generics().unwrap_or(&generics);
 +                        let subst = generics.placeholder_subst(self.db);
 +                        self.db.impl_self_ty(impl_id).substitute(Interner, &subst)
 +                    }
 +                    ParamLoweringMode::Variable => {
 +                        let starting_from = match def {
 +                            GenericDefId::ImplId(_) => 0,
 +                            // `def` is an item within impl. We need to substitute `BoundVar`s but
 +                            // remember that they are for parent (i.e. impl) generic params so they
 +                            // come after our own params.
 +                            _ => generics.len_self(),
 +                        };
 +                        TyBuilder::impl_self_ty(self.db, impl_id)
 +                            .fill_with_bound_vars(self.in_binders, starting_from)
 +                            .build()
 +                    }
 +                }
 +            }
 +            TypeNs::AdtSelfType(adt) => {
 +                let generics = generics(self.db.upcast(), adt.into());
 +                let substs = match self.type_param_mode {
 +                    ParamLoweringMode::Placeholder => generics.placeholder_subst(self.db),
 +                    ParamLoweringMode::Variable => {
 +                        generics.bound_vars_subst(self.db, self.in_binders)
 +                    }
 +                };
 +                self.db.ty(adt.into()).substitute(Interner, &substs)
 +            }
 +
 +            TypeNs::AdtId(it) => self.lower_path_inner(resolved_segment, it.into(), infer_args),
 +            TypeNs::BuiltinType(it) => {
 +                self.lower_path_inner(resolved_segment, it.into(), infer_args)
 +            }
 +            TypeNs::TypeAliasId(it) => {
 +                self.lower_path_inner(resolved_segment, it.into(), infer_args)
 +            }
 +            // FIXME: report error
 +            TypeNs::EnumVariantId(_) => return (TyKind::Error.intern(Interner), None),
 +        };
 +        self.lower_ty_relative_path(ty, Some(resolution), remaining_segments)
 +    }
 +
 +    pub(crate) fn lower_path(&self, path: &Path) -> (Ty, Option<TypeNs>) {
 +        // Resolve the path (in type namespace)
 +        if let Some(type_ref) = path.type_anchor() {
 +            let (ty, res) = self.lower_ty_ext(type_ref);
 +            return self.lower_ty_relative_path(ty, res, path.segments());
 +        }
 +
 +        let (resolution, remaining_index) =
 +            match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) {
 +                Some(it) => it,
 +                None => return (TyKind::Error.intern(Interner), None),
 +            };
 +
 +        if matches!(resolution, TypeNs::TraitId(_)) && remaining_index.is_none() {
 +            // trait object type without dyn
 +            let bound = TypeBound::Path(path.clone(), TraitBoundModifier::None);
 +            let ty = self.lower_dyn_trait(&[Interned::new(bound)]);
 +            return (ty, None);
 +        }
 +
 +        let (resolved_segment, remaining_segments) = match remaining_index {
 +            None => (
 +                path.segments().last().expect("resolved path has at least one element"),
 +                PathSegments::EMPTY,
 +            ),
 +            Some(i) => (path.segments().get(i - 1).unwrap(), path.segments().skip(i)),
 +        };
 +        self.lower_partly_resolved_path(resolution, resolved_segment, remaining_segments, false)
 +    }
 +
 +    fn select_associated_type(&self, res: Option<TypeNs>, segment: PathSegment<'_>) -> Ty {
 +        let (def, res) = match (self.resolver.generic_def(), res) {
 +            (Some(def), Some(res)) => (def, res),
 +            _ => return TyKind::Error.intern(Interner),
 +        };
 +        let ty = named_associated_type_shorthand_candidates(
 +            self.db,
 +            def,
 +            res,
 +            Some(segment.name.clone()),
 +            move |name, t, associated_ty| {
 +                if name != segment.name {
 +                    return None;
 +                }
 +
 +                // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent
 +                // generic params. It's inefficient to splice the `Substitution`s, so we may want
 +                // that method to optionally take parent `Substitution` as we already know them at
 +                // this point (`t.substitution`).
 +                let substs = self.substs_from_path_segment(
 +                    segment.clone(),
 +                    Some(associated_ty.into()),
 +                    false,
 +                    None,
 +                );
 +
 +                let len_self = generics(self.db.upcast(), associated_ty.into()).len_self();
 +
 +                let substs = Substitution::from_iter(
 +                    Interner,
 +                    substs.iter(Interner).take(len_self).chain(t.substitution.iter(Interner)),
 +                );
 +
 +                let substs = match self.type_param_mode {
 +                    ParamLoweringMode::Placeholder => {
 +                        // if we're lowering to placeholders, we have to put
 +                        // them in now
 +                        let generics = generics(self.db.upcast(), def);
 +                        let s = generics.placeholder_subst(self.db);
 +                        s.apply(substs, Interner)
 +                    }
 +                    ParamLoweringMode::Variable => substs,
 +                };
 +                // We need to shift in the bound vars, since
 +                // associated_type_shorthand_candidates does not do that
 +                let substs = substs.shifted_in_from(Interner, self.in_binders);
 +                Some(
 +                    TyKind::Alias(AliasTy::Projection(ProjectionTy {
 +                        associated_ty_id: to_assoc_type_id(associated_ty),
 +                        substitution: substs,
 +                    }))
 +                    .intern(Interner),
 +                )
 +            },
 +        );
 +
 +        ty.unwrap_or_else(|| TyKind::Error.intern(Interner))
 +    }
 +
 +    fn lower_path_inner(
 +        &self,
 +        segment: PathSegment<'_>,
 +        typeable: TyDefId,
 +        infer_args: bool,
 +    ) -> Ty {
 +        let generic_def = match typeable {
 +            TyDefId::BuiltinType(_) => None,
 +            TyDefId::AdtId(it) => Some(it.into()),
 +            TyDefId::TypeAliasId(it) => Some(it.into()),
 +        };
 +        let substs = self.substs_from_path_segment(segment, generic_def, infer_args, None);
 +        self.db.ty(typeable).substitute(Interner, &substs)
 +    }
 +
 +    /// Collect generic arguments from a path into a `Substs`. See also
 +    /// `create_substs_for_ast_path` and `def_to_ty` in rustc.
 +    pub(super) fn substs_from_path(
 +        &self,
 +        path: &Path,
 +        // Note that we don't call `db.value_type(resolved)` here,
 +        // `ValueTyDefId` is just a convenient way to pass generics and
 +        // special-case enum variants
 +        resolved: ValueTyDefId,
 +        infer_args: bool,
 +    ) -> Substitution {
 +        let last = path.segments().last().expect("path should have at least one segment");
 +        let (segment, generic_def) = match resolved {
 +            ValueTyDefId::FunctionId(it) => (last, Some(it.into())),
 +            ValueTyDefId::StructId(it) => (last, Some(it.into())),
 +            ValueTyDefId::UnionId(it) => (last, Some(it.into())),
 +            ValueTyDefId::ConstId(it) => (last, Some(it.into())),
 +            ValueTyDefId::StaticId(_) => (last, None),
 +            ValueTyDefId::EnumVariantId(var) => {
 +                // the generic args for an enum variant may be either specified
 +                // on the segment referring to the enum, or on the segment
 +                // referring to the variant. So `Option::<T>::None` and
 +                // `Option::None::<T>` are both allowed (though the former is
 +                // preferred). See also `def_ids_for_path_segments` in rustc.
 +                let len = path.segments().len();
 +                let penultimate = len.checked_sub(2).and_then(|idx| path.segments().get(idx));
 +                let segment = match penultimate {
 +                    Some(segment) if segment.args_and_bindings.is_some() => segment,
 +                    _ => last,
 +                };
 +                (segment, Some(var.parent.into()))
 +            }
 +        };
 +        self.substs_from_path_segment(segment, generic_def, infer_args, None)
 +    }
 +
 +    fn substs_from_path_segment(
 +        &self,
 +        segment: PathSegment<'_>,
 +        def: Option<GenericDefId>,
 +        infer_args: bool,
 +        explicit_self_ty: Option<Ty>,
 +    ) -> Substitution {
 +        // Remember that the item's own generic args come before its parent's.
 +        let mut substs = Vec::new();
 +        let def = if let Some(d) = def {
 +            d
 +        } else {
 +            return Substitution::empty(Interner);
 +        };
 +        let def_generics = generics(self.db.upcast(), def);
 +        let (parent_params, self_params, type_params, const_params, impl_trait_params) =
 +            def_generics.provenance_split();
 +        let item_len = self_params + type_params + const_params + impl_trait_params;
 +        let total_len = parent_params + item_len;
 +
 +        let ty_error = TyKind::Error.intern(Interner).cast(Interner);
 +
 +        let mut def_generic_iter = def_generics.iter_id();
 +
 +        let fill_self_params = || {
 +            for x in explicit_self_ty
 +                .into_iter()
 +                .map(|x| x.cast(Interner))
 +                .chain(iter::repeat(ty_error.clone()))
 +                .take(self_params)
 +            {
 +                if let Some(id) = def_generic_iter.next() {
 +                    assert!(id.is_left());
 +                    substs.push(x);
 +                }
 +            }
 +        };
 +        let mut had_explicit_args = false;
 +
 +        if let Some(generic_args) = &segment.args_and_bindings {
 +            if !generic_args.has_self_type {
 +                fill_self_params();
 +            }
 +            let expected_num = if generic_args.has_self_type {
 +                self_params + type_params + const_params
 +            } else {
 +                type_params + const_params
 +            };
 +            let skip = if generic_args.has_self_type && self_params == 0 { 1 } else { 0 };
 +            // if args are provided, it should be all of them, but we can't rely on that
 +            for arg in generic_args
 +                .args
 +                .iter()
 +                .filter(|arg| !matches!(arg, GenericArg::Lifetime(_)))
 +                .skip(skip)
 +                .take(expected_num)
 +            {
 +                if let Some(id) = def_generic_iter.next() {
 +                    if let Some(x) = generic_arg_to_chalk(
 +                        self.db,
 +                        id,
 +                        arg,
 +                        &mut (),
 +                        |_, type_ref| self.lower_ty(type_ref),
 +                        |_, c, ty| {
 +                            const_or_path_to_chalk(
 +                                self.db,
 +                                &self.resolver,
 +                                ty,
 +                                c,
 +                                self.type_param_mode,
 +                                || self.generics(),
 +                                self.in_binders,
 +                            )
 +                        },
 +                    ) {
 +                        had_explicit_args = true;
 +                        substs.push(x);
 +                    } else {
 +                        // we just filtered them out
 +                        never!("Unexpected lifetime argument");
 +                    }
 +                }
 +            }
 +        } else {
 +            fill_self_params();
 +        }
 +
 +        // These params include those of parent.
 +        let remaining_params: SmallVec<[_; 2]> = def_generic_iter
 +            .map(|eid| match eid {
 +                Either::Left(_) => ty_error.clone(),
 +                Either::Right(x) => unknown_const_as_generic(self.db.const_param_ty(x)),
 +            })
 +            .collect();
 +        assert_eq!(remaining_params.len() + substs.len(), total_len);
 +
 +        // handle defaults. In expression or pattern path segments without
 +        // explicitly specified type arguments, missing type arguments are inferred
 +        // (i.e. defaults aren't used).
 +        // Generic parameters for associated types are not supposed to have defaults, so we just
 +        // ignore them.
 +        let is_assoc_ty = if let GenericDefId::TypeAliasId(id) = def {
 +            let container = id.lookup(self.db.upcast()).container;
 +            matches!(container, ItemContainerId::TraitId(_))
 +        } else {
 +            false
 +        };
 +        if !is_assoc_ty && (!infer_args || had_explicit_args) {
 +            let defaults = self.db.generic_defaults(def);
 +            assert_eq!(total_len, defaults.len());
 +            let parent_from = item_len - substs.len();
 +
 +            for (idx, default_ty) in defaults[substs.len()..item_len].iter().enumerate() {
 +                // each default can depend on the previous parameters
 +                let substs_so_far = Substitution::from_iter(
 +                    Interner,
 +                    substs.iter().cloned().chain(remaining_params[idx..].iter().cloned()),
 +                );
 +                substs.push(default_ty.clone().substitute(Interner, &substs_so_far));
 +            }
 +
 +            // Keep parent's params as unknown.
 +            let mut remaining_params = remaining_params;
 +            substs.extend(remaining_params.drain(parent_from..));
 +        } else {
 +            substs.extend(remaining_params);
 +        }
 +
 +        assert_eq!(substs.len(), total_len);
 +        Substitution::from_iter(Interner, substs)
 +    }
 +
 +    fn lower_trait_ref_from_path(
 +        &self,
 +        path: &Path,
 +        explicit_self_ty: Option<Ty>,
 +    ) -> Option<TraitRef> {
 +        let resolved =
 +            match self.resolver.resolve_path_in_type_ns_fully(self.db.upcast(), path.mod_path())? {
 +                TypeNs::TraitId(tr) => tr,
 +                _ => return None,
 +            };
 +        let segment = path.segments().last().expect("path should have at least one segment");
 +        Some(self.lower_trait_ref_from_resolved_path(resolved, segment, explicit_self_ty))
 +    }
 +
 +    pub(crate) fn lower_trait_ref_from_resolved_path(
 +        &self,
 +        resolved: TraitId,
 +        segment: PathSegment<'_>,
 +        explicit_self_ty: Option<Ty>,
 +    ) -> TraitRef {
 +        let substs = self.trait_ref_substs_from_path(segment, resolved, explicit_self_ty);
 +        TraitRef { trait_id: to_chalk_trait_id(resolved), substitution: substs }
 +    }
 +
 +    fn lower_trait_ref(
 +        &self,
 +        trait_ref: &HirTraitRef,
 +        explicit_self_ty: Option<Ty>,
 +    ) -> Option<TraitRef> {
 +        self.lower_trait_ref_from_path(&trait_ref.path, explicit_self_ty)
 +    }
 +
 +    fn trait_ref_substs_from_path(
 +        &self,
 +        segment: PathSegment<'_>,
 +        resolved: TraitId,
 +        explicit_self_ty: Option<Ty>,
 +    ) -> Substitution {
 +        self.substs_from_path_segment(segment, Some(resolved.into()), false, explicit_self_ty)
 +    }
 +
 +    pub(crate) fn lower_where_predicate(
 +        &'a self,
 +        where_predicate: &'a WherePredicate,
 +        ignore_bindings: bool,
 +    ) -> impl Iterator<Item = QuantifiedWhereClause> + 'a {
 +        match where_predicate {
 +            WherePredicate::ForLifetime { target, bound, .. }
 +            | WherePredicate::TypeBound { target, bound } => {
 +                let self_ty = match target {
 +                    WherePredicateTypeTarget::TypeRef(type_ref) => self.lower_ty(type_ref),
 +                    WherePredicateTypeTarget::TypeOrConstParam(param_id) => {
 +                        let generic_def = self.resolver.generic_def().expect("generics in scope");
 +                        let generics = generics(self.db.upcast(), generic_def);
 +                        let param_id = hir_def::TypeOrConstParamId {
 +                            parent: generic_def,
 +                            local_id: *param_id,
 +                        };
 +                        let placeholder = to_placeholder_idx(self.db, param_id);
 +                        match self.type_param_mode {
 +                            ParamLoweringMode::Placeholder => TyKind::Placeholder(placeholder),
 +                            ParamLoweringMode::Variable => {
 +                                let idx = generics.param_idx(param_id).expect("matching generics");
 +                                TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, idx))
 +                            }
 +                        }
 +                        .intern(Interner)
 +                    }
 +                };
 +                self.lower_type_bound(bound, self_ty, ignore_bindings)
 +                    .collect::<Vec<_>>()
 +                    .into_iter()
 +            }
 +            WherePredicate::Lifetime { .. } => vec![].into_iter(),
 +        }
 +    }
 +
 +    pub(crate) fn lower_type_bound(
 +        &'a self,
 +        bound: &'a TypeBound,
 +        self_ty: Ty,
 +        ignore_bindings: bool,
 +    ) -> impl Iterator<Item = QuantifiedWhereClause> + 'a {
 +        let mut bindings = None;
 +        let trait_ref = match bound {
 +            TypeBound::Path(path, TraitBoundModifier::None) => {
 +                bindings = self.lower_trait_ref_from_path(path, Some(self_ty));
 +                bindings
 +                    .clone()
 +                    .filter(|tr| {
 +                        // ignore `T: Drop` or `T: Destruct` bounds.
 +                        // - `T: ~const Drop` has a special meaning in Rust 1.61 that we don't implement.
 +                        //   (So ideally, we'd only ignore `~const Drop` here)
 +                        // - `Destruct` impls are built-in in 1.62 (current nightlies as of 08-04-2022), so until
 +                        //   the builtin impls are supported by Chalk, we ignore them here.
 +                        if let Some(lang) = lang_attr(self.db.upcast(), tr.hir_trait_id()) {
 +                            if lang == "drop" || lang == "destruct" {
 +                                return false;
 +                            }
 +                        }
 +                        true
 +                    })
 +                    .map(WhereClause::Implemented)
 +                    .map(crate::wrap_empty_binders)
 +            }
 +            TypeBound::Path(path, TraitBoundModifier::Maybe) => {
 +                let sized_trait = self
 +                    .db
 +                    .lang_item(self.resolver.krate(), SmolStr::new_inline("sized"))
 +                    .and_then(|lang_item| lang_item.as_trait());
 +                // Don't lower associated type bindings as the only possible relaxed trait bound
 +                // `?Sized` has no of them.
 +                // If we got another trait here ignore the bound completely.
 +                let trait_id = self
 +                    .lower_trait_ref_from_path(path, Some(self_ty.clone()))
 +                    .map(|trait_ref| trait_ref.hir_trait_id());
 +                if trait_id == sized_trait {
 +                    self.unsized_types.borrow_mut().insert(self_ty);
 +                }
 +                None
 +            }
 +            TypeBound::ForLifetime(_, path) => {
 +                // FIXME Don't silently drop the hrtb lifetimes here
 +                bindings = self.lower_trait_ref_from_path(path, Some(self_ty));
 +                bindings.clone().map(WhereClause::Implemented).map(crate::wrap_empty_binders)
 +            }
 +            TypeBound::Lifetime(_) => None,
 +            TypeBound::Error => None,
 +        };
 +        trait_ref.into_iter().chain(
 +            bindings
 +                .into_iter()
 +                .filter(move |_| !ignore_bindings)
 +                .flat_map(move |tr| self.assoc_type_bindings_from_type_bound(bound, tr)),
 +        )
 +    }
 +
 +    fn assoc_type_bindings_from_type_bound(
 +        &'a self,
 +        bound: &'a TypeBound,
 +        trait_ref: TraitRef,
 +    ) -> impl Iterator<Item = QuantifiedWhereClause> + 'a {
 +        let last_segment = match bound {
 +            TypeBound::Path(path, TraitBoundModifier::None) | TypeBound::ForLifetime(_, path) => {
 +                path.segments().last()
 +            }
 +            TypeBound::Path(_, TraitBoundModifier::Maybe)
 +            | TypeBound::Error
 +            | TypeBound::Lifetime(_) => None,
 +        };
 +        last_segment
 +            .into_iter()
 +            .filter_map(|segment| segment.args_and_bindings)
 +            .flat_map(|args_and_bindings| &args_and_bindings.bindings)
 +            .flat_map(move |binding| {
 +                let found = associated_type_by_name_including_super_traits(
 +                    self.db,
 +                    trait_ref.clone(),
 +                    &binding.name,
 +                );
 +                let (super_trait_ref, associated_ty) = match found {
 +                    None => return SmallVec::new(),
 +                    Some(t) => t,
 +                };
 +                // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent
 +                // generic params. It's inefficient to splice the `Substitution`s, so we may want
 +                // that method to optionally take parent `Substitution` as we already know them at
 +                // this point (`super_trait_ref.substitution`).
 +                let substitution = self.substs_from_path_segment(
 +                    // FIXME: This is hack. We shouldn't really build `PathSegment` directly.
 +                    PathSegment { name: &binding.name, args_and_bindings: binding.args.as_deref() },
 +                    Some(associated_ty.into()),
 +                    false, // this is not relevant
 +                    Some(super_trait_ref.self_type_parameter(Interner)),
 +                );
 +                let self_params = generics(self.db.upcast(), associated_ty.into()).len_self();
 +                let substitution = Substitution::from_iter(
 +                    Interner,
 +                    substitution
 +                        .iter(Interner)
 +                        .take(self_params)
 +                        .chain(super_trait_ref.substitution.iter(Interner)),
 +                );
 +                let projection_ty = ProjectionTy {
 +                    associated_ty_id: to_assoc_type_id(associated_ty),
 +                    substitution,
 +                };
 +                let mut preds: SmallVec<[_; 1]> = SmallVec::with_capacity(
 +                    binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(),
 +                );
 +                if let Some(type_ref) = &binding.type_ref {
 +                    let ty = self.lower_ty(type_ref);
 +                    let alias_eq =
 +                        AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty };
 +                    preds.push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq)));
 +                }
 +                for bound in &binding.bounds {
 +                    preds.extend(self.lower_type_bound(
 +                        bound,
 +                        TyKind::Alias(AliasTy::Projection(projection_ty.clone())).intern(Interner),
 +                        false,
 +                    ));
 +                }
 +                preds
 +            })
 +    }
 +
 +    fn lower_dyn_trait(&self, bounds: &[Interned<TypeBound>]) -> Ty {
 +        let self_ty = TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(Interner);
 +        // INVARIANT: The principal trait bound, if present, must come first. Others may be in any
 +        // order but should be in the same order for the same set but possibly different order of
 +        // bounds in the input.
 +        // INVARIANT: If this function returns `DynTy`, there should be at least one trait bound.
 +        // These invariants are utilized by `TyExt::dyn_trait()` and chalk.
 +        let bounds = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
 +            let mut bounds: Vec<_> = bounds
 +                .iter()
 +                .flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false))
 +                .collect();
 +
 +            let mut multiple_regular_traits = false;
 +            let mut multiple_same_projection = false;
 +            bounds.sort_unstable_by(|lhs, rhs| {
 +                use std::cmp::Ordering;
 +                match (lhs.skip_binders(), rhs.skip_binders()) {
 +                    (WhereClause::Implemented(lhs), WhereClause::Implemented(rhs)) => {
 +                        let lhs_id = lhs.trait_id;
 +                        let lhs_is_auto = ctx.db.trait_data(from_chalk_trait_id(lhs_id)).is_auto;
 +                        let rhs_id = rhs.trait_id;
 +                        let rhs_is_auto = ctx.db.trait_data(from_chalk_trait_id(rhs_id)).is_auto;
 +
 +                        if !lhs_is_auto && !rhs_is_auto {
 +                            multiple_regular_traits = true;
 +                        }
 +                        // Note that the ordering here is important; this ensures the invariant
 +                        // mentioned above.
 +                        (lhs_is_auto, lhs_id).cmp(&(rhs_is_auto, rhs_id))
 +                    }
 +                    (WhereClause::Implemented(_), _) => Ordering::Less,
 +                    (_, WhereClause::Implemented(_)) => Ordering::Greater,
 +                    (WhereClause::AliasEq(lhs), WhereClause::AliasEq(rhs)) => {
 +                        match (&lhs.alias, &rhs.alias) {
 +                            (AliasTy::Projection(lhs_proj), AliasTy::Projection(rhs_proj)) => {
 +                                // We only compare the `associated_ty_id`s. We shouldn't have
 +                                // multiple bounds for an associated type in the correct Rust code,
 +                                // and if we do, we error out.
 +                                if lhs_proj.associated_ty_id == rhs_proj.associated_ty_id {
 +                                    multiple_same_projection = true;
 +                                }
 +                                lhs_proj.associated_ty_id.cmp(&rhs_proj.associated_ty_id)
 +                            }
 +                            // We don't produce `AliasTy::Opaque`s yet.
 +                            _ => unreachable!(),
 +                        }
 +                    }
 +                    // We don't produce `WhereClause::{TypeOutlives, LifetimeOutlives}` yet.
 +                    _ => unreachable!(),
 +                }
 +            });
 +
 +            if multiple_regular_traits || multiple_same_projection {
 +                return None;
 +            }
 +
 +            if bounds.first().and_then(|b| b.trait_id()).is_none() {
 +                // When there's no trait bound, that's an error. This happens when the trait refs
 +                // are unresolved.
 +                return None;
 +            }
 +
 +            // As multiple occurrences of the same auto traits *are* permitted, we dedulicate the
 +            // bounds. We shouldn't have repeated elements besides auto traits at this point.
 +            bounds.dedup();
 +
 +            Some(QuantifiedWhereClauses::from_iter(Interner, bounds))
 +        });
 +
 +        if let Some(bounds) = bounds {
 +            let bounds = crate::make_single_type_binders(bounds);
 +            TyKind::Dyn(DynTy { bounds, lifetime: static_lifetime() }).intern(Interner)
 +        } else {
 +            // FIXME: report error
 +            // (additional non-auto traits, associated type rebound, or no resolved trait)
 +            TyKind::Error.intern(Interner)
 +        }
 +    }
 +
 +    fn lower_impl_trait(
 +        &self,
 +        bounds: &[Interned<TypeBound>],
 +        func: FunctionId,
 +    ) -> ReturnTypeImplTrait {
 +        cov_mark::hit!(lower_rpit);
 +        let self_ty = TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(Interner);
 +        let predicates = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
 +            let mut predicates: Vec<_> = bounds
 +                .iter()
 +                .flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false))
 +                .collect();
 +
 +            if !ctx.unsized_types.borrow().contains(&self_ty) {
 +                let krate = func.lookup(ctx.db.upcast()).module(ctx.db.upcast()).krate();
 +                let sized_trait = ctx
 +                    .db
 +                    .lang_item(krate, SmolStr::new_inline("sized"))
 +                    .and_then(|lang_item| lang_item.as_trait().map(to_chalk_trait_id));
 +                let sized_clause = sized_trait.map(|trait_id| {
 +                    let clause = WhereClause::Implemented(TraitRef {
 +                        trait_id,
 +                        substitution: Substitution::from1(Interner, self_ty.clone()),
 +                    });
 +                    crate::wrap_empty_binders(clause)
 +                });
 +                predicates.extend(sized_clause.into_iter());
 +                predicates.shrink_to_fit();
 +            }
 +            predicates
 +        });
 +        ReturnTypeImplTrait { bounds: crate::make_single_type_binders(predicates) }
 +    }
 +}
 +
 +fn count_impl_traits(type_ref: &TypeRef) -> usize {
 +    let mut count = 0;
 +    type_ref.walk(&mut |type_ref| {
 +        if matches!(type_ref, TypeRef::ImplTrait(_)) {
 +            count += 1;
 +        }
 +    });
 +    count
 +}
 +
 +/// Build the signature of a callable item (function, struct or enum variant).
 +pub(crate) fn callable_item_sig(db: &dyn HirDatabase, def: CallableDefId) -> PolyFnSig {
 +    match def {
 +        CallableDefId::FunctionId(f) => fn_sig_for_fn(db, f),
 +        CallableDefId::StructId(s) => fn_sig_for_struct_constructor(db, s),
 +        CallableDefId::EnumVariantId(e) => fn_sig_for_enum_variant_constructor(db, e),
 +    }
 +}
 +
 +pub fn associated_type_shorthand_candidates<R>(
 +    db: &dyn HirDatabase,
 +    def: GenericDefId,
 +    res: TypeNs,
 +    cb: impl FnMut(&Name, &TraitRef, TypeAliasId) -> Option<R>,
 +) -> Option<R> {
 +    named_associated_type_shorthand_candidates(db, def, res, None, cb)
 +}
 +
 +fn named_associated_type_shorthand_candidates<R>(
 +    db: &dyn HirDatabase,
 +    // If the type parameter is defined in an impl and we're in a method, there
 +    // might be additional where clauses to consider
 +    def: GenericDefId,
 +    res: TypeNs,
 +    assoc_name: Option<Name>,
 +    mut cb: impl FnMut(&Name, &TraitRef, TypeAliasId) -> Option<R>,
 +) -> Option<R> {
 +    let mut search = |t| {
 +        for t in all_super_trait_refs(db, t) {
 +            let data = db.trait_data(t.hir_trait_id());
 +
 +            for (name, assoc_id) in &data.items {
 +                if let AssocItemId::TypeAliasId(alias) = assoc_id {
 +                    if let Some(result) = cb(name, &t, *alias) {
 +                        return Some(result);
 +                    }
 +                }
 +            }
 +        }
 +        None
 +    };
 +
 +    match res {
 +        TypeNs::SelfType(impl_id) => {
 +            // we're _in_ the impl -- the binders get added back later. Correct,
 +            // but it would be nice to make this more explicit
 +            let trait_ref = db.impl_trait(impl_id)?.into_value_and_skipped_binders().0;
 +
 +            let impl_id_as_generic_def: GenericDefId = impl_id.into();
 +            if impl_id_as_generic_def != def {
 +                // `trait_ref` contains `BoundVar`s bound by impl's `Binders`, but here we need
 +                // `BoundVar`s from `def`'s point of view.
 +                // FIXME: A `HirDatabase` query may be handy if this process is needed in more
 +                // places. It'd be almost identical as `impl_trait_query` where `resolver` would be
 +                // of `def` instead of `impl_id`.
 +                let starting_idx = generics(db.upcast(), def).len_self();
 +                let subst = TyBuilder::subst_for_def(db, impl_id, None)
 +                    .fill_with_bound_vars(DebruijnIndex::INNERMOST, starting_idx)
 +                    .build();
 +                let trait_ref = subst.apply(trait_ref, Interner);
 +                search(trait_ref)
 +            } else {
 +                search(trait_ref)
 +            }
 +        }
 +        TypeNs::GenericParam(param_id) => {
 +            let predicates = db.generic_predicates_for_param(def, param_id.into(), assoc_name);
 +            let res = predicates.iter().find_map(|pred| match pred.skip_binders().skip_binders() {
 +                // FIXME: how to correctly handle higher-ranked bounds here?
 +                WhereClause::Implemented(tr) => search(
 +                    tr.clone()
 +                        .shifted_out_to(Interner, DebruijnIndex::ONE)
 +                        .expect("FIXME unexpected higher-ranked trait bound"),
 +                ),
 +                _ => None,
 +            });
 +            if let Some(_) = res {
 +                return res;
 +            }
 +            // Handle `Self::Type` referring to own associated type in trait definitions
 +            if let GenericDefId::TraitId(trait_id) = param_id.parent() {
 +                let trait_generics = generics(db.upcast(), trait_id.into());
 +                if trait_generics.params.type_or_consts[param_id.local_id()].is_trait_self() {
 +                    let def_generics = generics(db.upcast(), def);
 +                    let starting_idx = match def {
 +                        GenericDefId::TraitId(_) => 0,
 +                        // `def` is an item within trait. We need to substitute `BoundVar`s but
 +                        // remember that they are for parent (i.e. trait) generic params so they
 +                        // come after our own params.
 +                        _ => def_generics.len_self(),
 +                    };
 +                    let trait_ref = TyBuilder::trait_ref(db, trait_id)
 +                        .fill_with_bound_vars(DebruijnIndex::INNERMOST, starting_idx)
 +                        .build();
 +                    return search(trait_ref);
 +                }
 +            }
 +            None
 +        }
 +        _ => None,
 +    }
 +}
 +
 +/// Build the type of all specific fields of a struct or enum variant.
 +pub(crate) fn field_types_query(
 +    db: &dyn HirDatabase,
 +    variant_id: VariantId,
 +) -> Arc<ArenaMap<LocalFieldId, Binders<Ty>>> {
 +    let var_data = variant_id.variant_data(db.upcast());
 +    let (resolver, def): (_, GenericDefId) = match variant_id {
 +        VariantId::StructId(it) => (it.resolver(db.upcast()), it.into()),
 +        VariantId::UnionId(it) => (it.resolver(db.upcast()), it.into()),
 +        VariantId::EnumVariantId(it) => (it.parent.resolver(db.upcast()), it.parent.into()),
 +    };
 +    let generics = generics(db.upcast(), def);
 +    let mut res = ArenaMap::default();
 +    let ctx =
 +        TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
 +    for (field_id, field_data) in var_data.fields().iter() {
 +        res.insert(field_id, make_binders(db, &generics, ctx.lower_ty(&field_data.type_ref)));
 +    }
 +    Arc::new(res)
 +}
 +
 +/// This query exists only to be used when resolving short-hand associated types
 +/// like `T::Item`.
 +///
 +/// See the analogous query in rustc and its comment:
 +/// <https://github.com/rust-lang/rust/blob/9150f844e2624eb013ec78ca08c1d416e6644026/src/librustc_typeck/astconv.rs#L46>
 +/// This is a query mostly to handle cycles somewhat gracefully; e.g. the
 +/// following bounds are disallowed: `T: Foo<U::Item>, U: Foo<T::Item>`, but
 +/// these are fine: `T: Foo<U::Item>, U: Foo<()>`.
 +pub(crate) fn generic_predicates_for_param_query(
 +    db: &dyn HirDatabase,
 +    def: GenericDefId,
 +    param_id: TypeOrConstParamId,
 +    assoc_name: Option<Name>,
 +) -> Arc<[Binders<QuantifiedWhereClause>]> {
 +    let resolver = def.resolver(db.upcast());
 +    let ctx =
 +        TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
 +    let generics = generics(db.upcast(), def);
 +    let mut predicates: Vec<_> = resolver
 +        .where_predicates_in_scope()
 +        // we have to filter out all other predicates *first*, before attempting to lower them
 +        .filter(|pred| match pred {
 +            WherePredicate::ForLifetime { target, bound, .. }
 +            | WherePredicate::TypeBound { target, bound, .. } => {
 +                match target {
 +                    WherePredicateTypeTarget::TypeRef(type_ref) => {
 +                        if ctx.lower_ty_only_param(type_ref) != Some(param_id) {
 +                            return false;
 +                        }
 +                    }
 +                    &WherePredicateTypeTarget::TypeOrConstParam(local_id) => {
 +                        let target_id = TypeOrConstParamId { parent: def, local_id };
 +                        if target_id != param_id {
 +                            return false;
 +                        }
 +                    }
 +                };
 +
 +                match &**bound {
 +                    TypeBound::ForLifetime(_, path) | TypeBound::Path(path, _) => {
 +                        // Only lower the bound if the trait could possibly define the associated
 +                        // type we're looking for.
 +
 +                        let assoc_name = match &assoc_name {
 +                            Some(it) => it,
 +                            None => return true,
 +                        };
 +                        let tr = match resolver
 +                            .resolve_path_in_type_ns_fully(db.upcast(), path.mod_path())
 +                        {
 +                            Some(TypeNs::TraitId(tr)) => tr,
 +                            _ => return false,
 +                        };
 +
 +                        all_super_traits(db.upcast(), tr).iter().any(|tr| {
 +                            db.trait_data(*tr).items.iter().any(|(name, item)| {
 +                                matches!(item, AssocItemId::TypeAliasId(_)) && name == assoc_name
 +                            })
 +                        })
 +                    }
 +                    TypeBound::Lifetime(_) | TypeBound::Error => false,
 +                }
 +            }
 +            WherePredicate::Lifetime { .. } => false,
 +        })
 +        .flat_map(|pred| {
 +            ctx.lower_where_predicate(pred, true).map(|p| make_binders(db, &generics, p))
 +        })
 +        .collect();
 +
 +    let subst = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST);
 +    let explicitly_unsized_tys = ctx.unsized_types.into_inner();
 +    let implicitly_sized_predicates =
 +        implicitly_sized_clauses(db, param_id.parent, &explicitly_unsized_tys, &subst, &resolver)
 +            .map(|p| make_binders(db, &generics, crate::wrap_empty_binders(p)));
 +    predicates.extend(implicitly_sized_predicates);
 +    predicates.into()
 +}
 +
 +pub(crate) fn generic_predicates_for_param_recover(
 +    _db: &dyn HirDatabase,
 +    _cycle: &[String],
 +    _def: &GenericDefId,
 +    _param_id: &TypeOrConstParamId,
 +    _assoc_name: &Option<Name>,
 +) -> Arc<[Binders<QuantifiedWhereClause>]> {
 +    Arc::new([])
 +}
 +
 +pub(crate) fn trait_environment_query(
 +    db: &dyn HirDatabase,
 +    def: GenericDefId,
 +) -> Arc<TraitEnvironment> {
 +    let resolver = def.resolver(db.upcast());
 +    let ctx =
 +        TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Placeholder);
 +    let mut traits_in_scope = Vec::new();
 +    let mut clauses = Vec::new();
 +    for pred in resolver.where_predicates_in_scope() {
 +        for pred in ctx.lower_where_predicate(pred, false) {
 +            if let WhereClause::Implemented(tr) = &pred.skip_binders() {
 +                traits_in_scope.push((tr.self_type_parameter(Interner).clone(), tr.hir_trait_id()));
 +            }
 +            let program_clause: chalk_ir::ProgramClause<Interner> = pred.cast(Interner);
 +            clauses.push(program_clause.into_from_env_clause(Interner));
 +        }
 +    }
 +
 +    let container: Option<ItemContainerId> = match def {
 +        // FIXME: is there a function for this?
 +        GenericDefId::FunctionId(f) => Some(f.lookup(db.upcast()).container),
 +        GenericDefId::AdtId(_) => None,
 +        GenericDefId::TraitId(_) => None,
 +        GenericDefId::TypeAliasId(t) => Some(t.lookup(db.upcast()).container),
 +        GenericDefId::ImplId(_) => None,
 +        GenericDefId::EnumVariantId(_) => None,
 +        GenericDefId::ConstId(c) => Some(c.lookup(db.upcast()).container),
 +    };
 +    if let Some(ItemContainerId::TraitId(trait_id)) = container {
 +        // add `Self: Trait<T1, T2, ...>` to the environment in trait
 +        // function default implementations (and speculative code
 +        // inside consts or type aliases)
 +        cov_mark::hit!(trait_self_implements_self);
 +        let substs = TyBuilder::placeholder_subst(db, trait_id);
 +        let trait_ref = TraitRef { trait_id: to_chalk_trait_id(trait_id), substitution: substs };
 +        let pred = WhereClause::Implemented(trait_ref);
 +        let program_clause: chalk_ir::ProgramClause<Interner> = pred.cast(Interner);
 +        clauses.push(program_clause.into_from_env_clause(Interner));
 +    }
 +
 +    let subst = generics(db.upcast(), def).placeholder_subst(db);
 +    let explicitly_unsized_tys = ctx.unsized_types.into_inner();
 +    let implicitly_sized_clauses =
 +        implicitly_sized_clauses(db, def, &explicitly_unsized_tys, &subst, &resolver).map(|pred| {
 +            let program_clause: chalk_ir::ProgramClause<Interner> = pred.cast(Interner);
 +            program_clause.into_from_env_clause(Interner)
 +        });
 +    clauses.extend(implicitly_sized_clauses);
 +
 +    let krate = def.module(db.upcast()).krate();
 +
 +    let env = chalk_ir::Environment::new(Interner).add_clauses(Interner, clauses);
 +
 +    Arc::new(TraitEnvironment { krate, traits_from_clauses: traits_in_scope, env })
 +}
 +
 +/// Resolve the where clause(s) of an item with generics.
 +pub(crate) fn generic_predicates_query(
 +    db: &dyn HirDatabase,
 +    def: GenericDefId,
 +) -> Arc<[Binders<QuantifiedWhereClause>]> {
 +    let resolver = def.resolver(db.upcast());
 +    let ctx =
 +        TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
 +    let generics = generics(db.upcast(), def);
 +
 +    let mut predicates = resolver
 +        .where_predicates_in_scope()
 +        .flat_map(|pred| {
 +            ctx.lower_where_predicate(pred, false).map(|p| make_binders(db, &generics, p))
 +        })
 +        .collect::<Vec<_>>();
 +
 +    let subst = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST);
 +    let explicitly_unsized_tys = ctx.unsized_types.into_inner();
 +    let implicitly_sized_predicates =
 +        implicitly_sized_clauses(db, def, &explicitly_unsized_tys, &subst, &resolver)
 +            .map(|p| make_binders(db, &generics, crate::wrap_empty_binders(p)));
 +    predicates.extend(implicitly_sized_predicates);
 +    predicates.into()
 +}
 +
 +/// Generate implicit `: Sized` predicates for all generics that has no `?Sized` bound.
 +/// Exception is Self of a trait def.
 +fn implicitly_sized_clauses<'a>(
 +    db: &dyn HirDatabase,
 +    def: GenericDefId,
 +    explicitly_unsized_tys: &'a FxHashSet<Ty>,
 +    substitution: &'a Substitution,
 +    resolver: &Resolver,
 +) -> impl Iterator<Item = WhereClause> + 'a {
 +    let is_trait_def = matches!(def, GenericDefId::TraitId(..));
 +    let generic_args = &substitution.as_slice(Interner)[is_trait_def as usize..];
 +    let sized_trait = db
 +        .lang_item(resolver.krate(), SmolStr::new_inline("sized"))
 +        .and_then(|lang_item| lang_item.as_trait().map(to_chalk_trait_id));
 +
 +    sized_trait.into_iter().flat_map(move |sized_trait| {
 +        let implicitly_sized_tys = generic_args
 +            .iter()
 +            .filter_map(|generic_arg| generic_arg.ty(Interner))
 +            .filter(move |&self_ty| !explicitly_unsized_tys.contains(self_ty));
 +        implicitly_sized_tys.map(move |self_ty| {
 +            WhereClause::Implemented(TraitRef {
 +                trait_id: sized_trait,
 +                substitution: Substitution::from1(Interner, self_ty.clone()),
 +            })
 +        })
 +    })
 +}
 +
 +/// Resolve the default type params from generics
 +pub(crate) fn generic_defaults_query(
 +    db: &dyn HirDatabase,
 +    def: GenericDefId,
 +) -> Arc<[Binders<chalk_ir::GenericArg<Interner>>]> {
 +    let resolver = def.resolver(db.upcast());
 +    let ctx =
 +        TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
 +    let generic_params = generics(db.upcast(), def);
 +    let parent_start_idx = generic_params.len_self();
 +
 +    let defaults = generic_params
 +        .iter()
 +        .enumerate()
 +        .map(|(idx, (id, p))| {
 +            let p = match p {
 +                TypeOrConstParamData::TypeParamData(p) => p,
 +                TypeOrConstParamData::ConstParamData(_) => {
 +                    // FIXME: implement const generic defaults
 +                    let val = unknown_const_as_generic(
 +                        db.const_param_ty(ConstParamId::from_unchecked(id)),
 +                    );
 +                    return make_binders(db, &generic_params, val);
 +                }
 +            };
 +            let mut ty =
 +                p.default.as_ref().map_or(TyKind::Error.intern(Interner), |t| ctx.lower_ty(t));
 +
 +            // Each default can only refer to previous parameters.
 +            // Type variable default referring to parameter coming
 +            // after it is forbidden (FIXME: report diagnostic)
 +            ty = fallback_bound_vars(ty, idx, parent_start_idx);
 +            crate::make_binders(db, &generic_params, ty.cast(Interner))
 +        })
 +        .collect();
 +
 +    defaults
 +}
 +
 +pub(crate) fn generic_defaults_recover(
 +    db: &dyn HirDatabase,
 +    _cycle: &[String],
 +    def: &GenericDefId,
 +) -> Arc<[Binders<crate::GenericArg>]> {
 +    let generic_params = generics(db.upcast(), *def);
 +    // FIXME: this code is not covered in tests.
 +    // we still need one default per parameter
 +    let defaults = generic_params
 +        .iter_id()
 +        .map(|id| {
 +            let val = match id {
 +                itertools::Either::Left(_) => {
 +                    GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner)
 +                }
 +                itertools::Either::Right(id) => unknown_const_as_generic(db.const_param_ty(id)),
 +            };
 +            crate::make_binders(db, &generic_params, val)
 +        })
 +        .collect();
 +
 +    defaults
 +}
 +
 +fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
 +    let data = db.function_data(def);
 +    let resolver = def.resolver(db.upcast());
 +    let ctx_params = TyLoweringContext::new(db, &resolver)
 +        .with_impl_trait_mode(ImplTraitLoweringMode::Variable)
 +        .with_type_param_mode(ParamLoweringMode::Variable);
 +    let params = data.params.iter().map(|(_, tr)| ctx_params.lower_ty(tr)).collect::<Vec<_>>();
 +    let ctx_ret = TyLoweringContext::new(db, &resolver)
 +        .with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
 +        .with_type_param_mode(ParamLoweringMode::Variable);
 +    let ret = ctx_ret.lower_ty(&data.ret_type);
 +    let generics = generics(db.upcast(), def.into());
-     Binders::new(binders, CallableSig::from_params_and_return(params, ret, false))
++    let sig = CallableSig::from_params_and_return(
++        params,
++        ret,
++        data.is_varargs(),
++        if data.has_unsafe_kw() { Safety::Unsafe } else { Safety::Safe },
++    );
 +    make_binders(db, &generics, sig)
 +}
 +
 +/// Build the declared type of a function. This should not need to look at the
 +/// function body.
 +fn type_for_fn(db: &dyn HirDatabase, def: FunctionId) -> Binders<Ty> {
 +    let generics = generics(db.upcast(), def.into());
 +    let substs = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST);
 +    make_binders(
 +        db,
 +        &generics,
 +        TyKind::FnDef(CallableDefId::FunctionId(def).to_chalk(db), substs).intern(Interner),
 +    )
 +}
 +
 +/// Build the declared type of a const.
 +fn type_for_const(db: &dyn HirDatabase, def: ConstId) -> Binders<Ty> {
 +    let data = db.const_data(def);
 +    let generics = generics(db.upcast(), def.into());
 +    let resolver = def.resolver(db.upcast());
 +    let ctx =
 +        TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
 +
 +    make_binders(db, &generics, ctx.lower_ty(&data.type_ref))
 +}
 +
 +/// Build the declared type of a static.
 +fn type_for_static(db: &dyn HirDatabase, def: StaticId) -> Binders<Ty> {
 +    let data = db.static_data(def);
 +    let resolver = def.resolver(db.upcast());
 +    let ctx = TyLoweringContext::new(db, &resolver);
 +
 +    Binders::empty(Interner, ctx.lower_ty(&data.type_ref))
 +}
 +
 +fn fn_sig_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> PolyFnSig {
 +    let struct_data = db.struct_data(def);
 +    let fields = struct_data.variant_data.fields();
 +    let resolver = def.resolver(db.upcast());
 +    let ctx =
 +        TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
 +    let params = fields.iter().map(|(_, field)| ctx.lower_ty(&field.type_ref)).collect::<Vec<_>>();
 +    let (ret, binders) = type_for_adt(db, def.into()).into_value_and_skipped_binders();
-     Binders::new(binders, CallableSig::from_params_and_return(params, ret, false))
++    Binders::new(binders, CallableSig::from_params_and_return(params, ret, false, Safety::Safe))
 +}
 +
 +/// Build the type of a tuple struct constructor.
 +fn type_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> Binders<Ty> {
 +    let struct_data = db.struct_data(def);
 +    if let StructKind::Unit = struct_data.variant_data.kind() {
 +        return type_for_adt(db, def.into());
 +    }
 +    let generics = generics(db.upcast(), def.into());
 +    let substs = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST);
 +    make_binders(
 +        db,
 +        &generics,
 +        TyKind::FnDef(CallableDefId::StructId(def).to_chalk(db), substs).intern(Interner),
 +    )
 +}
 +
 +fn fn_sig_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId) -> PolyFnSig {
 +    let enum_data = db.enum_data(def.parent);
 +    let var_data = &enum_data.variants[def.local_id];
 +    let fields = var_data.variant_data.fields();
 +    let resolver = def.parent.resolver(db.upcast());
 +    let ctx =
 +        TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
 +    let params = fields.iter().map(|(_, field)| ctx.lower_ty(&field.type_ref)).collect::<Vec<_>>();
 +    let (ret, binders) = type_for_adt(db, def.parent.into()).into_value_and_skipped_binders();
++    Binders::new(binders, CallableSig::from_params_and_return(params, ret, false, Safety::Safe))
 +}
 +
 +/// Build the type of a tuple enum variant constructor.
 +fn type_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId) -> Binders<Ty> {
 +    let enum_data = db.enum_data(def.parent);
 +    let var_data = &enum_data.variants[def.local_id].variant_data;
 +    if let StructKind::Unit = var_data.kind() {
 +        return type_for_adt(db, def.parent.into());
 +    }
 +    let generics = generics(db.upcast(), def.parent.into());
 +    let substs = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST);
 +    make_binders(
 +        db,
 +        &generics,
 +        TyKind::FnDef(CallableDefId::EnumVariantId(def).to_chalk(db), substs).intern(Interner),
 +    )
 +}
 +
 +fn type_for_adt(db: &dyn HirDatabase, adt: AdtId) -> Binders<Ty> {
 +    let generics = generics(db.upcast(), adt.into());
 +    let subst = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST);
 +    let ty = TyKind::Adt(crate::AdtId(adt), subst).intern(Interner);
 +    make_binders(db, &generics, ty)
 +}
 +
 +fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders<Ty> {
 +    let generics = generics(db.upcast(), t.into());
 +    let resolver = t.resolver(db.upcast());
 +    let ctx =
 +        TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
 +    if db.type_alias_data(t).is_extern {
 +        Binders::empty(Interner, TyKind::Foreign(crate::to_foreign_def_id(t)).intern(Interner))
 +    } else {
 +        let type_ref = &db.type_alias_data(t).type_ref;
 +        let inner = ctx.lower_ty(type_ref.as_deref().unwrap_or(&TypeRef::Error));
 +        make_binders(db, &generics, inner)
 +    }
 +}
 +
 +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 +pub enum CallableDefId {
 +    FunctionId(FunctionId),
 +    StructId(StructId),
 +    EnumVariantId(EnumVariantId),
 +}
 +impl_from!(FunctionId, StructId, EnumVariantId for CallableDefId);
 +
 +impl CallableDefId {
 +    pub fn krate(self, db: &dyn HirDatabase) -> CrateId {
 +        let db = db.upcast();
 +        match self {
 +            CallableDefId::FunctionId(f) => f.lookup(db).module(db),
 +            CallableDefId::StructId(s) => s.lookup(db).container,
 +            CallableDefId::EnumVariantId(e) => e.parent.lookup(db).container,
 +        }
 +        .krate()
 +    }
 +}
 +
 +impl From<CallableDefId> for GenericDefId {
 +    fn from(def: CallableDefId) -> GenericDefId {
 +        match def {
 +            CallableDefId::FunctionId(f) => f.into(),
 +            CallableDefId::StructId(s) => s.into(),
 +            CallableDefId::EnumVariantId(e) => e.into(),
 +        }
 +    }
 +}
 +
 +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 +pub enum TyDefId {
 +    BuiltinType(BuiltinType),
 +    AdtId(AdtId),
 +    TypeAliasId(TypeAliasId),
 +}
 +impl_from!(BuiltinType, AdtId(StructId, EnumId, UnionId), TypeAliasId for TyDefId);
 +
 +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 +pub enum ValueTyDefId {
 +    FunctionId(FunctionId),
 +    StructId(StructId),
 +    UnionId(UnionId),
 +    EnumVariantId(EnumVariantId),
 +    ConstId(ConstId),
 +    StaticId(StaticId),
 +}
 +impl_from!(FunctionId, StructId, UnionId, EnumVariantId, ConstId, StaticId for ValueTyDefId);
 +
 +impl ValueTyDefId {
 +    pub(crate) fn to_generic_def_id(self) -> Option<GenericDefId> {
 +        match self {
 +            Self::FunctionId(id) => Some(id.into()),
 +            Self::StructId(id) => Some(id.into()),
 +            Self::UnionId(id) => Some(id.into()),
 +            Self::EnumVariantId(var) => Some(var.into()),
 +            Self::ConstId(id) => Some(id.into()),
 +            Self::StaticId(_) => None,
 +        }
 +    }
 +}
 +
 +/// Build the declared type of an item. This depends on the namespace; e.g. for
 +/// `struct Foo(usize)`, we have two types: The type of the struct itself, and
 +/// the constructor function `(usize) -> Foo` which lives in the values
 +/// namespace.
 +pub(crate) fn ty_query(db: &dyn HirDatabase, def: TyDefId) -> Binders<Ty> {
 +    match def {
 +        TyDefId::BuiltinType(it) => Binders::empty(Interner, TyBuilder::builtin(it)),
 +        TyDefId::AdtId(it) => type_for_adt(db, it),
 +        TyDefId::TypeAliasId(it) => type_for_type_alias(db, it),
 +    }
 +}
 +
 +pub(crate) fn ty_recover(db: &dyn HirDatabase, _cycle: &[String], def: &TyDefId) -> Binders<Ty> {
 +    let generics = match *def {
 +        TyDefId::BuiltinType(_) => return Binders::empty(Interner, TyKind::Error.intern(Interner)),
 +        TyDefId::AdtId(it) => generics(db.upcast(), it.into()),
 +        TyDefId::TypeAliasId(it) => generics(db.upcast(), it.into()),
 +    };
 +    make_binders(db, &generics, TyKind::Error.intern(Interner))
 +}
 +
 +pub(crate) fn value_ty_query(db: &dyn HirDatabase, def: ValueTyDefId) -> Binders<Ty> {
 +    match def {
 +        ValueTyDefId::FunctionId(it) => type_for_fn(db, it),
 +        ValueTyDefId::StructId(it) => type_for_struct_constructor(db, it),
 +        ValueTyDefId::UnionId(it) => type_for_adt(db, it.into()),
 +        ValueTyDefId::EnumVariantId(it) => type_for_enum_variant_constructor(db, it),
 +        ValueTyDefId::ConstId(it) => type_for_const(db, it),
 +        ValueTyDefId::StaticId(it) => type_for_static(db, it),
 +    }
 +}
 +
 +pub(crate) fn impl_self_ty_query(db: &dyn HirDatabase, impl_id: ImplId) -> Binders<Ty> {
 +    let impl_loc = impl_id.lookup(db.upcast());
 +    let impl_data = db.impl_data(impl_id);
 +    let resolver = impl_id.resolver(db.upcast());
 +    let _cx = stdx::panic_context::enter(format!(
 +        "impl_self_ty_query({:?} -> {:?} -> {:?})",
 +        impl_id, impl_loc, impl_data
 +    ));
 +    let generics = generics(db.upcast(), impl_id.into());
 +    let ctx =
 +        TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
 +    make_binders(db, &generics, ctx.lower_ty(&impl_data.self_ty))
 +}
 +
 +// returns None if def is a type arg
 +pub(crate) fn const_param_ty_query(db: &dyn HirDatabase, def: ConstParamId) -> Ty {
 +    let parent_data = db.generic_params(def.parent());
 +    let data = &parent_data.type_or_consts[def.local_id()];
 +    let resolver = def.parent().resolver(db.upcast());
 +    let ctx = TyLoweringContext::new(db, &resolver);
 +    match data {
 +        TypeOrConstParamData::TypeParamData(_) => {
 +            never!();
 +            Ty::new(Interner, TyKind::Error)
 +        }
 +        TypeOrConstParamData::ConstParamData(d) => ctx.lower_ty(&d.ty),
 +    }
 +}
 +
 +pub(crate) fn impl_self_ty_recover(
 +    db: &dyn HirDatabase,
 +    _cycle: &[String],
 +    impl_id: &ImplId,
 +) -> Binders<Ty> {
 +    let generics = generics(db.upcast(), (*impl_id).into());
 +    make_binders(db, &generics, TyKind::Error.intern(Interner))
 +}
 +
 +pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option<Binders<TraitRef>> {
 +    let impl_loc = impl_id.lookup(db.upcast());
 +    let impl_data = db.impl_data(impl_id);
 +    let resolver = impl_id.resolver(db.upcast());
 +    let _cx = stdx::panic_context::enter(format!(
 +        "impl_trait_query({:?} -> {:?} -> {:?})",
 +        impl_id, impl_loc, impl_data
 +    ));
 +    let ctx =
 +        TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
 +    let (self_ty, binders) = db.impl_self_ty(impl_id).into_value_and_skipped_binders();
 +    let target_trait = impl_data.target_trait.as_ref()?;
 +    Some(Binders::new(binders, ctx.lower_trait_ref(target_trait, Some(self_ty))?))
 +}
 +
 +pub(crate) fn return_type_impl_traits(
 +    db: &dyn HirDatabase,
 +    def: hir_def::FunctionId,
 +) -> Option<Arc<Binders<ReturnTypeImplTraits>>> {
 +    // FIXME unify with fn_sig_for_fn instead of doing lowering twice, maybe
 +    let data = db.function_data(def);
 +    let resolver = def.resolver(db.upcast());
 +    let ctx_ret = TyLoweringContext::new(db, &resolver)
 +        .with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
 +        .with_type_param_mode(ParamLoweringMode::Variable);
 +    let _ret = (&ctx_ret).lower_ty(&data.ret_type);
 +    let generics = generics(db.upcast(), def.into());
 +    let return_type_impl_traits =
 +        ReturnTypeImplTraits { impl_traits: ctx_ret.opaque_type_data.into_inner() };
 +    if return_type_impl_traits.impl_traits.is_empty() {
 +        None
 +    } else {
 +        Some(Arc::new(make_binders(db, &generics, return_type_impl_traits)))
 +    }
 +}
 +
 +pub(crate) fn lower_to_chalk_mutability(m: hir_def::type_ref::Mutability) -> Mutability {
 +    match m {
 +        hir_def::type_ref::Mutability::Shared => Mutability::Not,
 +        hir_def::type_ref::Mutability::Mut => Mutability::Mut,
 +    }
 +}
 +
 +/// Checks if the provided generic arg matches its expected kind, then lower them via
 +/// provided closures. Use unknown if there was kind mismatch.
 +///
 +/// Returns `Some` of the lowered generic arg. `None` if the provided arg is a lifetime.
 +pub(crate) fn generic_arg_to_chalk<'a, T>(
 +    db: &dyn HirDatabase,
 +    kind_id: Either<TypeParamId, ConstParamId>,
 +    arg: &'a GenericArg,
 +    this: &mut T,
 +    for_type: impl FnOnce(&mut T, &TypeRef) -> Ty + 'a,
 +    for_const: impl FnOnce(&mut T, &ConstScalarOrPath, Ty) -> Const + 'a,
 +) -> Option<crate::GenericArg> {
 +    let kind = match kind_id {
 +        Either::Left(_) => ParamKind::Type,
 +        Either::Right(id) => {
 +            let ty = db.const_param_ty(id);
 +            ParamKind::Const(ty)
 +        }
 +    };
 +    Some(match (arg, kind) {
 +        (GenericArg::Type(type_ref), ParamKind::Type) => {
 +            let ty = for_type(this, type_ref);
 +            GenericArgData::Ty(ty).intern(Interner)
 +        }
 +        (GenericArg::Const(c), ParamKind::Const(c_ty)) => {
 +            GenericArgData::Const(for_const(this, c, c_ty)).intern(Interner)
 +        }
 +        (GenericArg::Const(_), ParamKind::Type) => {
 +            GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner)
 +        }
 +        (GenericArg::Type(t), ParamKind::Const(c_ty)) => {
 +            // We want to recover simple idents, which parser detects them
 +            // as types. Maybe here is not the best place to do it, but
 +            // it works.
 +            if let TypeRef::Path(p) = t {
 +                let p = p.mod_path();
 +                if p.kind == PathKind::Plain {
 +                    if let [n] = p.segments() {
 +                        let c = ConstScalarOrPath::Path(n.clone());
 +                        return Some(
 +                            GenericArgData::Const(for_const(this, &c, c_ty)).intern(Interner),
 +                        );
 +                    }
 +                }
 +            }
 +            unknown_const_as_generic(c_ty)
 +        }
 +        (GenericArg::Lifetime(_), _) => return None,
 +    })
 +}
 +
 +pub(crate) fn const_or_path_to_chalk(
 +    db: &dyn HirDatabase,
 +    resolver: &Resolver,
 +    expected_ty: Ty,
 +    value: &ConstScalarOrPath,
 +    mode: ParamLoweringMode,
 +    args: impl FnOnce() -> Generics,
 +    debruijn: DebruijnIndex,
 +) -> Const {
 +    match value {
 +        ConstScalarOrPath::Scalar(s) => intern_const_scalar(s.clone(), expected_ty),
 +        ConstScalarOrPath::Path(n) => {
 +            let path = ModPath::from_segments(PathKind::Plain, Some(n.clone()));
 +            path_to_const(db, resolver, &path, mode, args, debruijn)
 +                .unwrap_or_else(|| unknown_const(expected_ty))
 +        }
 +    }
 +}
 +
 +/// Replaces any 'free' `BoundVar`s in `s` by `TyKind::Error` from the perspective of generic
 +/// parameter whose index is `param_index`. A `BoundVar` is free when it is or (syntactically)
 +/// appears after the generic parameter of `param_index`.
 +fn fallback_bound_vars<T: TypeFoldable<Interner> + HasInterner<Interner = Interner>>(
 +    s: T,
 +    param_index: usize,
 +    parent_start: usize,
 +) -> T {
 +    // Keep in mind that parent generic parameters, if any, come *after* those of the item in
 +    // question. In the diagrams below, `c*` and `p*` represent generic parameters of the item and
 +    // its parent respectively.
 +    let is_allowed = |index| {
 +        if param_index < parent_start {
 +            // The parameter of `param_index` is one from the item in question. Any parent generic
 +            // parameters or the item's generic parameters that come before `param_index` is
 +            // allowed.
 +            // [c1, .., cj, .., ck, p1, .., pl] where cj is `param_index`
 +            //  ^^^^^^              ^^^^^^^^^^ these are allowed
 +            !(param_index..parent_start).contains(&index)
 +        } else {
 +            // The parameter of `param_index` is one from the parent generics. Only parent generic
 +            // parameters that come before `param_index` are allowed.
 +            // [c1, .., ck, p1, .., pj, .., pl] where pj is `param_index`
 +            //              ^^^^^^ these are allowed
 +            (parent_start..param_index).contains(&index)
 +        }
 +    };
 +
 +    crate::fold_free_vars(
 +        s,
 +        |bound, binders| {
 +            if bound.index_if_innermost().map_or(true, is_allowed) {
 +                bound.shifted_in_from(binders).to_ty(Interner)
 +            } else {
 +                TyKind::Error.intern(Interner)
 +            }
 +        },
 +        |ty, bound, binders| {
 +            if bound.index_if_innermost().map_or(true, is_allowed) {
 +                bound.shifted_in_from(binders).to_const(Interner, ty)
 +            } else {
 +                unknown_const(ty.clone())
 +            }
 +        },
 +    )
 +}
index d301595bcd98a5ac3f29fda2bc5626cf8e546a8c,0000000000000000000000000000000000000000..7e3aecc2ae0ae4688d687440390d81b29fc0356d
mode 100644,000000..100644
--- /dev/null
@@@ -1,773 -1,0 +1,809 @@@
-                                   // ^^^ adjustments: Pointer(ReifyFnPointer)
 +use super::{check, check_no_mismatches, check_types};
 +
 +#[test]
 +fn block_expr_type_mismatch() {
 +    check(
 +        r"
 +fn test() {
 +    let a: i32 = { 1i64 };
 +                // ^^^^ expected i32, got i64
 +}
 +        ",
 +    );
 +}
 +
 +#[test]
 +fn coerce_places() {
 +    check_no_mismatches(
 +        r#"
 +//- minicore: coerce_unsized
 +struct S<T> { a: T }
 +
 +fn f<T>(_: &[T]) -> T { loop {} }
 +fn g<T>(_: S<&[T]>) -> T { loop {} }
 +
 +fn gen<T>() -> *mut [T; 2] { loop {} }
 +fn test1<U>() -> *mut [U] {
 +    gen()
 +}
 +
 +fn test2() {
 +    let arr: &[u8; 1] = &[1];
 +
 +    let a: &[_] = arr;
 +    let b = f(arr);
 +    let c: &[_] = { arr };
 +    let d = g(S { a: arr });
 +    let e: [&[_]; 1] = [arr];
 +    let f: [&[_]; 2] = [arr; 2];
 +    let g: (&[_], &[_]) = (arr, arr);
 +}
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn let_stmt_coerce() {
 +    check(
 +        r"
 +//- minicore: coerce_unsized
 +fn test() {
 +    let x: &[isize] = &[1];
 +                   // ^^^^ adjustments: Deref(None), Borrow(Ref(Not)), Pointer(Unsize)
 +    let x: *const [isize] = &[1];
 +                         // ^^^^ adjustments: Deref(None), Borrow(RawPtr(Not)), Pointer(Unsize)
 +}
 +",
 +    );
 +}
 +
 +#[test]
 +fn custom_coerce_unsized() {
 +    check(
 +        r#"
 +//- minicore: coerce_unsized
 +use core::{marker::Unsize, ops::CoerceUnsized};
 +
 +struct A<T: ?Sized>(*const T);
 +struct B<T: ?Sized>(*const T);
 +struct C<T: ?Sized> { inner: *const T }
 +
 +impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<B<U>> for B<T> {}
 +impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<C<U>> for C<T> {}
 +
 +fn foo1<T>(x: A<[T]>) -> A<[T]> { x }
 +fn foo2<T>(x: B<[T]>) -> B<[T]> { x }
 +fn foo3<T>(x: C<[T]>) -> C<[T]> { x }
 +
 +fn test(a: A<[u8; 2]>, b: B<[u8; 2]>, c: C<[u8; 2]>) {
 +    let d = foo1(a);
 +              // ^ expected A<[{unknown}]>, got A<[u8; 2]>
 +    let e = foo2(b);
 +     // ^ type: B<[u8]>
 +    let f = foo3(c);
 +     // ^ type: C<[u8]>
 +}
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn if_coerce() {
 +    check_no_mismatches(
 +        r#"
 +//- minicore: coerce_unsized
 +fn foo<T>(x: &[T]) -> &[T] { x }
 +fn test() {
 +    let x = if true {
 +        foo(&[1])
 +         // ^^^^ adjustments: Deref(None), Borrow(Ref(Not)), Pointer(Unsize)
 +    } else {
 +        &[1]
 +    };
 +}
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn if_else_coerce() {
 +    check_no_mismatches(
 +        r#"
 +//- minicore: coerce_unsized
 +fn foo<T>(x: &[T]) -> &[T] { x }
 +fn test() {
 +    let x = if true {
 +        &[1]
 +    } else {
 +        foo(&[1])
 +    };
 +}
 +"#,
 +    )
 +}
 +
++#[test]
++fn if_else_adjust_for_branches_discard_type_var() {
++    check_no_mismatches(
++        r#"
++fn test() {
++    let f = || {
++        if true {
++            &""
++        } else {
++            ""
++        }
++    };
++}
++"#,
++    );
++}
++
 +#[test]
 +fn match_first_coerce() {
 +    check_no_mismatches(
 +        r#"
 +//- minicore: coerce_unsized
 +fn foo<T>(x: &[T]) -> &[T] { x }
 +fn test(i: i32) {
 +    let x = match i {
 +        2 => foo(&[2]),
 +              // ^^^^ adjustments: Deref(None), Borrow(Ref(Not)), Pointer(Unsize)
 +        1 => &[1],
 +        _ => &[3],
 +    };
 +}
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn match_second_coerce() {
 +    check_no_mismatches(
 +        r#"
 +//- minicore: coerce_unsized
 +fn foo<T>(x: &[T]) -> &[T] { loop {} }
 +                          // ^^^^^^^ adjustments: NeverToAny
 +fn test(i: i32) {
 +    let x = match i {
 +        1 => &[1],
 +        2 => foo(&[2]),
 +        _ => &[3],
 +    };
 +}
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn coerce_merge_one_by_one1() {
 +    cov_mark::check!(coerce_merge_fail_fallback);
 +
 +    check(
 +        r"
 +fn test() {
 +    let t = &mut 1;
 +    let x = match 1 {
 +        1 => t as *mut i32,
 +        2 => t as &i32,
 +           //^^^^^^^^^ expected *mut i32, got &i32
 +        _ => t as *const i32,
 +          // ^^^^^^^^^^^^^^^ adjustments: Pointer(MutToConstPointer)
 +
 +    };
 +    x;
 +  //^ type: *const i32
 +
 +}
 +        ",
 +    );
 +}
 +
++#[test]
++fn match_adjust_for_branches_discard_type_var() {
++    check_no_mismatches(
++        r#"
++fn test() {
++    let f = || {
++        match 0i32 {
++            0i32 => &"",
++            _ => "",
++        }
++    };
++}
++"#,
++    );
++}
++
 +#[test]
 +fn return_coerce_unknown() {
 +    check_types(
 +        r"
 +fn foo() -> u32 {
 +    return unknown;
 +         //^^^^^^^ u32
 +}
 +        ",
 +    );
 +}
 +
 +#[test]
 +fn coerce_autoderef() {
 +    check_no_mismatches(
 +        r"
 +struct Foo;
 +fn takes_ref_foo(x: &Foo) {}
 +fn test() {
 +    takes_ref_foo(&Foo);
 +    takes_ref_foo(&&Foo);
 +    takes_ref_foo(&&&Foo);
 +}",
 +    );
 +}
 +
 +#[test]
 +fn coerce_autoderef_generic() {
 +    check_no_mismatches(
 +        r#"
 +struct Foo;
 +fn takes_ref<T>(x: &T) -> T { *x }
 +fn test() {
 +    takes_ref(&Foo);
 +    takes_ref(&&Foo);
 +    takes_ref(&&&Foo);
 +}
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn coerce_autoderef_block() {
 +    check_no_mismatches(
 +        r#"
 +//- minicore: deref
 +struct String {}
 +impl core::ops::Deref for String { type Target = str; }
 +fn takes_ref_str(x: &str) {}
 +fn returns_string() -> String { loop {} }
 +fn test() {
 +    takes_ref_str(&{ returns_string() });
 +               // ^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(None), Deref(Some(OverloadedDeref(Not))), Borrow(Ref(Not))
 +}
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn coerce_autoderef_implication_1() {
 +    check_no_mismatches(
 +        r"
 +//- minicore: deref
 +struct Foo<T>;
 +impl core::ops::Deref for Foo<u32> { type Target = (); }
 +
 +fn takes_ref_foo<T>(x: &Foo<T>) {}
 +fn test() {
 +    let foo = Foo;
 +      //^^^ type: Foo<{unknown}>
 +    takes_ref_foo(&foo);
 +
 +    let foo = Foo;
 +      //^^^ type: Foo<u32>
 +    let _: &() = &foo;
 +}",
 +    );
 +}
 +
 +#[test]
 +fn coerce_autoderef_implication_2() {
 +    check(
 +        r"
 +//- minicore: deref
 +struct Foo<T>;
 +impl core::ops::Deref for Foo<u32> { type Target = (); }
 +
 +fn takes_ref_foo<T>(x: &Foo<T>) {}
 +fn test() {
 +    let foo = Foo;
 +      //^^^ type: Foo<{unknown}>
 +    let _: &u32 = &Foo;
 +                //^^^^ expected &u32, got &Foo<{unknown}>
 +}",
 +    );
 +}
 +
 +#[test]
 +fn closure_return_coerce() {
 +    check_no_mismatches(
 +        r"
 +fn foo() {
 +    let x = || {
 +        if true {
 +            return &1u32;
 +        }
 +        &&1u32
 +    };
 +}",
 +    );
 +}
 +
 +#[test]
 +fn generator_yield_return_coerce() {
 +    check_no_mismatches(
 +        r#"
 +fn test() {
 +    let g = || {
 +        yield &1u32;
 +        yield &&1u32;
 +        if true {
 +            return &1u32;
 +        }
 +        &&1u32
 +    };
 +}
 +        "#,
 +    );
 +}
 +
 +#[test]
 +fn assign_coerce() {
 +    check_no_mismatches(
 +        r"
 +//- minicore: deref
 +struct String;
 +impl core::ops::Deref for String { type Target = str; }
 +fn g(_text: &str) {}
 +fn f(text: &str) {
 +    let mut text = text;
 +    let tmp = String;
 +    text = &tmp;
 +    g(text);
 +}
 +",
 +    );
 +}
 +
 +#[test]
 +fn destructuring_assign_coerce() {
 +    check_no_mismatches(
 +        r"
 +//- minicore: deref
 +struct String;
 +impl core::ops::Deref for String { type Target = str; }
 +fn g(_text: &str) {}
 +fn f(text: &str) {
 +    let mut text = text;
 +    let tmp = String;
 +    [text, _] = [&tmp, &tmp];
 +    g(text);
 +}
 +",
 +    );
 +}
 +
 +#[test]
 +fn coerce_fn_item_to_fn_ptr() {
 +    check_no_mismatches(
 +        r"
 +fn foo(x: u32) -> isize { 1 }
 +fn test() {
 +    let f: fn(u32) -> isize = foo;
 +                           // ^^^ adjustments: Pointer(ReifyFnPointer)
 +    let f: unsafe fn(u32) -> isize = foo;
-     let f: fn(u32) -> isize = |x| { 1 };
++                                  // ^^^ adjustments: Pointer(ReifyFnPointer), Pointer(UnsafeFnPointer)
 +}",
 +    );
 +}
 +
 +#[test]
 +fn coerce_fn_items_in_match_arms() {
 +    cov_mark::check!(coerce_fn_reification);
 +
 +    check_types(
 +        r"
 +fn foo1(x: u32) -> isize { 1 }
 +fn foo2(x: u32) -> isize { 2 }
 +fn foo3(x: u32) -> isize { 3 }
 +fn test() {
 +    let x = match 1 {
 +        1 => foo1,
 +        2 => foo2,
 +        _ => foo3,
 +    };
 +    x;
 +  //^ fn(u32) -> isize
 +}",
 +    );
 +}
 +
 +#[test]
 +fn coerce_closure_to_fn_ptr() {
 +    check_no_mismatches(
 +        r"
 +fn test() {
++    let f: fn(u32) -> u32 = |x| x;
++                         // ^^^^^ adjustments: Pointer(ClosureFnPointer(Safe))
++    let f: unsafe fn(u32) -> u32 = |x| x;
++                                // ^^^^^ adjustments: Pointer(ClosureFnPointer(Unsafe))
 +}",
 +    );
 +}
 +
 +#[test]
 +fn coerce_placeholder_ref() {
 +    // placeholders should unify, even behind references
 +    check_no_mismatches(
 +        r"
 +struct S<T> { t: T }
 +impl<TT> S<TT> {
 +    fn get(&self) -> &TT {
 +        &self.t
 +    }
 +}",
 +    );
 +}
 +
 +#[test]
 +fn coerce_unsize_array() {
 +    check_types(
 +        r#"
 +//- minicore: coerce_unsized
 +fn test() {
 +    let f: &[usize] = &[1, 2, 3];
 +                      //^ usize
 +}"#,
 +    );
 +}
 +
 +#[test]
 +fn coerce_unsize_trait_object_simple() {
 +    check_types(
 +        r#"
 +//- minicore: coerce_unsized
 +trait Foo<T, U> {}
 +trait Bar<U, T, X>: Foo<T, U> {}
 +trait Baz<T, X>: Bar<usize, T, X> {}
 +
 +struct S<T, X>;
 +impl<T, X> Foo<T, usize> for S<T, X> {}
 +impl<T, X> Bar<usize, T, X> for S<T, X> {}
 +impl<T, X> Baz<T, X> for S<T, X> {}
 +
 +fn test() {
 +    let obj: &dyn Baz<i8, i16> = &S;
 +                                //^ S<i8, i16>
 +    let obj: &dyn Bar<_, i8, i16> = &S;
 +                                   //^ S<i8, i16>
 +    let obj: &dyn Foo<i8, _> = &S;
 +                              //^ S<i8, {unknown}>
 +}"#,
 +    );
 +}
 +
 +#[test]
 +fn coerce_unsize_super_trait_cycle() {
 +    check_no_mismatches(
 +        r#"
 +//- minicore: coerce_unsized
 +trait A {}
 +trait B: C + A {}
 +trait C: B {}
 +trait D: C
 +
 +struct S;
 +impl A for S {}
 +impl B for S {}
 +impl C for S {}
 +impl D for S {}
 +
 +fn test() {
 +    let obj: &dyn D = &S;
 +    let obj: &dyn A = &S;
 +}
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn coerce_unsize_generic() {
 +    // FIXME: fix the type mismatches here
 +    check(
 +        r#"
 +//- minicore: coerce_unsized
 +struct Foo<T> { t: T };
 +struct Bar<T>(Foo<T>);
 +
 +fn test() {
 +    let _: &Foo<[usize]> = &Foo { t: [1, 2, 3] };
 +                                   //^^^^^^^^^ expected [usize], got [usize; 3]
 +    let _: &Bar<[usize]> = &Bar(Foo { t: [1, 2, 3] });
 +                                       //^^^^^^^^^ expected [usize], got [usize; 3]
 +}
 +"#,
 +    );
 +}
 +
 +#[test]
 +fn coerce_unsize_apit() {
 +    check(
 +        r#"
 +//- minicore: coerce_unsized
 +trait Foo {}
 +
 +fn test(f: impl Foo, g: &(impl Foo + ?Sized)) {
 +    let _: &dyn Foo = &f;
 +    let _: &dyn Foo = g;
 +                    //^ expected &dyn Foo, got &impl Foo + ?Sized
 +}
 +        "#,
 +    );
 +}
 +
 +#[test]
 +fn two_closures_lub() {
 +    check_types(
 +        r#"
 +fn foo(c: i32) {
 +    let add = |a: i32, b: i32| a + b;
 +    let sub = |a, b| a - b;
 +            //^^^^^^^^^^^^ |i32, i32| -> i32
 +    if c > 42 { add } else { sub };
 +  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ fn(i32, i32) -> i32
 +}
 +        "#,
 +    )
 +}
 +
 +#[test]
 +fn match_diverging_branch_1() {
 +    check_types(
 +        r#"
 +enum Result<T> { Ok(T), Err }
 +fn parse<T>() -> T { loop {} }
 +
 +fn test() -> i32 {
 +    let a = match parse() {
 +        Ok(val) => val,
 +        Err => return 0,
 +    };
 +    a
 +  //^ i32
 +}
 +        "#,
 +    )
 +}
 +
 +#[test]
 +fn match_diverging_branch_2() {
 +    // same as 1 except for order of branches
 +    check_types(
 +        r#"
 +enum Result<T> { Ok(T), Err }
 +fn parse<T>() -> T { loop {} }
 +
 +fn test() -> i32 {
 +    let a = match parse() {
 +        Err => return 0,
 +        Ok(val) => val,
 +    };
 +    a
 +  //^ i32
 +}
 +        "#,
 +    )
 +}
 +
 +#[test]
 +fn panic_macro() {
 +    check_no_mismatches(
 +        r#"
 +mod panic {
 +    #[macro_export]
 +    pub macro panic_2015 {
 +        () => (
 +            $crate::panicking::panic()
 +        ),
 +    }
 +}
 +
 +mod panicking {
 +    pub fn panic() -> ! { loop {} }
 +}
 +
 +#[rustc_builtin_macro = "core_panic"]
 +macro_rules! panic {
 +    // Expands to either `$crate::panic::panic_2015` or `$crate::panic::panic_2021`
 +    // depending on the edition of the caller.
 +    ($($arg:tt)*) => {
 +        /* compiler built-in */
 +    };
 +}
 +
 +fn main() {
 +    panic!()
 +}
 +        "#,
 +    );
 +}
 +
 +#[test]
 +fn coerce_unsize_expected_type_1() {
 +    check_no_mismatches(
 +        r#"
 +//- minicore: coerce_unsized
 +fn main() {
 +    let foo: &[u32] = &[1, 2];
 +    let foo: &[u32] = match true {
 +        true => &[1, 2],
 +        false => &[1, 2, 3],
 +    };
 +    let foo: &[u32] = if true {
 +        &[1, 2]
 +    } else {
 +        &[1, 2, 3]
 +    };
 +}
 +        "#,
 +    );
 +}
 +
 +#[test]
 +fn coerce_unsize_expected_type_2() {
 +    check_no_mismatches(
 +        r#"
 +//- minicore: coerce_unsized
 +struct InFile<T>;
 +impl<T> InFile<T> {
 +    fn with_value<U>(self, value: U) -> InFile<U> { InFile }
 +}
 +struct RecordField;
 +trait AstNode {}
 +impl AstNode for RecordField {}
 +
 +fn takes_dyn(it: InFile<&dyn AstNode>) {}
 +
 +fn test() {
 +    let x: InFile<()> = InFile;
 +    let n = &RecordField;
 +    takes_dyn(x.with_value(n));
 +}
 +        "#,
 +    );
 +}
 +
 +#[test]
 +fn coerce_unsize_expected_type_3() {
 +    check_no_mismatches(
 +        r#"
 +//- minicore: coerce_unsized
 +enum Option<T> { Some(T), None }
 +struct RecordField;
 +trait AstNode {}
 +impl AstNode for RecordField {}
 +
 +fn takes_dyn(it: Option<&dyn AstNode>) {}
 +
 +fn test() {
 +    let x: InFile<()> = InFile;
 +    let n = &RecordField;
 +    takes_dyn(Option::Some(n));
 +}
 +        "#,
 +    );
 +}
 +
 +#[test]
 +fn coerce_unsize_expected_type_4() {
 +    check_no_mismatches(
 +        r#"
 +//- minicore: coerce_unsized
 +use core::{marker::Unsize, ops::CoerceUnsized};
 +
 +struct B<T: ?Sized>(*const T);
 +impl<T: ?Sized> B<T> {
 +    fn new(t: T) -> Self { B(&t) }
 +}
 +
 +impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<B<U>> for B<T> {}
 +
 +fn test() {
 +    let _: B<[isize]> = B::new({ [1, 2, 3] });
 +}
 +        "#,
 +    );
 +}
 +
 +#[test]
 +fn coerce_array_elems_lub() {
 +    check_no_mismatches(
 +        r#"
 +fn f() {}
 +fn g() {}
 +
 +fn test() {
 +    [f, g];
 +}
 +        "#,
 +    );
 +}
 +
 +#[test]
 +fn coerce_type_var() {
 +    check_types(
 +        r#"
 +//- minicore: from, coerce_unsized
 +fn test() {
 +    let x = ();
 +    let _: &() = &x.into();
 +}               //^^^^^^^^ ()
 +"#,
 +    )
 +}
 +
 +#[test]
 +fn coerce_overloaded_binary_op_rhs() {
 +    check_types(
 +        r#"
 +//- minicore: deref, add
 +
 +struct String {}
 +impl core::ops::Deref for String { type Target = str; }
 +
 +impl core::ops::Add<&str> for String {
 +    type Output = String;
 +}
 +
 +fn test() {
 +    let s1 = String {};
 +    let s2 = String {};
 +    s1 + &s2;
 +  //^^^^^^^^ String
 +}
 +
 +        "#,
 +    );
 +}
 +
 +#[test]
 +fn assign_coerce_struct_fields() {
 +    check_no_mismatches(
 +        r#"
 +//- minicore: coerce_unsized
 +struct S;
 +trait Tr {}
 +impl Tr for S {}
 +struct V<T> { t: T }
 +
 +fn main() {
 +    let a: V<&dyn Tr>;
 +    a = V { t: &S };
 +
 +    let mut a: V<&dyn Tr> = V { t: &S };
 +    a = V { t: &S };
 +}
 +        "#,
 +    );
 +}
 +
 +#[test]
 +fn destructuring_assign_coerce_struct_fields() {
 +    check(
 +        r#"
 +//- minicore: coerce_unsized
 +struct S;
 +trait Tr {}
 +impl Tr for S {}
 +struct V<T> { t: T }
 +
 +fn main() {
 +    let a: V<&dyn Tr>;
 +    (a,) = V { t: &S };
 +  //^^^^expected V<&S>, got (V<&dyn Tr>,)
 +
 +    let mut a: V<&dyn Tr> = V { t: &S };
 +    (a,) = V { t: &S };
 +  //^^^^expected V<&S>, got (V<&dyn Tr>,)
 +}
 +        "#,
 +    );
 +}
index f5324208c9a4ef12cf6704f1a9066b11c7378b27,0000000000000000000000000000000000000000..cbd9bf32a548655bcb81d5c469a6f4c794328de7
mode 100644,000000..100644
--- /dev/null
@@@ -1,3741 -1,0 +1,3755 @@@
-             _ => Callee::Def(self.ty.callable_def(db)?),
 +//! HIR (previously known as descriptors) provides a high-level object oriented
 +//! access to Rust code.
 +//!
 +//! The principal difference between HIR and syntax trees is that HIR is bound
 +//! to a particular crate instance. That is, it has cfg flags and features
 +//! applied. So, the relation between syntax and HIR is many-to-one.
 +//!
 +//! HIR is the public API of the all of the compiler logic above syntax trees.
 +//! It is written in "OO" style. Each type is self contained (as in, it knows it's
 +//! parents and full context). It should be "clean code".
 +//!
 +//! `hir_*` crates are the implementation of the compiler logic.
 +//! They are written in "ECS" style, with relatively little abstractions.
 +//! Many types are not self-contained, and explicitly use local indexes, arenas, etc.
 +//!
 +//! `hir` is what insulates the "we don't know how to actually write an incremental compiler"
 +//! from the ide with completions, hovers, etc. It is a (soft, internal) boundary:
 +//! <https://www.tedinski.com/2018/02/06/system-boundaries.html>.
 +
 +#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)]
 +#![recursion_limit = "512"]
 +
 +mod semantics;
 +mod source_analyzer;
 +
 +mod from_id;
 +mod attrs;
 +mod has_source;
 +
 +pub mod diagnostics;
 +pub mod db;
 +pub mod symbols;
 +
 +mod display;
 +
 +use std::{iter, ops::ControlFlow, sync::Arc};
 +
 +use arrayvec::ArrayVec;
 +use base_db::{CrateDisplayName, CrateId, CrateOrigin, Edition, FileId, ProcMacroKind};
 +use either::Either;
 +use hir_def::{
 +    adt::{ReprData, VariantData},
 +    body::{BodyDiagnostic, SyntheticSyntax},
 +    expr::{BindingAnnotation, LabelId, Pat, PatId},
 +    generics::{TypeOrConstParamData, TypeParamProvenance},
 +    item_tree::ItemTreeNode,
 +    lang_item::LangItemTarget,
 +    nameres::{self, diagnostics::DefDiagnostic},
 +    per_ns::PerNs,
 +    resolver::{HasResolver, Resolver},
 +    src::HasSource as _,
 +    AdtId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, DefWithBodyId, EnumId,
 +    EnumVariantId, FunctionId, GenericDefId, HasModule, ImplId, ItemContainerId, LifetimeParamId,
 +    LocalEnumVariantId, LocalFieldId, Lookup, MacroExpander, MacroId, ModuleId, StaticId, StructId,
 +    TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId,
 +};
 +use hir_expand::{name::name, MacroCallKind};
 +use hir_ty::{
 +    all_super_traits, autoderef,
 +    consteval::{unknown_const_as_generic, ComputedExpr, ConstEvalError, ConstExt},
 +    diagnostics::BodyValidationDiagnostic,
 +    method_resolution::{self, TyFingerprint},
 +    primitive::UintTy,
 +    traits::FnTrait,
 +    AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId,
 +    GenericArgData, Interner, ParamKind, QuantifiedWhereClause, Scalar, Substitution,
 +    TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, WhereClause,
 +};
 +use itertools::Itertools;
 +use nameres::diagnostics::DefDiagnosticKind;
 +use once_cell::unsync::Lazy;
 +use rustc_hash::FxHashSet;
 +use stdx::{impl_from, never};
 +use syntax::{
 +    ast::{self, Expr, HasAttrs as _, HasDocComments, HasName},
 +    AstNode, AstPtr, SmolStr, SyntaxNodePtr, TextRange, T,
 +};
 +
 +use crate::db::{DefDatabase, HirDatabase};
 +
 +pub use crate::{
 +    attrs::{HasAttrs, Namespace},
 +    diagnostics::{
 +        AnyDiagnostic, BreakOutsideOfLoop, InactiveCode, IncorrectCase, InvalidDeriveTarget,
 +        MacroError, MalformedDerive, MismatchedArgCount, MissingFields, MissingMatchArms,
 +        MissingUnsafe, NoSuchField, ReplaceFilterMapNextWithFindMap, TypeMismatch,
 +        UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall,
 +        UnresolvedModule, UnresolvedProcMacro,
 +    },
 +    has_source::HasSource,
 +    semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits},
 +};
 +
 +// Be careful with these re-exports.
 +//
 +// `hir` is the boundary between the compiler and the IDE. It should try hard to
 +// isolate the compiler from the ide, to allow the two to be refactored
 +// independently. Re-exporting something from the compiler is the sure way to
 +// breach the boundary.
 +//
 +// Generally, a refactoring which *removes* a name from this list is a good
 +// idea!
 +pub use {
 +    cfg::{CfgAtom, CfgExpr, CfgOptions},
 +    hir_def::{
 +        adt::StructKind,
 +        attr::{Attr, Attrs, AttrsWithOwner, Documentation},
 +        builtin_attr::AttributeTemplate,
 +        find_path::PrefixKind,
 +        import_map,
 +        nameres::ModuleSource,
 +        path::{ModPath, PathKind},
 +        type_ref::{Mutability, TypeRef},
 +        visibility::Visibility,
 +    },
 +    hir_expand::{
 +        name::{known, Name},
 +        ExpandResult, HirFileId, InFile, MacroFile, Origin,
 +    },
 +    hir_ty::display::HirDisplay,
 +};
 +
 +// These are negative re-exports: pub using these names is forbidden, they
 +// should remain private to hir internals.
 +#[allow(unused)]
 +use {
 +    hir_def::path::Path,
 +    hir_expand::{hygiene::Hygiene, name::AsName},
 +};
 +
 +/// hir::Crate describes a single crate. It's the main interface with which
 +/// a crate's dependencies interact. Mostly, it should be just a proxy for the
 +/// root module.
 +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 +pub struct Crate {
 +    pub(crate) id: CrateId,
 +}
 +
 +#[derive(Debug)]
 +pub struct CrateDependency {
 +    pub krate: Crate,
 +    pub name: Name,
 +}
 +
 +impl Crate {
 +    pub fn origin(self, db: &dyn HirDatabase) -> CrateOrigin {
 +        db.crate_graph()[self.id].origin.clone()
 +    }
 +
 +    pub fn is_builtin(self, db: &dyn HirDatabase) -> bool {
 +        matches!(self.origin(db), CrateOrigin::Lang(_))
 +    }
 +
 +    pub fn dependencies(self, db: &dyn HirDatabase) -> Vec<CrateDependency> {
 +        db.crate_graph()[self.id]
 +            .dependencies
 +            .iter()
 +            .map(|dep| {
 +                let krate = Crate { id: dep.crate_id };
 +                let name = dep.as_name();
 +                CrateDependency { krate, name }
 +            })
 +            .collect()
 +    }
 +
 +    pub fn reverse_dependencies(self, db: &dyn HirDatabase) -> Vec<Crate> {
 +        let crate_graph = db.crate_graph();
 +        crate_graph
 +            .iter()
 +            .filter(|&krate| {
 +                crate_graph[krate].dependencies.iter().any(|it| it.crate_id == self.id)
 +            })
 +            .map(|id| Crate { id })
 +            .collect()
 +    }
 +
 +    pub fn transitive_reverse_dependencies(
 +        self,
 +        db: &dyn HirDatabase,
 +    ) -> impl Iterator<Item = Crate> {
 +        db.crate_graph().transitive_rev_deps(self.id).map(|id| Crate { id })
 +    }
 +
 +    pub fn root_module(self, db: &dyn HirDatabase) -> Module {
 +        let def_map = db.crate_def_map(self.id);
 +        Module { id: def_map.module_id(def_map.root()) }
 +    }
 +
 +    pub fn modules(self, db: &dyn HirDatabase) -> Vec<Module> {
 +        let def_map = db.crate_def_map(self.id);
 +        def_map.modules().map(|(id, _)| def_map.module_id(id).into()).collect()
 +    }
 +
 +    pub fn root_file(self, db: &dyn HirDatabase) -> FileId {
 +        db.crate_graph()[self.id].root_file_id
 +    }
 +
 +    pub fn edition(self, db: &dyn HirDatabase) -> Edition {
 +        db.crate_graph()[self.id].edition
 +    }
 +
 +    pub fn version(self, db: &dyn HirDatabase) -> Option<String> {
 +        db.crate_graph()[self.id].version.clone()
 +    }
 +
 +    pub fn display_name(self, db: &dyn HirDatabase) -> Option<CrateDisplayName> {
 +        db.crate_graph()[self.id].display_name.clone()
 +    }
 +
 +    pub fn query_external_importables(
 +        self,
 +        db: &dyn DefDatabase,
 +        query: import_map::Query,
 +    ) -> impl Iterator<Item = Either<ModuleDef, Macro>> {
 +        let _p = profile::span("query_external_importables");
 +        import_map::search_dependencies(db, self.into(), query).into_iter().map(|item| {
 +            match ItemInNs::from(item) {
 +                ItemInNs::Types(mod_id) | ItemInNs::Values(mod_id) => Either::Left(mod_id),
 +                ItemInNs::Macros(mac_id) => Either::Right(mac_id),
 +            }
 +        })
 +    }
 +
 +    pub fn all(db: &dyn HirDatabase) -> Vec<Crate> {
 +        db.crate_graph().iter().map(|id| Crate { id }).collect()
 +    }
 +
 +    /// Try to get the root URL of the documentation of a crate.
 +    pub fn get_html_root_url(self: &Crate, db: &dyn HirDatabase) -> Option<String> {
 +        // Look for #![doc(html_root_url = "...")]
 +        let attrs = db.attrs(AttrDefId::ModuleId(self.root_module(db).into()));
 +        let doc_url = attrs.by_key("doc").find_string_value_in_tt("html_root_url");
 +        doc_url.map(|s| s.trim_matches('"').trim_end_matches('/').to_owned() + "/")
 +    }
 +
 +    pub fn cfg(&self, db: &dyn HirDatabase) -> CfgOptions {
 +        db.crate_graph()[self.id].cfg_options.clone()
 +    }
 +
 +    pub fn potential_cfg(&self, db: &dyn HirDatabase) -> CfgOptions {
 +        db.crate_graph()[self.id].potential_cfg_options.clone()
 +    }
 +}
 +
 +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 +pub struct Module {
 +    pub(crate) id: ModuleId,
 +}
 +
 +/// The defs which can be visible in the module.
 +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 +pub enum ModuleDef {
 +    Module(Module),
 +    Function(Function),
 +    Adt(Adt),
 +    // Can't be directly declared, but can be imported.
 +    Variant(Variant),
 +    Const(Const),
 +    Static(Static),
 +    Trait(Trait),
 +    TypeAlias(TypeAlias),
 +    BuiltinType(BuiltinType),
 +    Macro(Macro),
 +}
 +impl_from!(
 +    Module,
 +    Function,
 +    Adt(Struct, Enum, Union),
 +    Variant,
 +    Const,
 +    Static,
 +    Trait,
 +    TypeAlias,
 +    BuiltinType,
 +    Macro
 +    for ModuleDef
 +);
 +
 +impl From<VariantDef> for ModuleDef {
 +    fn from(var: VariantDef) -> Self {
 +        match var {
 +            VariantDef::Struct(t) => Adt::from(t).into(),
 +            VariantDef::Union(t) => Adt::from(t).into(),
 +            VariantDef::Variant(t) => t.into(),
 +        }
 +    }
 +}
 +
 +impl ModuleDef {
 +    pub fn module(self, db: &dyn HirDatabase) -> Option<Module> {
 +        match self {
 +            ModuleDef::Module(it) => it.parent(db),
 +            ModuleDef::Function(it) => Some(it.module(db)),
 +            ModuleDef::Adt(it) => Some(it.module(db)),
 +            ModuleDef::Variant(it) => Some(it.module(db)),
 +            ModuleDef::Const(it) => Some(it.module(db)),
 +            ModuleDef::Static(it) => Some(it.module(db)),
 +            ModuleDef::Trait(it) => Some(it.module(db)),
 +            ModuleDef::TypeAlias(it) => Some(it.module(db)),
 +            ModuleDef::Macro(it) => Some(it.module(db)),
 +            ModuleDef::BuiltinType(_) => None,
 +        }
 +    }
 +
 +    pub fn canonical_path(&self, db: &dyn HirDatabase) -> Option<String> {
 +        let mut segments = vec![self.name(db)?];
 +        for m in self.module(db)?.path_to_root(db) {
 +            segments.extend(m.name(db))
 +        }
 +        segments.reverse();
 +        Some(segments.into_iter().join("::"))
 +    }
 +
 +    pub fn canonical_module_path(
 +        &self,
 +        db: &dyn HirDatabase,
 +    ) -> Option<impl Iterator<Item = Module>> {
 +        self.module(db).map(|it| it.path_to_root(db).into_iter().rev())
 +    }
 +
 +    pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
 +        let name = match self {
 +            ModuleDef::Module(it) => it.name(db)?,
 +            ModuleDef::Const(it) => it.name(db)?,
 +            ModuleDef::Adt(it) => it.name(db),
 +            ModuleDef::Trait(it) => it.name(db),
 +            ModuleDef::Function(it) => it.name(db),
 +            ModuleDef::Variant(it) => it.name(db),
 +            ModuleDef::TypeAlias(it) => it.name(db),
 +            ModuleDef::Static(it) => it.name(db),
 +            ModuleDef::Macro(it) => it.name(db),
 +            ModuleDef::BuiltinType(it) => it.name(),
 +        };
 +        Some(name)
 +    }
 +
 +    pub fn diagnostics(self, db: &dyn HirDatabase) -> Vec<AnyDiagnostic> {
 +        let id = match self {
 +            ModuleDef::Adt(it) => match it {
 +                Adt::Struct(it) => it.id.into(),
 +                Adt::Enum(it) => it.id.into(),
 +                Adt::Union(it) => it.id.into(),
 +            },
 +            ModuleDef::Trait(it) => it.id.into(),
 +            ModuleDef::Function(it) => it.id.into(),
 +            ModuleDef::TypeAlias(it) => it.id.into(),
 +            ModuleDef::Module(it) => it.id.into(),
 +            ModuleDef::Const(it) => it.id.into(),
 +            ModuleDef::Static(it) => it.id.into(),
 +            ModuleDef::Variant(it) => {
 +                EnumVariantId { parent: it.parent.into(), local_id: it.id }.into()
 +            }
 +            ModuleDef::BuiltinType(_) | ModuleDef::Macro(_) => return Vec::new(),
 +        };
 +
 +        let module = match self.module(db) {
 +            Some(it) => it,
 +            None => return Vec::new(),
 +        };
 +
 +        let mut acc = Vec::new();
 +
 +        match self.as_def_with_body() {
 +            Some(def) => {
 +                def.diagnostics(db, &mut acc);
 +            }
 +            None => {
 +                for diag in hir_ty::diagnostics::incorrect_case(db, module.id.krate(), id) {
 +                    acc.push(diag.into())
 +                }
 +            }
 +        }
 +
 +        acc
 +    }
 +
 +    pub fn as_def_with_body(self) -> Option<DefWithBody> {
 +        match self {
 +            ModuleDef::Function(it) => Some(it.into()),
 +            ModuleDef::Const(it) => Some(it.into()),
 +            ModuleDef::Static(it) => Some(it.into()),
 +            ModuleDef::Variant(it) => Some(it.into()),
 +
 +            ModuleDef::Module(_)
 +            | ModuleDef::Adt(_)
 +            | ModuleDef::Trait(_)
 +            | ModuleDef::TypeAlias(_)
 +            | ModuleDef::Macro(_)
 +            | ModuleDef::BuiltinType(_) => None,
 +        }
 +    }
 +
 +    pub fn attrs(&self, db: &dyn HirDatabase) -> Option<AttrsWithOwner> {
 +        Some(match self {
 +            ModuleDef::Module(it) => it.attrs(db),
 +            ModuleDef::Function(it) => it.attrs(db),
 +            ModuleDef::Adt(it) => it.attrs(db),
 +            ModuleDef::Variant(it) => it.attrs(db),
 +            ModuleDef::Const(it) => it.attrs(db),
 +            ModuleDef::Static(it) => it.attrs(db),
 +            ModuleDef::Trait(it) => it.attrs(db),
 +            ModuleDef::TypeAlias(it) => it.attrs(db),
 +            ModuleDef::Macro(it) => it.attrs(db),
 +            ModuleDef::BuiltinType(_) => return None,
 +        })
 +    }
 +}
 +
 +impl HasVisibility for ModuleDef {
 +    fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
 +        match *self {
 +            ModuleDef::Module(it) => it.visibility(db),
 +            ModuleDef::Function(it) => it.visibility(db),
 +            ModuleDef::Adt(it) => it.visibility(db),
 +            ModuleDef::Const(it) => it.visibility(db),
 +            ModuleDef::Static(it) => it.visibility(db),
 +            ModuleDef::Trait(it) => it.visibility(db),
 +            ModuleDef::TypeAlias(it) => it.visibility(db),
 +            ModuleDef::Variant(it) => it.visibility(db),
 +            ModuleDef::Macro(it) => it.visibility(db),
 +            ModuleDef::BuiltinType(_) => Visibility::Public,
 +        }
 +    }
 +}
 +
 +impl Module {
 +    /// Name of this module.
 +    pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
 +        let def_map = self.id.def_map(db.upcast());
 +        let parent = def_map[self.id.local_id].parent?;
 +        def_map[parent].children.iter().find_map(|(name, module_id)| {
 +            if *module_id == self.id.local_id {
 +                Some(name.clone())
 +            } else {
 +                None
 +            }
 +        })
 +    }
 +
 +    /// Returns the crate this module is part of.
 +    pub fn krate(self) -> Crate {
 +        Crate { id: self.id.krate() }
 +    }
 +
 +    /// Topmost parent of this module. Every module has a `crate_root`, but some
 +    /// might be missing `krate`. This can happen if a module's file is not included
 +    /// in the module tree of any target in `Cargo.toml`.
 +    pub fn crate_root(self, db: &dyn HirDatabase) -> Module {
 +        let def_map = db.crate_def_map(self.id.krate());
 +        Module { id: def_map.module_id(def_map.root()) }
 +    }
 +
 +    pub fn is_crate_root(self, db: &dyn HirDatabase) -> bool {
 +        let def_map = db.crate_def_map(self.id.krate());
 +        def_map.root() == self.id.local_id
 +    }
 +
 +    /// Iterates over all child modules.
 +    pub fn children(self, db: &dyn HirDatabase) -> impl Iterator<Item = Module> {
 +        let def_map = self.id.def_map(db.upcast());
 +        let children = def_map[self.id.local_id]
 +            .children
 +            .iter()
 +            .map(|(_, module_id)| Module { id: def_map.module_id(*module_id) })
 +            .collect::<Vec<_>>();
 +        children.into_iter()
 +    }
 +
 +    /// Finds a parent module.
 +    pub fn parent(self, db: &dyn HirDatabase) -> Option<Module> {
 +        // FIXME: handle block expressions as modules (their parent is in a different DefMap)
 +        let def_map = self.id.def_map(db.upcast());
 +        let parent_id = def_map[self.id.local_id].parent?;
 +        Some(Module { id: def_map.module_id(parent_id) })
 +    }
 +
 +    pub fn path_to_root(self, db: &dyn HirDatabase) -> Vec<Module> {
 +        let mut res = vec![self];
 +        let mut curr = self;
 +        while let Some(next) = curr.parent(db) {
 +            res.push(next);
 +            curr = next
 +        }
 +        res
 +    }
 +
 +    /// Returns a `ModuleScope`: a set of items, visible in this module.
 +    pub fn scope(
 +        self,
 +        db: &dyn HirDatabase,
 +        visible_from: Option<Module>,
 +    ) -> Vec<(Name, ScopeDef)> {
 +        self.id.def_map(db.upcast())[self.id.local_id]
 +            .scope
 +            .entries()
 +            .filter_map(|(name, def)| {
 +                if let Some(m) = visible_from {
 +                    let filtered =
 +                        def.filter_visibility(|vis| vis.is_visible_from(db.upcast(), m.id));
 +                    if filtered.is_none() && !def.is_none() {
 +                        None
 +                    } else {
 +                        Some((name, filtered))
 +                    }
 +                } else {
 +                    Some((name, def))
 +                }
 +            })
 +            .flat_map(|(name, def)| {
 +                ScopeDef::all_items(def).into_iter().map(move |item| (name.clone(), item))
 +            })
 +            .collect()
 +    }
 +
 +    /// Fills `acc` with the module's diagnostics.
 +    pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>) {
 +        let _p = profile::span("Module::diagnostics").detail(|| {
 +            format!("{:?}", self.name(db).map_or("<unknown>".into(), |name| name.to_string()))
 +        });
 +        let def_map = self.id.def_map(db.upcast());
 +        for diag in def_map.diagnostics() {
 +            if diag.in_module != self.id.local_id {
 +                // FIXME: This is accidentally quadratic.
 +                continue;
 +            }
 +            emit_def_diagnostic(db, acc, diag);
 +        }
 +        for decl in self.declarations(db) {
 +            match decl {
 +                ModuleDef::Module(m) => {
 +                    // Only add diagnostics from inline modules
 +                    if def_map[m.id.local_id].origin.is_inline() {
 +                        m.diagnostics(db, acc)
 +                    }
 +                }
 +                ModuleDef::Trait(t) => {
 +                    for diag in db.trait_data_with_diagnostics(t.id).1.iter() {
 +                        emit_def_diagnostic(db, acc, diag);
 +                    }
 +                    acc.extend(decl.diagnostics(db))
 +                }
 +                ModuleDef::Adt(adt) => {
 +                    match adt {
 +                        Adt::Struct(s) => {
 +                            for diag in db.struct_data_with_diagnostics(s.id).1.iter() {
 +                                emit_def_diagnostic(db, acc, diag);
 +                            }
 +                        }
 +                        Adt::Union(u) => {
 +                            for diag in db.union_data_with_diagnostics(u.id).1.iter() {
 +                                emit_def_diagnostic(db, acc, diag);
 +                            }
 +                        }
 +                        Adt::Enum(e) => {
 +                            for v in e.variants(db) {
 +                                acc.extend(ModuleDef::Variant(v).diagnostics(db));
 +                            }
 +
 +                            for diag in db.enum_data_with_diagnostics(e.id).1.iter() {
 +                                emit_def_diagnostic(db, acc, diag);
 +                            }
 +                        }
 +                    }
 +                    acc.extend(decl.diagnostics(db))
 +                }
 +                _ => acc.extend(decl.diagnostics(db)),
 +            }
 +        }
 +
 +        for impl_def in self.impl_defs(db) {
 +            for diag in db.impl_data_with_diagnostics(impl_def.id).1.iter() {
 +                emit_def_diagnostic(db, acc, diag);
 +            }
 +
 +            for item in impl_def.items(db) {
 +                let def: DefWithBody = match item {
 +                    AssocItem::Function(it) => it.into(),
 +                    AssocItem::Const(it) => it.into(),
 +                    AssocItem::TypeAlias(_) => continue,
 +                };
 +
 +                def.diagnostics(db, acc);
 +            }
 +        }
 +    }
 +
 +    pub fn declarations(self, db: &dyn HirDatabase) -> Vec<ModuleDef> {
 +        let def_map = self.id.def_map(db.upcast());
 +        let scope = &def_map[self.id.local_id].scope;
 +        scope
 +            .declarations()
 +            .map(ModuleDef::from)
 +            .chain(scope.unnamed_consts().map(|id| ModuleDef::Const(Const::from(id))))
 +            .collect()
 +    }
 +
 +    pub fn legacy_macros(self, db: &dyn HirDatabase) -> Vec<Macro> {
 +        let def_map = self.id.def_map(db.upcast());
 +        let scope = &def_map[self.id.local_id].scope;
 +        scope.legacy_macros().flat_map(|(_, it)| it).map(|&it| MacroId::from(it).into()).collect()
 +    }
 +
 +    pub fn impl_defs(self, db: &dyn HirDatabase) -> Vec<Impl> {
 +        let def_map = self.id.def_map(db.upcast());
 +        def_map[self.id.local_id].scope.impls().map(Impl::from).collect()
 +    }
 +
 +    /// Finds a path that can be used to refer to the given item from within
 +    /// this module, if possible.
 +    pub fn find_use_path(
 +        self,
 +        db: &dyn DefDatabase,
 +        item: impl Into<ItemInNs>,
 +        prefer_no_std: bool,
 +    ) -> Option<ModPath> {
 +        hir_def::find_path::find_path(db, item.into().into(), self.into(), prefer_no_std)
 +    }
 +
 +    /// Finds a path that can be used to refer to the given item from within
 +    /// this module, if possible. This is used for returning import paths for use-statements.
 +    pub fn find_use_path_prefixed(
 +        self,
 +        db: &dyn DefDatabase,
 +        item: impl Into<ItemInNs>,
 +        prefix_kind: PrefixKind,
 +        prefer_no_std: bool,
 +    ) -> Option<ModPath> {
 +        hir_def::find_path::find_path_prefixed(
 +            db,
 +            item.into().into(),
 +            self.into(),
 +            prefix_kind,
 +            prefer_no_std,
 +        )
 +    }
 +}
 +
 +fn emit_def_diagnostic(db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>, diag: &DefDiagnostic) {
 +    match &diag.kind {
 +        DefDiagnosticKind::UnresolvedModule { ast: declaration, candidates } => {
 +            let decl = declaration.to_node(db.upcast());
 +            acc.push(
 +                UnresolvedModule {
 +                    decl: InFile::new(declaration.file_id, AstPtr::new(&decl)),
 +                    candidates: candidates.clone(),
 +                }
 +                .into(),
 +            )
 +        }
 +        DefDiagnosticKind::UnresolvedExternCrate { ast } => {
 +            let item = ast.to_node(db.upcast());
 +            acc.push(
 +                UnresolvedExternCrate { decl: InFile::new(ast.file_id, AstPtr::new(&item)) }.into(),
 +            );
 +        }
 +
 +        DefDiagnosticKind::UnresolvedImport { id, index } => {
 +            let file_id = id.file_id();
 +            let item_tree = id.item_tree(db.upcast());
 +            let import = &item_tree[id.value];
 +
 +            let use_tree = import.use_tree_to_ast(db.upcast(), file_id, *index);
 +            acc.push(
 +                UnresolvedImport { decl: InFile::new(file_id, AstPtr::new(&use_tree)) }.into(),
 +            );
 +        }
 +
 +        DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } => {
 +            let item = ast.to_node(db.upcast());
 +            acc.push(
 +                InactiveCode {
 +                    node: ast.with_value(AstPtr::new(&item).into()),
 +                    cfg: cfg.clone(),
 +                    opts: opts.clone(),
 +                }
 +                .into(),
 +            );
 +        }
 +
 +        DefDiagnosticKind::UnresolvedProcMacro { ast, krate } => {
 +            let (node, precise_location, macro_name, kind) = precise_macro_call_location(ast, db);
 +            acc.push(
 +                UnresolvedProcMacro { node, precise_location, macro_name, kind, krate: *krate }
 +                    .into(),
 +            );
 +        }
 +
 +        DefDiagnosticKind::UnresolvedMacroCall { ast, path } => {
 +            let (node, precise_location, _, _) = precise_macro_call_location(ast, db);
 +            acc.push(
 +                UnresolvedMacroCall {
 +                    macro_call: node,
 +                    precise_location,
 +                    path: path.clone(),
 +                    is_bang: matches!(ast, MacroCallKind::FnLike { .. }),
 +                }
 +                .into(),
 +            );
 +        }
 +
 +        DefDiagnosticKind::MacroError { ast, message } => {
 +            let (node, precise_location, _, _) = precise_macro_call_location(ast, db);
 +            acc.push(MacroError { node, precise_location, message: message.clone() }.into());
 +        }
 +
 +        DefDiagnosticKind::UnimplementedBuiltinMacro { ast } => {
 +            let node = ast.to_node(db.upcast());
 +            // Must have a name, otherwise we wouldn't emit it.
 +            let name = node.name().expect("unimplemented builtin macro with no name");
 +            acc.push(
 +                UnimplementedBuiltinMacro {
 +                    node: ast.with_value(SyntaxNodePtr::from(AstPtr::new(&name))),
 +                }
 +                .into(),
 +            );
 +        }
 +        DefDiagnosticKind::InvalidDeriveTarget { ast, id } => {
 +            let node = ast.to_node(db.upcast());
 +            let derive = node.attrs().nth(*id as usize);
 +            match derive {
 +                Some(derive) => {
 +                    acc.push(
 +                        InvalidDeriveTarget {
 +                            node: ast.with_value(SyntaxNodePtr::from(AstPtr::new(&derive))),
 +                        }
 +                        .into(),
 +                    );
 +                }
 +                None => stdx::never!("derive diagnostic on item without derive attribute"),
 +            }
 +        }
 +        DefDiagnosticKind::MalformedDerive { ast, id } => {
 +            let node = ast.to_node(db.upcast());
 +            let derive = node.attrs().nth(*id as usize);
 +            match derive {
 +                Some(derive) => {
 +                    acc.push(
 +                        MalformedDerive {
 +                            node: ast.with_value(SyntaxNodePtr::from(AstPtr::new(&derive))),
 +                        }
 +                        .into(),
 +                    );
 +                }
 +                None => stdx::never!("derive diagnostic on item without derive attribute"),
 +            }
 +        }
 +    }
 +}
 +
 +fn precise_macro_call_location(
 +    ast: &MacroCallKind,
 +    db: &dyn HirDatabase,
 +) -> (InFile<SyntaxNodePtr>, Option<TextRange>, Option<String>, MacroKind) {
 +    // FIXME: maaybe we actually want slightly different ranges for the different macro diagnostics
 +    // - e.g. the full attribute for macro errors, but only the name for name resolution
 +    match ast {
 +        MacroCallKind::FnLike { ast_id, .. } => {
 +            let node = ast_id.to_node(db.upcast());
 +            (
 +                ast_id.with_value(SyntaxNodePtr::from(AstPtr::new(&node))),
 +                node.path()
 +                    .and_then(|it| it.segment())
 +                    .and_then(|it| it.name_ref())
 +                    .map(|it| it.syntax().text_range()),
 +                node.path().and_then(|it| it.segment()).map(|it| it.to_string()),
 +                MacroKind::ProcMacro,
 +            )
 +        }
 +        MacroCallKind::Derive { ast_id, derive_attr_index, derive_index } => {
 +            let node = ast_id.to_node(db.upcast());
 +            // Compute the precise location of the macro name's token in the derive
 +            // list.
 +            let token = (|| {
 +                let derive_attr = node
 +                    .doc_comments_and_attrs()
 +                    .nth(*derive_attr_index as usize)
 +                    .and_then(Either::left)?;
 +                let token_tree = derive_attr.meta()?.token_tree()?;
 +                let group_by = token_tree
 +                    .syntax()
 +                    .children_with_tokens()
 +                    .filter_map(|elem| match elem {
 +                        syntax::NodeOrToken::Token(tok) => Some(tok),
 +                        _ => None,
 +                    })
 +                    .group_by(|t| t.kind() == T![,]);
 +                let (_, mut group) = group_by
 +                    .into_iter()
 +                    .filter(|&(comma, _)| !comma)
 +                    .nth(*derive_index as usize)?;
 +                group.find(|t| t.kind() == T![ident])
 +            })();
 +            (
 +                ast_id.with_value(SyntaxNodePtr::from(AstPtr::new(&node))),
 +                token.as_ref().map(|tok| tok.text_range()),
 +                token.as_ref().map(ToString::to_string),
 +                MacroKind::Derive,
 +            )
 +        }
 +        MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => {
 +            let node = ast_id.to_node(db.upcast());
 +            let attr = node
 +                .doc_comments_and_attrs()
 +                .nth((*invoc_attr_index) as usize)
 +                .and_then(Either::left)
 +                .unwrap_or_else(|| panic!("cannot find attribute #{}", invoc_attr_index));
 +
 +            (
 +                ast_id.with_value(SyntaxNodePtr::from(AstPtr::new(&attr))),
 +                Some(attr.syntax().text_range()),
 +                attr.path()
 +                    .and_then(|path| path.segment())
 +                    .and_then(|seg| seg.name_ref())
 +                    .as_ref()
 +                    .map(ToString::to_string),
 +                MacroKind::Attr,
 +            )
 +        }
 +    }
 +}
 +
 +impl HasVisibility for Module {
 +    fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
 +        let def_map = self.id.def_map(db.upcast());
 +        let module_data = &def_map[self.id.local_id];
 +        module_data.visibility
 +    }
 +}
 +
 +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 +pub struct Field {
 +    pub(crate) parent: VariantDef,
 +    pub(crate) id: LocalFieldId,
 +}
 +
 +#[derive(Debug, PartialEq, Eq)]
 +pub enum FieldSource {
 +    Named(ast::RecordField),
 +    Pos(ast::TupleField),
 +}
 +
 +impl Field {
 +    pub fn name(&self, db: &dyn HirDatabase) -> Name {
 +        self.parent.variant_data(db).fields()[self.id].name.clone()
 +    }
 +
 +    /// Returns the type as in the signature of the struct (i.e., with
 +    /// placeholder types for type parameters). Only use this in the context of
 +    /// the field definition.
 +    pub fn ty(&self, db: &dyn HirDatabase) -> Type {
 +        let var_id = self.parent.into();
 +        let generic_def_id: GenericDefId = match self.parent {
 +            VariantDef::Struct(it) => it.id.into(),
 +            VariantDef::Union(it) => it.id.into(),
 +            VariantDef::Variant(it) => it.parent.id.into(),
 +        };
 +        let substs = TyBuilder::placeholder_subst(db, generic_def_id);
 +        let ty = db.field_types(var_id)[self.id].clone().substitute(Interner, &substs);
 +        Type::new(db, var_id, ty)
 +    }
 +
 +    pub fn parent_def(&self, _db: &dyn HirDatabase) -> VariantDef {
 +        self.parent
 +    }
 +}
 +
 +impl HasVisibility for Field {
 +    fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
 +        let variant_data = self.parent.variant_data(db);
 +        let visibility = &variant_data.fields()[self.id].visibility;
 +        let parent_id: hir_def::VariantId = self.parent.into();
 +        visibility.resolve(db.upcast(), &parent_id.resolver(db.upcast()))
 +    }
 +}
 +
 +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 +pub struct Struct {
 +    pub(crate) id: StructId,
 +}
 +
 +impl Struct {
 +    pub fn module(self, db: &dyn HirDatabase) -> Module {
 +        Module { id: self.id.lookup(db.upcast()).container }
 +    }
 +
 +    pub fn name(self, db: &dyn HirDatabase) -> Name {
 +        db.struct_data(self.id).name.clone()
 +    }
 +
 +    pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> {
 +        db.struct_data(self.id)
 +            .variant_data
 +            .fields()
 +            .iter()
 +            .map(|(id, _)| Field { parent: self.into(), id })
 +            .collect()
 +    }
 +
 +    pub fn ty(self, db: &dyn HirDatabase) -> Type {
 +        Type::from_def(db, self.id)
 +    }
 +
 +    pub fn repr(self, db: &dyn HirDatabase) -> Option<ReprData> {
 +        db.struct_data(self.id).repr.clone()
 +    }
 +
 +    pub fn kind(self, db: &dyn HirDatabase) -> StructKind {
 +        self.variant_data(db).kind()
 +    }
 +
 +    fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
 +        db.struct_data(self.id).variant_data.clone()
 +    }
 +}
 +
 +impl HasVisibility for Struct {
 +    fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
 +        db.struct_data(self.id).visibility.resolve(db.upcast(), &self.id.resolver(db.upcast()))
 +    }
 +}
 +
 +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 +pub struct Union {
 +    pub(crate) id: UnionId,
 +}
 +
 +impl Union {
 +    pub fn name(self, db: &dyn HirDatabase) -> Name {
 +        db.union_data(self.id).name.clone()
 +    }
 +
 +    pub fn module(self, db: &dyn HirDatabase) -> Module {
 +        Module { id: self.id.lookup(db.upcast()).container }
 +    }
 +
 +    pub fn ty(self, db: &dyn HirDatabase) -> Type {
 +        Type::from_def(db, self.id)
 +    }
 +
 +    pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> {
 +        db.union_data(self.id)
 +            .variant_data
 +            .fields()
 +            .iter()
 +            .map(|(id, _)| Field { parent: self.into(), id })
 +            .collect()
 +    }
 +
 +    fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
 +        db.union_data(self.id).variant_data.clone()
 +    }
 +}
 +
 +impl HasVisibility for Union {
 +    fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
 +        db.union_data(self.id).visibility.resolve(db.upcast(), &self.id.resolver(db.upcast()))
 +    }
 +}
 +
 +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 +pub struct Enum {
 +    pub(crate) id: EnumId,
 +}
 +
 +impl Enum {
 +    pub fn module(self, db: &dyn HirDatabase) -> Module {
 +        Module { id: self.id.lookup(db.upcast()).container }
 +    }
 +
 +    pub fn name(self, db: &dyn HirDatabase) -> Name {
 +        db.enum_data(self.id).name.clone()
 +    }
 +
 +    pub fn variants(self, db: &dyn HirDatabase) -> Vec<Variant> {
 +        db.enum_data(self.id).variants.iter().map(|(id, _)| Variant { parent: self, id }).collect()
 +    }
 +
 +    pub fn ty(self, db: &dyn HirDatabase) -> Type {
 +        Type::from_def(db, self.id)
 +    }
 +
 +    /// The type of the enum variant bodies.
 +    pub fn variant_body_ty(self, db: &dyn HirDatabase) -> Type {
 +        Type::new_for_crate(
 +            self.id.lookup(db.upcast()).container.krate(),
 +            TyBuilder::builtin(match db.enum_data(self.id).variant_body_type() {
 +                Either::Left(builtin) => hir_def::builtin_type::BuiltinType::Int(builtin),
 +                Either::Right(builtin) => hir_def::builtin_type::BuiltinType::Uint(builtin),
 +            }),
 +        )
 +    }
 +
 +    pub fn is_data_carrying(self, db: &dyn HirDatabase) -> bool {
 +        self.variants(db).iter().any(|v| !matches!(v.kind(db), StructKind::Unit))
 +    }
 +}
 +
 +impl HasVisibility for Enum {
 +    fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
 +        db.enum_data(self.id).visibility.resolve(db.upcast(), &self.id.resolver(db.upcast()))
 +    }
 +}
 +
 +impl From<&Variant> for DefWithBodyId {
 +    fn from(&v: &Variant) -> Self {
 +        DefWithBodyId::VariantId(v.into())
 +    }
 +}
 +
 +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 +pub struct Variant {
 +    pub(crate) parent: Enum,
 +    pub(crate) id: LocalEnumVariantId,
 +}
 +
 +impl Variant {
 +    pub fn module(self, db: &dyn HirDatabase) -> Module {
 +        self.parent.module(db)
 +    }
 +
 +    pub fn parent_enum(self, _db: &dyn HirDatabase) -> Enum {
 +        self.parent
 +    }
 +
 +    pub fn name(self, db: &dyn HirDatabase) -> Name {
 +        db.enum_data(self.parent.id).variants[self.id].name.clone()
 +    }
 +
 +    pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> {
 +        self.variant_data(db)
 +            .fields()
 +            .iter()
 +            .map(|(id, _)| Field { parent: self.into(), id })
 +            .collect()
 +    }
 +
 +    pub fn kind(self, db: &dyn HirDatabase) -> StructKind {
 +        self.variant_data(db).kind()
 +    }
 +
 +    pub(crate) fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
 +        db.enum_data(self.parent.id).variants[self.id].variant_data.clone()
 +    }
 +
 +    pub fn value(self, db: &dyn HirDatabase) -> Option<Expr> {
 +        self.source(db)?.value.expr()
 +    }
 +
 +    pub fn eval(self, db: &dyn HirDatabase) -> Result<ComputedExpr, ConstEvalError> {
 +        db.const_eval_variant(self.into())
 +    }
 +}
 +
 +/// Variants inherit visibility from the parent enum.
 +impl HasVisibility for Variant {
 +    fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
 +        self.parent_enum(db).visibility(db)
 +    }
 +}
 +
 +/// A Data Type
 +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 +pub enum Adt {
 +    Struct(Struct),
 +    Union(Union),
 +    Enum(Enum),
 +}
 +impl_from!(Struct, Union, Enum for Adt);
 +
 +impl Adt {
 +    pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool {
 +        let subst = db.generic_defaults(self.into());
 +        subst.iter().any(|ty| match ty.skip_binders().data(Interner) {
 +            GenericArgData::Ty(x) => x.is_unknown(),
 +            _ => false,
 +        })
 +    }
 +
 +    /// Turns this ADT into a type. Any type parameters of the ADT will be
 +    /// turned into unknown types, which is good for e.g. finding the most
 +    /// general set of completions, but will not look very nice when printed.
 +    pub fn ty(self, db: &dyn HirDatabase) -> Type {
 +        let id = AdtId::from(self);
 +        Type::from_def(db, id)
 +    }
 +
 +    /// Turns this ADT into a type with the given type parameters. This isn't
 +    /// the greatest API, FIXME find a better one.
 +    pub fn ty_with_args(self, db: &dyn HirDatabase, args: &[Type]) -> Type {
 +        let id = AdtId::from(self);
 +        let mut it = args.iter().map(|t| t.ty.clone());
 +        let ty = TyBuilder::def_ty(db, id.into(), None)
 +            .fill(|x| {
 +                let r = it.next().unwrap_or_else(|| TyKind::Error.intern(Interner));
 +                match x {
 +                    ParamKind::Type => GenericArgData::Ty(r).intern(Interner),
 +                    ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
 +                }
 +            })
 +            .build();
 +        Type::new(db, id, ty)
 +    }
 +
 +    pub fn module(self, db: &dyn HirDatabase) -> Module {
 +        match self {
 +            Adt::Struct(s) => s.module(db),
 +            Adt::Union(s) => s.module(db),
 +            Adt::Enum(e) => e.module(db),
 +        }
 +    }
 +
 +    pub fn name(self, db: &dyn HirDatabase) -> Name {
 +        match self {
 +            Adt::Struct(s) => s.name(db),
 +            Adt::Union(u) => u.name(db),
 +            Adt::Enum(e) => e.name(db),
 +        }
 +    }
 +
 +    pub fn as_enum(&self) -> Option<Enum> {
 +        if let Self::Enum(v) = self {
 +            Some(*v)
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +impl HasVisibility for Adt {
 +    fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
 +        match self {
 +            Adt::Struct(it) => it.visibility(db),
 +            Adt::Union(it) => it.visibility(db),
 +            Adt::Enum(it) => it.visibility(db),
 +        }
 +    }
 +}
 +
 +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 +pub enum VariantDef {
 +    Struct(Struct),
 +    Union(Union),
 +    Variant(Variant),
 +}
 +impl_from!(Struct, Union, Variant for VariantDef);
 +
 +impl VariantDef {
 +    pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> {
 +        match self {
 +            VariantDef::Struct(it) => it.fields(db),
 +            VariantDef::Union(it) => it.fields(db),
 +            VariantDef::Variant(it) => it.fields(db),
 +        }
 +    }
 +
 +    pub fn module(self, db: &dyn HirDatabase) -> Module {
 +        match self {
 +            VariantDef::Struct(it) => it.module(db),
 +            VariantDef::Union(it) => it.module(db),
 +            VariantDef::Variant(it) => it.module(db),
 +        }
 +    }
 +
 +    pub fn name(&self, db: &dyn HirDatabase) -> Name {
 +        match self {
 +            VariantDef::Struct(s) => s.name(db),
 +            VariantDef::Union(u) => u.name(db),
 +            VariantDef::Variant(e) => e.name(db),
 +        }
 +    }
 +
 +    pub(crate) fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
 +        match self {
 +            VariantDef::Struct(it) => it.variant_data(db),
 +            VariantDef::Union(it) => it.variant_data(db),
 +            VariantDef::Variant(it) => it.variant_data(db),
 +        }
 +    }
 +}
 +
 +/// The defs which have a body.
 +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 +pub enum DefWithBody {
 +    Function(Function),
 +    Static(Static),
 +    Const(Const),
 +    Variant(Variant),
 +}
 +impl_from!(Function, Const, Static, Variant for DefWithBody);
 +
 +impl DefWithBody {
 +    pub fn module(self, db: &dyn HirDatabase) -> Module {
 +        match self {
 +            DefWithBody::Const(c) => c.module(db),
 +            DefWithBody::Function(f) => f.module(db),
 +            DefWithBody::Static(s) => s.module(db),
 +            DefWithBody::Variant(v) => v.module(db),
 +        }
 +    }
 +
 +    pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
 +        match self {
 +            DefWithBody::Function(f) => Some(f.name(db)),
 +            DefWithBody::Static(s) => Some(s.name(db)),
 +            DefWithBody::Const(c) => c.name(db),
 +            DefWithBody::Variant(v) => Some(v.name(db)),
 +        }
 +    }
 +
 +    /// Returns the type this def's body has to evaluate to.
 +    pub fn body_type(self, db: &dyn HirDatabase) -> Type {
 +        match self {
 +            DefWithBody::Function(it) => it.ret_type(db),
 +            DefWithBody::Static(it) => it.ty(db),
 +            DefWithBody::Const(it) => it.ty(db),
 +            DefWithBody::Variant(it) => it.parent.variant_body_ty(db),
 +        }
 +    }
 +
 +    fn id(&self) -> DefWithBodyId {
 +        match self {
 +            DefWithBody::Function(it) => it.id.into(),
 +            DefWithBody::Static(it) => it.id.into(),
 +            DefWithBody::Const(it) => it.id.into(),
 +            DefWithBody::Variant(it) => it.into(),
 +        }
 +    }
 +
 +    /// A textual representation of the HIR of this def's body for debugging purposes.
 +    pub fn debug_hir(self, db: &dyn HirDatabase) -> String {
 +        let body = db.body(self.id());
 +        body.pretty_print(db.upcast(), self.id())
 +    }
 +
 +    pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>) {
 +        let krate = self.module(db).id.krate();
 +
 +        let (body, source_map) = db.body_with_source_map(self.into());
 +
 +        for (_, def_map) in body.blocks(db.upcast()) {
 +            for diag in def_map.diagnostics() {
 +                emit_def_diagnostic(db, acc, diag);
 +            }
 +        }
 +
 +        for diag in source_map.diagnostics() {
 +            match diag {
 +                BodyDiagnostic::InactiveCode { node, cfg, opts } => acc.push(
 +                    InactiveCode { node: node.clone(), cfg: cfg.clone(), opts: opts.clone() }
 +                        .into(),
 +                ),
 +                BodyDiagnostic::MacroError { node, message } => acc.push(
 +                    MacroError {
 +                        node: node.clone().map(|it| it.into()),
 +                        precise_location: None,
 +                        message: message.to_string(),
 +                    }
 +                    .into(),
 +                ),
 +                BodyDiagnostic::UnresolvedProcMacro { node, krate } => acc.push(
 +                    UnresolvedProcMacro {
 +                        node: node.clone().map(|it| it.into()),
 +                        precise_location: None,
 +                        macro_name: None,
 +                        kind: MacroKind::ProcMacro,
 +                        krate: *krate,
 +                    }
 +                    .into(),
 +                ),
 +                BodyDiagnostic::UnresolvedMacroCall { node, path } => acc.push(
 +                    UnresolvedMacroCall {
 +                        macro_call: node.clone().map(|ast_ptr| ast_ptr.into()),
 +                        precise_location: None,
 +                        path: path.clone(),
 +                        is_bang: true,
 +                    }
 +                    .into(),
 +                ),
 +            }
 +        }
 +
 +        let infer = db.infer(self.into());
 +        let source_map = Lazy::new(|| db.body_with_source_map(self.into()).1);
 +        for d in &infer.diagnostics {
 +            match d {
 +                hir_ty::InferenceDiagnostic::NoSuchField { expr } => {
 +                    let field = source_map.field_syntax(*expr);
 +                    acc.push(NoSuchField { field }.into())
 +                }
 +                &hir_ty::InferenceDiagnostic::BreakOutsideOfLoop { expr, is_break } => {
 +                    let expr = source_map
 +                        .expr_syntax(expr)
 +                        .expect("break outside of loop in synthetic syntax");
 +                    acc.push(BreakOutsideOfLoop { expr, is_break }.into())
 +                }
 +                hir_ty::InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => {
 +                    match source_map.expr_syntax(*call_expr) {
 +                        Ok(source_ptr) => acc.push(
 +                            MismatchedArgCount {
 +                                call_expr: source_ptr,
 +                                expected: *expected,
 +                                found: *found,
 +                            }
 +                            .into(),
 +                        ),
 +                        Err(SyntheticSyntax) => (),
 +                    }
 +                }
 +            }
 +        }
 +        for (expr, mismatch) in infer.expr_type_mismatches() {
 +            let expr = match source_map.expr_syntax(expr) {
 +                Ok(expr) => expr,
 +                Err(SyntheticSyntax) => continue,
 +            };
 +            acc.push(
 +                TypeMismatch {
 +                    expr,
 +                    expected: Type::new(db, DefWithBodyId::from(self), mismatch.expected.clone()),
 +                    actual: Type::new(db, DefWithBodyId::from(self), mismatch.actual.clone()),
 +                }
 +                .into(),
 +            );
 +        }
 +
 +        for expr in hir_ty::diagnostics::missing_unsafe(db, self.into()) {
 +            match source_map.expr_syntax(expr) {
 +                Ok(expr) => acc.push(MissingUnsafe { expr }.into()),
 +                Err(SyntheticSyntax) => {
 +                    // FIXME: Here and eslwhere in this file, the `expr` was
 +                    // desugared, report or assert that this doesn't happen.
 +                }
 +            }
 +        }
 +
 +        for diagnostic in BodyValidationDiagnostic::collect(db, self.into()) {
 +            match diagnostic {
 +                BodyValidationDiagnostic::RecordMissingFields {
 +                    record,
 +                    variant,
 +                    missed_fields,
 +                } => {
 +                    let variant_data = variant.variant_data(db.upcast());
 +                    let missed_fields = missed_fields
 +                        .into_iter()
 +                        .map(|idx| variant_data.fields()[idx].name.clone())
 +                        .collect();
 +
 +                    match record {
 +                        Either::Left(record_expr) => match source_map.expr_syntax(record_expr) {
 +                            Ok(source_ptr) => {
 +                                let root = source_ptr.file_syntax(db.upcast());
 +                                if let ast::Expr::RecordExpr(record_expr) =
 +                                    &source_ptr.value.to_node(&root)
 +                                {
 +                                    if record_expr.record_expr_field_list().is_some() {
 +                                        acc.push(
 +                                            MissingFields {
 +                                                file: source_ptr.file_id,
 +                                                field_list_parent: Either::Left(AstPtr::new(
 +                                                    record_expr,
 +                                                )),
 +                                                field_list_parent_path: record_expr
 +                                                    .path()
 +                                                    .map(|path| AstPtr::new(&path)),
 +                                                missed_fields,
 +                                            }
 +                                            .into(),
 +                                        )
 +                                    }
 +                                }
 +                            }
 +                            Err(SyntheticSyntax) => (),
 +                        },
 +                        Either::Right(record_pat) => match source_map.pat_syntax(record_pat) {
 +                            Ok(source_ptr) => {
 +                                if let Some(expr) = source_ptr.value.as_ref().left() {
 +                                    let root = source_ptr.file_syntax(db.upcast());
 +                                    if let ast::Pat::RecordPat(record_pat) = expr.to_node(&root) {
 +                                        if record_pat.record_pat_field_list().is_some() {
 +                                            acc.push(
 +                                                MissingFields {
 +                                                    file: source_ptr.file_id,
 +                                                    field_list_parent: Either::Right(AstPtr::new(
 +                                                        &record_pat,
 +                                                    )),
 +                                                    field_list_parent_path: record_pat
 +                                                        .path()
 +                                                        .map(|path| AstPtr::new(&path)),
 +                                                    missed_fields,
 +                                                }
 +                                                .into(),
 +                                            )
 +                                        }
 +                                    }
 +                                }
 +                            }
 +                            Err(SyntheticSyntax) => (),
 +                        },
 +                    }
 +                }
 +                BodyValidationDiagnostic::ReplaceFilterMapNextWithFindMap { method_call_expr } => {
 +                    if let Ok(next_source_ptr) = source_map.expr_syntax(method_call_expr) {
 +                        acc.push(
 +                            ReplaceFilterMapNextWithFindMap {
 +                                file: next_source_ptr.file_id,
 +                                next_expr: next_source_ptr.value,
 +                            }
 +                            .into(),
 +                        );
 +                    }
 +                }
 +                BodyValidationDiagnostic::MissingMatchArms { match_expr, uncovered_patterns } => {
 +                    match source_map.expr_syntax(match_expr) {
 +                        Ok(source_ptr) => {
 +                            let root = source_ptr.file_syntax(db.upcast());
 +                            if let ast::Expr::MatchExpr(match_expr) =
 +                                &source_ptr.value.to_node(&root)
 +                            {
 +                                if let Some(match_expr) = match_expr.expr() {
 +                                    acc.push(
 +                                        MissingMatchArms {
 +                                            file: source_ptr.file_id,
 +                                            match_expr: AstPtr::new(&match_expr),
 +                                            uncovered_patterns,
 +                                        }
 +                                        .into(),
 +                                    );
 +                                }
 +                            }
 +                        }
 +                        Err(SyntheticSyntax) => (),
 +                    }
 +                }
 +            }
 +        }
 +
 +        let def: ModuleDef = match self {
 +            DefWithBody::Function(it) => it.into(),
 +            DefWithBody::Static(it) => it.into(),
 +            DefWithBody::Const(it) => it.into(),
 +            DefWithBody::Variant(it) => it.into(),
 +        };
 +        for diag in hir_ty::diagnostics::incorrect_case(db, krate, def.into()) {
 +            acc.push(diag.into())
 +        }
 +    }
 +}
 +
 +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 +pub struct Function {
 +    pub(crate) id: FunctionId,
 +}
 +
 +impl Function {
 +    pub fn module(self, db: &dyn HirDatabase) -> Module {
 +        self.id.lookup(db.upcast()).module(db.upcast()).into()
 +    }
 +
 +    pub fn name(self, db: &dyn HirDatabase) -> Name {
 +        db.function_data(self.id).name.clone()
 +    }
 +
 +    /// Get this function's return type
 +    pub fn ret_type(self, db: &dyn HirDatabase) -> Type {
 +        let resolver = self.id.resolver(db.upcast());
 +        let substs = TyBuilder::placeholder_subst(db, self.id);
 +        let callable_sig = db.callable_item_signature(self.id.into()).substitute(Interner, &substs);
 +        let ty = callable_sig.ret().clone();
 +        Type::new_with_resolver_inner(db, &resolver, ty)
 +    }
 +
 +    pub fn async_ret_type(self, db: &dyn HirDatabase) -> Option<Type> {
 +        if !self.is_async(db) {
 +            return None;
 +        }
 +        let resolver = self.id.resolver(db.upcast());
 +        let substs = TyBuilder::placeholder_subst(db, self.id);
 +        let callable_sig = db.callable_item_signature(self.id.into()).substitute(Interner, &substs);
 +        let ret_ty = callable_sig.ret().clone();
 +        for pred in ret_ty.impl_trait_bounds(db).into_iter().flatten() {
 +            if let WhereClause::AliasEq(output_eq) = pred.into_value_and_skipped_binders().0 {
 +                return Type::new_with_resolver_inner(db, &resolver, output_eq.ty).into();
 +            }
 +        }
 +        never!("Async fn ret_type should be impl Future");
 +        None
 +    }
 +
 +    pub fn has_self_param(self, db: &dyn HirDatabase) -> bool {
 +        db.function_data(self.id).has_self_param()
 +    }
 +
 +    pub fn self_param(self, db: &dyn HirDatabase) -> Option<SelfParam> {
 +        self.has_self_param(db).then(|| SelfParam { func: self.id })
 +    }
 +
 +    pub fn assoc_fn_params(self, db: &dyn HirDatabase) -> Vec<Param> {
 +        let environment = db.trait_environment(self.id.into());
 +        let substs = TyBuilder::placeholder_subst(db, self.id);
 +        let callable_sig = db.callable_item_signature(self.id.into()).substitute(Interner, &substs);
 +        callable_sig
 +            .params()
 +            .iter()
 +            .enumerate()
 +            .map(|(idx, ty)| {
 +                let ty = Type { env: environment.clone(), ty: ty.clone() };
 +                Param { func: self, ty, idx }
 +            })
 +            .collect()
 +    }
 +
 +    pub fn method_params(self, db: &dyn HirDatabase) -> Option<Vec<Param>> {
 +        if self.self_param(db).is_none() {
 +            return None;
 +        }
 +        Some(self.params_without_self(db))
 +    }
 +
 +    pub fn params_without_self(self, db: &dyn HirDatabase) -> Vec<Param> {
 +        let environment = db.trait_environment(self.id.into());
 +        let substs = TyBuilder::placeholder_subst(db, self.id);
 +        let callable_sig = db.callable_item_signature(self.id.into()).substitute(Interner, &substs);
 +        let skip = if db.function_data(self.id).has_self_param() { 1 } else { 0 };
 +        callable_sig
 +            .params()
 +            .iter()
 +            .enumerate()
 +            .skip(skip)
 +            .map(|(idx, ty)| {
 +                let ty = Type { env: environment.clone(), ty: ty.clone() };
 +                Param { func: self, ty, idx }
 +            })
 +            .collect()
 +    }
 +
 +    pub fn is_const(self, db: &dyn HirDatabase) -> bool {
 +        db.function_data(self.id).has_const_kw()
 +    }
 +
 +    pub fn is_async(self, db: &dyn HirDatabase) -> bool {
 +        db.function_data(self.id).has_async_kw()
 +    }
 +
 +    pub fn is_unsafe_to_call(self, db: &dyn HirDatabase) -> bool {
 +        hir_ty::is_fn_unsafe_to_call(db, self.id)
 +    }
 +
 +    /// Whether this function declaration has a definition.
 +    ///
 +    /// This is false in the case of required (not provided) trait methods.
 +    pub fn has_body(self, db: &dyn HirDatabase) -> bool {
 +        db.function_data(self.id).has_body()
 +    }
 +
 +    pub fn as_proc_macro(self, db: &dyn HirDatabase) -> Option<Macro> {
 +        let function_data = db.function_data(self.id);
 +        let attrs = &function_data.attrs;
 +        // FIXME: Store this in FunctionData flags?
 +        if !(attrs.is_proc_macro()
 +            || attrs.is_proc_macro_attribute()
 +            || attrs.is_proc_macro_derive())
 +        {
 +            return None;
 +        }
 +        let loc = self.id.lookup(db.upcast());
 +        let def_map = db.crate_def_map(loc.krate(db).into());
 +        def_map.fn_as_proc_macro(self.id).map(|id| Macro { id: id.into() })
 +    }
 +}
 +
 +// Note: logically, this belongs to `hir_ty`, but we are not using it there yet.
 +#[derive(Clone, Copy, PartialEq, Eq)]
 +pub enum Access {
 +    Shared,
 +    Exclusive,
 +    Owned,
 +}
 +
 +impl From<hir_ty::Mutability> for Access {
 +    fn from(mutability: hir_ty::Mutability) -> Access {
 +        match mutability {
 +            hir_ty::Mutability::Not => Access::Shared,
 +            hir_ty::Mutability::Mut => Access::Exclusive,
 +        }
 +    }
 +}
 +
 +#[derive(Clone, Debug)]
 +pub struct Param {
 +    func: Function,
 +    /// The index in parameter list, including self parameter.
 +    idx: usize,
 +    ty: Type,
 +}
 +
 +impl Param {
 +    pub fn ty(&self) -> &Type {
 +        &self.ty
 +    }
 +
 +    pub fn name(&self, db: &dyn HirDatabase) -> Option<Name> {
 +        db.function_data(self.func.id).params[self.idx].0.clone()
 +    }
 +
 +    pub fn as_local(&self, db: &dyn HirDatabase) -> Option<Local> {
 +        let parent = DefWithBodyId::FunctionId(self.func.into());
 +        let body = db.body(parent);
 +        let pat_id = body.params[self.idx];
 +        if let Pat::Bind { .. } = &body[pat_id] {
 +            Some(Local { parent, pat_id: body.params[self.idx] })
 +        } else {
 +            None
 +        }
 +    }
 +
 +    pub fn pattern_source(&self, db: &dyn HirDatabase) -> Option<ast::Pat> {
 +        self.source(db).and_then(|p| p.value.pat())
 +    }
 +
 +    pub fn source(&self, db: &dyn HirDatabase) -> Option<InFile<ast::Param>> {
 +        let InFile { file_id, value } = self.func.source(db)?;
 +        let params = value.param_list()?;
 +        if params.self_param().is_some() {
 +            params.params().nth(self.idx.checked_sub(1)?)
 +        } else {
 +            params.params().nth(self.idx)
 +        }
 +        .map(|value| InFile { file_id, value })
 +    }
 +}
 +
 +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 +pub struct SelfParam {
 +    func: FunctionId,
 +}
 +
 +impl SelfParam {
 +    pub fn access(self, db: &dyn HirDatabase) -> Access {
 +        let func_data = db.function_data(self.func);
 +        func_data
 +            .params
 +            .first()
 +            .map(|(_, param)| match &**param {
 +                TypeRef::Reference(.., mutability) => match mutability {
 +                    hir_def::type_ref::Mutability::Shared => Access::Shared,
 +                    hir_def::type_ref::Mutability::Mut => Access::Exclusive,
 +                },
 +                _ => Access::Owned,
 +            })
 +            .unwrap_or(Access::Owned)
 +    }
 +
 +    pub fn display(self, db: &dyn HirDatabase) -> &'static str {
 +        match self.access(db) {
 +            Access::Shared => "&self",
 +            Access::Exclusive => "&mut self",
 +            Access::Owned => "self",
 +        }
 +    }
 +
 +    pub fn source(&self, db: &dyn HirDatabase) -> Option<InFile<ast::SelfParam>> {
 +        let InFile { file_id, value } = Function::from(self.func).source(db)?;
 +        value
 +            .param_list()
 +            .and_then(|params| params.self_param())
 +            .map(|value| InFile { file_id, value })
 +    }
 +
 +    pub fn ty(&self, db: &dyn HirDatabase) -> Type {
 +        let substs = TyBuilder::placeholder_subst(db, self.func);
 +        let callable_sig =
 +            db.callable_item_signature(self.func.into()).substitute(Interner, &substs);
 +        let environment = db.trait_environment(self.func.into());
 +        let ty = callable_sig.params()[0].clone();
 +        Type { env: environment, ty }
 +    }
 +}
 +
 +impl HasVisibility for Function {
 +    fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
 +        db.function_visibility(self.id)
 +    }
 +}
 +
 +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 +pub struct Const {
 +    pub(crate) id: ConstId,
 +}
 +
 +impl Const {
 +    pub fn module(self, db: &dyn HirDatabase) -> Module {
 +        Module { id: self.id.lookup(db.upcast()).module(db.upcast()) }
 +    }
 +
 +    pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
 +        db.const_data(self.id).name.clone()
 +    }
 +
 +    pub fn value(self, db: &dyn HirDatabase) -> Option<ast::Expr> {
 +        self.source(db)?.value.body()
 +    }
 +
 +    pub fn ty(self, db: &dyn HirDatabase) -> Type {
 +        let data = db.const_data(self.id);
 +        let resolver = self.id.resolver(db.upcast());
 +        let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
 +        let ty = ctx.lower_ty(&data.type_ref);
 +        Type::new_with_resolver_inner(db, &resolver, ty)
 +    }
 +
 +    pub fn eval(self, db: &dyn HirDatabase) -> Result<ComputedExpr, ConstEvalError> {
 +        db.const_eval(self.id)
 +    }
 +}
 +
 +impl HasVisibility for Const {
 +    fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
 +        db.const_visibility(self.id)
 +    }
 +}
 +
 +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 +pub struct Static {
 +    pub(crate) id: StaticId,
 +}
 +
 +impl Static {
 +    pub fn module(self, db: &dyn HirDatabase) -> Module {
 +        Module { id: self.id.lookup(db.upcast()).module(db.upcast()) }
 +    }
 +
 +    pub fn name(self, db: &dyn HirDatabase) -> Name {
 +        db.static_data(self.id).name.clone()
 +    }
 +
 +    pub fn is_mut(self, db: &dyn HirDatabase) -> bool {
 +        db.static_data(self.id).mutable
 +    }
 +
 +    pub fn value(self, db: &dyn HirDatabase) -> Option<ast::Expr> {
 +        self.source(db)?.value.body()
 +    }
 +
 +    pub fn ty(self, db: &dyn HirDatabase) -> Type {
 +        let data = db.static_data(self.id);
 +        let resolver = self.id.resolver(db.upcast());
 +        let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
 +        let ty = ctx.lower_ty(&data.type_ref);
 +        Type::new_with_resolver_inner(db, &resolver, ty)
 +    }
 +}
 +
 +impl HasVisibility for Static {
 +    fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
 +        db.static_data(self.id).visibility.resolve(db.upcast(), &self.id.resolver(db.upcast()))
 +    }
 +}
 +
 +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 +pub struct Trait {
 +    pub(crate) id: TraitId,
 +}
 +
 +impl Trait {
 +    pub fn lang(db: &dyn HirDatabase, krate: Crate, name: &Name) -> Option<Trait> {
 +        db.lang_item(krate.into(), name.to_smol_str())
 +            .and_then(LangItemTarget::as_trait)
 +            .map(Into::into)
 +    }
 +
 +    pub fn module(self, db: &dyn HirDatabase) -> Module {
 +        Module { id: self.id.lookup(db.upcast()).container }
 +    }
 +
 +    pub fn name(self, db: &dyn HirDatabase) -> Name {
 +        db.trait_data(self.id).name.clone()
 +    }
 +
 +    pub fn items(self, db: &dyn HirDatabase) -> Vec<AssocItem> {
 +        db.trait_data(self.id).items.iter().map(|(_name, it)| (*it).into()).collect()
 +    }
 +
 +    pub fn items_with_supertraits(self, db: &dyn HirDatabase) -> Vec<AssocItem> {
 +        let traits = all_super_traits(db.upcast(), self.into());
 +        traits.iter().flat_map(|tr| Trait::from(*tr).items(db)).collect()
 +    }
 +
 +    pub fn is_auto(self, db: &dyn HirDatabase) -> bool {
 +        db.trait_data(self.id).is_auto
 +    }
 +
 +    pub fn is_unsafe(&self, db: &dyn HirDatabase) -> bool {
 +        db.trait_data(self.id).is_unsafe
 +    }
 +
 +    pub fn type_or_const_param_count(
 +        &self,
 +        db: &dyn HirDatabase,
 +        count_required_only: bool,
 +    ) -> usize {
 +        db.generic_params(GenericDefId::from(self.id))
 +            .type_or_consts
 +            .iter()
 +            .filter(|(_, ty)| match ty {
 +                TypeOrConstParamData::TypeParamData(ty)
 +                    if ty.provenance != TypeParamProvenance::TypeParamList =>
 +                {
 +                    false
 +                }
 +                _ => true,
 +            })
 +            .filter(|(_, ty)| !count_required_only || !ty.has_default())
 +            .count()
 +    }
 +}
 +
 +impl HasVisibility for Trait {
 +    fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
 +        db.trait_data(self.id).visibility.resolve(db.upcast(), &self.id.resolver(db.upcast()))
 +    }
 +}
 +
 +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 +pub struct TypeAlias {
 +    pub(crate) id: TypeAliasId,
 +}
 +
 +impl TypeAlias {
 +    pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool {
 +        let subst = db.generic_defaults(self.id.into());
 +        subst.iter().any(|ty| match ty.skip_binders().data(Interner) {
 +            GenericArgData::Ty(x) => x.is_unknown(),
 +            _ => false,
 +        })
 +    }
 +
 +    pub fn module(self, db: &dyn HirDatabase) -> Module {
 +        Module { id: self.id.lookup(db.upcast()).module(db.upcast()) }
 +    }
 +
 +    pub fn type_ref(self, db: &dyn HirDatabase) -> Option<TypeRef> {
 +        db.type_alias_data(self.id).type_ref.as_deref().cloned()
 +    }
 +
 +    pub fn ty(self, db: &dyn HirDatabase) -> Type {
 +        Type::from_def(db, self.id)
 +    }
 +
 +    pub fn name(self, db: &dyn HirDatabase) -> Name {
 +        db.type_alias_data(self.id).name.clone()
 +    }
 +}
 +
 +impl HasVisibility for TypeAlias {
 +    fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
 +        let function_data = db.type_alias_data(self.id);
 +        let visibility = &function_data.visibility;
 +        visibility.resolve(db.upcast(), &self.id.resolver(db.upcast()))
 +    }
 +}
 +
 +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 +pub struct BuiltinType {
 +    pub(crate) inner: hir_def::builtin_type::BuiltinType,
 +}
 +
 +impl BuiltinType {
 +    pub fn str() -> BuiltinType {
 +        BuiltinType { inner: hir_def::builtin_type::BuiltinType::Str }
 +    }
 +
 +    pub fn ty(self, db: &dyn HirDatabase) -> Type {
 +        Type::new_for_crate(db.crate_graph().iter().next().unwrap(), TyBuilder::builtin(self.inner))
 +    }
 +
 +    pub fn name(self) -> Name {
 +        self.inner.as_name()
 +    }
 +
 +    pub fn is_int(&self) -> bool {
 +        matches!(self.inner, hir_def::builtin_type::BuiltinType::Int(_))
 +    }
 +
 +    pub fn is_uint(&self) -> bool {
 +        matches!(self.inner, hir_def::builtin_type::BuiltinType::Uint(_))
 +    }
 +
 +    pub fn is_float(&self) -> bool {
 +        matches!(self.inner, hir_def::builtin_type::BuiltinType::Float(_))
 +    }
 +
 +    pub fn is_char(&self) -> bool {
 +        matches!(self.inner, hir_def::builtin_type::BuiltinType::Char)
 +    }
 +
 +    pub fn is_bool(&self) -> bool {
 +        matches!(self.inner, hir_def::builtin_type::BuiltinType::Bool)
 +    }
 +
 +    pub fn is_str(&self) -> bool {
 +        matches!(self.inner, hir_def::builtin_type::BuiltinType::Str)
 +    }
 +}
 +
 +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 +pub enum MacroKind {
 +    /// `macro_rules!` or Macros 2.0 macro.
 +    Declarative,
 +    /// A built-in or custom derive.
 +    Derive,
 +    /// A built-in function-like macro.
 +    BuiltIn,
 +    /// A procedural attribute macro.
 +    Attr,
 +    /// A function-like procedural macro.
 +    ProcMacro,
 +}
 +
 +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 +pub struct Macro {
 +    pub(crate) id: MacroId,
 +}
 +
 +impl Macro {
 +    pub fn module(self, db: &dyn HirDatabase) -> Module {
 +        Module { id: self.id.module(db.upcast()) }
 +    }
 +
 +    pub fn name(self, db: &dyn HirDatabase) -> Name {
 +        match self.id {
 +            MacroId::Macro2Id(id) => db.macro2_data(id).name.clone(),
 +            MacroId::MacroRulesId(id) => db.macro_rules_data(id).name.clone(),
 +            MacroId::ProcMacroId(id) => db.proc_macro_data(id).name.clone(),
 +        }
 +    }
 +
 +    pub fn is_macro_export(self, db: &dyn HirDatabase) -> bool {
 +        matches!(self.id, MacroId::MacroRulesId(id) if db.macro_rules_data(id).macro_export)
 +    }
 +
 +    pub fn kind(&self, db: &dyn HirDatabase) -> MacroKind {
 +        match self.id {
 +            MacroId::Macro2Id(it) => match it.lookup(db.upcast()).expander {
 +                MacroExpander::Declarative => MacroKind::Declarative,
 +                MacroExpander::BuiltIn(_) | MacroExpander::BuiltInEager(_) => MacroKind::BuiltIn,
 +                MacroExpander::BuiltInAttr(_) => MacroKind::Attr,
 +                MacroExpander::BuiltInDerive(_) => MacroKind::Derive,
 +            },
 +            MacroId::MacroRulesId(it) => match it.lookup(db.upcast()).expander {
 +                MacroExpander::Declarative => MacroKind::Declarative,
 +                MacroExpander::BuiltIn(_) | MacroExpander::BuiltInEager(_) => MacroKind::BuiltIn,
 +                MacroExpander::BuiltInAttr(_) => MacroKind::Attr,
 +                MacroExpander::BuiltInDerive(_) => MacroKind::Derive,
 +            },
 +            MacroId::ProcMacroId(it) => match it.lookup(db.upcast()).kind {
 +                ProcMacroKind::CustomDerive => MacroKind::Derive,
 +                ProcMacroKind::FuncLike => MacroKind::ProcMacro,
 +                ProcMacroKind::Attr => MacroKind::Attr,
 +            },
 +        }
 +    }
 +
 +    pub fn is_fn_like(&self, db: &dyn HirDatabase) -> bool {
 +        match self.kind(db) {
 +            MacroKind::Declarative | MacroKind::BuiltIn | MacroKind::ProcMacro => true,
 +            MacroKind::Attr | MacroKind::Derive => false,
 +        }
 +    }
 +
 +    pub fn is_builtin_derive(&self, db: &dyn HirDatabase) -> bool {
 +        match self.id {
 +            MacroId::Macro2Id(it) => {
 +                matches!(it.lookup(db.upcast()).expander, MacroExpander::BuiltInDerive(_))
 +            }
 +            MacroId::MacroRulesId(it) => {
 +                matches!(it.lookup(db.upcast()).expander, MacroExpander::BuiltInDerive(_))
 +            }
 +            MacroId::ProcMacroId(_) => false,
 +        }
 +    }
 +
 +    pub fn is_attr(&self, db: &dyn HirDatabase) -> bool {
 +        matches!(self.kind(db), MacroKind::Attr)
 +    }
 +
 +    pub fn is_derive(&self, db: &dyn HirDatabase) -> bool {
 +        matches!(self.kind(db), MacroKind::Derive)
 +    }
 +}
 +
 +impl HasVisibility for Macro {
 +    fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
 +        match self.id {
 +            MacroId::Macro2Id(id) => {
 +                let data = db.macro2_data(id);
 +                let visibility = &data.visibility;
 +                visibility.resolve(db.upcast(), &self.id.resolver(db.upcast()))
 +            }
 +            MacroId::MacroRulesId(_) => Visibility::Public,
 +            MacroId::ProcMacroId(_) => Visibility::Public,
 +        }
 +    }
 +}
 +
 +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
 +pub enum ItemInNs {
 +    Types(ModuleDef),
 +    Values(ModuleDef),
 +    Macros(Macro),
 +}
 +
 +impl From<Macro> for ItemInNs {
 +    fn from(it: Macro) -> Self {
 +        Self::Macros(it)
 +    }
 +}
 +
 +impl From<ModuleDef> for ItemInNs {
 +    fn from(module_def: ModuleDef) -> Self {
 +        match module_def {
 +            ModuleDef::Static(_) | ModuleDef::Const(_) | ModuleDef::Function(_) => {
 +                ItemInNs::Values(module_def)
 +            }
 +            _ => ItemInNs::Types(module_def),
 +        }
 +    }
 +}
 +
 +impl ItemInNs {
 +    pub fn as_module_def(self) -> Option<ModuleDef> {
 +        match self {
 +            ItemInNs::Types(id) | ItemInNs::Values(id) => Some(id),
 +            ItemInNs::Macros(_) => None,
 +        }
 +    }
 +
 +    /// Returns the crate defining this item (or `None` if `self` is built-in).
 +    pub fn krate(&self, db: &dyn HirDatabase) -> Option<Crate> {
 +        match self {
 +            ItemInNs::Types(did) | ItemInNs::Values(did) => did.module(db).map(|m| m.krate()),
 +            ItemInNs::Macros(id) => Some(id.module(db).krate()),
 +        }
 +    }
 +
 +    pub fn attrs(&self, db: &dyn HirDatabase) -> Option<AttrsWithOwner> {
 +        match self {
 +            ItemInNs::Types(it) | ItemInNs::Values(it) => it.attrs(db),
 +            ItemInNs::Macros(it) => Some(it.attrs(db)),
 +        }
 +    }
 +}
 +
 +/// Invariant: `inner.as_assoc_item(db).is_some()`
 +/// We do not actively enforce this invariant.
 +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
 +pub enum AssocItem {
 +    Function(Function),
 +    Const(Const),
 +    TypeAlias(TypeAlias),
 +}
 +#[derive(Debug)]
 +pub enum AssocItemContainer {
 +    Trait(Trait),
 +    Impl(Impl),
 +}
 +pub trait AsAssocItem {
 +    fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem>;
 +}
 +
 +impl AsAssocItem for Function {
 +    fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
 +        as_assoc_item(db, AssocItem::Function, self.id)
 +    }
 +}
 +impl AsAssocItem for Const {
 +    fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
 +        as_assoc_item(db, AssocItem::Const, self.id)
 +    }
 +}
 +impl AsAssocItem for TypeAlias {
 +    fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
 +        as_assoc_item(db, AssocItem::TypeAlias, self.id)
 +    }
 +}
 +impl AsAssocItem for ModuleDef {
 +    fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
 +        match self {
 +            ModuleDef::Function(it) => it.as_assoc_item(db),
 +            ModuleDef::Const(it) => it.as_assoc_item(db),
 +            ModuleDef::TypeAlias(it) => it.as_assoc_item(db),
 +            _ => None,
 +        }
 +    }
 +}
 +fn as_assoc_item<ID, DEF, CTOR, AST>(db: &dyn HirDatabase, ctor: CTOR, id: ID) -> Option<AssocItem>
 +where
 +    ID: Lookup<Data = AssocItemLoc<AST>>,
 +    DEF: From<ID>,
 +    CTOR: FnOnce(DEF) -> AssocItem,
 +    AST: ItemTreeNode,
 +{
 +    match id.lookup(db.upcast()).container {
 +        ItemContainerId::TraitId(_) | ItemContainerId::ImplId(_) => Some(ctor(DEF::from(id))),
 +        ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => None,
 +    }
 +}
 +
 +impl AssocItem {
 +    pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
 +        match self {
 +            AssocItem::Function(it) => Some(it.name(db)),
 +            AssocItem::Const(it) => it.name(db),
 +            AssocItem::TypeAlias(it) => Some(it.name(db)),
 +        }
 +    }
 +    pub fn module(self, db: &dyn HirDatabase) -> Module {
 +        match self {
 +            AssocItem::Function(f) => f.module(db),
 +            AssocItem::Const(c) => c.module(db),
 +            AssocItem::TypeAlias(t) => t.module(db),
 +        }
 +    }
 +    pub fn container(self, db: &dyn HirDatabase) -> AssocItemContainer {
 +        let container = match self {
 +            AssocItem::Function(it) => it.id.lookup(db.upcast()).container,
 +            AssocItem::Const(it) => it.id.lookup(db.upcast()).container,
 +            AssocItem::TypeAlias(it) => it.id.lookup(db.upcast()).container,
 +        };
 +        match container {
 +            ItemContainerId::TraitId(id) => AssocItemContainer::Trait(id.into()),
 +            ItemContainerId::ImplId(id) => AssocItemContainer::Impl(id.into()),
 +            ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => {
 +                panic!("invalid AssocItem")
 +            }
 +        }
 +    }
 +
 +    pub fn containing_trait(self, db: &dyn HirDatabase) -> Option<Trait> {
 +        match self.container(db) {
 +            AssocItemContainer::Trait(t) => Some(t),
 +            _ => None,
 +        }
 +    }
 +
 +    pub fn containing_trait_impl(self, db: &dyn HirDatabase) -> Option<Trait> {
 +        match self.container(db) {
 +            AssocItemContainer::Impl(i) => i.trait_(db),
 +            _ => None,
 +        }
 +    }
 +
 +    pub fn containing_trait_or_trait_impl(self, db: &dyn HirDatabase) -> Option<Trait> {
 +        match self.container(db) {
 +            AssocItemContainer::Trait(t) => Some(t),
 +            AssocItemContainer::Impl(i) => i.trait_(db),
 +        }
 +    }
 +}
 +
 +impl HasVisibility for AssocItem {
 +    fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
 +        match self {
 +            AssocItem::Function(f) => f.visibility(db),
 +            AssocItem::Const(c) => c.visibility(db),
 +            AssocItem::TypeAlias(t) => t.visibility(db),
 +        }
 +    }
 +}
 +
 +impl From<AssocItem> for ModuleDef {
 +    fn from(assoc: AssocItem) -> Self {
 +        match assoc {
 +            AssocItem::Function(it) => ModuleDef::Function(it),
 +            AssocItem::Const(it) => ModuleDef::Const(it),
 +            AssocItem::TypeAlias(it) => ModuleDef::TypeAlias(it),
 +        }
 +    }
 +}
 +
 +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
 +pub enum GenericDef {
 +    Function(Function),
 +    Adt(Adt),
 +    Trait(Trait),
 +    TypeAlias(TypeAlias),
 +    Impl(Impl),
 +    // enum variants cannot have generics themselves, but their parent enums
 +    // can, and this makes some code easier to write
 +    Variant(Variant),
 +    // consts can have type parameters from their parents (i.e. associated consts of traits)
 +    Const(Const),
 +}
 +impl_from!(
 +    Function,
 +    Adt(Struct, Enum, Union),
 +    Trait,
 +    TypeAlias,
 +    Impl,
 +    Variant,
 +    Const
 +    for GenericDef
 +);
 +
 +impl GenericDef {
 +    pub fn params(self, db: &dyn HirDatabase) -> Vec<GenericParam> {
 +        let generics = db.generic_params(self.into());
 +        let ty_params = generics.type_or_consts.iter().map(|(local_id, _)| {
 +            let toc = TypeOrConstParam { id: TypeOrConstParamId { parent: self.into(), local_id } };
 +            match toc.split(db) {
 +                Either::Left(x) => GenericParam::ConstParam(x),
 +                Either::Right(x) => GenericParam::TypeParam(x),
 +            }
 +        });
 +        let lt_params = generics
 +            .lifetimes
 +            .iter()
 +            .map(|(local_id, _)| LifetimeParam {
 +                id: LifetimeParamId { parent: self.into(), local_id },
 +            })
 +            .map(GenericParam::LifetimeParam);
 +        lt_params.chain(ty_params).collect()
 +    }
 +
 +    pub fn type_params(self, db: &dyn HirDatabase) -> Vec<TypeOrConstParam> {
 +        let generics = db.generic_params(self.into());
 +        generics
 +            .type_or_consts
 +            .iter()
 +            .map(|(local_id, _)| TypeOrConstParam {
 +                id: TypeOrConstParamId { parent: self.into(), local_id },
 +            })
 +            .collect()
 +    }
 +}
 +
 +/// A single local definition.
 +///
 +/// If the definition of this is part of a "MultiLocal", that is a local that has multiple declarations due to or-patterns
 +/// then this only references a single one of those.
 +/// To retrieve the other locals you should use [`Local::associated_locals`]
 +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 +pub struct Local {
 +    pub(crate) parent: DefWithBodyId,
 +    pub(crate) pat_id: PatId,
 +}
 +
 +impl Local {
 +    pub fn is_param(self, db: &dyn HirDatabase) -> bool {
 +        let src = self.source(db);
 +        match src.value {
 +            Either::Left(pat) => pat
 +                .syntax()
 +                .ancestors()
 +                .map(|it| it.kind())
 +                .take_while(|&kind| ast::Pat::can_cast(kind) || ast::Param::can_cast(kind))
 +                .any(ast::Param::can_cast),
 +            Either::Right(_) => true,
 +        }
 +    }
 +
 +    pub fn as_self_param(self, db: &dyn HirDatabase) -> Option<SelfParam> {
 +        match self.parent {
 +            DefWithBodyId::FunctionId(func) if self.is_self(db) => Some(SelfParam { func }),
 +            _ => None,
 +        }
 +    }
 +
 +    pub fn name(self, db: &dyn HirDatabase) -> Name {
 +        let body = db.body(self.parent);
 +        match &body[self.pat_id] {
 +            Pat::Bind { name, .. } => name.clone(),
 +            _ => {
 +                stdx::never!("hir::Local is missing a name!");
 +                Name::missing()
 +            }
 +        }
 +    }
 +
 +    pub fn is_self(self, db: &dyn HirDatabase) -> bool {
 +        self.name(db) == name![self]
 +    }
 +
 +    pub fn is_mut(self, db: &dyn HirDatabase) -> bool {
 +        let body = db.body(self.parent);
 +        matches!(&body[self.pat_id], Pat::Bind { mode: BindingAnnotation::Mutable, .. })
 +    }
 +
 +    pub fn is_ref(self, db: &dyn HirDatabase) -> bool {
 +        let body = db.body(self.parent);
 +        matches!(
 +            &body[self.pat_id],
 +            Pat::Bind { mode: BindingAnnotation::Ref | BindingAnnotation::RefMut, .. }
 +        )
 +    }
 +
 +    pub fn parent(self, _db: &dyn HirDatabase) -> DefWithBody {
 +        self.parent.into()
 +    }
 +
 +    pub fn module(self, db: &dyn HirDatabase) -> Module {
 +        self.parent(db).module(db)
 +    }
 +
 +    pub fn ty(self, db: &dyn HirDatabase) -> Type {
 +        let def = self.parent;
 +        let infer = db.infer(def);
 +        let ty = infer[self.pat_id].clone();
 +        Type::new(db, def, ty)
 +    }
 +
 +    pub fn associated_locals(self, db: &dyn HirDatabase) -> Box<[Local]> {
 +        let body = db.body(self.parent);
 +        body.ident_patterns_for(&self.pat_id)
 +            .iter()
 +            .map(|&pat_id| Local { parent: self.parent, pat_id })
 +            .collect()
 +    }
 +
 +    /// If this local is part of a multi-local, retrieve the representative local.
 +    /// That is the local that references are being resolved to.
 +    pub fn representative(self, db: &dyn HirDatabase) -> Local {
 +        let body = db.body(self.parent);
 +        Local { pat_id: body.pattern_representative(self.pat_id), ..self }
 +    }
 +
 +    pub fn source(self, db: &dyn HirDatabase) -> InFile<Either<ast::IdentPat, ast::SelfParam>> {
 +        let (_body, source_map) = db.body_with_source_map(self.parent);
 +        let src = source_map.pat_syntax(self.pat_id).unwrap(); // Hmm...
 +        let root = src.file_syntax(db.upcast());
 +        src.map(|ast| match ast {
 +            // Suspicious unwrap
 +            Either::Left(it) => Either::Left(it.cast().unwrap().to_node(&root)),
 +            Either::Right(it) => Either::Right(it.to_node(&root)),
 +        })
 +    }
 +}
 +
 +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 +pub struct DeriveHelper {
 +    pub(crate) derive: MacroId,
 +    pub(crate) idx: usize,
 +}
 +
 +impl DeriveHelper {
 +    pub fn derive(&self) -> Macro {
 +        Macro { id: self.derive.into() }
 +    }
 +
 +    pub fn name(&self, db: &dyn HirDatabase) -> Name {
 +        match self.derive {
 +            MacroId::Macro2Id(_) => None,
 +            MacroId::MacroRulesId(_) => None,
 +            MacroId::ProcMacroId(proc_macro) => db
 +                .proc_macro_data(proc_macro)
 +                .helpers
 +                .as_ref()
 +                .and_then(|it| it.get(self.idx))
 +                .cloned(),
 +        }
 +        .unwrap_or_else(|| Name::missing())
 +    }
 +}
 +
 +// FIXME: Wrong name? This is could also be a registered attribute
 +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 +pub struct BuiltinAttr {
 +    krate: Option<CrateId>,
 +    idx: usize,
 +}
 +
 +impl BuiltinAttr {
 +    // FIXME: consider crates\hir_def\src\nameres\attr_resolution.rs?
 +    pub(crate) fn by_name(db: &dyn HirDatabase, krate: Crate, name: &str) -> Option<Self> {
 +        if let builtin @ Some(_) = Self::builtin(name) {
 +            return builtin;
 +        }
 +        let idx = db.crate_def_map(krate.id).registered_attrs().iter().position(|it| it == name)?;
 +        Some(BuiltinAttr { krate: Some(krate.id), idx })
 +    }
 +
 +    fn builtin(name: &str) -> Option<Self> {
 +        hir_def::builtin_attr::INERT_ATTRIBUTES
 +            .iter()
 +            .position(|tool| tool.name == name)
 +            .map(|idx| BuiltinAttr { krate: None, idx })
 +    }
 +
 +    pub fn name(&self, db: &dyn HirDatabase) -> SmolStr {
 +        // FIXME: Return a `Name` here
 +        match self.krate {
 +            Some(krate) => db.crate_def_map(krate).registered_attrs()[self.idx].clone(),
 +            None => SmolStr::new(hir_def::builtin_attr::INERT_ATTRIBUTES[self.idx].name),
 +        }
 +    }
 +
 +    pub fn template(&self, _: &dyn HirDatabase) -> Option<AttributeTemplate> {
 +        match self.krate {
 +            Some(_) => None,
 +            None => Some(hir_def::builtin_attr::INERT_ATTRIBUTES[self.idx].template),
 +        }
 +    }
 +}
 +
 +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 +pub struct ToolModule {
 +    krate: Option<CrateId>,
 +    idx: usize,
 +}
 +
 +impl ToolModule {
 +    // FIXME: consider crates\hir_def\src\nameres\attr_resolution.rs?
 +    pub(crate) fn by_name(db: &dyn HirDatabase, krate: Crate, name: &str) -> Option<Self> {
 +        if let builtin @ Some(_) = Self::builtin(name) {
 +            return builtin;
 +        }
 +        let idx = db.crate_def_map(krate.id).registered_tools().iter().position(|it| it == name)?;
 +        Some(ToolModule { krate: Some(krate.id), idx })
 +    }
 +
 +    fn builtin(name: &str) -> Option<Self> {
 +        hir_def::builtin_attr::TOOL_MODULES
 +            .iter()
 +            .position(|&tool| tool == name)
 +            .map(|idx| ToolModule { krate: None, idx })
 +    }
 +
 +    pub fn name(&self, db: &dyn HirDatabase) -> SmolStr {
 +        // FIXME: Return a `Name` here
 +        match self.krate {
 +            Some(krate) => db.crate_def_map(krate).registered_tools()[self.idx].clone(),
 +            None => SmolStr::new(hir_def::builtin_attr::TOOL_MODULES[self.idx]),
 +        }
 +    }
 +}
 +
 +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 +pub struct Label {
 +    pub(crate) parent: DefWithBodyId,
 +    pub(crate) label_id: LabelId,
 +}
 +
 +impl Label {
 +    pub fn module(self, db: &dyn HirDatabase) -> Module {
 +        self.parent(db).module(db)
 +    }
 +
 +    pub fn parent(self, _db: &dyn HirDatabase) -> DefWithBody {
 +        self.parent.into()
 +    }
 +
 +    pub fn name(self, db: &dyn HirDatabase) -> Name {
 +        let body = db.body(self.parent);
 +        body[self.label_id].name.clone()
 +    }
 +
 +    pub fn source(self, db: &dyn HirDatabase) -> InFile<ast::Label> {
 +        let (_body, source_map) = db.body_with_source_map(self.parent);
 +        let src = source_map.label_syntax(self.label_id);
 +        let root = src.file_syntax(db.upcast());
 +        src.map(|ast| ast.to_node(&root))
 +    }
 +}
 +
 +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 +pub enum GenericParam {
 +    TypeParam(TypeParam),
 +    ConstParam(ConstParam),
 +    LifetimeParam(LifetimeParam),
 +}
 +impl_from!(TypeParam, ConstParam, LifetimeParam for GenericParam);
 +
 +impl GenericParam {
 +    pub fn module(self, db: &dyn HirDatabase) -> Module {
 +        match self {
 +            GenericParam::TypeParam(it) => it.module(db),
 +            GenericParam::ConstParam(it) => it.module(db),
 +            GenericParam::LifetimeParam(it) => it.module(db),
 +        }
 +    }
 +
 +    pub fn name(self, db: &dyn HirDatabase) -> Name {
 +        match self {
 +            GenericParam::TypeParam(it) => it.name(db),
 +            GenericParam::ConstParam(it) => it.name(db),
 +            GenericParam::LifetimeParam(it) => it.name(db),
 +        }
 +    }
 +}
 +
 +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 +pub struct TypeParam {
 +    pub(crate) id: TypeParamId,
 +}
 +
 +impl TypeParam {
 +    pub fn merge(self) -> TypeOrConstParam {
 +        TypeOrConstParam { id: self.id.into() }
 +    }
 +
 +    pub fn name(self, db: &dyn HirDatabase) -> Name {
 +        self.merge().name(db)
 +    }
 +
 +    pub fn module(self, db: &dyn HirDatabase) -> Module {
 +        self.id.parent().module(db.upcast()).into()
 +    }
 +
 +    /// Is this type parameter implicitly introduced (eg. `Self` in a trait or an `impl Trait`
 +    /// argument)?
 +    pub fn is_implicit(self, db: &dyn HirDatabase) -> bool {
 +        let params = db.generic_params(self.id.parent());
 +        let data = &params.type_or_consts[self.id.local_id()];
 +        match data.type_param().unwrap().provenance {
 +            hir_def::generics::TypeParamProvenance::TypeParamList => false,
 +            hir_def::generics::TypeParamProvenance::TraitSelf
 +            | hir_def::generics::TypeParamProvenance::ArgumentImplTrait => true,
 +        }
 +    }
 +
 +    pub fn ty(self, db: &dyn HirDatabase) -> Type {
 +        let resolver = self.id.parent().resolver(db.upcast());
 +        let ty =
 +            TyKind::Placeholder(hir_ty::to_placeholder_idx(db, self.id.into())).intern(Interner);
 +        Type::new_with_resolver_inner(db, &resolver, ty)
 +    }
 +
 +    /// FIXME: this only lists trait bounds from the item defining the type
 +    /// parameter, not additional bounds that might be added e.g. by a method if
 +    /// the parameter comes from an impl!
 +    pub fn trait_bounds(self, db: &dyn HirDatabase) -> Vec<Trait> {
 +        db.generic_predicates_for_param(self.id.parent(), self.id.into(), None)
 +            .iter()
 +            .filter_map(|pred| match &pred.skip_binders().skip_binders() {
 +                hir_ty::WhereClause::Implemented(trait_ref) => {
 +                    Some(Trait::from(trait_ref.hir_trait_id()))
 +                }
 +                _ => None,
 +            })
 +            .collect()
 +    }
 +
 +    pub fn default(self, db: &dyn HirDatabase) -> Option<Type> {
 +        let params = db.generic_defaults(self.id.parent());
 +        let local_idx = hir_ty::param_idx(db, self.id.into())?;
 +        let resolver = self.id.parent().resolver(db.upcast());
 +        let ty = params.get(local_idx)?.clone();
 +        let subst = TyBuilder::placeholder_subst(db, self.id.parent());
 +        let ty = ty.substitute(Interner, &subst);
 +        match ty.data(Interner) {
 +            GenericArgData::Ty(x) => Some(Type::new_with_resolver_inner(db, &resolver, x.clone())),
 +            _ => None,
 +        }
 +    }
 +}
 +
 +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 +pub struct LifetimeParam {
 +    pub(crate) id: LifetimeParamId,
 +}
 +
 +impl LifetimeParam {
 +    pub fn name(self, db: &dyn HirDatabase) -> Name {
 +        let params = db.generic_params(self.id.parent);
 +        params.lifetimes[self.id.local_id].name.clone()
 +    }
 +
 +    pub fn module(self, db: &dyn HirDatabase) -> Module {
 +        self.id.parent.module(db.upcast()).into()
 +    }
 +
 +    pub fn parent(self, _db: &dyn HirDatabase) -> GenericDef {
 +        self.id.parent.into()
 +    }
 +}
 +
 +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 +pub struct ConstParam {
 +    pub(crate) id: ConstParamId,
 +}
 +
 +impl ConstParam {
 +    pub fn merge(self) -> TypeOrConstParam {
 +        TypeOrConstParam { id: self.id.into() }
 +    }
 +
 +    pub fn name(self, db: &dyn HirDatabase) -> Name {
 +        let params = db.generic_params(self.id.parent());
 +        match params.type_or_consts[self.id.local_id()].name() {
 +            Some(x) => x.clone(),
 +            None => {
 +                never!();
 +                Name::missing()
 +            }
 +        }
 +    }
 +
 +    pub fn module(self, db: &dyn HirDatabase) -> Module {
 +        self.id.parent().module(db.upcast()).into()
 +    }
 +
 +    pub fn parent(self, _db: &dyn HirDatabase) -> GenericDef {
 +        self.id.parent().into()
 +    }
 +
 +    pub fn ty(self, db: &dyn HirDatabase) -> Type {
 +        Type::new(db, self.id.parent(), db.const_param_ty(self.id))
 +    }
 +}
 +
 +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 +pub struct TypeOrConstParam {
 +    pub(crate) id: TypeOrConstParamId,
 +}
 +
 +impl TypeOrConstParam {
 +    pub fn name(self, db: &dyn HirDatabase) -> Name {
 +        let params = db.generic_params(self.id.parent);
 +        match params.type_or_consts[self.id.local_id].name() {
 +            Some(n) => n.clone(),
 +            _ => Name::missing(),
 +        }
 +    }
 +
 +    pub fn module(self, db: &dyn HirDatabase) -> Module {
 +        self.id.parent.module(db.upcast()).into()
 +    }
 +
 +    pub fn parent(self, _db: &dyn HirDatabase) -> GenericDef {
 +        self.id.parent.into()
 +    }
 +
 +    pub fn split(self, db: &dyn HirDatabase) -> Either<ConstParam, TypeParam> {
 +        let params = db.generic_params(self.id.parent);
 +        match &params.type_or_consts[self.id.local_id] {
 +            hir_def::generics::TypeOrConstParamData::TypeParamData(_) => {
 +                Either::Right(TypeParam { id: TypeParamId::from_unchecked(self.id) })
 +            }
 +            hir_def::generics::TypeOrConstParamData::ConstParamData(_) => {
 +                Either::Left(ConstParam { id: ConstParamId::from_unchecked(self.id) })
 +            }
 +        }
 +    }
 +
 +    pub fn ty(self, db: &dyn HirDatabase) -> Type {
 +        match self.split(db) {
 +            Either::Left(x) => x.ty(db),
 +            Either::Right(x) => x.ty(db),
 +        }
 +    }
 +}
 +
 +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 +pub struct Impl {
 +    pub(crate) id: ImplId,
 +}
 +
 +impl Impl {
 +    pub fn all_in_crate(db: &dyn HirDatabase, krate: Crate) -> Vec<Impl> {
 +        let inherent = db.inherent_impls_in_crate(krate.id);
 +        let trait_ = db.trait_impls_in_crate(krate.id);
 +
 +        inherent.all_impls().chain(trait_.all_impls()).map(Self::from).collect()
 +    }
 +
 +    pub fn all_for_type(db: &dyn HirDatabase, Type { ty, env }: Type) -> Vec<Impl> {
 +        let def_crates = match method_resolution::def_crates(db, &ty, env.krate) {
 +            Some(def_crates) => def_crates,
 +            None => return Vec::new(),
 +        };
 +
 +        let filter = |impl_def: &Impl| {
 +            let self_ty = impl_def.self_ty(db);
 +            let rref = self_ty.remove_ref();
 +            ty.equals_ctor(rref.as_ref().map_or(&self_ty.ty, |it| &it.ty))
 +        };
 +
 +        let fp = TyFingerprint::for_inherent_impl(&ty);
 +        let fp = match fp {
 +            Some(fp) => fp,
 +            None => return Vec::new(),
 +        };
 +
 +        let mut all = Vec::new();
 +        def_crates.iter().for_each(|&id| {
 +            all.extend(
 +                db.inherent_impls_in_crate(id)
 +                    .for_self_ty(&ty)
 +                    .iter()
 +                    .cloned()
 +                    .map(Self::from)
 +                    .filter(filter),
 +            )
 +        });
 +        for id in def_crates
 +            .iter()
 +            .flat_map(|&id| Crate { id }.transitive_reverse_dependencies(db))
 +            .map(|Crate { id }| id)
 +            .chain(def_crates.iter().copied())
 +            .unique()
 +        {
 +            all.extend(
 +                db.trait_impls_in_crate(id)
 +                    .for_self_ty_without_blanket_impls(fp)
 +                    .map(Self::from)
 +                    .filter(filter),
 +            );
 +        }
 +        all
 +    }
 +
 +    pub fn all_for_trait(db: &dyn HirDatabase, trait_: Trait) -> Vec<Impl> {
 +        let krate = trait_.module(db).krate();
 +        let mut all = Vec::new();
 +        for Crate { id } in krate.transitive_reverse_dependencies(db).into_iter() {
 +            let impls = db.trait_impls_in_crate(id);
 +            all.extend(impls.for_trait(trait_.id).map(Self::from))
 +        }
 +        all
 +    }
 +
 +    // FIXME: the return type is wrong. This should be a hir version of
 +    // `TraitRef` (to account for parameters and qualifiers)
 +    pub fn trait_(self, db: &dyn HirDatabase) -> Option<Trait> {
 +        let trait_ref = db.impl_trait(self.id)?.skip_binders().clone();
 +        let id = hir_ty::from_chalk_trait_id(trait_ref.trait_id);
 +        Some(Trait { id })
 +    }
 +
 +    pub fn self_ty(self, db: &dyn HirDatabase) -> Type {
 +        let resolver = self.id.resolver(db.upcast());
 +        let substs = TyBuilder::placeholder_subst(db, self.id);
 +        let ty = db.impl_self_ty(self.id).substitute(Interner, &substs);
 +        Type::new_with_resolver_inner(db, &resolver, ty)
 +    }
 +
 +    pub fn items(self, db: &dyn HirDatabase) -> Vec<AssocItem> {
 +        db.impl_data(self.id).items.iter().map(|it| (*it).into()).collect()
 +    }
 +
 +    pub fn is_negative(self, db: &dyn HirDatabase) -> bool {
 +        db.impl_data(self.id).is_negative
 +    }
 +
 +    pub fn module(self, db: &dyn HirDatabase) -> Module {
 +        self.id.lookup(db.upcast()).container.into()
 +    }
 +
 +    pub fn is_builtin_derive(self, db: &dyn HirDatabase) -> Option<InFile<ast::Attr>> {
 +        let src = self.source(db)?;
 +        src.file_id.is_builtin_derive(db.upcast())
 +    }
 +}
 +
 +#[derive(Clone, PartialEq, Eq, Debug)]
 +pub struct Type {
 +    env: Arc<TraitEnvironment>,
 +    ty: Ty,
 +}
 +
 +impl Type {
 +    pub(crate) fn new_with_resolver(db: &dyn HirDatabase, resolver: &Resolver, ty: Ty) -> Type {
 +        Type::new_with_resolver_inner(db, resolver, ty)
 +    }
 +
 +    pub(crate) fn new_with_resolver_inner(
 +        db: &dyn HirDatabase,
 +        resolver: &Resolver,
 +        ty: Ty,
 +    ) -> Type {
 +        let environment = resolver.generic_def().map_or_else(
 +            || Arc::new(TraitEnvironment::empty(resolver.krate())),
 +            |d| db.trait_environment(d),
 +        );
 +        Type { env: environment, ty }
 +    }
 +
 +    pub(crate) fn new_for_crate(krate: CrateId, ty: Ty) -> Type {
 +        Type { env: Arc::new(TraitEnvironment::empty(krate)), ty }
 +    }
 +
 +    pub fn reference(inner: &Type, m: Mutability) -> Type {
 +        inner.derived(
 +            TyKind::Ref(
 +                if m.is_mut() { hir_ty::Mutability::Mut } else { hir_ty::Mutability::Not },
 +                hir_ty::static_lifetime(),
 +                inner.ty.clone(),
 +            )
 +            .intern(Interner),
 +        )
 +    }
 +
 +    fn new(db: &dyn HirDatabase, lexical_env: impl HasResolver, ty: Ty) -> Type {
 +        let resolver = lexical_env.resolver(db.upcast());
 +        let environment = resolver.generic_def().map_or_else(
 +            || Arc::new(TraitEnvironment::empty(resolver.krate())),
 +            |d| db.trait_environment(d),
 +        );
 +        Type { env: environment, ty }
 +    }
 +
 +    fn from_def(db: &dyn HirDatabase, def: impl HasResolver + Into<TyDefId>) -> Type {
 +        let ty_def = def.into();
 +        let parent_subst = match ty_def {
 +            TyDefId::TypeAliasId(id) => match id.lookup(db.upcast()).container {
 +                ItemContainerId::TraitId(id) => {
 +                    let subst = TyBuilder::subst_for_def(db, id, None).fill_with_unknown().build();
 +                    Some(subst)
 +                }
 +                ItemContainerId::ImplId(id) => {
 +                    let subst = TyBuilder::subst_for_def(db, id, None).fill_with_unknown().build();
 +                    Some(subst)
 +                }
 +                _ => None,
 +            },
 +            _ => None,
 +        };
 +        let ty = TyBuilder::def_ty(db, ty_def, parent_subst).fill_with_unknown().build();
 +        Type::new(db, def, ty)
 +    }
 +
 +    pub fn new_slice(ty: Type) -> Type {
 +        Type { env: ty.env, ty: TyBuilder::slice(ty.ty) }
 +    }
 +
 +    pub fn is_unit(&self) -> bool {
 +        matches!(self.ty.kind(Interner), TyKind::Tuple(0, ..))
 +    }
 +
 +    pub fn is_bool(&self) -> bool {
 +        matches!(self.ty.kind(Interner), TyKind::Scalar(Scalar::Bool))
 +    }
 +
 +    pub fn is_never(&self) -> bool {
 +        matches!(self.ty.kind(Interner), TyKind::Never)
 +    }
 +
 +    pub fn is_mutable_reference(&self) -> bool {
 +        matches!(self.ty.kind(Interner), TyKind::Ref(hir_ty::Mutability::Mut, ..))
 +    }
 +
 +    pub fn is_reference(&self) -> bool {
 +        matches!(self.ty.kind(Interner), TyKind::Ref(..))
 +    }
 +
 +    pub fn as_reference(&self) -> Option<(Type, Mutability)> {
 +        let (ty, _lt, m) = self.ty.as_reference()?;
 +        let m = Mutability::from_mutable(matches!(m, hir_ty::Mutability::Mut));
 +        Some((self.derived(ty.clone()), m))
 +    }
 +
 +    pub fn is_slice(&self) -> bool {
 +        matches!(self.ty.kind(Interner), TyKind::Slice(..))
 +    }
 +
 +    pub fn is_usize(&self) -> bool {
 +        matches!(self.ty.kind(Interner), TyKind::Scalar(Scalar::Uint(UintTy::Usize)))
 +    }
 +
 +    pub fn remove_ref(&self) -> Option<Type> {
 +        match &self.ty.kind(Interner) {
 +            TyKind::Ref(.., ty) => Some(self.derived(ty.clone())),
 +            _ => None,
 +        }
 +    }
 +
 +    pub fn strip_references(&self) -> Type {
 +        self.derived(self.ty.strip_references().clone())
 +    }
 +
 +    pub fn strip_reference(&self) -> Type {
 +        self.derived(self.ty.strip_reference().clone())
 +    }
 +
 +    pub fn is_unknown(&self) -> bool {
 +        self.ty.is_unknown()
 +    }
 +
 +    /// Checks that particular type `ty` implements `std::future::IntoFuture` or
 +    /// `std::future::Future`.
 +    /// This function is used in `.await` syntax completion.
 +    pub fn impls_into_future(&self, db: &dyn HirDatabase) -> bool {
 +        let trait_ = db
 +            .lang_item(self.env.krate, SmolStr::new_inline("into_future"))
 +            .and_then(|it| {
 +                let into_future_fn = it.as_function()?;
 +                let assoc_item = as_assoc_item(db, AssocItem::Function, into_future_fn)?;
 +                let into_future_trait = assoc_item.containing_trait_or_trait_impl(db)?;
 +                Some(into_future_trait.id)
 +            })
 +            .or_else(|| {
 +                let future_trait =
 +                    db.lang_item(self.env.krate, SmolStr::new_inline("future_trait"))?;
 +                future_trait.as_trait()
 +            });
 +
 +        let trait_ = match trait_ {
 +            Some(it) => it,
 +            None => return false,
 +        };
 +
 +        let canonical_ty =
 +            Canonical { value: self.ty.clone(), binders: CanonicalVarKinds::empty(Interner) };
 +        method_resolution::implements_trait(&canonical_ty, db, self.env.clone(), trait_)
 +    }
 +
 +    /// Checks that particular type `ty` implements `std::ops::FnOnce`.
 +    ///
 +    /// This function can be used to check if a particular type is callable, since FnOnce is a
 +    /// supertrait of Fn and FnMut, so all callable types implements at least FnOnce.
 +    pub fn impls_fnonce(&self, db: &dyn HirDatabase) -> bool {
 +        let fnonce_trait = match FnTrait::FnOnce.get_id(db, self.env.krate) {
 +            Some(it) => it,
 +            None => return false,
 +        };
 +
 +        let canonical_ty =
 +            Canonical { value: self.ty.clone(), binders: CanonicalVarKinds::empty(Interner) };
 +        method_resolution::implements_trait_unique(
 +            &canonical_ty,
 +            db,
 +            self.env.clone(),
 +            fnonce_trait,
 +        )
 +    }
 +
 +    pub fn impls_trait(&self, db: &dyn HirDatabase, trait_: Trait, args: &[Type]) -> bool {
 +        let mut it = args.iter().map(|t| t.ty.clone());
 +        let trait_ref = TyBuilder::trait_ref(db, trait_.id)
 +            .push(self.ty.clone())
 +            .fill(|x| {
 +                let r = it.next().unwrap();
 +                match x {
 +                    ParamKind::Type => GenericArgData::Ty(r).intern(Interner),
 +                    ParamKind::Const(ty) => {
 +                        // FIXME: this code is not covered in tests.
 +                        unknown_const_as_generic(ty.clone())
 +                    }
 +                }
 +            })
 +            .build();
 +
 +        let goal = Canonical {
 +            value: hir_ty::InEnvironment::new(&self.env.env, trait_ref.cast(Interner)),
 +            binders: CanonicalVarKinds::empty(Interner),
 +        };
 +
 +        db.trait_solve(self.env.krate, goal).is_some()
 +    }
 +
 +    pub fn normalize_trait_assoc_type(
 +        &self,
 +        db: &dyn HirDatabase,
 +        args: &[Type],
 +        alias: TypeAlias,
 +    ) -> Option<Type> {
 +        let mut args = args.iter();
 +        let trait_id = match alias.id.lookup(db.upcast()).container {
 +            ItemContainerId::TraitId(id) => id,
 +            _ => unreachable!("non assoc type alias reached in normalize_trait_assoc_type()"),
 +        };
 +        let parent_subst = TyBuilder::subst_for_def(db, trait_id, None)
 +            .push(self.ty.clone())
 +            .fill(|x| {
 +                // FIXME: this code is not covered in tests.
 +                match x {
 +                    ParamKind::Type => {
 +                        GenericArgData::Ty(args.next().unwrap().ty.clone()).intern(Interner)
 +                    }
 +                    ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
 +                }
 +            })
 +            .build();
 +        // FIXME: We don't handle GATs yet.
 +        let projection = TyBuilder::assoc_type_projection(db, alias.id, Some(parent_subst)).build();
 +
 +        let ty = db.normalize_projection(projection, self.env.clone());
 +        if ty.is_unknown() {
 +            None
 +        } else {
 +            Some(self.derived(ty))
 +        }
 +    }
 +
 +    pub fn is_copy(&self, db: &dyn HirDatabase) -> bool {
 +        let lang_item = db.lang_item(self.env.krate, SmolStr::new_inline("copy"));
 +        let copy_trait = match lang_item {
 +            Some(LangItemTarget::TraitId(it)) => it,
 +            _ => return false,
 +        };
 +        self.impls_trait(db, copy_trait.into(), &[])
 +    }
 +
 +    pub fn as_callable(&self, db: &dyn HirDatabase) -> Option<Callable> {
 +        let callee = match self.ty.kind(Interner) {
 +            TyKind::Closure(id, _) => Callee::Closure(*id),
 +            TyKind::Function(_) => Callee::FnPtr,
++            TyKind::FnDef(..) => Callee::Def(self.ty.callable_def(db)?),
++            _ => {
++                let ty = hir_ty::replace_errors_with_variables(&self.ty);
++                let sig = hir_ty::callable_sig_from_fnonce(&ty, self.env.clone(), db)?;
++                return Some(Callable {
++                    ty: self.clone(),
++                    sig,
++                    callee: Callee::Other,
++                    is_bound_method: false,
++                });
++            }
 +        };
 +
 +        let sig = self.ty.callable_sig(db)?;
 +        Some(Callable { ty: self.clone(), sig, callee, is_bound_method: false })
 +    }
 +
 +    pub fn is_closure(&self) -> bool {
 +        matches!(&self.ty.kind(Interner), TyKind::Closure { .. })
 +    }
 +
 +    pub fn is_fn(&self) -> bool {
 +        matches!(&self.ty.kind(Interner), TyKind::FnDef(..) | TyKind::Function { .. })
 +    }
 +
 +    pub fn is_array(&self) -> bool {
 +        matches!(&self.ty.kind(Interner), TyKind::Array(..))
 +    }
 +
 +    pub fn is_packed(&self, db: &dyn HirDatabase) -> bool {
 +        let adt_id = match *self.ty.kind(Interner) {
 +            TyKind::Adt(hir_ty::AdtId(adt_id), ..) => adt_id,
 +            _ => return false,
 +        };
 +
 +        let adt = adt_id.into();
 +        match adt {
 +            Adt::Struct(s) => matches!(s.repr(db), Some(ReprData { packed: true, .. })),
 +            _ => false,
 +        }
 +    }
 +
 +    pub fn is_raw_ptr(&self) -> bool {
 +        matches!(&self.ty.kind(Interner), TyKind::Raw(..))
 +    }
 +
 +    pub fn contains_unknown(&self) -> bool {
 +        return go(&self.ty);
 +
 +        fn go(ty: &Ty) -> bool {
 +            match ty.kind(Interner) {
 +                TyKind::Error => true,
 +
 +                TyKind::Adt(_, substs)
 +                | TyKind::AssociatedType(_, substs)
 +                | TyKind::Tuple(_, substs)
 +                | TyKind::OpaqueType(_, substs)
 +                | TyKind::FnDef(_, substs)
 +                | TyKind::Closure(_, substs) => {
 +                    substs.iter(Interner).filter_map(|a| a.ty(Interner)).any(go)
 +                }
 +
 +                TyKind::Array(_ty, len) if len.is_unknown() => true,
 +                TyKind::Array(ty, _)
 +                | TyKind::Slice(ty)
 +                | TyKind::Raw(_, ty)
 +                | TyKind::Ref(_, _, ty) => go(ty),
 +
 +                TyKind::Scalar(_)
 +                | TyKind::Str
 +                | TyKind::Never
 +                | TyKind::Placeholder(_)
 +                | TyKind::BoundVar(_)
 +                | TyKind::InferenceVar(_, _)
 +                | TyKind::Dyn(_)
 +                | TyKind::Function(_)
 +                | TyKind::Alias(_)
 +                | TyKind::Foreign(_)
 +                | TyKind::Generator(..)
 +                | TyKind::GeneratorWitness(..) => false,
 +            }
 +        }
 +    }
 +
 +    pub fn fields(&self, db: &dyn HirDatabase) -> Vec<(Field, Type)> {
 +        let (variant_id, substs) = match self.ty.kind(Interner) {
 +            TyKind::Adt(hir_ty::AdtId(AdtId::StructId(s)), substs) => ((*s).into(), substs),
 +            TyKind::Adt(hir_ty::AdtId(AdtId::UnionId(u)), substs) => ((*u).into(), substs),
 +            _ => return Vec::new(),
 +        };
 +
 +        db.field_types(variant_id)
 +            .iter()
 +            .map(|(local_id, ty)| {
 +                let def = Field { parent: variant_id.into(), id: local_id };
 +                let ty = ty.clone().substitute(Interner, substs);
 +                (def, self.derived(ty))
 +            })
 +            .collect()
 +    }
 +
 +    pub fn tuple_fields(&self, _db: &dyn HirDatabase) -> Vec<Type> {
 +        if let TyKind::Tuple(_, substs) = &self.ty.kind(Interner) {
 +            substs
 +                .iter(Interner)
 +                .map(|ty| self.derived(ty.assert_ty_ref(Interner).clone()))
 +                .collect()
 +        } else {
 +            Vec::new()
 +        }
 +    }
 +
 +    pub fn autoderef<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Type> + 'a {
 +        self.autoderef_(db).map(move |ty| self.derived(ty))
 +    }
 +
 +    fn autoderef_<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Ty> + 'a {
 +        // There should be no inference vars in types passed here
 +        let canonical = hir_ty::replace_errors_with_variables(&self.ty);
 +        let environment = self.env.clone();
 +        autoderef(db, environment, canonical).map(|canonical| canonical.value)
 +    }
 +
 +    // This would be nicer if it just returned an iterator, but that runs into
 +    // lifetime problems, because we need to borrow temp `CrateImplDefs`.
 +    pub fn iterate_assoc_items<T>(
 +        &self,
 +        db: &dyn HirDatabase,
 +        krate: Crate,
 +        mut callback: impl FnMut(AssocItem) -> Option<T>,
 +    ) -> Option<T> {
 +        let mut slot = None;
 +        self.iterate_assoc_items_dyn(db, krate, &mut |assoc_item_id| {
 +            slot = callback(assoc_item_id.into());
 +            slot.is_some()
 +        });
 +        slot
 +    }
 +
 +    fn iterate_assoc_items_dyn(
 +        &self,
 +        db: &dyn HirDatabase,
 +        krate: Crate,
 +        callback: &mut dyn FnMut(AssocItemId) -> bool,
 +    ) {
 +        let def_crates = match method_resolution::def_crates(db, &self.ty, krate.id) {
 +            Some(it) => it,
 +            None => return,
 +        };
 +        for krate in def_crates {
 +            let impls = db.inherent_impls_in_crate(krate);
 +
 +            for impl_def in impls.for_self_ty(&self.ty) {
 +                for &item in db.impl_data(*impl_def).items.iter() {
 +                    if callback(item) {
 +                        return;
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    pub fn type_arguments(&self) -> impl Iterator<Item = Type> + '_ {
 +        self.ty
 +            .strip_references()
 +            .as_adt()
 +            .into_iter()
 +            .flat_map(|(_, substs)| substs.iter(Interner))
 +            .filter_map(|arg| arg.ty(Interner).cloned())
 +            .map(move |ty| self.derived(ty))
 +    }
 +
 +    pub fn iterate_method_candidates<T>(
 +        &self,
 +        db: &dyn HirDatabase,
 +        scope: &SemanticsScope<'_>,
 +        // FIXME this can be retrieved from `scope`, except autoimport uses this
 +        // to specify a different set, so the method needs to be split
 +        traits_in_scope: &FxHashSet<TraitId>,
 +        with_local_impls: Option<Module>,
 +        name: Option<&Name>,
 +        mut callback: impl FnMut(Function) -> Option<T>,
 +    ) -> Option<T> {
 +        let _p = profile::span("iterate_method_candidates");
 +        let mut slot = None;
 +
 +        self.iterate_method_candidates_dyn(
 +            db,
 +            scope,
 +            traits_in_scope,
 +            with_local_impls,
 +            name,
 +            &mut |assoc_item_id| {
 +                if let AssocItemId::FunctionId(func) = assoc_item_id {
 +                    if let Some(res) = callback(func.into()) {
 +                        slot = Some(res);
 +                        return ControlFlow::Break(());
 +                    }
 +                }
 +                ControlFlow::Continue(())
 +            },
 +        );
 +        slot
 +    }
 +
 +    fn iterate_method_candidates_dyn(
 +        &self,
 +        db: &dyn HirDatabase,
 +        scope: &SemanticsScope<'_>,
 +        traits_in_scope: &FxHashSet<TraitId>,
 +        with_local_impls: Option<Module>,
 +        name: Option<&Name>,
 +        callback: &mut dyn FnMut(AssocItemId) -> ControlFlow<()>,
 +    ) {
 +        // There should be no inference vars in types passed here
 +        let canonical = hir_ty::replace_errors_with_variables(&self.ty);
 +
 +        let krate = scope.krate();
 +        let environment = scope.resolver().generic_def().map_or_else(
 +            || Arc::new(TraitEnvironment::empty(krate.id)),
 +            |d| db.trait_environment(d),
 +        );
 +
 +        method_resolution::iterate_method_candidates_dyn(
 +            &canonical,
 +            db,
 +            environment,
 +            traits_in_scope,
 +            with_local_impls.and_then(|b| b.id.containing_block()).into(),
 +            name,
 +            method_resolution::LookupMode::MethodCall,
 +            &mut |_adj, id| callback(id),
 +        );
 +    }
 +
 +    pub fn iterate_path_candidates<T>(
 +        &self,
 +        db: &dyn HirDatabase,
 +        scope: &SemanticsScope<'_>,
 +        traits_in_scope: &FxHashSet<TraitId>,
 +        with_local_impls: Option<Module>,
 +        name: Option<&Name>,
 +        mut callback: impl FnMut(AssocItem) -> Option<T>,
 +    ) -> Option<T> {
 +        let _p = profile::span("iterate_path_candidates");
 +        let mut slot = None;
 +        self.iterate_path_candidates_dyn(
 +            db,
 +            scope,
 +            traits_in_scope,
 +            with_local_impls,
 +            name,
 +            &mut |assoc_item_id| {
 +                if let Some(res) = callback(assoc_item_id.into()) {
 +                    slot = Some(res);
 +                    return ControlFlow::Break(());
 +                }
 +                ControlFlow::Continue(())
 +            },
 +        );
 +        slot
 +    }
 +
 +    fn iterate_path_candidates_dyn(
 +        &self,
 +        db: &dyn HirDatabase,
 +        scope: &SemanticsScope<'_>,
 +        traits_in_scope: &FxHashSet<TraitId>,
 +        with_local_impls: Option<Module>,
 +        name: Option<&Name>,
 +        callback: &mut dyn FnMut(AssocItemId) -> ControlFlow<()>,
 +    ) {
 +        let canonical = hir_ty::replace_errors_with_variables(&self.ty);
 +
 +        let krate = scope.krate();
 +        let environment = scope.resolver().generic_def().map_or_else(
 +            || Arc::new(TraitEnvironment::empty(krate.id)),
 +            |d| db.trait_environment(d),
 +        );
 +
 +        method_resolution::iterate_path_candidates(
 +            &canonical,
 +            db,
 +            environment,
 +            traits_in_scope,
 +            with_local_impls.and_then(|b| b.id.containing_block()).into(),
 +            name,
 +            &mut |id| callback(id),
 +        );
 +    }
 +
 +    pub fn as_adt(&self) -> Option<Adt> {
 +        let (adt, _subst) = self.ty.as_adt()?;
 +        Some(adt.into())
 +    }
 +
 +    pub fn as_builtin(&self) -> Option<BuiltinType> {
 +        self.ty.as_builtin().map(|inner| BuiltinType { inner })
 +    }
 +
 +    pub fn as_dyn_trait(&self) -> Option<Trait> {
 +        self.ty.dyn_trait().map(Into::into)
 +    }
 +
 +    /// If a type can be represented as `dyn Trait`, returns all traits accessible via this type,
 +    /// or an empty iterator otherwise.
 +    pub fn applicable_inherent_traits<'a>(
 +        &'a self,
 +        db: &'a dyn HirDatabase,
 +    ) -> impl Iterator<Item = Trait> + 'a {
 +        let _p = profile::span("applicable_inherent_traits");
 +        self.autoderef_(db)
 +            .filter_map(|ty| ty.dyn_trait())
 +            .flat_map(move |dyn_trait_id| hir_ty::all_super_traits(db.upcast(), dyn_trait_id))
 +            .map(Trait::from)
 +    }
 +
 +    pub fn env_traits<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Trait> + 'a {
 +        let _p = profile::span("env_traits");
 +        self.autoderef_(db)
 +            .filter(|ty| matches!(ty.kind(Interner), TyKind::Placeholder(_)))
 +            .flat_map(|ty| {
 +                self.env
 +                    .traits_in_scope_from_clauses(ty)
 +                    .flat_map(|t| hir_ty::all_super_traits(db.upcast(), t))
 +            })
 +            .map(Trait::from)
 +    }
 +
 +    pub fn as_impl_traits(&self, db: &dyn HirDatabase) -> Option<impl Iterator<Item = Trait>> {
 +        self.ty.impl_trait_bounds(db).map(|it| {
 +            it.into_iter().filter_map(|pred| match pred.skip_binders() {
 +                hir_ty::WhereClause::Implemented(trait_ref) => {
 +                    Some(Trait::from(trait_ref.hir_trait_id()))
 +                }
 +                _ => None,
 +            })
 +        })
 +    }
 +
 +    pub fn as_associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<Trait> {
 +        self.ty.associated_type_parent_trait(db).map(Into::into)
 +    }
 +
 +    fn derived(&self, ty: Ty) -> Type {
 +        Type { env: self.env.clone(), ty }
 +    }
 +
 +    pub fn walk(&self, db: &dyn HirDatabase, mut cb: impl FnMut(Type)) {
 +        // TypeWalk::walk for a Ty at first visits parameters and only after that the Ty itself.
 +        // We need a different order here.
 +
 +        fn walk_substs(
 +            db: &dyn HirDatabase,
 +            type_: &Type,
 +            substs: &Substitution,
 +            cb: &mut impl FnMut(Type),
 +        ) {
 +            for ty in substs.iter(Interner).filter_map(|a| a.ty(Interner)) {
 +                walk_type(db, &type_.derived(ty.clone()), cb);
 +            }
 +        }
 +
 +        fn walk_bounds(
 +            db: &dyn HirDatabase,
 +            type_: &Type,
 +            bounds: &[QuantifiedWhereClause],
 +            cb: &mut impl FnMut(Type),
 +        ) {
 +            for pred in bounds {
 +                if let WhereClause::Implemented(trait_ref) = pred.skip_binders() {
 +                    cb(type_.clone());
 +                    // skip the self type. it's likely the type we just got the bounds from
 +                    for ty in
 +                        trait_ref.substitution.iter(Interner).skip(1).filter_map(|a| a.ty(Interner))
 +                    {
 +                        walk_type(db, &type_.derived(ty.clone()), cb);
 +                    }
 +                }
 +            }
 +        }
 +
 +        fn walk_type(db: &dyn HirDatabase, type_: &Type, cb: &mut impl FnMut(Type)) {
 +            let ty = type_.ty.strip_references();
 +            match ty.kind(Interner) {
 +                TyKind::Adt(_, substs) => {
 +                    cb(type_.derived(ty.clone()));
 +                    walk_substs(db, type_, substs, cb);
 +                }
 +                TyKind::AssociatedType(_, substs) => {
 +                    if ty.associated_type_parent_trait(db).is_some() {
 +                        cb(type_.derived(ty.clone()));
 +                    }
 +                    walk_substs(db, type_, substs, cb);
 +                }
 +                TyKind::OpaqueType(_, subst) => {
 +                    if let Some(bounds) = ty.impl_trait_bounds(db) {
 +                        walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb);
 +                    }
 +
 +                    walk_substs(db, type_, subst, cb);
 +                }
 +                TyKind::Alias(AliasTy::Opaque(opaque_ty)) => {
 +                    if let Some(bounds) = ty.impl_trait_bounds(db) {
 +                        walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb);
 +                    }
 +
 +                    walk_substs(db, type_, &opaque_ty.substitution, cb);
 +                }
 +                TyKind::Placeholder(_) => {
 +                    if let Some(bounds) = ty.impl_trait_bounds(db) {
 +                        walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb);
 +                    }
 +                }
 +                TyKind::Dyn(bounds) => {
 +                    walk_bounds(
 +                        db,
 +                        &type_.derived(ty.clone()),
 +                        bounds.bounds.skip_binders().interned(),
 +                        cb,
 +                    );
 +                }
 +
 +                TyKind::Ref(_, _, ty)
 +                | TyKind::Raw(_, ty)
 +                | TyKind::Array(ty, _)
 +                | TyKind::Slice(ty) => {
 +                    walk_type(db, &type_.derived(ty.clone()), cb);
 +                }
 +
 +                TyKind::FnDef(_, substs)
 +                | TyKind::Tuple(_, substs)
 +                | TyKind::Closure(.., substs) => {
 +                    walk_substs(db, type_, substs, cb);
 +                }
 +                TyKind::Function(hir_ty::FnPointer { substitution, .. }) => {
 +                    walk_substs(db, type_, &substitution.0, cb);
 +                }
 +
 +                _ => {}
 +            }
 +        }
 +
 +        walk_type(db, self, &mut cb);
 +    }
 +
 +    pub fn could_unify_with(&self, db: &dyn HirDatabase, other: &Type) -> bool {
 +        let tys = hir_ty::replace_errors_with_variables(&(self.ty.clone(), other.ty.clone()));
 +        hir_ty::could_unify(db, self.env.clone(), &tys)
 +    }
 +
 +    pub fn could_coerce_to(&self, db: &dyn HirDatabase, to: &Type) -> bool {
 +        let tys = hir_ty::replace_errors_with_variables(&(self.ty.clone(), to.ty.clone()));
 +        hir_ty::could_coerce(db, self.env.clone(), &tys)
 +    }
 +
 +    pub fn as_type_param(&self, db: &dyn HirDatabase) -> Option<TypeParam> {
 +        match self.ty.kind(Interner) {
 +            TyKind::Placeholder(p) => Some(TypeParam {
 +                id: TypeParamId::from_unchecked(hir_ty::from_placeholder_idx(db, *p)),
 +            }),
 +            _ => None,
 +        }
 +    }
 +}
 +
 +#[derive(Debug)]
 +pub struct Callable {
 +    ty: Type,
 +    sig: CallableSig,
 +    callee: Callee,
 +    pub(crate) is_bound_method: bool,
 +}
 +
 +#[derive(Debug)]
 +enum Callee {
 +    Def(CallableDefId),
 +    Closure(ClosureId),
 +    FnPtr,
++    Other,
 +}
 +
 +pub enum CallableKind {
 +    Function(Function),
 +    TupleStruct(Struct),
 +    TupleEnumVariant(Variant),
 +    Closure,
 +    FnPtr,
++    /// Some other type that implements `FnOnce`.
++    Other,
 +}
 +
 +impl Callable {
 +    pub fn kind(&self) -> CallableKind {
 +        use Callee::*;
 +        match self.callee {
 +            Def(CallableDefId::FunctionId(it)) => CallableKind::Function(it.into()),
 +            Def(CallableDefId::StructId(it)) => CallableKind::TupleStruct(it.into()),
 +            Def(CallableDefId::EnumVariantId(it)) => CallableKind::TupleEnumVariant(it.into()),
 +            Closure(_) => CallableKind::Closure,
 +            FnPtr => CallableKind::FnPtr,
++            Other => CallableKind::Other,
 +        }
 +    }
 +    pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option<ast::SelfParam> {
 +        let func = match self.callee {
 +            Callee::Def(CallableDefId::FunctionId(it)) if self.is_bound_method => it,
 +            _ => return None,
 +        };
 +        let src = func.lookup(db.upcast()).source(db.upcast());
 +        let param_list = src.value.param_list()?;
 +        param_list.self_param()
 +    }
 +    pub fn n_params(&self) -> usize {
 +        self.sig.params().len() - if self.is_bound_method { 1 } else { 0 }
 +    }
 +    pub fn params(
 +        &self,
 +        db: &dyn HirDatabase,
 +    ) -> Vec<(Option<Either<ast::SelfParam, ast::Pat>>, Type)> {
 +        let types = self
 +            .sig
 +            .params()
 +            .iter()
 +            .skip(if self.is_bound_method { 1 } else { 0 })
 +            .map(|ty| self.ty.derived(ty.clone()));
 +        let map_param = |it: ast::Param| it.pat().map(Either::Right);
 +        let patterns = match self.callee {
 +            Callee::Def(CallableDefId::FunctionId(func)) => {
 +                let src = func.lookup(db.upcast()).source(db.upcast());
 +                src.value.param_list().map(|param_list| {
 +                    param_list
 +                        .self_param()
 +                        .map(|it| Some(Either::Left(it)))
 +                        .filter(|_| !self.is_bound_method)
 +                        .into_iter()
 +                        .chain(param_list.params().map(map_param))
 +                })
 +            }
 +            Callee::Closure(closure_id) => match closure_source(db, closure_id) {
 +                Some(src) => src.param_list().map(|param_list| {
 +                    param_list
 +                        .self_param()
 +                        .map(|it| Some(Either::Left(it)))
 +                        .filter(|_| !self.is_bound_method)
 +                        .into_iter()
 +                        .chain(param_list.params().map(map_param))
 +                }),
 +                None => None,
 +            },
 +            _ => None,
 +        };
 +        patterns.into_iter().flatten().chain(iter::repeat(None)).zip(types).collect()
 +    }
 +    pub fn return_type(&self) -> Type {
 +        self.ty.derived(self.sig.ret().clone())
 +    }
 +}
 +
 +fn closure_source(db: &dyn HirDatabase, closure: ClosureId) -> Option<ast::ClosureExpr> {
 +    let (owner, expr_id) = db.lookup_intern_closure(closure.into());
 +    let (_, source_map) = db.body_with_source_map(owner);
 +    let ast = source_map.expr_syntax(expr_id).ok()?;
 +    let root = ast.file_syntax(db.upcast());
 +    let expr = ast.value.to_node(&root);
 +    match expr {
 +        ast::Expr::ClosureExpr(it) => Some(it),
 +        _ => None,
 +    }
 +}
 +
 +#[derive(Copy, Clone, Debug, Eq, PartialEq)]
 +pub enum BindingMode {
 +    Move,
 +    Ref(Mutability),
 +}
 +
 +/// For IDE only
 +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
 +pub enum ScopeDef {
 +    ModuleDef(ModuleDef),
 +    GenericParam(GenericParam),
 +    ImplSelfType(Impl),
 +    AdtSelfType(Adt),
 +    Local(Local),
 +    Label(Label),
 +    Unknown,
 +}
 +
 +impl ScopeDef {
 +    pub fn all_items(def: PerNs) -> ArrayVec<Self, 3> {
 +        let mut items = ArrayVec::new();
 +
 +        match (def.take_types(), def.take_values()) {
 +            (Some(m1), None) => items.push(ScopeDef::ModuleDef(m1.into())),
 +            (None, Some(m2)) => items.push(ScopeDef::ModuleDef(m2.into())),
 +            (Some(m1), Some(m2)) => {
 +                // Some items, like unit structs and enum variants, are
 +                // returned as both a type and a value. Here we want
 +                // to de-duplicate them.
 +                if m1 != m2 {
 +                    items.push(ScopeDef::ModuleDef(m1.into()));
 +                    items.push(ScopeDef::ModuleDef(m2.into()));
 +                } else {
 +                    items.push(ScopeDef::ModuleDef(m1.into()));
 +                }
 +            }
 +            (None, None) => {}
 +        };
 +
 +        if let Some(macro_def_id) = def.take_macros() {
 +            items.push(ScopeDef::ModuleDef(ModuleDef::Macro(macro_def_id.into())));
 +        }
 +
 +        if items.is_empty() {
 +            items.push(ScopeDef::Unknown);
 +        }
 +
 +        items
 +    }
 +
 +    pub fn attrs(&self, db: &dyn HirDatabase) -> Option<AttrsWithOwner> {
 +        match self {
 +            ScopeDef::ModuleDef(it) => it.attrs(db),
 +            ScopeDef::GenericParam(it) => Some(it.attrs(db)),
 +            ScopeDef::ImplSelfType(_)
 +            | ScopeDef::AdtSelfType(_)
 +            | ScopeDef::Local(_)
 +            | ScopeDef::Label(_)
 +            | ScopeDef::Unknown => None,
 +        }
 +    }
 +
 +    pub fn krate(&self, db: &dyn HirDatabase) -> Option<Crate> {
 +        match self {
 +            ScopeDef::ModuleDef(it) => it.module(db).map(|m| m.krate()),
 +            ScopeDef::GenericParam(it) => Some(it.module(db).krate()),
 +            ScopeDef::ImplSelfType(_) => None,
 +            ScopeDef::AdtSelfType(it) => Some(it.module(db).krate()),
 +            ScopeDef::Local(it) => Some(it.module(db).krate()),
 +            ScopeDef::Label(it) => Some(it.module(db).krate()),
 +            ScopeDef::Unknown => None,
 +        }
 +    }
 +}
 +
 +impl From<ItemInNs> for ScopeDef {
 +    fn from(item: ItemInNs) -> Self {
 +        match item {
 +            ItemInNs::Types(id) => ScopeDef::ModuleDef(id),
 +            ItemInNs::Values(id) => ScopeDef::ModuleDef(id),
 +            ItemInNs::Macros(id) => ScopeDef::ModuleDef(ModuleDef::Macro(id)),
 +        }
 +    }
 +}
 +
 +pub trait HasVisibility {
 +    fn visibility(&self, db: &dyn HirDatabase) -> Visibility;
 +    fn is_visible_from(&self, db: &dyn HirDatabase, module: Module) -> bool {
 +        let vis = self.visibility(db);
 +        vis.is_visible_from(db.upcast(), module.id)
 +    }
 +}
 +
 +/// Trait for obtaining the defining crate of an item.
 +pub trait HasCrate {
 +    fn krate(&self, db: &dyn HirDatabase) -> Crate;
 +}
 +
 +impl<T: hir_def::HasModule> HasCrate for T {
 +    fn krate(&self, db: &dyn HirDatabase) -> Crate {
 +        self.module(db.upcast()).krate().into()
 +    }
 +}
 +
 +impl HasCrate for AssocItem {
 +    fn krate(&self, db: &dyn HirDatabase) -> Crate {
 +        self.module(db).krate()
 +    }
 +}
 +
 +impl HasCrate for Struct {
 +    fn krate(&self, db: &dyn HirDatabase) -> Crate {
 +        self.module(db).krate()
 +    }
 +}
 +
 +impl HasCrate for Union {
 +    fn krate(&self, db: &dyn HirDatabase) -> Crate {
 +        self.module(db).krate()
 +    }
 +}
 +
 +impl HasCrate for Field {
 +    fn krate(&self, db: &dyn HirDatabase) -> Crate {
 +        self.parent_def(db).module(db).krate()
 +    }
 +}
 +
 +impl HasCrate for Variant {
 +    fn krate(&self, db: &dyn HirDatabase) -> Crate {
 +        self.module(db).krate()
 +    }
 +}
 +
 +impl HasCrate for Function {
 +    fn krate(&self, db: &dyn HirDatabase) -> Crate {
 +        self.module(db).krate()
 +    }
 +}
 +
 +impl HasCrate for Const {
 +    fn krate(&self, db: &dyn HirDatabase) -> Crate {
 +        self.module(db).krate()
 +    }
 +}
 +
 +impl HasCrate for TypeAlias {
 +    fn krate(&self, db: &dyn HirDatabase) -> Crate {
 +        self.module(db).krate()
 +    }
 +}
 +
 +impl HasCrate for Type {
 +    fn krate(&self, _db: &dyn HirDatabase) -> Crate {
 +        self.env.krate.into()
 +    }
 +}
 +
 +impl HasCrate for Macro {
 +    fn krate(&self, db: &dyn HirDatabase) -> Crate {
 +        self.module(db).krate()
 +    }
 +}
 +
 +impl HasCrate for Trait {
 +    fn krate(&self, db: &dyn HirDatabase) -> Crate {
 +        self.module(db).krate()
 +    }
 +}
 +
 +impl HasCrate for Static {
 +    fn krate(&self, db: &dyn HirDatabase) -> Crate {
 +        self.module(db).krate()
 +    }
 +}
 +
 +impl HasCrate for Adt {
 +    fn krate(&self, db: &dyn HirDatabase) -> Crate {
 +        self.module(db).krate()
 +    }
 +}
 +
 +impl HasCrate for Module {
 +    fn krate(&self, _: &dyn HirDatabase) -> Crate {
 +        Module::krate(*self)
 +    }
 +}
index 60d1588a44e54dba41e753f2511ffd879ac64f73,0000000000000000000000000000000000000000..b273ebc85a50687c8afc65097e8f182a40c935a4
mode 100644,000000..100644
--- /dev/null
@@@ -1,17 -1,0 +1,18 @@@
 +//! Settings for tweaking assists.
 +//!
 +//! The fun thing here is `SnippetCap` -- this type can only be created in this
 +//! module, and we use to statically check that we only produce snippet
 +//! assists if we are allowed to.
 +
 +use ide_db::{imports::insert_use::InsertUseConfig, SnippetCap};
 +
 +use crate::AssistKind;
 +
 +#[derive(Clone, Debug, PartialEq, Eq)]
 +pub struct AssistConfig {
 +    pub snippet_cap: Option<SnippetCap>,
 +    pub allowed: Option<Vec<AssistKind>>,
 +    pub insert_use: InsertUseConfig,
 +    pub prefer_no_std: bool,
++    pub assist_emit_must_use: bool,
 +}
index bfa9759ec84bea47b1abbe4e9687e93103af8bbc,0000000000000000000000000000000000000000..b5f99726fe1c8a815a5656e07e7dc73b57366b71
mode 100644,000000..100644
--- /dev/null
@@@ -1,325 -1,0 +1,325 @@@
-         format!("Insert explicit type `{}`", inferred_type),
 +use hir::HirDisplay;
 +use ide_db::syntax_helpers::node_ext::walk_ty;
 +use syntax::ast::{self, AstNode, LetStmt, Param};
 +
 +use crate::{AssistContext, AssistId, AssistKind, Assists};
 +
 +// Assist: add_explicit_type
 +//
 +// Specify type for a let binding.
 +//
 +// ```
 +// fn main() {
 +//     let x$0 = 92;
 +// }
 +// ```
 +// ->
 +// ```
 +// fn main() {
 +//     let x: i32 = 92;
 +// }
 +// ```
 +pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    let (ascribed_ty, expr, pat) = if let Some(let_stmt) = ctx.find_node_at_offset::<LetStmt>() {
 +        let cursor_in_range = {
 +            let eq_range = let_stmt.eq_token()?.text_range();
 +            ctx.offset() < eq_range.start()
 +        };
 +        if !cursor_in_range {
 +            cov_mark::hit!(add_explicit_type_not_applicable_if_cursor_after_equals);
 +            return None;
 +        }
 +
 +        (let_stmt.ty(), let_stmt.initializer(), let_stmt.pat()?)
 +    } else if let Some(param) = ctx.find_node_at_offset::<Param>() {
 +        if param.syntax().ancestors().nth(2).and_then(ast::ClosureExpr::cast).is_none() {
 +            cov_mark::hit!(add_explicit_type_not_applicable_in_fn_param);
 +            return None;
 +        }
 +        (param.ty(), None, param.pat()?)
 +    } else {
 +        return None;
 +    };
 +
 +    let module = ctx.sema.scope(pat.syntax())?.module();
 +    let pat_range = pat.syntax().text_range();
 +
 +    // Don't enable the assist if there is a type ascription without any placeholders
 +    if let Some(ty) = &ascribed_ty {
 +        let mut contains_infer_ty = false;
 +        walk_ty(ty, &mut |ty| contains_infer_ty |= matches!(ty, ast::Type::InferType(_)));
 +        if !contains_infer_ty {
 +            cov_mark::hit!(add_explicit_type_not_applicable_if_ty_already_specified);
 +            return None;
 +        }
 +    }
 +
 +    let ty = match (pat, expr) {
 +        (ast::Pat::IdentPat(_), Some(expr)) => ctx.sema.type_of_expr(&expr)?,
 +        (pat, _) => ctx.sema.type_of_pat(&pat)?,
 +    }
 +    .adjusted();
 +
 +    // Fully unresolved or unnameable types can't be annotated
 +    if (ty.contains_unknown() && ty.type_arguments().count() == 0) || ty.is_closure() {
 +        cov_mark::hit!(add_explicit_type_not_applicable_if_ty_not_inferred);
 +        return None;
 +    }
 +
 +    let inferred_type = ty.display_source_code(ctx.db(), module.into()).ok()?;
 +    acc.add(
 +        AssistId("add_explicit_type", AssistKind::RefactorRewrite),
-                 builder.insert(pat_range.end(), format!(": {}", inferred_type));
++        format!("Insert explicit type `{inferred_type}`"),
 +        pat_range,
 +        |builder| match ascribed_ty {
 +            Some(ascribed_ty) => {
 +                builder.replace(ascribed_ty.syntax().text_range(), inferred_type);
 +            }
 +            None => {
++                builder.insert(pat_range.end(), format!(": {inferred_type}"));
 +            }
 +        },
 +    )
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use super::*;
 +
 +    use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
 +
 +    #[test]
 +    fn add_explicit_type_target() {
 +        check_assist_target(add_explicit_type, r#"fn f() { let a$0 = 1; }"#, "a");
 +    }
 +
 +    #[test]
 +    fn add_explicit_type_simple() {
 +        check_assist(
 +            add_explicit_type,
 +            r#"fn f() { let a$0 = 1; }"#,
 +            r#"fn f() { let a: i32 = 1; }"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn add_explicit_type_simple_on_infer_ty() {
 +        check_assist(
 +            add_explicit_type,
 +            r#"fn f() { let a$0: _ = 1; }"#,
 +            r#"fn f() { let a: i32 = 1; }"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn add_explicit_type_simple_nested_infer_ty() {
 +        check_assist(
 +            add_explicit_type,
 +            r#"
 +//- minicore: option
 +fn f() {
 +    let a$0: Option<_> = Option::Some(1);
 +}
 +"#,
 +            r#"
 +fn f() {
 +    let a: Option<i32> = Option::Some(1);
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn add_explicit_type_macro_call_expr() {
 +        check_assist(
 +            add_explicit_type,
 +            r"macro_rules! v { () => {0u64} } fn f() { let a$0 = v!(); }",
 +            r"macro_rules! v { () => {0u64} } fn f() { let a: u64 = v!(); }",
 +        );
 +    }
 +
 +    #[test]
 +    fn add_explicit_type_not_applicable_for_fully_unresolved() {
 +        cov_mark::check!(add_explicit_type_not_applicable_if_ty_not_inferred);
 +        check_assist_not_applicable(add_explicit_type, r#"fn f() { let a$0 = None; }"#);
 +    }
 +
 +    #[test]
 +    fn add_explicit_type_applicable_for_partially_unresolved() {
 +        check_assist(
 +            add_explicit_type,
 +            r#"
 +        struct Vec<T, V> { t: T, v: V }
 +        impl<T> Vec<T, Vec<ZZZ, i32>> {
 +            fn new() -> Self {
 +                panic!()
 +            }
 +        }
 +        fn f() { let a$0 = Vec::new(); }"#,
 +            r#"
 +        struct Vec<T, V> { t: T, v: V }
 +        impl<T> Vec<T, Vec<ZZZ, i32>> {
 +            fn new() -> Self {
 +                panic!()
 +            }
 +        }
 +        fn f() { let a: Vec<_, Vec<_, i32>> = Vec::new(); }"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn add_explicit_type_not_applicable_closure_expr() {
 +        check_assist_not_applicable(add_explicit_type, r#"fn f() { let a$0 = || {}; }"#);
 +    }
 +
 +    #[test]
 +    fn add_explicit_type_not_applicable_ty_already_specified() {
 +        cov_mark::check!(add_explicit_type_not_applicable_if_ty_already_specified);
 +        check_assist_not_applicable(add_explicit_type, r#"fn f() { let a$0: i32 = 1; }"#);
 +    }
 +
 +    #[test]
 +    fn add_explicit_type_not_applicable_cursor_after_equals_of_let() {
 +        cov_mark::check!(add_explicit_type_not_applicable_if_cursor_after_equals);
 +        check_assist_not_applicable(
 +            add_explicit_type,
 +            r#"fn f() {let a =$0 match 1 {2 => 3, 3 => 5};}"#,
 +        )
 +    }
 +
 +    /// https://github.com/rust-lang/rust-analyzer/issues/2922
 +    #[test]
 +    fn regression_issue_2922() {
 +        check_assist(
 +            add_explicit_type,
 +            r#"
 +fn main() {
 +    let $0v = [0.0; 2];
 +}
 +"#,
 +            r#"
 +fn main() {
 +    let v: [f64; 2] = [0.0; 2];
 +}
 +"#,
 +        );
 +        // note: this may break later if we add more consteval. it just needs to be something that our
 +        // consteval engine doesn't understand
 +        check_assist_not_applicable(
 +            add_explicit_type,
 +            r#"
 +//- minicore: option
 +
 +fn main() {
 +    let $0l = [0.0; Some(2).unwrap()];
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn default_generics_should_not_be_added() {
 +        check_assist(
 +            add_explicit_type,
 +            r#"
 +struct Test<K, T = u8> { k: K, t: T }
 +
 +fn main() {
 +    let test$0 = Test { t: 23u8, k: 33 };
 +}
 +"#,
 +            r#"
 +struct Test<K, T = u8> { k: K, t: T }
 +
 +fn main() {
 +    let test: Test<i32> = Test { t: 23u8, k: 33 };
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn type_should_be_added_after_pattern() {
 +        // LetStmt = Attr* 'let' Pat (':' Type)? '=' initializer:Expr ';'
 +        check_assist(
 +            add_explicit_type,
 +            r#"
 +fn main() {
 +    let $0test @ () = ();
 +}
 +"#,
 +            r#"
 +fn main() {
 +    let test @ (): () = ();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn add_explicit_type_inserts_coercions() {
 +        check_assist(
 +            add_explicit_type,
 +            r#"
 +//- minicore: coerce_unsized
 +fn f() {
 +    let $0x: *const [_] = &[3];
 +}
 +"#,
 +            r#"
 +fn f() {
 +    let x: *const [i32] = &[3];
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn add_explicit_type_not_applicable_fn_param() {
 +        cov_mark::check!(add_explicit_type_not_applicable_in_fn_param);
 +        check_assist_not_applicable(add_explicit_type, r#"fn f(x$0: ()) {}"#);
 +    }
 +
 +    #[test]
 +    fn add_explicit_type_ascribes_closure_param() {
 +        check_assist(
 +            add_explicit_type,
 +            r#"
 +fn f() {
 +    |y$0| {
 +        let x: i32 = y;
 +    };
 +}
 +"#,
 +            r#"
 +fn f() {
 +    |y: i32| {
 +        let x: i32 = y;
 +    };
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn add_explicit_type_ascribes_closure_param_already_ascribed() {
 +        check_assist(
 +            add_explicit_type,
 +            r#"
 +//- minicore: option
 +fn f() {
 +    |mut y$0: Option<_>| {
 +        y = Some(3);
 +    };
 +}
 +"#,
 +            r#"
 +fn f() {
 +    |mut y: Option<i32>| {
 +        y = Some(3);
 +    };
 +}
 +"#,
 +        );
 +    }
 +}
index f858d7a15c24276fb591c28996e1ea7f3b2f2bbe,0000000000000000000000000000000000000000..89040a8569e63cb200ae5986c59e903e5693313c
mode 100644,000000..100644
--- /dev/null
@@@ -1,447 -1,0 +1,447 @@@
-                     builder.insert(insert_pos, &format!("{}-> {} ", preceeding_whitespace, ty))
 +use hir::HirDisplay;
 +use syntax::{ast, match_ast, AstNode, SyntaxKind, SyntaxToken, TextRange, TextSize};
 +
 +use crate::{AssistContext, AssistId, AssistKind, Assists};
 +
 +// Assist: add_return_type
 +//
 +// Adds the return type to a function or closure inferred from its tail expression if it doesn't have a return
 +// type specified. This assists is useable in a functions or closures tail expression or return type position.
 +//
 +// ```
 +// fn foo() { 4$02i32 }
 +// ```
 +// ->
 +// ```
 +// fn foo() -> i32 { 42i32 }
 +// ```
 +pub(crate) fn add_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    let (fn_type, tail_expr, builder_edit_pos) = extract_tail(ctx)?;
 +    let module = ctx.sema.scope(tail_expr.syntax())?.module();
 +    let ty = ctx.sema.type_of_expr(&peel_blocks(tail_expr.clone()))?.original();
 +    if ty.is_unit() {
 +        return None;
 +    }
 +    let ty = ty.display_source_code(ctx.db(), module.into()).ok()?;
 +
 +    acc.add(
 +        AssistId("add_return_type", AssistKind::RefactorRewrite),
 +        match fn_type {
 +            FnType::Function => "Add this function's return type",
 +            FnType::Closure { .. } => "Add this closure's return type",
 +        },
 +        tail_expr.syntax().text_range(),
 +        |builder| {
 +            match builder_edit_pos {
 +                InsertOrReplace::Insert(insert_pos, needs_whitespace) => {
 +                    let preceeding_whitespace = if needs_whitespace { " " } else { "" };
-                     builder.replace(text_range, &format!("-> {}", ty))
++                    builder.insert(insert_pos, &format!("{preceeding_whitespace}-> {ty} "))
 +                }
 +                InsertOrReplace::Replace(text_range) => {
-                 builder.replace(tail_expr.syntax().text_range(), &format!("{{{}}}", tail_expr));
++                    builder.replace(text_range, &format!("-> {ty}"))
 +                }
 +            }
 +            if let FnType::Closure { wrap_expr: true } = fn_type {
 +                cov_mark::hit!(wrap_closure_non_block_expr);
 +                // `|x| x` becomes `|x| -> T x` which is invalid, so wrap it in a block
++                builder.replace(tail_expr.syntax().text_range(), &format!("{{{tail_expr}}}"));
 +            }
 +        },
 +    )
 +}
 +
 +enum InsertOrReplace {
 +    Insert(TextSize, bool),
 +    Replace(TextRange),
 +}
 +
 +/// Check the potentially already specified return type and reject it or turn it into a builder command
 +/// if allowed.
 +fn ret_ty_to_action(
 +    ret_ty: Option<ast::RetType>,
 +    insert_after: SyntaxToken,
 +) -> Option<InsertOrReplace> {
 +    match ret_ty {
 +        Some(ret_ty) => match ret_ty.ty() {
 +            Some(ast::Type::InferType(_)) | None => {
 +                cov_mark::hit!(existing_infer_ret_type);
 +                cov_mark::hit!(existing_infer_ret_type_closure);
 +                Some(InsertOrReplace::Replace(ret_ty.syntax().text_range()))
 +            }
 +            _ => {
 +                cov_mark::hit!(existing_ret_type);
 +                cov_mark::hit!(existing_ret_type_closure);
 +                None
 +            }
 +        },
 +        None => {
 +            let insert_after_pos = insert_after.text_range().end();
 +            let (insert_pos, needs_whitespace) = match insert_after.next_token() {
 +                Some(it) if it.kind() == SyntaxKind::WHITESPACE => {
 +                    (insert_after_pos + TextSize::from(1), false)
 +                }
 +                _ => (insert_after_pos, true),
 +            };
 +
 +            Some(InsertOrReplace::Insert(insert_pos, needs_whitespace))
 +        }
 +    }
 +}
 +
 +enum FnType {
 +    Function,
 +    Closure { wrap_expr: bool },
 +}
 +
 +/// If we're looking at a block that is supposed to return `()`, type inference
 +/// will just tell us it has type `()`. We have to look at the tail expression
 +/// to see the mismatched actual type. This 'unpeels' the various blocks to
 +/// hopefully let us see the type the user intends. (This still doesn't handle
 +/// all situations fully correctly; the 'ideal' way to handle this would be to
 +/// run type inference on the function again, but with a variable as the return
 +/// type.)
 +fn peel_blocks(mut expr: ast::Expr) -> ast::Expr {
 +    loop {
 +        match_ast! {
 +            match (expr.syntax()) {
 +                ast::BlockExpr(it) => {
 +                    if let Some(tail) = it.tail_expr() {
 +                        expr = tail.clone();
 +                    } else {
 +                        break;
 +                    }
 +                },
 +                ast::IfExpr(it) => {
 +                    if let Some(then_branch) = it.then_branch() {
 +                        expr = ast::Expr::BlockExpr(then_branch.clone());
 +                    } else {
 +                        break;
 +                    }
 +                },
 +                ast::MatchExpr(it) => {
 +                    if let Some(arm_expr) = it.match_arm_list().and_then(|l| l.arms().next()).and_then(|a| a.expr()) {
 +                        expr = arm_expr;
 +                    } else {
 +                        break;
 +                    }
 +                },
 +                _ => break,
 +            }
 +        }
 +    }
 +    expr
 +}
 +
 +fn extract_tail(ctx: &AssistContext<'_>) -> Option<(FnType, ast::Expr, InsertOrReplace)> {
 +    let (fn_type, tail_expr, return_type_range, action) =
 +        if let Some(closure) = ctx.find_node_at_offset::<ast::ClosureExpr>() {
 +            let rpipe = closure.param_list()?.syntax().last_token()?;
 +            let rpipe_pos = rpipe.text_range().end();
 +
 +            let action = ret_ty_to_action(closure.ret_type(), rpipe)?;
 +
 +            let body = closure.body()?;
 +            let body_start = body.syntax().first_token()?.text_range().start();
 +            let (tail_expr, wrap_expr) = match body {
 +                ast::Expr::BlockExpr(block) => (block.tail_expr()?, false),
 +                body => (body, true),
 +            };
 +
 +            let ret_range = TextRange::new(rpipe_pos, body_start);
 +            (FnType::Closure { wrap_expr }, tail_expr, ret_range, action)
 +        } else {
 +            let func = ctx.find_node_at_offset::<ast::Fn>()?;
 +
 +            let rparen = func.param_list()?.r_paren_token()?;
 +            let rparen_pos = rparen.text_range().end();
 +            let action = ret_ty_to_action(func.ret_type(), rparen)?;
 +
 +            let body = func.body()?;
 +            let stmt_list = body.stmt_list()?;
 +            let tail_expr = stmt_list.tail_expr()?;
 +
 +            let ret_range_end = stmt_list.l_curly_token()?.text_range().start();
 +            let ret_range = TextRange::new(rparen_pos, ret_range_end);
 +            (FnType::Function, tail_expr, ret_range, action)
 +        };
 +    let range = ctx.selection_trimmed();
 +    if return_type_range.contains_range(range) {
 +        cov_mark::hit!(cursor_in_ret_position);
 +        cov_mark::hit!(cursor_in_ret_position_closure);
 +    } else if tail_expr.syntax().text_range().contains_range(range) {
 +        cov_mark::hit!(cursor_on_tail);
 +        cov_mark::hit!(cursor_on_tail_closure);
 +    } else {
 +        return None;
 +    }
 +    Some((fn_type, tail_expr, action))
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use crate::tests::{check_assist, check_assist_not_applicable};
 +
 +    use super::*;
 +
 +    #[test]
 +    fn infer_return_type_specified_inferred() {
 +        cov_mark::check!(existing_infer_ret_type);
 +        check_assist(
 +            add_return_type,
 +            r#"fn foo() -> $0_ {
 +    45
 +}"#,
 +            r#"fn foo() -> i32 {
 +    45
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn infer_return_type_specified_inferred_closure() {
 +        cov_mark::check!(existing_infer_ret_type_closure);
 +        check_assist(
 +            add_return_type,
 +            r#"fn foo() {
 +    || -> _ {$045};
 +}"#,
 +            r#"fn foo() {
 +    || -> i32 {45};
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn infer_return_type_cursor_at_return_type_pos() {
 +        cov_mark::check!(cursor_in_ret_position);
 +        check_assist(
 +            add_return_type,
 +            r#"fn foo() $0{
 +    45
 +}"#,
 +            r#"fn foo() -> i32 {
 +    45
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn infer_return_type_cursor_at_return_type_pos_closure() {
 +        cov_mark::check!(cursor_in_ret_position_closure);
 +        check_assist(
 +            add_return_type,
 +            r#"fn foo() {
 +    || $045
 +}"#,
 +            r#"fn foo() {
 +    || -> i32 {45}
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn infer_return_type() {
 +        cov_mark::check!(cursor_on_tail);
 +        check_assist(
 +            add_return_type,
 +            r#"fn foo() {
 +    45$0
 +}"#,
 +            r#"fn foo() -> i32 {
 +    45
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn infer_return_type_no_whitespace() {
 +        check_assist(
 +            add_return_type,
 +            r#"fn foo(){
 +    45$0
 +}"#,
 +            r#"fn foo() -> i32 {
 +    45
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn infer_return_type_nested() {
 +        check_assist(
 +            add_return_type,
 +            r#"fn foo() {
 +    if true {
 +        3$0
 +    } else {
 +        5
 +    }
 +}"#,
 +            r#"fn foo() -> i32 {
 +    if true {
 +        3
 +    } else {
 +        5
 +    }
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn infer_return_type_nested_match() {
 +        check_assist(
 +            add_return_type,
 +            r#"fn foo() {
 +    match true {
 +        true => { 3$0 },
 +        false => { 5 },
 +    }
 +}"#,
 +            r#"fn foo() -> i32 {
 +    match true {
 +        true => { 3 },
 +        false => { 5 },
 +    }
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn not_applicable_ret_type_specified() {
 +        cov_mark::check!(existing_ret_type);
 +        check_assist_not_applicable(
 +            add_return_type,
 +            r#"fn foo() -> i32 {
 +    ( 45$0 + 32 ) * 123
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn not_applicable_non_tail_expr() {
 +        check_assist_not_applicable(
 +            add_return_type,
 +            r#"fn foo() {
 +    let x = $03;
 +    ( 45 + 32 ) * 123
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn not_applicable_unit_return_type() {
 +        check_assist_not_applicable(
 +            add_return_type,
 +            r#"fn foo() {
 +    ($0)
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn infer_return_type_closure_block() {
 +        cov_mark::check!(cursor_on_tail_closure);
 +        check_assist(
 +            add_return_type,
 +            r#"fn foo() {
 +    |x: i32| {
 +        x$0
 +    };
 +}"#,
 +            r#"fn foo() {
 +    |x: i32| -> i32 {
 +        x
 +    };
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn infer_return_type_closure() {
 +        check_assist(
 +            add_return_type,
 +            r#"fn foo() {
 +    |x: i32| { x$0 };
 +}"#,
 +            r#"fn foo() {
 +    |x: i32| -> i32 { x };
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn infer_return_type_closure_no_whitespace() {
 +        check_assist(
 +            add_return_type,
 +            r#"fn foo() {
 +    |x: i32|{ x$0 };
 +}"#,
 +            r#"fn foo() {
 +    |x: i32| -> i32 { x };
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn infer_return_type_closure_wrap() {
 +        cov_mark::check!(wrap_closure_non_block_expr);
 +        check_assist(
 +            add_return_type,
 +            r#"fn foo() {
 +    |x: i32| x$0;
 +}"#,
 +            r#"fn foo() {
 +    |x: i32| -> i32 {x};
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn infer_return_type_nested_closure() {
 +        check_assist(
 +            add_return_type,
 +            r#"fn foo() {
 +    || {
 +        if true {
 +            3$0
 +        } else {
 +            5
 +        }
 +    }
 +}"#,
 +            r#"fn foo() {
 +    || -> i32 {
 +        if true {
 +            3
 +        } else {
 +            5
 +        }
 +    }
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn not_applicable_ret_type_specified_closure() {
 +        cov_mark::check!(existing_ret_type_closure);
 +        check_assist_not_applicable(
 +            add_return_type,
 +            r#"fn foo() {
 +    || -> i32 { 3$0 }
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn not_applicable_non_tail_expr_closure() {
 +        check_assist_not_applicable(
 +            add_return_type,
 +            r#"fn foo() {
 +    || -> i32 {
 +        let x = 3$0;
 +        6
 +    }
 +}"#,
 +        );
 +    }
 +}
index c0bf238db7317a78756dd3fb97a4f04b573e4637,0000000000000000000000000000000000000000..acf82e4b257943df6e974a934beb6f6f3d454feb
mode 100644,000000..100644
--- /dev/null
@@@ -1,400 -1,0 +1,401 @@@
-                     let snip = format!("::<{}>", get_snippet_fish_head(number_of_arguments));
 +use ide_db::defs::{Definition, NameRefClass};
 +use itertools::Itertools;
 +use syntax::{ast, AstNode, SyntaxKind, T};
 +
 +use crate::{
 +    assist_context::{AssistContext, Assists},
 +    AssistId, AssistKind,
 +};
 +
 +// Assist: add_turbo_fish
 +//
 +// Adds `::<_>` to a call of a generic method or function.
 +//
 +// ```
 +// fn make<T>() -> T { todo!() }
 +// fn main() {
 +//     let x = make$0();
 +// }
 +// ```
 +// ->
 +// ```
 +// fn make<T>() -> T { todo!() }
 +// fn main() {
 +//     let x = make::<${0:_}>();
 +// }
 +// ```
 +pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    let ident = ctx.find_token_syntax_at_offset(SyntaxKind::IDENT).or_else(|| {
 +        let arg_list = ctx.find_node_at_offset::<ast::ArgList>()?;
 +        if arg_list.args().next().is_some() {
 +            return None;
 +        }
 +        cov_mark::hit!(add_turbo_fish_after_call);
 +        cov_mark::hit!(add_type_ascription_after_call);
 +        arg_list.l_paren_token()?.prev_token().filter(|it| it.kind() == SyntaxKind::IDENT)
 +    })?;
 +    let next_token = ident.next_token()?;
 +    if next_token.kind() == T![::] {
 +        cov_mark::hit!(add_turbo_fish_one_fish_is_enough);
 +        return None;
 +    }
 +    let name_ref = ast::NameRef::cast(ident.parent()?)?;
 +    let def = match NameRefClass::classify(&ctx.sema, &name_ref)? {
 +        NameRefClass::Definition(def) => def,
 +        NameRefClass::FieldShorthand { .. } => return None,
 +    };
 +    let fun = match def {
 +        Definition::Function(it) => it,
 +        _ => return None,
 +    };
 +    let generics = hir::GenericDef::Function(fun).params(ctx.sema.db);
 +    if generics.is_empty() {
 +        cov_mark::hit!(add_turbo_fish_non_generic);
 +        return None;
 +    }
 +
 +    if let Some(let_stmt) = ctx.find_node_at_offset::<ast::LetStmt>() {
 +        if let_stmt.colon_token().is_none() {
 +            let type_pos = let_stmt.pat()?.syntax().last_token()?.text_range().end();
 +            let semi_pos = let_stmt.syntax().last_token()?.text_range().end();
 +
 +            acc.add(
 +                AssistId("add_type_ascription", AssistKind::RefactorRewrite),
 +                "Add `: _` before assignment operator",
 +                ident.text_range(),
 +                |builder| {
 +                    if let_stmt.semicolon_token().is_none() {
 +                        builder.insert(semi_pos, ";");
 +                    }
 +                    match ctx.config.snippet_cap {
 +                        Some(cap) => builder.insert_snippet(cap, type_pos, ": ${0:_}"),
 +                        None => builder.insert(type_pos, ": _"),
 +                    }
 +                },
 +            )?
 +        } else {
 +            cov_mark::hit!(add_type_ascription_already_typed);
 +        }
 +    }
 +
 +    let number_of_arguments = generics
 +        .iter()
 +        .filter(|param| {
 +            matches!(param, hir::GenericParam::TypeParam(_) | hir::GenericParam::ConstParam(_))
 +        })
 +        .count();
 +
 +    acc.add(
 +        AssistId("add_turbo_fish", AssistKind::RefactorRewrite),
 +        "Add `::<>`",
 +        ident.text_range(),
 +        |builder| {
 +            builder.trigger_signature_help();
 +            match ctx.config.snippet_cap {
 +                Some(cap) => {
-                     let snip = format!("::<{}>", fish_head);
++                    let fish_head = get_snippet_fish_head(number_of_arguments);
++                    let snip = format!("::<{fish_head}>");
 +                    builder.insert_snippet(cap, ident.text_range().end(), snip)
 +                }
 +                None => {
 +                    let fish_head = std::iter::repeat("_").take(number_of_arguments).format(", ");
-         .format_with("", |i, f| f(&format_args!("${{{}:_}}, ", i)))
++                    let snip = format!("::<{fish_head}>");
 +                    builder.insert(ident.text_range().end(), snip);
 +                }
 +            }
 +        },
 +    )
 +}
 +
 +/// This will create a snippet string with tabstops marked
 +fn get_snippet_fish_head(number_of_arguments: usize) -> String {
 +    let mut fish_head = (1..number_of_arguments)
++        .format_with("", |i, f| f(&format_args!("${{{i}:_}}, ")))
 +        .to_string();
 +
 +    // tabstop 0 is a special case and always the last one
 +    fish_head.push_str("${0:_}");
 +    fish_head
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use crate::tests::{check_assist, check_assist_by_label, check_assist_not_applicable};
 +
 +    use super::*;
 +
 +    #[test]
 +    fn add_turbo_fish_function() {
 +        check_assist(
 +            add_turbo_fish,
 +            r#"
 +fn make<T>() -> T {}
 +fn main() {
 +    make$0();
 +}
 +"#,
 +            r#"
 +fn make<T>() -> T {}
 +fn main() {
 +    make::<${0:_}>();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn add_turbo_fish_function_multiple_generic_types() {
 +        check_assist(
 +            add_turbo_fish,
 +            r#"
 +fn make<T, A>() -> T {}
 +fn main() {
 +    make$0();
 +}
 +"#,
 +            r#"
 +fn make<T, A>() -> T {}
 +fn main() {
 +    make::<${1:_}, ${0:_}>();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn add_turbo_fish_function_many_generic_types() {
 +        check_assist(
 +            add_turbo_fish,
 +            r#"
 +fn make<T, A, B, C, D, E, F>() -> T {}
 +fn main() {
 +    make$0();
 +}
 +"#,
 +            r#"
 +fn make<T, A, B, C, D, E, F>() -> T {}
 +fn main() {
 +    make::<${1:_}, ${2:_}, ${3:_}, ${4:_}, ${5:_}, ${6:_}, ${0:_}>();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn add_turbo_fish_after_call() {
 +        cov_mark::check!(add_turbo_fish_after_call);
 +        check_assist(
 +            add_turbo_fish,
 +            r#"
 +fn make<T>() -> T {}
 +fn main() {
 +    make()$0;
 +}
 +"#,
 +            r#"
 +fn make<T>() -> T {}
 +fn main() {
 +    make::<${0:_}>();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn add_turbo_fish_method() {
 +        check_assist(
 +            add_turbo_fish,
 +            r#"
 +struct S;
 +impl S {
 +    fn make<T>(&self) -> T {}
 +}
 +fn main() {
 +    S.make$0();
 +}
 +"#,
 +            r#"
 +struct S;
 +impl S {
 +    fn make<T>(&self) -> T {}
 +}
 +fn main() {
 +    S.make::<${0:_}>();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn add_turbo_fish_one_fish_is_enough() {
 +        cov_mark::check!(add_turbo_fish_one_fish_is_enough);
 +        check_assist_not_applicable(
 +            add_turbo_fish,
 +            r#"
 +fn make<T>() -> T {}
 +fn main() {
 +    make$0::<()>();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn add_turbo_fish_non_generic() {
 +        cov_mark::check!(add_turbo_fish_non_generic);
 +        check_assist_not_applicable(
 +            add_turbo_fish,
 +            r#"
 +fn make() -> () {}
 +fn main() {
 +    make$0();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn add_type_ascription_function() {
 +        check_assist_by_label(
 +            add_turbo_fish,
 +            r#"
 +fn make<T>() -> T {}
 +fn main() {
 +    let x = make$0();
 +}
 +"#,
 +            r#"
 +fn make<T>() -> T {}
 +fn main() {
 +    let x: ${0:_} = make();
 +}
 +"#,
 +            "Add `: _` before assignment operator",
 +        );
 +    }
 +
 +    #[test]
 +    fn add_type_ascription_after_call() {
 +        cov_mark::check!(add_type_ascription_after_call);
 +        check_assist_by_label(
 +            add_turbo_fish,
 +            r#"
 +fn make<T>() -> T {}
 +fn main() {
 +    let x = make()$0;
 +}
 +"#,
 +            r#"
 +fn make<T>() -> T {}
 +fn main() {
 +    let x: ${0:_} = make();
 +}
 +"#,
 +            "Add `: _` before assignment operator",
 +        );
 +    }
 +
 +    #[test]
 +    fn add_type_ascription_method() {
 +        check_assist_by_label(
 +            add_turbo_fish,
 +            r#"
 +struct S;
 +impl S {
 +    fn make<T>(&self) -> T {}
 +}
 +fn main() {
 +    let x = S.make$0();
 +}
 +"#,
 +            r#"
 +struct S;
 +impl S {
 +    fn make<T>(&self) -> T {}
 +}
 +fn main() {
 +    let x: ${0:_} = S.make();
 +}
 +"#,
 +            "Add `: _` before assignment operator",
 +        );
 +    }
 +
 +    #[test]
 +    fn add_type_ascription_already_typed() {
 +        cov_mark::check!(add_type_ascription_already_typed);
 +        check_assist(
 +            add_turbo_fish,
 +            r#"
 +fn make<T>() -> T {}
 +fn main() {
 +    let x: () = make$0();
 +}
 +"#,
 +            r#"
 +fn make<T>() -> T {}
 +fn main() {
 +    let x: () = make::<${0:_}>();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn add_type_ascription_append_semicolon() {
 +        check_assist_by_label(
 +            add_turbo_fish,
 +            r#"
 +fn make<T>() -> T {}
 +fn main() {
 +    let x = make$0()
 +}
 +"#,
 +            r#"
 +fn make<T>() -> T {}
 +fn main() {
 +    let x: ${0:_} = make();
 +}
 +"#,
 +            "Add `: _` before assignment operator",
 +        );
 +    }
 +
 +    #[test]
 +    fn add_turbo_fish_function_lifetime_parameter() {
 +        check_assist(
 +            add_turbo_fish,
 +            r#"
 +fn make<'a, T, A>(t: T, a: A) {}
 +fn main() {
 +    make$0(5, 2);
 +}
 +"#,
 +            r#"
 +fn make<'a, T, A>(t: T, a: A) {}
 +fn main() {
 +    make::<${1:_}, ${0:_}>(5, 2);
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn add_turbo_fish_function_const_parameter() {
 +        check_assist(
 +            add_turbo_fish,
 +            r#"
 +fn make<T, const N: usize>(t: T) {}
 +fn main() {
 +    make$0(3);
 +}
 +"#,
 +            r#"
 +fn make<T, const N: usize>(t: T) {}
 +fn main() {
 +    make::<${1:_}, ${0:_}>(3);
 +}
 +"#,
 +        );
 +    }
 +}
index 2853d1d1be3cd57b5935f61bc4899cd2d68d30b3,0000000000000000000000000000000000000000..57cfa17cc8e13ae7fe39b15347416f3d045bf553
mode 100644,000000..100644
--- /dev/null
@@@ -1,234 -1,0 +1,234 @@@
-                     edit.replace(lhs_range, format!("!({}", not_lhs.syntax().text()));
 +use std::collections::VecDeque;
 +
 +use syntax::ast::{self, AstNode};
 +
 +use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKind, Assists};
 +
 +// Assist: apply_demorgan
 +//
 +// Apply https://en.wikipedia.org/wiki/De_Morgan%27s_laws[De Morgan's law].
 +// This transforms expressions of the form `!l || !r` into `!(l && r)`.
 +// This also works with `&&`. This assist can only be applied with the cursor
 +// on either `||` or `&&`.
 +//
 +// ```
 +// fn main() {
 +//     if x != 4 ||$0 y < 3.14 {}
 +// }
 +// ```
 +// ->
 +// ```
 +// fn main() {
 +//     if !(x == 4 && y >= 3.14) {}
 +// }
 +// ```
 +pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    let expr = ctx.find_node_at_offset::<ast::BinExpr>()?;
 +    let op = expr.op_kind()?;
 +    let op_range = expr.op_token()?.text_range();
 +
 +    let opposite_op = match op {
 +        ast::BinaryOp::LogicOp(ast::LogicOp::And) => "||",
 +        ast::BinaryOp::LogicOp(ast::LogicOp::Or) => "&&",
 +        _ => return None,
 +    };
 +
 +    let cursor_in_range = op_range.contains_range(ctx.selection_trimmed());
 +    if !cursor_in_range {
 +        return None;
 +    }
 +
 +    let mut expr = expr;
 +
 +    // Walk up the tree while we have the same binary operator
 +    while let Some(parent_expr) = expr.syntax().parent().and_then(ast::BinExpr::cast) {
 +        match expr.op_kind() {
 +            Some(parent_op) if parent_op == op => {
 +                expr = parent_expr;
 +            }
 +            _ => break,
 +        }
 +    }
 +
 +    let mut expr_stack = vec![expr.clone()];
 +    let mut terms = Vec::new();
 +    let mut op_ranges = Vec::new();
 +
 +    // Find all the children with the same binary operator
 +    while let Some(expr) = expr_stack.pop() {
 +        let mut traverse_bin_expr_arm = |expr| {
 +            if let ast::Expr::BinExpr(bin_expr) = expr {
 +                if let Some(expr_op) = bin_expr.op_kind() {
 +                    if expr_op == op {
 +                        expr_stack.push(bin_expr);
 +                    } else {
 +                        terms.push(ast::Expr::BinExpr(bin_expr));
 +                    }
 +                } else {
 +                    terms.push(ast::Expr::BinExpr(bin_expr));
 +                }
 +            } else {
 +                terms.push(expr);
 +            }
 +        };
 +
 +        op_ranges.extend(expr.op_token().map(|t| t.text_range()));
 +        traverse_bin_expr_arm(expr.lhs()?);
 +        traverse_bin_expr_arm(expr.rhs()?);
 +    }
 +
 +    acc.add(
 +        AssistId("apply_demorgan", AssistKind::RefactorRewrite),
 +        "Apply De Morgan's law",
 +        op_range,
 +        |edit| {
 +            terms.sort_by_key(|t| t.syntax().text_range().start());
 +            let mut terms = VecDeque::from(terms);
 +
 +            let paren_expr = expr.syntax().parent().and_then(ast::ParenExpr::cast);
 +
 +            let neg_expr = paren_expr
 +                .clone()
 +                .and_then(|paren_expr| paren_expr.syntax().parent())
 +                .and_then(ast::PrefixExpr::cast)
 +                .and_then(|prefix_expr| {
 +                    if prefix_expr.op_kind().unwrap() == ast::UnaryOp::Not {
 +                        Some(prefix_expr)
 +                    } else {
 +                        None
 +                    }
 +                });
 +
 +            for op_range in op_ranges {
 +                edit.replace(op_range, opposite_op);
 +            }
 +
 +            if let Some(paren_expr) = paren_expr {
 +                for term in terms {
 +                    let range = term.syntax().text_range();
 +                    let not_term = invert_boolean_expression(term);
 +
 +                    edit.replace(range, not_term.syntax().text());
 +                }
 +
 +                if let Some(neg_expr) = neg_expr {
 +                    cov_mark::hit!(demorgan_double_negation);
 +                    edit.replace(neg_expr.op_token().unwrap().text_range(), "");
 +                } else {
 +                    cov_mark::hit!(demorgan_double_parens);
 +                    edit.replace(paren_expr.l_paren_token().unwrap().text_range(), "!(");
 +                }
 +            } else {
 +                if let Some(lhs) = terms.pop_front() {
 +                    let lhs_range = lhs.syntax().text_range();
 +                    let not_lhs = invert_boolean_expression(lhs);
 +
-                     edit.replace(rhs_range, format!("{})", not_rhs.syntax().text()));
++                    edit.replace(lhs_range, format!("!({not_lhs}"));
 +                }
 +
 +                if let Some(rhs) = terms.pop_back() {
 +                    let rhs_range = rhs.syntax().text_range();
 +                    let not_rhs = invert_boolean_expression(rhs);
 +
-                     edit.replace(term_range, not_term.syntax().text());
++                    edit.replace(rhs_range, format!("{not_rhs})"));
 +                }
 +
 +                for term in terms {
 +                    let term_range = term.syntax().text_range();
 +                    let not_term = invert_boolean_expression(term);
++                    edit.replace(term_range, not_term.to_string());
 +                }
 +            }
 +        },
 +    )
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use crate::tests::{check_assist, check_assist_not_applicable};
 +
 +    use super::*;
 +
 +    #[test]
 +    fn demorgan_handles_leq() {
 +        check_assist(
 +            apply_demorgan,
 +            r#"
 +struct S;
 +fn f() { S < S &&$0 S <= S }
 +"#,
 +            r#"
 +struct S;
 +fn f() { !(S >= S || S > S) }
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn demorgan_handles_geq() {
 +        check_assist(
 +            apply_demorgan,
 +            r#"
 +struct S;
 +fn f() { S > S &&$0 S >= S }
 +"#,
 +            r#"
 +struct S;
 +fn f() { !(S <= S || S < S) }
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn demorgan_turns_and_into_or() {
 +        check_assist(apply_demorgan, "fn f() { !x &&$0 !x }", "fn f() { !(x || x) }")
 +    }
 +
 +    #[test]
 +    fn demorgan_turns_or_into_and() {
 +        check_assist(apply_demorgan, "fn f() { !x ||$0 !x }", "fn f() { !(x && x) }")
 +    }
 +
 +    #[test]
 +    fn demorgan_removes_inequality() {
 +        check_assist(apply_demorgan, "fn f() { x != x ||$0 !x }", "fn f() { !(x == x && x) }")
 +    }
 +
 +    #[test]
 +    fn demorgan_general_case() {
 +        check_assist(apply_demorgan, "fn f() { x ||$0 x }", "fn f() { !(!x && !x) }")
 +    }
 +
 +    #[test]
 +    fn demorgan_multiple_terms() {
 +        check_assist(apply_demorgan, "fn f() { x ||$0 y || z }", "fn f() { !(!x && !y && !z) }");
 +        check_assist(apply_demorgan, "fn f() { x || y ||$0 z }", "fn f() { !(!x && !y && !z) }");
 +    }
 +
 +    #[test]
 +    fn demorgan_doesnt_apply_with_cursor_not_on_op() {
 +        check_assist_not_applicable(apply_demorgan, "fn f() { $0 !x || !x }")
 +    }
 +
 +    #[test]
 +    fn demorgan_doesnt_double_negation() {
 +        cov_mark::check!(demorgan_double_negation);
 +        check_assist(apply_demorgan, "fn f() { !(x ||$0 x) }", "fn f() { (!x && !x) }")
 +    }
 +
 +    #[test]
 +    fn demorgan_doesnt_double_parens() {
 +        cov_mark::check!(demorgan_double_parens);
 +        check_assist(apply_demorgan, "fn f() { (x ||$0 x) }", "fn f() { !(!x && !x) }")
 +    }
 +
 +    // https://github.com/rust-lang/rust-analyzer/issues/10963
 +    #[test]
 +    fn demorgan_doesnt_hang() {
 +        check_assist(
 +            apply_demorgan,
 +            "fn f() { 1 || 3 &&$0 4 || 5 }",
 +            "fn f() { !(!1 || !3 || !4) || 5 }",
 +        )
 +    }
 +}
index 678dc877d1381c69028953ac882dedc9d606b1e1,0000000000000000000000000000000000000000..a689270bc0915b13a62253c92fbe62623618ab42
mode 100644,000000..100644
--- /dev/null
@@@ -1,1311 -1,0 +1,1313 @@@
-             format!("Import `{}`", import.import_path),
 +use std::cmp::Reverse;
 +
 +use hir::{db::HirDatabase, Module};
 +use ide_db::{
 +    helpers::mod_path_to_ast,
 +    imports::{
 +        import_assets::{ImportAssets, ImportCandidate, LocatedImport},
 +        insert_use::{insert_use, ImportScope},
 +    },
 +};
 +use syntax::{ast, AstNode, NodeOrToken, SyntaxElement};
 +
 +use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
 +
 +// Feature: Auto Import
 +//
 +// Using the `auto-import` assist it is possible to insert missing imports for unresolved items.
 +// When inserting an import it will do so in a structured manner by keeping imports grouped,
 +// separated by a newline in the following order:
 +//
 +// - `std` and `core`
 +// - External Crates
 +// - Current Crate, paths prefixed by `crate`
 +// - Current Module, paths prefixed by `self`
 +// - Super Module, paths prefixed by `super`
 +//
 +// Example:
 +// ```rust
 +// use std::fs::File;
 +//
 +// use itertools::Itertools;
 +// use syntax::ast;
 +//
 +// use crate::utils::insert_use;
 +//
 +// use self::auto_import;
 +//
 +// use super::AssistContext;
 +// ```
 +//
 +// .Import Granularity
 +//
 +// It is possible to configure how use-trees are merged with the `imports.granularity.group` setting.
 +// It has the following configurations:
 +//
 +// - `crate`: Merge imports from the same crate into a single use statement. This kind of
 +//  nesting is only supported in Rust versions later than 1.24.
 +// - `module`: Merge imports from the same module into a single use statement.
 +// - `item`: Don't merge imports at all, creating one import per item.
 +// - `preserve`: Do not change the granularity of any imports. For auto-import this has the same
 +//  effect as `item`.
 +//
 +// In `VS Code` the configuration for this is `rust-analyzer.imports.granularity.group`.
 +//
 +// .Import Prefix
 +//
 +// The style of imports in the same crate is configurable through the `imports.prefix` setting.
 +// It has the following configurations:
 +//
 +// - `crate`: This setting will force paths to be always absolute, starting with the `crate`
 +//  prefix, unless the item is defined outside of the current crate.
 +// - `self`: This setting will force paths that are relative to the current module to always
 +//  start with `self`. This will result in paths that always start with either `crate`, `self`,
 +//  `super` or an extern crate identifier.
 +// - `plain`: This setting does not impose any restrictions in imports.
 +//
 +// In `VS Code` the configuration for this is `rust-analyzer.imports.prefix`.
 +//
 +// image::https://user-images.githubusercontent.com/48062697/113020673-b85be580-917a-11eb-9022-59585f35d4f8.gif[]
 +
 +// Assist: auto_import
 +//
 +// If the name is unresolved, provides all possible imports for it.
 +//
 +// ```
 +// fn main() {
 +//     let map = HashMap$0::new();
 +// }
 +// # pub mod std { pub mod collections { pub struct HashMap { } } }
 +// ```
 +// ->
 +// ```
 +// use std::collections::HashMap;
 +//
 +// fn main() {
 +//     let map = HashMap::new();
 +// }
 +// # pub mod std { pub mod collections { pub struct HashMap { } } }
 +// ```
 +pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    let (import_assets, syntax_under_caret) = find_importable_node(ctx)?;
 +    let mut proposed_imports = import_assets.search_for_imports(
 +        &ctx.sema,
 +        ctx.config.insert_use.prefix_kind,
 +        ctx.config.prefer_no_std,
 +    );
 +    if proposed_imports.is_empty() {
 +        return None;
 +    }
 +
 +    let range = match &syntax_under_caret {
 +        NodeOrToken::Node(node) => ctx.sema.original_range(node).range,
 +        NodeOrToken::Token(token) => token.text_range(),
 +    };
 +    let group_label = group_label(import_assets.import_candidate());
 +    let scope = ImportScope::find_insert_use_container(
 +        &match syntax_under_caret {
 +            NodeOrToken::Node(it) => it,
 +            NodeOrToken::Token(it) => it.parent()?,
 +        },
 +        &ctx.sema,
 +    )?;
 +
 +    // we aren't interested in different namespaces
 +    proposed_imports.dedup_by(|a, b| a.import_path == b.import_path);
 +
 +    let current_node = match ctx.covering_element() {
 +        NodeOrToken::Node(node) => Some(node),
 +        NodeOrToken::Token(token) => token.parent(),
 +    };
 +
 +    let current_module =
 +        current_node.as_ref().and_then(|node| ctx.sema.scope(node)).map(|scope| scope.module());
 +
 +    // prioritize more relevant imports
 +    proposed_imports
 +        .sort_by_key(|import| Reverse(relevance_score(ctx, import, current_module.as_ref())));
 +
 +    for import in proposed_imports {
++        let import_path = import.import_path;
++
 +        acc.add_group(
 +            &group_label,
 +            AssistId("auto_import", AssistKind::QuickFix),
-                 insert_use(&scope, mod_path_to_ast(&import.import_path), &ctx.config.insert_use);
++            format!("Import `{import_path}`"),
 +            range,
 +            |builder| {
 +                let scope = match scope.clone() {
 +                    ImportScope::File(it) => ImportScope::File(builder.make_mut(it)),
 +                    ImportScope::Module(it) => ImportScope::Module(builder.make_mut(it)),
 +                    ImportScope::Block(it) => ImportScope::Block(builder.make_mut(it)),
 +                };
++                insert_use(&scope, mod_path_to_ast(&import_path), &ctx.config.insert_use);
 +            },
 +        );
 +    }
 +    Some(())
 +}
 +
 +pub(super) fn find_importable_node(
 +    ctx: &AssistContext<'_>,
 +) -> Option<(ImportAssets, SyntaxElement)> {
 +    if let Some(path_under_caret) = ctx.find_node_at_offset_with_descend::<ast::Path>() {
 +        ImportAssets::for_exact_path(&path_under_caret, &ctx.sema)
 +            .zip(Some(path_under_caret.syntax().clone().into()))
 +    } else if let Some(method_under_caret) =
 +        ctx.find_node_at_offset_with_descend::<ast::MethodCallExpr>()
 +    {
 +        ImportAssets::for_method_call(&method_under_caret, &ctx.sema)
 +            .zip(Some(method_under_caret.syntax().clone().into()))
 +    } else if let Some(_) = ctx.find_node_at_offset_with_descend::<ast::Param>() {
 +        None
 +    } else if let Some(pat) = ctx
 +        .find_node_at_offset_with_descend::<ast::IdentPat>()
 +        .filter(ast::IdentPat::is_simple_ident)
 +    {
 +        ImportAssets::for_ident_pat(&ctx.sema, &pat).zip(Some(pat.syntax().clone().into()))
 +    } else {
 +        None
 +    }
 +}
 +
 +fn group_label(import_candidate: &ImportCandidate) -> GroupLabel {
 +    let name = match import_candidate {
 +        ImportCandidate::Path(candidate) => format!("Import {}", candidate.name.text()),
 +        ImportCandidate::TraitAssocItem(candidate) => {
 +            format!("Import a trait for item {}", candidate.assoc_item_name.text())
 +        }
 +        ImportCandidate::TraitMethod(candidate) => {
 +            format!("Import a trait for method {}", candidate.assoc_item_name.text())
 +        }
 +    };
 +    GroupLabel(name)
 +}
 +
 +/// Determine how relevant a given import is in the current context. Higher scores are more
 +/// relevant.
 +fn relevance_score(
 +    ctx: &AssistContext<'_>,
 +    import: &LocatedImport,
 +    current_module: Option<&Module>,
 +) -> i32 {
 +    let mut score = 0;
 +
 +    let db = ctx.db();
 +
 +    let item_module = match import.item_to_import {
 +        hir::ItemInNs::Types(item) | hir::ItemInNs::Values(item) => item.module(db),
 +        hir::ItemInNs::Macros(makro) => Some(makro.module(db)),
 +    };
 +
 +    match item_module.zip(current_module) {
 +        // get the distance between the imported path and the current module
 +        // (prefer items that are more local)
 +        Some((item_module, current_module)) => {
 +            score -= module_distance_hueristic(db, &current_module, &item_module) as i32;
 +        }
 +
 +        // could not find relevant modules, so just use the length of the path as an estimate
 +        None => return -(2 * import.import_path.len() as i32),
 +    }
 +
 +    score
 +}
 +
 +/// A heuristic that gives a higher score to modules that are more separated.
 +fn module_distance_hueristic(db: &dyn HirDatabase, current: &Module, item: &Module) -> usize {
 +    // get the path starting from the item to the respective crate roots
 +    let mut current_path = current.path_to_root(db);
 +    let mut item_path = item.path_to_root(db);
 +
 +    // we want paths going from the root to the item
 +    current_path.reverse();
 +    item_path.reverse();
 +
 +    // length of the common prefix of the two paths
 +    let prefix_length = current_path.iter().zip(&item_path).take_while(|(a, b)| a == b).count();
 +
 +    // how many modules differ between the two paths (all modules, removing any duplicates)
 +    let distinct_length = current_path.len() + item_path.len() - 2 * prefix_length;
 +
 +    // cost of importing from another crate
 +    let crate_boundary_cost = if current.krate() == item.krate() {
 +        0
 +    } else if item.krate().is_builtin(db) {
 +        2
 +    } else {
 +        4
 +    };
 +
 +    distinct_length + crate_boundary_cost
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use super::*;
 +
 +    use hir::Semantics;
 +    use ide_db::{
 +        assists::AssistResolveStrategy,
 +        base_db::{fixture::WithFixture, FileRange},
 +        RootDatabase,
 +    };
 +
 +    use crate::tests::{
 +        check_assist, check_assist_not_applicable, check_assist_target, TEST_CONFIG,
 +    };
 +
 +    fn check_auto_import_order(before: &str, order: &[&str]) {
 +        let (db, file_id, range_or_offset) = RootDatabase::with_range_or_offset(before);
 +        let frange = FileRange { file_id, range: range_or_offset.into() };
 +
 +        let sema = Semantics::new(&db);
 +        let config = TEST_CONFIG;
 +        let ctx = AssistContext::new(sema, &config, frange);
 +        let mut acc = Assists::new(&ctx, AssistResolveStrategy::All);
 +        auto_import(&mut acc, &ctx);
 +        let assists = acc.finish();
 +
 +        let labels = assists.iter().map(|assist| assist.label.to_string()).collect::<Vec<_>>();
 +
 +        assert_eq!(labels, order);
 +    }
 +
 +    #[test]
 +    fn ignore_parameter_name() {
 +        check_assist_not_applicable(
 +            auto_import,
 +            r"
 +            mod foo {
 +                pub mod bar {}
 +            }
 +
 +            fn foo(bar$0: &str) {}
 +            ",
 +        );
 +    }
 +
 +    #[test]
 +    fn prefer_shorter_paths() {
 +        let before = r"
 +//- /main.rs crate:main deps:foo,bar
 +HashMap$0::new();
 +
 +//- /lib.rs crate:foo
 +pub mod collections { pub struct HashMap; }
 +
 +//- /lib.rs crate:bar
 +pub mod collections { pub mod hash_map { pub struct HashMap; } }
 +        ";
 +
 +        check_auto_import_order(
 +            before,
 +            &["Import `foo::collections::HashMap`", "Import `bar::collections::hash_map::HashMap`"],
 +        )
 +    }
 +
 +    #[test]
 +    fn prefer_same_crate() {
 +        let before = r"
 +//- /main.rs crate:main deps:foo
 +HashMap$0::new();
 +
 +mod collections {
 +    pub mod hash_map {
 +        pub struct HashMap;
 +    }
 +}
 +
 +//- /lib.rs crate:foo
 +pub struct HashMap;
 +        ";
 +
 +        check_auto_import_order(
 +            before,
 +            &["Import `collections::hash_map::HashMap`", "Import `foo::HashMap`"],
 +        )
 +    }
 +
 +    #[test]
 +    fn not_applicable_if_scope_inside_macro() {
 +        check_assist_not_applicable(
 +            auto_import,
 +            r"
 +mod bar {
 +    pub struct Baz;
 +}
 +macro_rules! foo {
 +    ($it:ident) => {
 +        mod __ {
 +            fn __(x: $it) {}
 +        }
 +    };
 +}
 +foo! {
 +    Baz$0
 +}
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn applicable_in_attributes() {
 +        check_assist(
 +            auto_import,
 +            r"
 +//- proc_macros: identity
 +#[proc_macros::identity]
 +mod foo {
 +    mod bar {
 +        const _: Baz$0 = ();
 +    }
 +}
 +mod baz {
 +    pub struct Baz;
 +}
 +",
 +            r"
 +#[proc_macros::identity]
 +mod foo {
 +    mod bar {
 +        use crate::baz::Baz;
 +
 +        const _: Baz = ();
 +    }
 +}
 +mod baz {
 +    pub struct Baz;
 +}
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn applicable_when_found_an_import_partial() {
 +        check_assist(
 +            auto_import,
 +            r"
 +            mod std {
 +                pub mod fmt {
 +                    pub struct Formatter;
 +                }
 +            }
 +
 +            use std::fmt;
 +
 +            $0Formatter
 +            ",
 +            r"
 +            mod std {
 +                pub mod fmt {
 +                    pub struct Formatter;
 +                }
 +            }
 +
 +            use std::fmt::{self, Formatter};
 +
 +            Formatter
 +            ",
 +        );
 +    }
 +
 +    #[test]
 +    fn applicable_when_found_an_import() {
 +        check_assist(
 +            auto_import,
 +            r"
 +            $0PubStruct
 +
 +            pub mod PubMod {
 +                pub struct PubStruct;
 +            }
 +            ",
 +            r"
 +            use PubMod::PubStruct;
 +
 +            PubStruct
 +
 +            pub mod PubMod {
 +                pub struct PubStruct;
 +            }
 +            ",
 +        );
 +    }
 +
 +    #[test]
 +    fn applicable_when_found_an_import_in_macros() {
 +        check_assist(
 +            auto_import,
 +            r"
 +            macro_rules! foo {
 +                ($i:ident) => { fn foo(a: $i) {} }
 +            }
 +            foo!(Pub$0Struct);
 +
 +            pub mod PubMod {
 +                pub struct PubStruct;
 +            }
 +            ",
 +            r"
 +            use PubMod::PubStruct;
 +
 +            macro_rules! foo {
 +                ($i:ident) => { fn foo(a: $i) {} }
 +            }
 +            foo!(PubStruct);
 +
 +            pub mod PubMod {
 +                pub struct PubStruct;
 +            }
 +            ",
 +        );
 +    }
 +
 +    #[test]
 +    fn applicable_when_found_multiple_imports() {
 +        check_assist(
 +            auto_import,
 +            r"
 +            PubSt$0ruct
 +
 +            pub mod PubMod1 {
 +                pub struct PubStruct;
 +            }
 +            pub mod PubMod2 {
 +                pub struct PubStruct;
 +            }
 +            pub mod PubMod3 {
 +                pub struct PubStruct;
 +            }
 +            ",
 +            r"
 +            use PubMod3::PubStruct;
 +
 +            PubStruct
 +
 +            pub mod PubMod1 {
 +                pub struct PubStruct;
 +            }
 +            pub mod PubMod2 {
 +                pub struct PubStruct;
 +            }
 +            pub mod PubMod3 {
 +                pub struct PubStruct;
 +            }
 +            ",
 +        );
 +    }
 +
 +    #[test]
 +    fn not_applicable_for_already_imported_types() {
 +        check_assist_not_applicable(
 +            auto_import,
 +            r"
 +            use PubMod::PubStruct;
 +
 +            PubStruct$0
 +
 +            pub mod PubMod {
 +                pub struct PubStruct;
 +            }
 +            ",
 +        );
 +    }
 +
 +    #[test]
 +    fn not_applicable_for_types_with_private_paths() {
 +        check_assist_not_applicable(
 +            auto_import,
 +            r"
 +            PrivateStruct$0
 +
 +            pub mod PubMod {
 +                struct PrivateStruct;
 +            }
 +            ",
 +        );
 +    }
 +
 +    #[test]
 +    fn not_applicable_when_no_imports_found() {
 +        check_assist_not_applicable(
 +            auto_import,
 +            "
 +            PubStruct$0",
 +        );
 +    }
 +
 +    #[test]
 +    fn function_import() {
 +        check_assist(
 +            auto_import,
 +            r"
 +            test_function$0
 +
 +            pub mod PubMod {
 +                pub fn test_function() {};
 +            }
 +            ",
 +            r"
 +            use PubMod::test_function;
 +
 +            test_function
 +
 +            pub mod PubMod {
 +                pub fn test_function() {};
 +            }
 +            ",
 +        );
 +    }
 +
 +    #[test]
 +    fn macro_import() {
 +        check_assist(
 +            auto_import,
 +            r"
 +//- /lib.rs crate:crate_with_macro
 +#[macro_export]
 +macro_rules! foo {
 +    () => ()
 +}
 +
 +//- /main.rs crate:main deps:crate_with_macro
 +fn main() {
 +    foo$0
 +}
 +",
 +            r"use crate_with_macro::foo;
 +
 +fn main() {
 +    foo
 +}
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn auto_import_target() {
 +        check_assist_target(
 +            auto_import,
 +            r"
 +            struct AssistInfo {
 +                group_label: Option<$0GroupLabel>,
 +            }
 +
 +            mod m { pub struct GroupLabel; }
 +            ",
 +            "GroupLabel",
 +        )
 +    }
 +
 +    #[test]
 +    fn not_applicable_when_path_start_is_imported() {
 +        check_assist_not_applicable(
 +            auto_import,
 +            r"
 +            pub mod mod1 {
 +                pub mod mod2 {
 +                    pub mod mod3 {
 +                        pub struct TestStruct;
 +                    }
 +                }
 +            }
 +
 +            use mod1::mod2;
 +            fn main() {
 +                mod2::mod3::TestStruct$0
 +            }
 +            ",
 +        );
 +    }
 +
 +    #[test]
 +    fn not_applicable_for_imported_function() {
 +        check_assist_not_applicable(
 +            auto_import,
 +            r"
 +            pub mod test_mod {
 +                pub fn test_function() {}
 +            }
 +
 +            use test_mod::test_function;
 +            fn main() {
 +                test_function$0
 +            }
 +            ",
 +        );
 +    }
 +
 +    #[test]
 +    fn associated_struct_function() {
 +        check_assist(
 +            auto_import,
 +            r"
 +            mod test_mod {
 +                pub struct TestStruct {}
 +                impl TestStruct {
 +                    pub fn test_function() {}
 +                }
 +            }
 +
 +            fn main() {
 +                TestStruct::test_function$0
 +            }
 +            ",
 +            r"
 +            use test_mod::TestStruct;
 +
 +            mod test_mod {
 +                pub struct TestStruct {}
 +                impl TestStruct {
 +                    pub fn test_function() {}
 +                }
 +            }
 +
 +            fn main() {
 +                TestStruct::test_function
 +            }
 +            ",
 +        );
 +    }
 +
 +    #[test]
 +    fn associated_struct_const() {
 +        check_assist(
 +            auto_import,
 +            r"
 +            mod test_mod {
 +                pub struct TestStruct {}
 +                impl TestStruct {
 +                    const TEST_CONST: u8 = 42;
 +                }
 +            }
 +
 +            fn main() {
 +                TestStruct::TEST_CONST$0
 +            }
 +            ",
 +            r"
 +            use test_mod::TestStruct;
 +
 +            mod test_mod {
 +                pub struct TestStruct {}
 +                impl TestStruct {
 +                    const TEST_CONST: u8 = 42;
 +                }
 +            }
 +
 +            fn main() {
 +                TestStruct::TEST_CONST
 +            }
 +            ",
 +        );
 +    }
 +
 +    #[test]
 +    fn associated_trait_function() {
 +        check_assist(
 +            auto_import,
 +            r"
 +            mod test_mod {
 +                pub trait TestTrait {
 +                    fn test_function();
 +                }
 +                pub struct TestStruct {}
 +                impl TestTrait for TestStruct {
 +                    fn test_function() {}
 +                }
 +            }
 +
 +            fn main() {
 +                test_mod::TestStruct::test_function$0
 +            }
 +            ",
 +            r"
 +            use test_mod::TestTrait;
 +
 +            mod test_mod {
 +                pub trait TestTrait {
 +                    fn test_function();
 +                }
 +                pub struct TestStruct {}
 +                impl TestTrait for TestStruct {
 +                    fn test_function() {}
 +                }
 +            }
 +
 +            fn main() {
 +                test_mod::TestStruct::test_function
 +            }
 +            ",
 +        );
 +    }
 +
 +    #[test]
 +    fn not_applicable_for_imported_trait_for_function() {
 +        check_assist_not_applicable(
 +            auto_import,
 +            r"
 +            mod test_mod {
 +                pub trait TestTrait {
 +                    fn test_function();
 +                }
 +                pub trait TestTrait2 {
 +                    fn test_function();
 +                }
 +                pub enum TestEnum {
 +                    One,
 +                    Two,
 +                }
 +                impl TestTrait2 for TestEnum {
 +                    fn test_function() {}
 +                }
 +                impl TestTrait for TestEnum {
 +                    fn test_function() {}
 +                }
 +            }
 +
 +            use test_mod::TestTrait2;
 +            fn main() {
 +                test_mod::TestEnum::test_function$0;
 +            }
 +            ",
 +        )
 +    }
 +
 +    #[test]
 +    fn associated_trait_const() {
 +        check_assist(
 +            auto_import,
 +            r"
 +            mod test_mod {
 +                pub trait TestTrait {
 +                    const TEST_CONST: u8;
 +                }
 +                pub struct TestStruct {}
 +                impl TestTrait for TestStruct {
 +                    const TEST_CONST: u8 = 42;
 +                }
 +            }
 +
 +            fn main() {
 +                test_mod::TestStruct::TEST_CONST$0
 +            }
 +            ",
 +            r"
 +            use test_mod::TestTrait;
 +
 +            mod test_mod {
 +                pub trait TestTrait {
 +                    const TEST_CONST: u8;
 +                }
 +                pub struct TestStruct {}
 +                impl TestTrait for TestStruct {
 +                    const TEST_CONST: u8 = 42;
 +                }
 +            }
 +
 +            fn main() {
 +                test_mod::TestStruct::TEST_CONST
 +            }
 +            ",
 +        );
 +    }
 +
 +    #[test]
 +    fn not_applicable_for_imported_trait_for_const() {
 +        check_assist_not_applicable(
 +            auto_import,
 +            r"
 +            mod test_mod {
 +                pub trait TestTrait {
 +                    const TEST_CONST: u8;
 +                }
 +                pub trait TestTrait2 {
 +                    const TEST_CONST: f64;
 +                }
 +                pub enum TestEnum {
 +                    One,
 +                    Two,
 +                }
 +                impl TestTrait2 for TestEnum {
 +                    const TEST_CONST: f64 = 42.0;
 +                }
 +                impl TestTrait for TestEnum {
 +                    const TEST_CONST: u8 = 42;
 +                }
 +            }
 +
 +            use test_mod::TestTrait2;
 +            fn main() {
 +                test_mod::TestEnum::TEST_CONST$0;
 +            }
 +            ",
 +        )
 +    }
 +
 +    #[test]
 +    fn trait_method() {
 +        check_assist(
 +            auto_import,
 +            r"
 +            mod test_mod {
 +                pub trait TestTrait {
 +                    fn test_method(&self);
 +                }
 +                pub struct TestStruct {}
 +                impl TestTrait for TestStruct {
 +                    fn test_method(&self) {}
 +                }
 +            }
 +
 +            fn main() {
 +                let test_struct = test_mod::TestStruct {};
 +                test_struct.test_meth$0od()
 +            }
 +            ",
 +            r"
 +            use test_mod::TestTrait;
 +
 +            mod test_mod {
 +                pub trait TestTrait {
 +                    fn test_method(&self);
 +                }
 +                pub struct TestStruct {}
 +                impl TestTrait for TestStruct {
 +                    fn test_method(&self) {}
 +                }
 +            }
 +
 +            fn main() {
 +                let test_struct = test_mod::TestStruct {};
 +                test_struct.test_method()
 +            }
 +            ",
 +        );
 +    }
 +
 +    #[test]
 +    fn trait_method_cross_crate() {
 +        check_assist(
 +            auto_import,
 +            r"
 +            //- /main.rs crate:main deps:dep
 +            fn main() {
 +                let test_struct = dep::test_mod::TestStruct {};
 +                test_struct.test_meth$0od()
 +            }
 +            //- /dep.rs crate:dep
 +            pub mod test_mod {
 +                pub trait TestTrait {
 +                    fn test_method(&self);
 +                }
 +                pub struct TestStruct {}
 +                impl TestTrait for TestStruct {
 +                    fn test_method(&self) {}
 +                }
 +            }
 +            ",
 +            r"
 +            use dep::test_mod::TestTrait;
 +
 +            fn main() {
 +                let test_struct = dep::test_mod::TestStruct {};
 +                test_struct.test_method()
 +            }
 +            ",
 +        );
 +    }
 +
 +    #[test]
 +    fn assoc_fn_cross_crate() {
 +        check_assist(
 +            auto_import,
 +            r"
 +            //- /main.rs crate:main deps:dep
 +            fn main() {
 +                dep::test_mod::TestStruct::test_func$0tion
 +            }
 +            //- /dep.rs crate:dep
 +            pub mod test_mod {
 +                pub trait TestTrait {
 +                    fn test_function();
 +                }
 +                pub struct TestStruct {}
 +                impl TestTrait for TestStruct {
 +                    fn test_function() {}
 +                }
 +            }
 +            ",
 +            r"
 +            use dep::test_mod::TestTrait;
 +
 +            fn main() {
 +                dep::test_mod::TestStruct::test_function
 +            }
 +            ",
 +        );
 +    }
 +
 +    #[test]
 +    fn assoc_const_cross_crate() {
 +        check_assist(
 +            auto_import,
 +            r"
 +            //- /main.rs crate:main deps:dep
 +            fn main() {
 +                dep::test_mod::TestStruct::CONST$0
 +            }
 +            //- /dep.rs crate:dep
 +            pub mod test_mod {
 +                pub trait TestTrait {
 +                    const CONST: bool;
 +                }
 +                pub struct TestStruct {}
 +                impl TestTrait for TestStruct {
 +                    const CONST: bool = true;
 +                }
 +            }
 +            ",
 +            r"
 +            use dep::test_mod::TestTrait;
 +
 +            fn main() {
 +                dep::test_mod::TestStruct::CONST
 +            }
 +            ",
 +        );
 +    }
 +
 +    #[test]
 +    fn assoc_fn_as_method_cross_crate() {
 +        check_assist_not_applicable(
 +            auto_import,
 +            r"
 +            //- /main.rs crate:main deps:dep
 +            fn main() {
 +                let test_struct = dep::test_mod::TestStruct {};
 +                test_struct.test_func$0tion()
 +            }
 +            //- /dep.rs crate:dep
 +            pub mod test_mod {
 +                pub trait TestTrait {
 +                    fn test_function();
 +                }
 +                pub struct TestStruct {}
 +                impl TestTrait for TestStruct {
 +                    fn test_function() {}
 +                }
 +            }
 +            ",
 +        );
 +    }
 +
 +    #[test]
 +    fn private_trait_cross_crate() {
 +        check_assist_not_applicable(
 +            auto_import,
 +            r"
 +            //- /main.rs crate:main deps:dep
 +            fn main() {
 +                let test_struct = dep::test_mod::TestStruct {};
 +                test_struct.test_meth$0od()
 +            }
 +            //- /dep.rs crate:dep
 +            pub mod test_mod {
 +                trait TestTrait {
 +                    fn test_method(&self);
 +                }
 +                pub struct TestStruct {}
 +                impl TestTrait for TestStruct {
 +                    fn test_method(&self) {}
 +                }
 +            }
 +            ",
 +        );
 +    }
 +
 +    #[test]
 +    fn not_applicable_for_imported_trait_for_method() {
 +        check_assist_not_applicable(
 +            auto_import,
 +            r"
 +            mod test_mod {
 +                pub trait TestTrait {
 +                    fn test_method(&self);
 +                }
 +                pub trait TestTrait2 {
 +                    fn test_method(&self);
 +                }
 +                pub enum TestEnum {
 +                    One,
 +                    Two,
 +                }
 +                impl TestTrait2 for TestEnum {
 +                    fn test_method(&self) {}
 +                }
 +                impl TestTrait for TestEnum {
 +                    fn test_method(&self) {}
 +                }
 +            }
 +
 +            use test_mod::TestTrait2;
 +            fn main() {
 +                let one = test_mod::TestEnum::One;
 +                one.test$0_method();
 +            }
 +            ",
 +        )
 +    }
 +
 +    #[test]
 +    fn dep_import() {
 +        check_assist(
 +            auto_import,
 +            r"
 +//- /lib.rs crate:dep
 +pub struct Struct;
 +
 +//- /main.rs crate:main deps:dep
 +fn main() {
 +    Struct$0
 +}
 +",
 +            r"use dep::Struct;
 +
 +fn main() {
 +    Struct
 +}
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn whole_segment() {
 +        // Tests that only imports whose last segment matches the identifier get suggested.
 +        check_assist(
 +            auto_import,
 +            r"
 +//- /lib.rs crate:dep
 +pub mod fmt {
 +    pub trait Display {}
 +}
 +
 +pub fn panic_fmt() {}
 +
 +//- /main.rs crate:main deps:dep
 +struct S;
 +
 +impl f$0mt::Display for S {}
 +",
 +            r"use dep::fmt;
 +
 +struct S;
 +
 +impl fmt::Display for S {}
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn macro_generated() {
 +        // Tests that macro-generated items are suggested from external crates.
 +        check_assist(
 +            auto_import,
 +            r"
 +//- /lib.rs crate:dep
 +macro_rules! mac {
 +    () => {
 +        pub struct Cheese;
 +    };
 +}
 +
 +mac!();
 +
 +//- /main.rs crate:main deps:dep
 +fn main() {
 +    Cheese$0;
 +}
 +",
 +            r"use dep::Cheese;
 +
 +fn main() {
 +    Cheese;
 +}
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn casing() {
 +        // Tests that differently cased names don't interfere and we only suggest the matching one.
 +        check_assist(
 +            auto_import,
 +            r"
 +//- /lib.rs crate:dep
 +pub struct FMT;
 +pub struct fmt;
 +
 +//- /main.rs crate:main deps:dep
 +fn main() {
 +    FMT$0;
 +}
 +",
 +            r"use dep::FMT;
 +
 +fn main() {
 +    FMT;
 +}
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn inner_items() {
 +        check_assist(
 +            auto_import,
 +            r#"
 +mod baz {
 +    pub struct Foo {}
 +}
 +
 +mod bar {
 +    fn bar() {
 +        Foo$0;
 +        println!("Hallo");
 +    }
 +}
 +"#,
 +            r#"
 +mod baz {
 +    pub struct Foo {}
 +}
 +
 +mod bar {
 +    use crate::baz::Foo;
 +
 +    fn bar() {
 +        Foo;
 +        println!("Hallo");
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn uses_abs_path_with_extern_crate_clash() {
 +        cov_mark::check!(ambiguous_crate_start);
 +        check_assist(
 +            auto_import,
 +            r#"
 +//- /main.rs crate:main deps:foo
 +mod foo {}
 +
 +const _: () = {
 +    Foo$0
 +};
 +//- /foo.rs crate:foo
 +pub struct Foo
 +"#,
 +            r#"
 +use ::foo::Foo;
 +
 +mod foo {}
 +
 +const _: () = {
 +    Foo
 +};
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn works_on_ident_patterns() {
 +        check_assist(
 +            auto_import,
 +            r#"
 +mod foo {
 +    pub struct Foo {}
 +}
 +fn foo() {
 +    let Foo$0;
 +}
 +"#,
 +            r#"
 +use foo::Foo;
 +
 +mod foo {
 +    pub struct Foo {}
 +}
 +fn foo() {
 +    let Foo;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn works_in_derives() {
 +        check_assist(
 +            auto_import,
 +            r#"
 +//- minicore:derive
 +mod foo {
 +    #[rustc_builtin_macro]
 +    pub macro Copy {}
 +}
 +#[derive(Copy$0)]
 +struct Foo;
 +"#,
 +            r#"
 +use foo::Copy;
 +
 +mod foo {
 +    #[rustc_builtin_macro]
 +    pub macro Copy {}
 +}
 +#[derive(Copy)]
 +struct Foo;
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn works_in_use_start() {
 +        check_assist(
 +            auto_import,
 +            r#"
 +mod bar {
 +    pub mod foo {
 +        pub struct Foo;
 +    }
 +}
 +use foo$0::Foo;
 +"#,
 +            r#"
 +mod bar {
 +    pub mod foo {
 +        pub struct Foo;
 +    }
 +}
 +use bar::foo;
 +use foo::Foo;
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn not_applicable_in_non_start_use() {
 +        check_assist_not_applicable(
 +            auto_import,
 +            r"
 +mod bar {
 +    pub mod foo {
 +        pub struct Foo;
 +    }
 +}
 +use foo::Foo$0;
 +",
 +        );
 +    }
 +}
index f171dd81a811e7183d70e673e37806fbe813ce87,0000000000000000000000000000000000000000..312cb65abd2a1ce5d7699db05308bbb912c92981
mode 100644,000000..100644
--- /dev/null
@@@ -1,395 -1,0 +1,396 @@@
-                 .map(|l| l.trim_start_matches(&indent_spaces))
-                 .map(|l| {
 +use itertools::Itertools;
 +use syntax::{
 +    ast::{self, edit::IndentLevel, Comment, CommentKind, CommentShape, Whitespace},
 +    AstToken, Direction, SyntaxElement, TextRange,
 +};
 +
 +use crate::{AssistContext, AssistId, AssistKind, Assists};
 +
 +// Assist: line_to_block
 +//
 +// Converts comments between block and single-line form.
 +//
 +// ```
 +//    // Multi-line$0
 +//    // comment
 +// ```
 +// ->
 +// ```
 +//   /*
 +//   Multi-line
 +//   comment
 +//   */
 +// ```
 +pub(crate) fn convert_comment_block(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    let comment = ctx.find_token_at_offset::<ast::Comment>()?;
 +    // Only allow comments which are alone on their line
 +    if let Some(prev) = comment.syntax().prev_token() {
 +        if Whitespace::cast(prev).filter(|w| w.text().contains('\n')).is_none() {
 +            return None;
 +        }
 +    }
 +
 +    match comment.kind().shape {
 +        ast::CommentShape::Block => block_to_line(acc, comment),
 +        ast::CommentShape::Line => line_to_block(acc, comment),
 +    }
 +}
 +
 +fn block_to_line(acc: &mut Assists, comment: ast::Comment) -> Option<()> {
 +    let target = comment.syntax().text_range();
 +
 +    acc.add(
 +        AssistId("block_to_line", AssistKind::RefactorRewrite),
 +        "Replace block comment with line comments",
 +        target,
 +        |edit| {
 +            let indentation = IndentLevel::from_token(comment.syntax());
 +            let line_prefix = CommentKind { shape: CommentShape::Line, ..comment.kind() }.prefix();
 +
 +            let text = comment.text();
 +            let text = &text[comment.prefix().len()..(text.len() - "*/".len())].trim();
 +
 +            let lines = text.lines().peekable();
 +
 +            let indent_spaces = indentation.to_string();
 +            let output = lines
-                     if l.is_empty() {
++                .map(|line| {
++                    let line = line.trim_start_matches(&indent_spaces);
++
 +                    // Don't introduce trailing whitespace
-                         format!("{} {}", line_prefix, l.trim_start_matches(&indent_spaces))
++                    if line.is_empty() {
 +                        line_prefix.to_string()
 +                    } else {
-                 .join(&format!("\n{}", indent_spaces));
++                        format!("{line_prefix} {line}")
 +                    }
 +                })
-             let output = format!("{}\n{}\n{}*/", block_prefix, block_comment_body, indentation);
++                .join(&format!("\n{indent_spaces}"));
 +
 +            edit.replace(target, output)
 +        },
 +    )
 +}
 +
 +fn line_to_block(acc: &mut Assists, comment: ast::Comment) -> Option<()> {
 +    // Find all the comments we'll be collapsing into a block
 +    let comments = relevant_line_comments(&comment);
 +
 +    // Establish the target of our edit based on the comments we found
 +    let target = TextRange::new(
 +        comments[0].syntax().text_range().start(),
 +        comments.last().unwrap().syntax().text_range().end(),
 +    );
 +
 +    acc.add(
 +        AssistId("line_to_block", AssistKind::RefactorRewrite),
 +        "Replace line comments with a single block comment",
 +        target,
 +        |edit| {
 +            // We pick a single indentation level for the whole block comment based on the
 +            // comment where the assist was invoked. This will be prepended to the
 +            // contents of each line comment when they're put into the block comment.
 +            let indentation = IndentLevel::from_token(comment.syntax());
 +
 +            let block_comment_body =
 +                comments.into_iter().map(|c| line_comment_text(indentation, c)).join("\n");
 +
 +            let block_prefix =
 +                CommentKind { shape: CommentShape::Block, ..comment.kind() }.prefix();
 +
++            let output = format!("{block_prefix}\n{block_comment_body}\n{indentation}*/");
 +
 +            edit.replace(target, output)
 +        },
 +    )
 +}
 +
 +/// The line -> block assist can  be invoked from anywhere within a sequence of line comments.
 +/// relevant_line_comments crawls backwards and forwards finding the complete sequence of comments that will
 +/// be joined.
 +fn relevant_line_comments(comment: &ast::Comment) -> Vec<Comment> {
 +    // The prefix identifies the kind of comment we're dealing with
 +    let prefix = comment.prefix();
 +    let same_prefix = |c: &ast::Comment| c.prefix() == prefix;
 +
 +    // These tokens are allowed to exist between comments
 +    let skippable = |not: &SyntaxElement| {
 +        not.clone()
 +            .into_token()
 +            .and_then(Whitespace::cast)
 +            .map(|w| !w.spans_multiple_lines())
 +            .unwrap_or(false)
 +    };
 +
 +    // Find all preceding comments (in reverse order) that have the same prefix
 +    let prev_comments = comment
 +        .syntax()
 +        .siblings_with_tokens(Direction::Prev)
 +        .filter(|s| !skippable(s))
 +        .map(|not| not.into_token().and_then(Comment::cast).filter(same_prefix))
 +        .take_while(|opt_com| opt_com.is_some())
 +        .flatten()
 +        .skip(1); // skip the first element so we don't duplicate it in next_comments
 +
 +    let next_comments = comment
 +        .syntax()
 +        .siblings_with_tokens(Direction::Next)
 +        .filter(|s| !skippable(s))
 +        .map(|not| not.into_token().and_then(Comment::cast).filter(same_prefix))
 +        .take_while(|opt_com| opt_com.is_some())
 +        .flatten();
 +
 +    let mut comments: Vec<_> = prev_comments.collect();
 +    comments.reverse();
 +    comments.extend(next_comments);
 +    comments
 +}
 +
 +// Line comments usually begin with a single space character following the prefix as seen here:
 +//^
 +// But comments can also include indented text:
 +//    > Hello there
 +//
 +// We handle this by stripping *AT MOST* one space character from the start of the line
 +// This has its own problems because it can cause alignment issues:
 +//
 +//              /*
 +// a      ----> a
 +//b       ----> b
 +//              */
 +//
 +// But since such comments aren't idiomatic we're okay with this.
 +fn line_comment_text(indentation: IndentLevel, comm: ast::Comment) -> String {
 +    let contents_without_prefix = comm.text().strip_prefix(comm.prefix()).unwrap();
 +    let contents = contents_without_prefix.strip_prefix(' ').unwrap_or(contents_without_prefix);
 +
 +    // Don't add the indentation if the line is empty
 +    if contents.is_empty() {
 +        contents.to_owned()
 +    } else {
 +        indentation.to_string() + contents
 +    }
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use crate::tests::{check_assist, check_assist_not_applicable};
 +
 +    use super::*;
 +
 +    #[test]
 +    fn single_line_to_block() {
 +        check_assist(
 +            convert_comment_block,
 +            r#"
 +// line$0 comment
 +fn main() {
 +    foo();
 +}
 +"#,
 +            r#"
 +/*
 +line comment
 +*/
 +fn main() {
 +    foo();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn single_line_to_block_indented() {
 +        check_assist(
 +            convert_comment_block,
 +            r#"
 +fn main() {
 +    // line$0 comment
 +    foo();
 +}
 +"#,
 +            r#"
 +fn main() {
 +    /*
 +    line comment
 +    */
 +    foo();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn multiline_to_block() {
 +        check_assist(
 +            convert_comment_block,
 +            r#"
 +fn main() {
 +    // above
 +    // line$0 comment
 +    //
 +    // below
 +    foo();
 +}
 +"#,
 +            r#"
 +fn main() {
 +    /*
 +    above
 +    line comment
 +
 +    below
 +    */
 +    foo();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn end_of_line_to_block() {
 +        check_assist_not_applicable(
 +            convert_comment_block,
 +            r#"
 +fn main() {
 +    foo(); // end-of-line$0 comment
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn single_line_different_kinds() {
 +        check_assist(
 +            convert_comment_block,
 +            r#"
 +fn main() {
 +    /// different prefix
 +    // line$0 comment
 +    // below
 +    foo();
 +}
 +"#,
 +            r#"
 +fn main() {
 +    /// different prefix
 +    /*
 +    line comment
 +    below
 +    */
 +    foo();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn single_line_separate_chunks() {
 +        check_assist(
 +            convert_comment_block,
 +            r#"
 +fn main() {
 +    // different chunk
 +
 +    // line$0 comment
 +    // below
 +    foo();
 +}
 +"#,
 +            r#"
 +fn main() {
 +    // different chunk
 +
 +    /*
 +    line comment
 +    below
 +    */
 +    foo();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn doc_block_comment_to_lines() {
 +        check_assist(
 +            convert_comment_block,
 +            r#"
 +/**
 + hi$0 there
 +*/
 +"#,
 +            r#"
 +/// hi there
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn block_comment_to_lines() {
 +        check_assist(
 +            convert_comment_block,
 +            r#"
 +/*
 + hi$0 there
 +*/
 +"#,
 +            r#"
 +// hi there
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn inner_doc_block_to_lines() {
 +        check_assist(
 +            convert_comment_block,
 +            r#"
 +/*!
 + hi$0 there
 +*/
 +"#,
 +            r#"
 +//! hi there
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn block_to_lines_indent() {
 +        check_assist(
 +            convert_comment_block,
 +            r#"
 +fn main() {
 +    /*!
 +    hi$0 there
 +
 +    ```
 +      code_sample
 +    ```
 +    */
 +}
 +"#,
 +            r#"
 +fn main() {
 +    //! hi there
 +    //!
 +    //! ```
 +    //!   code_sample
 +    //! ```
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn end_of_line_block_to_line() {
 +        check_assist_not_applicable(
 +            convert_comment_block,
 +            r#"
 +fn main() {
 +    foo(); /* end-of-line$0 comment */
 +}
 +"#,
 +        );
 +    }
 +}
index 9060696cdc8e9c76fd26cd5c7eda993d0ebb15d6,0000000000000000000000000000000000000000..ff2195f7e6c4a2ebf6829889b3336eb52570c779
mode 100644,000000..100644
--- /dev/null
@@@ -1,268 -1,0 +1,268 @@@
-             Radix::Binary => format!("0b{:b}", value),
-             Radix::Octal => format!("0o{:o}", value),
 +use syntax::{ast, ast::Radix, AstToken};
 +
 +use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
 +
 +// Assist: convert_integer_literal
 +//
 +// Converts the base of integer literals to other bases.
 +//
 +// ```
 +// const _: i32 = 10$0;
 +// ```
 +// ->
 +// ```
 +// const _: i32 = 0b1010;
 +// ```
 +pub(crate) fn convert_integer_literal(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    let literal = ctx.find_node_at_offset::<ast::Literal>()?;
 +    let literal = match literal.kind() {
 +        ast::LiteralKind::IntNumber(it) => it,
 +        _ => return None,
 +    };
 +    let radix = literal.radix();
 +    let value = literal.value()?;
 +    let suffix = literal.suffix();
 +
 +    let range = literal.syntax().text_range();
 +    let group_id = GroupLabel("Convert integer base".into());
 +
 +    for &target_radix in Radix::ALL {
 +        if target_radix == radix {
 +            continue;
 +        }
 +
 +        let mut converted = match target_radix {
-             Radix::Hexadecimal => format!("0x{:X}", value),
++            Radix::Binary => format!("0b{value:b}"),
++            Radix::Octal => format!("0o{value:o}"),
 +            Radix::Decimal => value.to_string(),
-         let label = format!("Convert {} to {}{}", literal, converted, suffix.unwrap_or_default());
++            Radix::Hexadecimal => format!("0x{value:X}"),
 +        };
 +
 +        // Appends the type suffix back into the new literal if it exists.
 +        if let Some(suffix) = suffix {
 +            converted.push_str(suffix);
 +        }
 +
++        let label = format!("Convert {literal} to {converted}");
++
 +        acc.add_group(
 +            &group_id,
 +            AssistId("convert_integer_literal", AssistKind::RefactorInline),
 +            label,
 +            range,
 +            |builder| builder.replace(range, converted),
 +        );
 +    }
 +
 +    Some(())
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use crate::tests::{check_assist_by_label, check_assist_not_applicable, check_assist_target};
 +
 +    use super::*;
 +
 +    #[test]
 +    fn binary_target() {
 +        check_assist_target(convert_integer_literal, "const _: i32 = 0b1010$0;", "0b1010");
 +    }
 +
 +    #[test]
 +    fn octal_target() {
 +        check_assist_target(convert_integer_literal, "const _: i32 = 0o12$0;", "0o12");
 +    }
 +
 +    #[test]
 +    fn decimal_target() {
 +        check_assist_target(convert_integer_literal, "const _: i32 = 10$0;", "10");
 +    }
 +
 +    #[test]
 +    fn hexadecimal_target() {
 +        check_assist_target(convert_integer_literal, "const _: i32 = 0xA$0;", "0xA");
 +    }
 +
 +    #[test]
 +    fn binary_target_with_underscores() {
 +        check_assist_target(convert_integer_literal, "const _: i32 = 0b10_10$0;", "0b10_10");
 +    }
 +
 +    #[test]
 +    fn octal_target_with_underscores() {
 +        check_assist_target(convert_integer_literal, "const _: i32 = 0o1_2$0;", "0o1_2");
 +    }
 +
 +    #[test]
 +    fn decimal_target_with_underscores() {
 +        check_assist_target(convert_integer_literal, "const _: i32 = 1_0$0;", "1_0");
 +    }
 +
 +    #[test]
 +    fn hexadecimal_target_with_underscores() {
 +        check_assist_target(convert_integer_literal, "const _: i32 = 0x_A$0;", "0x_A");
 +    }
 +
 +    #[test]
 +    fn convert_decimal_integer() {
 +        let before = "const _: i32 = 1000$0;";
 +
 +        check_assist_by_label(
 +            convert_integer_literal,
 +            before,
 +            "const _: i32 = 0b1111101000;",
 +            "Convert 1000 to 0b1111101000",
 +        );
 +
 +        check_assist_by_label(
 +            convert_integer_literal,
 +            before,
 +            "const _: i32 = 0o1750;",
 +            "Convert 1000 to 0o1750",
 +        );
 +
 +        check_assist_by_label(
 +            convert_integer_literal,
 +            before,
 +            "const _: i32 = 0x3E8;",
 +            "Convert 1000 to 0x3E8",
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_hexadecimal_integer() {
 +        let before = "const _: i32 = 0xFF$0;";
 +
 +        check_assist_by_label(
 +            convert_integer_literal,
 +            before,
 +            "const _: i32 = 0b11111111;",
 +            "Convert 0xFF to 0b11111111",
 +        );
 +
 +        check_assist_by_label(
 +            convert_integer_literal,
 +            before,
 +            "const _: i32 = 0o377;",
 +            "Convert 0xFF to 0o377",
 +        );
 +
 +        check_assist_by_label(
 +            convert_integer_literal,
 +            before,
 +            "const _: i32 = 255;",
 +            "Convert 0xFF to 255",
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_binary_integer() {
 +        let before = "const _: i32 = 0b11111111$0;";
 +
 +        check_assist_by_label(
 +            convert_integer_literal,
 +            before,
 +            "const _: i32 = 0o377;",
 +            "Convert 0b11111111 to 0o377",
 +        );
 +
 +        check_assist_by_label(
 +            convert_integer_literal,
 +            before,
 +            "const _: i32 = 255;",
 +            "Convert 0b11111111 to 255",
 +        );
 +
 +        check_assist_by_label(
 +            convert_integer_literal,
 +            before,
 +            "const _: i32 = 0xFF;",
 +            "Convert 0b11111111 to 0xFF",
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_octal_integer() {
 +        let before = "const _: i32 = 0o377$0;";
 +
 +        check_assist_by_label(
 +            convert_integer_literal,
 +            before,
 +            "const _: i32 = 0b11111111;",
 +            "Convert 0o377 to 0b11111111",
 +        );
 +
 +        check_assist_by_label(
 +            convert_integer_literal,
 +            before,
 +            "const _: i32 = 255;",
 +            "Convert 0o377 to 255",
 +        );
 +
 +        check_assist_by_label(
 +            convert_integer_literal,
 +            before,
 +            "const _: i32 = 0xFF;",
 +            "Convert 0o377 to 0xFF",
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_integer_with_underscores() {
 +        let before = "const _: i32 = 1_00_0$0;";
 +
 +        check_assist_by_label(
 +            convert_integer_literal,
 +            before,
 +            "const _: i32 = 0b1111101000;",
 +            "Convert 1_00_0 to 0b1111101000",
 +        );
 +
 +        check_assist_by_label(
 +            convert_integer_literal,
 +            before,
 +            "const _: i32 = 0o1750;",
 +            "Convert 1_00_0 to 0o1750",
 +        );
 +
 +        check_assist_by_label(
 +            convert_integer_literal,
 +            before,
 +            "const _: i32 = 0x3E8;",
 +            "Convert 1_00_0 to 0x3E8",
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_integer_with_suffix() {
 +        let before = "const _: i32 = 1000i32$0;";
 +
 +        check_assist_by_label(
 +            convert_integer_literal,
 +            before,
 +            "const _: i32 = 0b1111101000i32;",
 +            "Convert 1000i32 to 0b1111101000i32",
 +        );
 +
 +        check_assist_by_label(
 +            convert_integer_literal,
 +            before,
 +            "const _: i32 = 0o1750i32;",
 +            "Convert 1000i32 to 0o1750i32",
 +        );
 +
 +        check_assist_by_label(
 +            convert_integer_literal,
 +            before,
 +            "const _: i32 = 0x3E8i32;",
 +            "Convert 1000i32 to 0x3E8i32",
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_overflowing_literal() {
 +        let before = "const _: i32 =
 +            111111111111111111111111111111111111111111111111111111111111111111111111$0;";
 +        check_assist_not_applicable(convert_integer_literal, before);
 +    }
 +}
index 95d11abe8bc0f6fea075297b0c1473e81ec7a77b,0000000000000000000000000000000000000000..872b52c98fff2fbf5a3e56d1c27165eb2bdb61f9
mode 100644,000000..100644
--- /dev/null
@@@ -1,351 -1,0 +1,351 @@@
-             builder.replace(ast_trait.syntax().text_range(), format!("From<{}>", src_type));
 +use ide_db::{famous_defs::FamousDefs, helpers::mod_path_to_ast, traits::resolve_target_trait};
 +use syntax::ast::{self, AstNode, HasName};
 +
 +use crate::{AssistContext, AssistId, AssistKind, Assists};
 +
 +// FIXME: this should be a diagnostic
 +
 +// Assist: convert_into_to_from
 +//
 +// Converts an Into impl to an equivalent From impl.
 +//
 +// ```
 +// # //- minicore: from
 +// impl $0Into<Thing> for usize {
 +//     fn into(self) -> Thing {
 +//         Thing {
 +//             b: self.to_string(),
 +//             a: self
 +//         }
 +//     }
 +// }
 +// ```
 +// ->
 +// ```
 +// impl From<usize> for Thing {
 +//     fn from(val: usize) -> Self {
 +//         Thing {
 +//             b: val.to_string(),
 +//             a: val
 +//         }
 +//     }
 +// }
 +// ```
 +pub(crate) fn convert_into_to_from(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    let impl_ = ctx.find_node_at_offset::<ast::Impl>()?;
 +    let src_type = impl_.self_ty()?;
 +    let ast_trait = impl_.trait_()?;
 +
 +    let module = ctx.sema.scope(impl_.syntax())?.module();
 +
 +    let trait_ = resolve_target_trait(&ctx.sema, &impl_)?;
 +    if trait_ != FamousDefs(&ctx.sema, module.krate()).core_convert_Into()? {
 +        return None;
 +    }
 +
 +    let src_type_path = {
 +        let src_type_path = src_type.syntax().descendants().find_map(ast::Path::cast)?;
 +        let src_type_def = match ctx.sema.resolve_path(&src_type_path) {
 +            Some(hir::PathResolution::Def(module_def)) => module_def,
 +            _ => return None,
 +        };
 +
 +        mod_path_to_ast(&module.find_use_path(ctx.db(), src_type_def, ctx.config.prefer_no_std)?)
 +    };
 +
 +    let dest_type = match &ast_trait {
 +        ast::Type::PathType(path) => {
 +            path.path()?.segment()?.generic_arg_list()?.generic_args().next()?
 +        }
 +        _ => return None,
 +    };
 +
 +    let into_fn = impl_.assoc_item_list()?.assoc_items().find_map(|item| {
 +        if let ast::AssocItem::Fn(f) = item {
 +            if f.name()?.text() == "into" {
 +                return Some(f);
 +            }
 +        };
 +        None
 +    })?;
 +
 +    let into_fn_name = into_fn.name()?;
 +    let into_fn_params = into_fn.param_list()?;
 +    let into_fn_return = into_fn.ret_type()?;
 +
 +    let selfs = into_fn
 +        .body()?
 +        .syntax()
 +        .descendants()
 +        .filter_map(ast::NameRef::cast)
 +        .filter(|name| name.text() == "self" || name.text() == "Self");
 +
 +    acc.add(
 +        AssistId("convert_into_to_from", AssistKind::RefactorRewrite),
 +        "Convert Into to From",
 +        impl_.syntax().text_range(),
 +        |builder| {
 +            builder.replace(src_type.syntax().text_range(), dest_type.to_string());
-             builder.replace(into_fn_params.syntax().text_range(), format!("(val: {})", src_type));
++            builder.replace(ast_trait.syntax().text_range(), format!("From<{src_type}>"));
 +            builder.replace(into_fn_return.syntax().text_range(), "-> Self");
++            builder.replace(into_fn_params.syntax().text_range(), format!("(val: {src_type})"));
 +            builder.replace(into_fn_name.syntax().text_range(), "from");
 +
 +            for s in selfs {
 +                match s.text().as_ref() {
 +                    "self" => builder.replace(s.syntax().text_range(), "val"),
 +                    "Self" => builder.replace(s.syntax().text_range(), src_type_path.to_string()),
 +                    _ => {}
 +                }
 +            }
 +        },
 +    )
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use super::*;
 +
 +    use crate::tests::{check_assist, check_assist_not_applicable};
 +
 +    #[test]
 +    fn convert_into_to_from_converts_a_struct() {
 +        check_assist(
 +            convert_into_to_from,
 +            r#"
 +//- minicore: from
 +struct Thing {
 +    a: String,
 +    b: usize
 +}
 +
 +impl $0core::convert::Into<Thing> for usize {
 +    fn into(self) -> Thing {
 +        Thing {
 +            b: self.to_string(),
 +            a: self
 +        }
 +    }
 +}
 +"#,
 +            r#"
 +struct Thing {
 +    a: String,
 +    b: usize
 +}
 +
 +impl From<usize> for Thing {
 +    fn from(val: usize) -> Self {
 +        Thing {
 +            b: val.to_string(),
 +            a: val
 +        }
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn convert_into_to_from_converts_enums() {
 +        check_assist(
 +            convert_into_to_from,
 +            r#"
 +//- minicore: from
 +enum Thing {
 +    Foo(String),
 +    Bar(String)
 +}
 +
 +impl $0core::convert::Into<String> for Thing {
 +    fn into(self) -> String {
 +        match self {
 +            Self::Foo(s) => s,
 +            Self::Bar(s) => s
 +        }
 +    }
 +}
 +"#,
 +            r#"
 +enum Thing {
 +    Foo(String),
 +    Bar(String)
 +}
 +
 +impl From<Thing> for String {
 +    fn from(val: Thing) -> Self {
 +        match val {
 +            Thing::Foo(s) => s,
 +            Thing::Bar(s) => s
 +        }
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn convert_into_to_from_on_enum_with_lifetimes() {
 +        check_assist(
 +            convert_into_to_from,
 +            r#"
 +//- minicore: from
 +enum Thing<'a> {
 +    Foo(&'a str),
 +    Bar(&'a str)
 +}
 +
 +impl<'a> $0core::convert::Into<&'a str> for Thing<'a> {
 +    fn into(self) -> &'a str {
 +        match self {
 +            Self::Foo(s) => s,
 +            Self::Bar(s) => s
 +        }
 +    }
 +}
 +"#,
 +            r#"
 +enum Thing<'a> {
 +    Foo(&'a str),
 +    Bar(&'a str)
 +}
 +
 +impl<'a> From<Thing<'a>> for &'a str {
 +    fn from(val: Thing<'a>) -> Self {
 +        match val {
 +            Thing::Foo(s) => s,
 +            Thing::Bar(s) => s
 +        }
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn convert_into_to_from_works_on_references() {
 +        check_assist(
 +            convert_into_to_from,
 +            r#"
 +//- minicore: from
 +struct Thing(String);
 +
 +impl $0core::convert::Into<String> for &Thing {
 +    fn into(self) -> Thing {
 +        self.0.clone()
 +    }
 +}
 +"#,
 +            r#"
 +struct Thing(String);
 +
 +impl From<&Thing> for String {
 +    fn from(val: &Thing) -> Self {
 +        val.0.clone()
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn convert_into_to_from_works_on_qualified_structs() {
 +        check_assist(
 +            convert_into_to_from,
 +            r#"
 +//- minicore: from
 +mod things {
 +    pub struct Thing(String);
 +    pub struct BetterThing(String);
 +}
 +
 +impl $0core::convert::Into<things::BetterThing> for &things::Thing {
 +    fn into(self) -> Thing {
 +        things::BetterThing(self.0.clone())
 +    }
 +}
 +"#,
 +            r#"
 +mod things {
 +    pub struct Thing(String);
 +    pub struct BetterThing(String);
 +}
 +
 +impl From<&things::Thing> for things::BetterThing {
 +    fn from(val: &things::Thing) -> Self {
 +        things::BetterThing(val.0.clone())
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn convert_into_to_from_works_on_qualified_enums() {
 +        check_assist(
 +            convert_into_to_from,
 +            r#"
 +//- minicore: from
 +mod things {
 +    pub enum Thing {
 +        A(String)
 +    }
 +    pub struct BetterThing {
 +        B(String)
 +    }
 +}
 +
 +impl $0core::convert::Into<things::BetterThing> for &things::Thing {
 +    fn into(self) -> Thing {
 +        match self {
 +            Self::A(s) => things::BetterThing::B(s)
 +        }
 +    }
 +}
 +"#,
 +            r#"
 +mod things {
 +    pub enum Thing {
 +        A(String)
 +    }
 +    pub struct BetterThing {
 +        B(String)
 +    }
 +}
 +
 +impl From<&things::Thing> for things::BetterThing {
 +    fn from(val: &things::Thing) -> Self {
 +        match val {
 +            things::Thing::A(s) => things::BetterThing::B(s)
 +        }
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn convert_into_to_from_not_applicable_on_any_trait_named_into() {
 +        check_assist_not_applicable(
 +            convert_into_to_from,
 +            r#"
 +//- minicore: from
 +pub trait Into<T> {
 +    pub fn into(self) -> T;
 +}
 +
 +struct Thing {
 +    a: String,
 +}
 +
 +impl $0Into<Thing> for String {
 +    fn into(self) -> Thing {
 +        Thing {
 +            a: self
 +        }
 +    }
 +}
 +"#,
 +        );
 +    }
 +}
index 2cf370c090743da3730e37a47fa9785496c6343e,0000000000000000000000000000000000000000..80eecf4a09868980f94d68b7ed8854674bcd2eee
mode 100644,000000..100644
--- /dev/null
@@@ -1,556 -1,0 +1,556 @@@
-                 format_to!(buf, "{}.{}()", expr_behind_ref, method);
 +use hir::known;
 +use ide_db::famous_defs::FamousDefs;
 +use stdx::format_to;
 +use syntax::{
 +    ast::{self, edit_in_place::Indent, make, HasArgList, HasLoopBody},
 +    AstNode,
 +};
 +
 +use crate::{AssistContext, AssistId, AssistKind, Assists};
 +
 +// Assist: convert_iter_for_each_to_for
 +//
 +// Converts an Iterator::for_each function into a for loop.
 +//
 +// ```
 +// # //- minicore: iterators
 +// # use core::iter;
 +// fn main() {
 +//     let iter = iter::repeat((9, 2));
 +//     iter.for_each$0(|(x, y)| {
 +//         println!("x: {}, y: {}", x, y);
 +//     });
 +// }
 +// ```
 +// ->
 +// ```
 +// # use core::iter;
 +// fn main() {
 +//     let iter = iter::repeat((9, 2));
 +//     for (x, y) in iter {
 +//         println!("x: {}, y: {}", x, y);
 +//     }
 +// }
 +// ```
 +pub(crate) fn convert_iter_for_each_to_for(
 +    acc: &mut Assists,
 +    ctx: &AssistContext<'_>,
 +) -> Option<()> {
 +    let method = ctx.find_node_at_offset::<ast::MethodCallExpr>()?;
 +
 +    let closure = match method.arg_list()?.args().next()? {
 +        ast::Expr::ClosureExpr(expr) => expr,
 +        _ => return None,
 +    };
 +
 +    let (method, receiver) = validate_method_call_expr(ctx, method)?;
 +
 +    let param_list = closure.param_list()?;
 +    let param = param_list.params().next()?.pat()?;
 +    let body = closure.body()?;
 +
 +    let stmt = method.syntax().parent().and_then(ast::ExprStmt::cast);
 +    let range = stmt.as_ref().map_or(method.syntax(), AstNode::syntax).text_range();
 +
 +    acc.add(
 +        AssistId("convert_iter_for_each_to_for", AssistKind::RefactorRewrite),
 +        "Replace this `Iterator::for_each` with a for loop",
 +        range,
 +        |builder| {
 +            let indent =
 +                stmt.as_ref().map_or_else(|| method.indent_level(), ast::ExprStmt::indent_level);
 +
 +            let block = match body {
 +                ast::Expr::BlockExpr(block) => block,
 +                _ => make::block_expr(Vec::new(), Some(body)),
 +            }
 +            .clone_for_update();
 +            block.reindent_to(indent);
 +
 +            let expr_for_loop = make::expr_for_loop(param, receiver, block);
 +            builder.replace(range, expr_for_loop.to_string())
 +        },
 +    )
 +}
 +
 +// Assist: convert_for_loop_with_for_each
 +//
 +// Converts a for loop into a for_each loop on the Iterator.
 +//
 +// ```
 +// fn main() {
 +//     let x = vec![1, 2, 3];
 +//     for$0 v in x {
 +//         let y = v * 2;
 +//     }
 +// }
 +// ```
 +// ->
 +// ```
 +// fn main() {
 +//     let x = vec![1, 2, 3];
 +//     x.into_iter().for_each(|v| {
 +//         let y = v * 2;
 +//     });
 +// }
 +// ```
 +pub(crate) fn convert_for_loop_with_for_each(
 +    acc: &mut Assists,
 +    ctx: &AssistContext<'_>,
 +) -> Option<()> {
 +    let for_loop = ctx.find_node_at_offset::<ast::ForExpr>()?;
 +    let iterable = for_loop.iterable()?;
 +    let pat = for_loop.pat()?;
 +    let body = for_loop.loop_body()?;
 +    if body.syntax().text_range().start() < ctx.offset() {
 +        cov_mark::hit!(not_available_in_body);
 +        return None;
 +    }
 +
 +    acc.add(
 +        AssistId("convert_for_loop_with_for_each", AssistKind::RefactorRewrite),
 +        "Replace this for loop with `Iterator::for_each`",
 +        for_loop.syntax().text_range(),
 +        |builder| {
 +            let mut buf = String::new();
 +
 +            if let Some((expr_behind_ref, method)) =
 +                is_ref_and_impls_iter_method(&ctx.sema, &iterable)
 +            {
 +                // We have either "for x in &col" and col implements a method called iter
 +                //             or "for x in &mut col" and col implements a method called iter_mut
-                 format_to!(buf, "({})", iterable);
++                format_to!(buf, "{expr_behind_ref}.{method}()");
 +            } else if let ast::Expr::RangeExpr(..) = iterable {
 +                // range expressions need to be parenthesized for the syntax to be correct
-                 format_to!(buf, "{}", iterable);
++                format_to!(buf, "({iterable})");
 +            } else if impls_core_iter(&ctx.sema, &iterable) {
-                 format_to!(buf, "({}).into_iter()", iterable);
++                format_to!(buf, "{iterable}");
 +            } else if let ast::Expr::RefExpr(_) = iterable {
-                 format_to!(buf, "{}.into_iter()", iterable);
++                format_to!(buf, "({iterable}).into_iter()");
 +            } else {
-             format_to!(buf, ".for_each(|{}| {});", pat, body);
++                format_to!(buf, "{iterable}.into_iter()");
 +            }
 +
++            format_to!(buf, ".for_each(|{pat}| {body});");
 +
 +            builder.replace(for_loop.syntax().text_range(), buf)
 +        },
 +    )
 +}
 +
 +/// If iterable is a reference where the expression behind the reference implements a method
 +/// returning an Iterator called iter or iter_mut (depending on the type of reference) then return
 +/// the expression behind the reference and the method name
 +fn is_ref_and_impls_iter_method(
 +    sema: &hir::Semantics<'_, ide_db::RootDatabase>,
 +    iterable: &ast::Expr,
 +) -> Option<(ast::Expr, hir::Name)> {
 +    let ref_expr = match iterable {
 +        ast::Expr::RefExpr(r) => r,
 +        _ => return None,
 +    };
 +    let wanted_method = if ref_expr.mut_token().is_some() { known::iter_mut } else { known::iter };
 +    let expr_behind_ref = ref_expr.expr()?;
 +    let ty = sema.type_of_expr(&expr_behind_ref)?.adjusted();
 +    let scope = sema.scope(iterable.syntax())?;
 +    let krate = scope.krate();
 +    let iter_trait = FamousDefs(sema, krate).core_iter_Iterator()?;
 +
 +    let has_wanted_method = ty
 +        .iterate_method_candidates(
 +            sema.db,
 +            &scope,
 +            &scope.visible_traits().0,
 +            None,
 +            Some(&wanted_method),
 +            |func| {
 +                if func.ret_type(sema.db).impls_trait(sema.db, iter_trait, &[]) {
 +                    return Some(());
 +                }
 +                None
 +            },
 +        )
 +        .is_some();
 +    if !has_wanted_method {
 +        return None;
 +    }
 +
 +    Some((expr_behind_ref, wanted_method))
 +}
 +
 +/// Whether iterable implements core::Iterator
 +fn impls_core_iter(sema: &hir::Semantics<'_, ide_db::RootDatabase>, iterable: &ast::Expr) -> bool {
 +    (|| {
 +        let it_typ = sema.type_of_expr(iterable)?.adjusted();
 +
 +        let module = sema.scope(iterable.syntax())?.module();
 +
 +        let krate = module.krate();
 +        let iter_trait = FamousDefs(sema, krate).core_iter_Iterator()?;
 +        cov_mark::hit!(test_already_impls_iterator);
 +        Some(it_typ.impls_trait(sema.db, iter_trait, &[]))
 +    })()
 +    .unwrap_or(false)
 +}
 +
 +fn validate_method_call_expr(
 +    ctx: &AssistContext<'_>,
 +    expr: ast::MethodCallExpr,
 +) -> Option<(ast::Expr, ast::Expr)> {
 +    let name_ref = expr.name_ref()?;
 +    if !name_ref.syntax().text_range().contains_range(ctx.selection_trimmed()) {
 +        cov_mark::hit!(test_for_each_not_applicable_invalid_cursor_pos);
 +        return None;
 +    }
 +    if name_ref.text() != "for_each" {
 +        return None;
 +    }
 +
 +    let sema = &ctx.sema;
 +
 +    let receiver = expr.receiver()?;
 +    let expr = ast::Expr::MethodCallExpr(expr);
 +
 +    let it_type = sema.type_of_expr(&receiver)?.adjusted();
 +    let module = sema.scope(receiver.syntax())?.module();
 +    let krate = module.krate();
 +
 +    let iter_trait = FamousDefs(sema, krate).core_iter_Iterator()?;
 +    it_type.impls_trait(sema.db, iter_trait, &[]).then(|| (expr, receiver))
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use crate::tests::{check_assist, check_assist_not_applicable};
 +
 +    use super::*;
 +
 +    #[test]
 +    fn test_for_each_in_method_stmt() {
 +        check_assist(
 +            convert_iter_for_each_to_for,
 +            r#"
 +//- minicore: iterators
 +fn main() {
 +    let it = core::iter::repeat(92);
 +    it.$0for_each(|(x, y)| {
 +        println!("x: {}, y: {}", x, y);
 +    });
 +}
 +"#,
 +            r#"
 +fn main() {
 +    let it = core::iter::repeat(92);
 +    for (x, y) in it {
 +        println!("x: {}, y: {}", x, y);
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn test_for_each_in_method() {
 +        check_assist(
 +            convert_iter_for_each_to_for,
 +            r#"
 +//- minicore: iterators
 +fn main() {
 +    let it = core::iter::repeat(92);
 +    it.$0for_each(|(x, y)| {
 +        println!("x: {}, y: {}", x, y);
 +    })
 +}
 +"#,
 +            r#"
 +fn main() {
 +    let it = core::iter::repeat(92);
 +    for (x, y) in it {
 +        println!("x: {}, y: {}", x, y);
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn test_for_each_without_braces_stmt() {
 +        check_assist(
 +            convert_iter_for_each_to_for,
 +            r#"
 +//- minicore: iterators
 +fn main() {
 +    let it = core::iter::repeat(92);
 +    it.$0for_each(|(x, y)| println!("x: {}, y: {}", x, y));
 +}
 +"#,
 +            r#"
 +fn main() {
 +    let it = core::iter::repeat(92);
 +    for (x, y) in it {
 +        println!("x: {}, y: {}", x, y)
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn test_for_each_not_applicable() {
 +        check_assist_not_applicable(
 +            convert_iter_for_each_to_for,
 +            r#"
 +//- minicore: iterators
 +fn main() {
 +    ().$0for_each(|x| println!("{}", x));
 +}"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn test_for_each_not_applicable_invalid_cursor_pos() {
 +        cov_mark::check!(test_for_each_not_applicable_invalid_cursor_pos);
 +        check_assist_not_applicable(
 +            convert_iter_for_each_to_for,
 +            r#"
 +//- minicore: iterators
 +fn main() {
 +    core::iter::repeat(92).for_each(|(x, y)| $0println!("x: {}, y: {}", x, y));
 +}"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn each_to_for_not_for() {
 +        check_assist_not_applicable(
 +            convert_for_loop_with_for_each,
 +            r"
 +let mut x = vec![1, 2, 3];
 +x.iter_mut().$0for_each(|v| *v *= 2);
 +        ",
 +        )
 +    }
 +
 +    #[test]
 +    fn each_to_for_simple_for() {
 +        check_assist(
 +            convert_for_loop_with_for_each,
 +            r"
 +fn main() {
 +    let x = vec![1, 2, 3];
 +    for $0v in x {
 +        v *= 2;
 +    }
 +}",
 +            r"
 +fn main() {
 +    let x = vec![1, 2, 3];
 +    x.into_iter().for_each(|v| {
 +        v *= 2;
 +    });
 +}",
 +        )
 +    }
 +
 +    #[test]
 +    fn each_to_for_for_in_range() {
 +        check_assist(
 +            convert_for_loop_with_for_each,
 +            r#"
 +//- minicore: range, iterators
 +impl<T> core::iter::Iterator for core::ops::Range<T> {
 +    type Item = T;
 +
 +    fn next(&mut self) -> Option<Self::Item> {
 +        None
 +    }
 +}
 +
 +fn main() {
 +    for $0x in 0..92 {
 +        print!("{}", x);
 +    }
 +}"#,
 +            r#"
 +impl<T> core::iter::Iterator for core::ops::Range<T> {
 +    type Item = T;
 +
 +    fn next(&mut self) -> Option<Self::Item> {
 +        None
 +    }
 +}
 +
 +fn main() {
 +    (0..92).for_each(|x| {
 +        print!("{}", x);
 +    });
 +}"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn each_to_for_not_available_in_body() {
 +        cov_mark::check!(not_available_in_body);
 +        check_assist_not_applicable(
 +            convert_for_loop_with_for_each,
 +            r"
 +fn main() {
 +    let x = vec![1, 2, 3];
 +    for v in x {
 +        $0v *= 2;
 +    }
 +}",
 +        )
 +    }
 +
 +    #[test]
 +    fn each_to_for_for_borrowed() {
 +        check_assist(
 +            convert_for_loop_with_for_each,
 +            r#"
 +//- minicore: iterators
 +use core::iter::{Repeat, repeat};
 +
 +struct S;
 +impl S {
 +    fn iter(&self) -> Repeat<i32> { repeat(92) }
 +    fn iter_mut(&mut self) -> Repeat<i32> { repeat(92) }
 +}
 +
 +fn main() {
 +    let x = S;
 +    for $0v in &x {
 +        let a = v * 2;
 +    }
 +}
 +"#,
 +            r#"
 +use core::iter::{Repeat, repeat};
 +
 +struct S;
 +impl S {
 +    fn iter(&self) -> Repeat<i32> { repeat(92) }
 +    fn iter_mut(&mut self) -> Repeat<i32> { repeat(92) }
 +}
 +
 +fn main() {
 +    let x = S;
 +    x.iter().for_each(|v| {
 +        let a = v * 2;
 +    });
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn each_to_for_for_borrowed_no_iter_method() {
 +        check_assist(
 +            convert_for_loop_with_for_each,
 +            r"
 +struct NoIterMethod;
 +fn main() {
 +    let x = NoIterMethod;
 +    for $0v in &x {
 +        let a = v * 2;
 +    }
 +}
 +",
 +            r"
 +struct NoIterMethod;
 +fn main() {
 +    let x = NoIterMethod;
 +    (&x).into_iter().for_each(|v| {
 +        let a = v * 2;
 +    });
 +}
 +",
 +        )
 +    }
 +
 +    #[test]
 +    fn each_to_for_for_borrowed_mut() {
 +        check_assist(
 +            convert_for_loop_with_for_each,
 +            r#"
 +//- minicore: iterators
 +use core::iter::{Repeat, repeat};
 +
 +struct S;
 +impl S {
 +    fn iter(&self) -> Repeat<i32> { repeat(92) }
 +    fn iter_mut(&mut self) -> Repeat<i32> { repeat(92) }
 +}
 +
 +fn main() {
 +    let x = S;
 +    for $0v in &mut x {
 +        let a = v * 2;
 +    }
 +}
 +"#,
 +            r#"
 +use core::iter::{Repeat, repeat};
 +
 +struct S;
 +impl S {
 +    fn iter(&self) -> Repeat<i32> { repeat(92) }
 +    fn iter_mut(&mut self) -> Repeat<i32> { repeat(92) }
 +}
 +
 +fn main() {
 +    let x = S;
 +    x.iter_mut().for_each(|v| {
 +        let a = v * 2;
 +    });
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn each_to_for_for_borrowed_mut_behind_var() {
 +        check_assist(
 +            convert_for_loop_with_for_each,
 +            r"
 +fn main() {
 +    let x = vec![1, 2, 3];
 +    let y = &mut x;
 +    for $0v in y {
 +        *v *= 2;
 +    }
 +}",
 +            r"
 +fn main() {
 +    let x = vec![1, 2, 3];
 +    let y = &mut x;
 +    y.into_iter().for_each(|v| {
 +        *v *= 2;
 +    });
 +}",
 +        )
 +    }
 +
 +    #[test]
 +    fn each_to_for_already_impls_iterator() {
 +        cov_mark::check!(test_already_impls_iterator);
 +        check_assist(
 +            convert_for_loop_with_for_each,
 +            r#"
 +//- minicore: iterators
 +fn main() {
 +    for$0 a in core::iter::repeat(92).take(1) {
 +        println!("{}", a);
 +    }
 +}
 +"#,
 +            r#"
 +fn main() {
 +    core::iter::repeat(92).take(1).for_each(|a| {
 +        println!("{}", a);
 +    });
 +}
 +"#,
 +        );
 +    }
 +}
index 00095de257d5ab19922c22df209b76b763281a8c,0000000000000000000000000000000000000000..c82a3b53032595217e7f7b69f12557829c644574
mode 100644,000000..100644
--- /dev/null
@@@ -1,497 -1,0 +1,497 @@@
-                     format!("mut {}", ident)
 +use hir::Semantics;
 +use ide_db::RootDatabase;
 +use syntax::ast::{edit::AstNodeEdit, AstNode, HasName, LetStmt, Name, Pat};
 +use syntax::T;
 +
 +use crate::{AssistContext, AssistId, AssistKind, Assists};
 +
 +/// Gets a list of binders in a pattern, and whether they are mut.
 +fn binders_in_pat(
 +    acc: &mut Vec<(Name, bool)>,
 +    pat: &Pat,
 +    sem: &Semantics<'_, RootDatabase>,
 +) -> Option<()> {
 +    use Pat::*;
 +    match pat {
 +        IdentPat(p) => {
 +            let ident = p.name()?;
 +            let ismut = p.ref_token().is_none() && p.mut_token().is_some();
 +            // check for const reference
 +            if sem.resolve_bind_pat_to_const(p).is_none() {
 +                acc.push((ident, ismut));
 +            }
 +            if let Some(inner) = p.pat() {
 +                binders_in_pat(acc, &inner, sem)?;
 +            }
 +            Some(())
 +        }
 +        BoxPat(p) => p.pat().and_then(|p| binders_in_pat(acc, &p, sem)),
 +        RestPat(_) | LiteralPat(_) | PathPat(_) | WildcardPat(_) | ConstBlockPat(_) => Some(()),
 +        OrPat(p) => {
 +            for p in p.pats() {
 +                binders_in_pat(acc, &p, sem)?;
 +            }
 +            Some(())
 +        }
 +        ParenPat(p) => p.pat().and_then(|p| binders_in_pat(acc, &p, sem)),
 +        RangePat(p) => {
 +            if let Some(st) = p.start() {
 +                binders_in_pat(acc, &st, sem)?
 +            }
 +            if let Some(ed) = p.end() {
 +                binders_in_pat(acc, &ed, sem)?
 +            }
 +            Some(())
 +        }
 +        RecordPat(p) => {
 +            for f in p.record_pat_field_list()?.fields() {
 +                let pat = f.pat()?;
 +                binders_in_pat(acc, &pat, sem)?;
 +            }
 +            Some(())
 +        }
 +        RefPat(p) => p.pat().and_then(|p| binders_in_pat(acc, &p, sem)),
 +        SlicePat(p) => {
 +            for p in p.pats() {
 +                binders_in_pat(acc, &p, sem)?;
 +            }
 +            Some(())
 +        }
 +        TuplePat(p) => {
 +            for p in p.fields() {
 +                binders_in_pat(acc, &p, sem)?;
 +            }
 +            Some(())
 +        }
 +        TupleStructPat(p) => {
 +            for p in p.fields() {
 +                binders_in_pat(acc, &p, sem)?;
 +            }
 +            Some(())
 +        }
 +        // don't support macro pat yet
 +        MacroPat(_) => None,
 +    }
 +}
 +
 +fn binders_to_str(binders: &[(Name, bool)], addmut: bool) -> String {
 +    let vars = binders
 +        .iter()
 +        .map(
 +            |(ident, ismut)| {
 +                if *ismut && addmut {
-         format!("({})", vars)
++                    format!("mut {ident}")
 +                } else {
 +                    ident.to_string()
 +                }
 +            },
 +        )
 +        .collect::<Vec<_>>()
 +        .join(", ");
 +    if binders.is_empty() {
 +        String::from("{}")
 +    } else if binders.len() == 1 {
 +        vars
 +    } else {
-                 Some(tail) if only_expr => format!("{},", tail.syntax().text()),
++        format!("({vars})")
 +    }
 +}
 +
 +// Assist: convert_let_else_to_match
 +//
 +// Converts let-else statement to let statement and match expression.
 +//
 +// ```
 +// fn main() {
 +//     let Ok(mut x) = f() else$0 { return };
 +// }
 +// ```
 +// ->
 +// ```
 +// fn main() {
 +//     let mut x = match f() {
 +//         Ok(x) => x,
 +//         _ => return,
 +//     };
 +// }
 +// ```
 +pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    // should focus on else token to trigger
 +    let else_token = ctx.find_token_syntax_at_offset(T![else])?;
 +    let let_stmt = LetStmt::cast(else_token.parent()?.parent()?)?;
 +    let let_else_block = let_stmt.let_else()?.block_expr()?;
 +    let let_init = let_stmt.initializer()?;
 +    if let_stmt.ty().is_some() {
 +        // don't support let with type annotation
 +        return None;
 +    }
 +    let pat = let_stmt.pat()?;
 +    let mut binders = Vec::new();
 +    binders_in_pat(&mut binders, &pat, &ctx.sema)?;
 +
 +    let target = let_stmt.syntax().text_range();
 +    acc.add(
 +        AssistId("convert_let_else_to_match", AssistKind::RefactorRewrite),
 +        "Convert let-else to let and match",
 +        target,
 +        |edit| {
 +            let indent_level = let_stmt.indent_level().0 as usize;
 +            let indent = "    ".repeat(indent_level);
 +            let indent1 = "    ".repeat(indent_level + 1);
 +
 +            let binders_str = binders_to_str(&binders, false);
 +            let binders_str_mut = binders_to_str(&binders, true);
 +
 +            let init_expr = let_init.syntax().text();
 +            let mut pat_no_mut = pat.syntax().text().to_string();
 +            // remove the mut from the pattern
 +            for (b, ismut) in binders.iter() {
 +                if *ismut {
 +                    pat_no_mut = pat_no_mut.replace(&format!("mut {b}"), &b.to_string());
 +                }
 +            }
 +
 +            let only_expr = let_else_block.statements().next().is_none();
 +            let branch2 = match &let_else_block.tail_expr() {
++                Some(tail) if only_expr => format!("{tail},"),
 +                _ => let_else_block.syntax().text().to_string(),
 +            };
 +            let replace = if binders.is_empty() {
 +                format!(
 +                    "match {init_expr} {{
 +{indent1}{pat_no_mut} => {binders_str}
 +{indent1}_ => {branch2}
 +{indent}}}"
 +                )
 +            } else {
 +                format!(
 +                    "let {binders_str_mut} = match {init_expr} {{
 +{indent1}{pat_no_mut} => {binders_str},
 +{indent1}_ => {branch2}
 +{indent}}};"
 +                )
 +            };
 +            edit.replace(target, replace);
 +        },
 +    )
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use super::*;
 +
 +    use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
 +
 +    #[test]
 +    fn convert_let_else_to_match_no_type_let() {
 +        check_assist_not_applicable(
 +            convert_let_else_to_match,
 +            r#"
 +fn main() {
 +    let 1: u32 = v.iter().sum() else$0 { return };
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_let_else_to_match_on_else() {
 +        check_assist_not_applicable(
 +            convert_let_else_to_match,
 +            r#"
 +fn main() {
 +    let Ok(x) = f() else {$0 return };
 +}
 +            "#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_let_else_to_match_no_macropat() {
 +        check_assist_not_applicable(
 +            convert_let_else_to_match,
 +            r#"
 +fn main() {
 +    let m!() = g() else$0 { return };
 +}
 +            "#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_let_else_to_match_target() {
 +        check_assist_target(
 +            convert_let_else_to_match,
 +            r"
 +fn main() {
 +    let Ok(x) = f() else$0 { continue };
 +}",
 +            "let Ok(x) = f() else { continue };",
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_let_else_to_match_basic() {
 +        check_assist(
 +            convert_let_else_to_match,
 +            r"
 +fn main() {
 +    let Ok(x) = f() else$0 { continue };
 +}",
 +            r"
 +fn main() {
 +    let x = match f() {
 +        Ok(x) => x,
 +        _ => continue,
 +    };
 +}",
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_let_else_to_match_const_ref() {
 +        check_assist(
 +            convert_let_else_to_match,
 +            r"
 +enum Option<T> {
 +    Some(T),
 +    None,
 +}
 +use Option::*;
 +fn main() {
 +    let None = f() el$0se { continue };
 +}",
 +            r"
 +enum Option<T> {
 +    Some(T),
 +    None,
 +}
 +use Option::*;
 +fn main() {
 +    match f() {
 +        None => {}
 +        _ => continue,
 +    }
 +}",
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_let_else_to_match_const_ref_const() {
 +        check_assist(
 +            convert_let_else_to_match,
 +            r"
 +const NEG1: i32 = -1;
 +fn main() {
 +    let NEG1 = f() el$0se { continue };
 +}",
 +            r"
 +const NEG1: i32 = -1;
 +fn main() {
 +    match f() {
 +        NEG1 => {}
 +        _ => continue,
 +    }
 +}",
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_let_else_to_match_mut() {
 +        check_assist(
 +            convert_let_else_to_match,
 +            r"
 +fn main() {
 +    let Ok(mut x) = f() el$0se { continue };
 +}",
 +            r"
 +fn main() {
 +    let mut x = match f() {
 +        Ok(x) => x,
 +        _ => continue,
 +    };
 +}",
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_let_else_to_match_multi_binders() {
 +        check_assist(
 +            convert_let_else_to_match,
 +            r#"
 +fn main() {
 +    let ControlFlow::Break((x, "tag", y, ..)) = f() else$0 { g(); return };
 +}"#,
 +            r#"
 +fn main() {
 +    let (x, y) = match f() {
 +        ControlFlow::Break((x, "tag", y, ..)) => (x, y),
 +        _ => { g(); return }
 +    };
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_let_else_to_match_slice() {
 +        check_assist(
 +            convert_let_else_to_match,
 +            r#"
 +fn main() {
 +    let [one, 1001, other] = f() else$0 { break };
 +}"#,
 +            r#"
 +fn main() {
 +    let (one, other) = match f() {
 +        [one, 1001, other] => (one, other),
 +        _ => break,
 +    };
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_let_else_to_match_struct() {
 +        check_assist(
 +            convert_let_else_to_match,
 +            r#"
 +fn main() {
 +    let [Struct { inner: Some(it) }, 1001, other] = f() else$0 { break };
 +}"#,
 +            r#"
 +fn main() {
 +    let (it, other) = match f() {
 +        [Struct { inner: Some(it) }, 1001, other] => (it, other),
 +        _ => break,
 +    };
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_let_else_to_match_struct_ident_pat() {
 +        check_assist(
 +            convert_let_else_to_match,
 +            r#"
 +fn main() {
 +    let [Struct { inner }, 1001, other] = f() else$0 { break };
 +}"#,
 +            r#"
 +fn main() {
 +    let (inner, other) = match f() {
 +        [Struct { inner }, 1001, other] => (inner, other),
 +        _ => break,
 +    };
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_let_else_to_match_no_binder() {
 +        check_assist(
 +            convert_let_else_to_match,
 +            r#"
 +fn main() {
 +    let (8 | 9) = f() else$0 { panic!() };
 +}"#,
 +            r#"
 +fn main() {
 +    match f() {
 +        (8 | 9) => {}
 +        _ => panic!(),
 +    }
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_let_else_to_match_range() {
 +        check_assist(
 +            convert_let_else_to_match,
 +            r#"
 +fn main() {
 +    let 1.. = f() e$0lse { return };
 +}"#,
 +            r#"
 +fn main() {
 +    match f() {
 +        1.. => {}
 +        _ => return,
 +    }
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_let_else_to_match_refpat() {
 +        check_assist(
 +            convert_let_else_to_match,
 +            r#"
 +fn main() {
 +    let Ok(&mut x) = f(&mut 0) else$0 { return };
 +}"#,
 +            r#"
 +fn main() {
 +    let x = match f(&mut 0) {
 +        Ok(&mut x) => x,
 +        _ => return,
 +    };
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_let_else_to_match_refmut() {
 +        check_assist(
 +            convert_let_else_to_match,
 +            r#"
 +fn main() {
 +    let Ok(ref mut x) = f() else$0 { return };
 +}"#,
 +            r#"
 +fn main() {
 +    let x = match f() {
 +        Ok(ref mut x) => x,
 +        _ => return,
 +    };
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_let_else_to_match_atpat() {
 +        check_assist(
 +            convert_let_else_to_match,
 +            r#"
 +fn main() {
 +    let out @ Ok(ins) = f() else$0 { return };
 +}"#,
 +            r#"
 +fn main() {
 +    let (out, ins) = match f() {
 +        out @ Ok(ins) => (out, ins),
 +        _ => return,
 +    };
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_let_else_to_match_complex_init() {
 +        check_assist(
 +            convert_let_else_to_match,
 +            r#"
 +fn main() {
 +    let v = vec![1, 2, 3];
 +    let &[mut x, y, ..] = &v.iter().collect::<Vec<_>>()[..] else$0 { return };
 +}"#,
 +            r#"
 +fn main() {
 +    let v = vec![1, 2, 3];
 +    let (mut x, y) = match &v.iter().collect::<Vec<_>>()[..] {
 +        &[x, y, ..] => (x, y),
 +        _ => return,
 +    };
 +}"#,
 +        );
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5bf04a3ad3719fadf64a91d989b00eff4bac0e2d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,413 @@@
++use ide_db::defs::{Definition, NameRefClass};
++use syntax::{
++    ast::{self, HasName},
++    ted, AstNode, SyntaxNode,
++};
++
++use crate::{
++    assist_context::{AssistContext, Assists},
++    AssistId, AssistKind,
++};
++
++// Assist: convert_match_to_let_else
++//
++// Converts let statement with match initializer to let-else statement.
++//
++// ```
++// # //- minicore: option
++// fn foo(opt: Option<()>) {
++//     let val = $0match opt {
++//         Some(it) => it,
++//         None => return,
++//     };
++// }
++// ```
++// ->
++// ```
++// fn foo(opt: Option<()>) {
++//     let Some(val) = opt else { return };
++// }
++// ```
++pub(crate) fn convert_match_to_let_else(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
++    let let_stmt: ast::LetStmt = ctx.find_node_at_offset()?;
++    let binding = find_binding(let_stmt.pat()?)?;
++
++    let initializer = match let_stmt.initializer() {
++        Some(ast::Expr::MatchExpr(it)) => it,
++        _ => return None,
++    };
++    let initializer_expr = initializer.expr()?;
++
++    let (extracting_arm, diverging_arm) = match find_arms(ctx, &initializer) {
++        Some(it) => it,
++        None => return None,
++    };
++    if extracting_arm.guard().is_some() {
++        cov_mark::hit!(extracting_arm_has_guard);
++        return None;
++    }
++
++    let diverging_arm_expr = diverging_arm.expr()?;
++    let extracting_arm_pat = extracting_arm.pat()?;
++    let extracted_variable = find_extracted_variable(ctx, &extracting_arm)?;
++
++    acc.add(
++        AssistId("convert_match_to_let_else", AssistKind::RefactorRewrite),
++        "Convert match to let-else",
++        let_stmt.syntax().text_range(),
++        |builder| {
++            let extracting_arm_pat = rename_variable(&extracting_arm_pat, extracted_variable, binding);
++            builder.replace(
++                let_stmt.syntax().text_range(),
++                format!("let {extracting_arm_pat} = {initializer_expr} else {{ {diverging_arm_expr} }};")
++            )
++        },
++    )
++}
++
++// Given a pattern, find the name introduced to the surrounding scope.
++fn find_binding(pat: ast::Pat) -> Option<ast::IdentPat> {
++    if let ast::Pat::IdentPat(ident) = pat {
++        Some(ident)
++    } else {
++        None
++    }
++}
++
++// Given a match expression, find extracting and diverging arms.
++fn find_arms(
++    ctx: &AssistContext<'_>,
++    match_expr: &ast::MatchExpr,
++) -> Option<(ast::MatchArm, ast::MatchArm)> {
++    let arms = match_expr.match_arm_list()?.arms().collect::<Vec<_>>();
++    if arms.len() != 2 {
++        return None;
++    }
++
++    let mut extracting = None;
++    let mut diverging = None;
++    for arm in arms {
++        if ctx.sema.type_of_expr(&arm.expr().unwrap()).unwrap().original().is_never() {
++            diverging = Some(arm);
++        } else {
++            extracting = Some(arm);
++        }
++    }
++
++    match (extracting, diverging) {
++        (Some(extracting), Some(diverging)) => Some((extracting, diverging)),
++        _ => {
++            cov_mark::hit!(non_diverging_match);
++            None
++        }
++    }
++}
++
++// Given an extracting arm, find the extracted variable.
++fn find_extracted_variable(ctx: &AssistContext<'_>, arm: &ast::MatchArm) -> Option<ast::Name> {
++    match arm.expr()? {
++        ast::Expr::PathExpr(path) => {
++            let name_ref = path.syntax().descendants().find_map(ast::NameRef::cast)?;
++            match NameRefClass::classify(&ctx.sema, &name_ref)? {
++                NameRefClass::Definition(Definition::Local(local)) => {
++                    let source = local.source(ctx.db()).value.left()?;
++                    Some(source.name()?)
++                }
++                _ => None,
++            }
++        }
++        _ => {
++            cov_mark::hit!(extracting_arm_is_not_an_identity_expr);
++            return None;
++        }
++    }
++}
++
++// Rename `extracted` with `binding` in `pat`.
++fn rename_variable(pat: &ast::Pat, extracted: ast::Name, binding: ast::IdentPat) -> SyntaxNode {
++    let syntax = pat.syntax().clone_for_update();
++    let extracted_syntax = syntax.covering_element(extracted.syntax().text_range());
++
++    // If `extracted` variable is a record field, we should rename it to `binding`,
++    // otherwise we just need to replace `extracted` with `binding`.
++
++    if let Some(record_pat_field) = extracted_syntax.ancestors().find_map(ast::RecordPatField::cast)
++    {
++        if let Some(name_ref) = record_pat_field.field_name() {
++            ted::replace(
++                record_pat_field.syntax(),
++                ast::make::record_pat_field(ast::make::name_ref(&name_ref.text()), binding.into())
++                    .syntax()
++                    .clone_for_update(),
++            );
++        }
++    } else {
++        ted::replace(extracted_syntax, binding.syntax().clone_for_update());
++    }
++
++    syntax
++}
++
++#[cfg(test)]
++mod tests {
++    use crate::tests::{check_assist, check_assist_not_applicable};
++
++    use super::*;
++
++    #[test]
++    fn should_not_be_applicable_for_non_diverging_match() {
++        cov_mark::check!(non_diverging_match);
++        check_assist_not_applicable(
++            convert_match_to_let_else,
++            r#"
++//- minicore: option
++fn foo(opt: Option<()>) {
++    let val = $0match opt {
++        Some(it) => it,
++        None => (),
++    };
++}
++"#,
++        );
++    }
++
++    #[test]
++    fn should_not_be_applicable_if_extracting_arm_is_not_an_identity_expr() {
++        cov_mark::check_count!(extracting_arm_is_not_an_identity_expr, 2);
++        check_assist_not_applicable(
++            convert_match_to_let_else,
++            r#"
++//- minicore: option
++fn foo(opt: Option<i32>) {
++    let val = $0match opt {
++        Some(it) => it + 1,
++        None => return,
++    };
++}
++"#,
++        );
++
++        check_assist_not_applicable(
++            convert_match_to_let_else,
++            r#"
++//- minicore: option
++fn foo(opt: Option<()>) {
++    let val = $0match opt {
++        Some(it) => {
++            let _ = 1 + 1;
++            it
++        },
++        None => return,
++    };
++}
++"#,
++        );
++    }
++
++    #[test]
++    fn should_not_be_applicable_if_extracting_arm_has_guard() {
++        cov_mark::check!(extracting_arm_has_guard);
++        check_assist_not_applicable(
++            convert_match_to_let_else,
++            r#"
++//- minicore: option
++fn foo(opt: Option<()>) {
++    let val = $0match opt {
++        Some(it) if 2 > 1 => it,
++        None => return,
++    };
++}
++"#,
++        );
++    }
++
++    #[test]
++    fn basic_pattern() {
++        check_assist(
++            convert_match_to_let_else,
++            r#"
++//- minicore: option
++fn foo(opt: Option<()>) {
++    let val = $0match opt {
++        Some(it) => it,
++        None => return,
++    };
++}
++    "#,
++            r#"
++fn foo(opt: Option<()>) {
++    let Some(val) = opt else { return };
++}
++    "#,
++        );
++    }
++
++    #[test]
++    fn keeps_modifiers() {
++        check_assist(
++            convert_match_to_let_else,
++            r#"
++//- minicore: option
++fn foo(opt: Option<()>) {
++    let ref mut val = $0match opt {
++        Some(it) => it,
++        None => return,
++    };
++}
++    "#,
++            r#"
++fn foo(opt: Option<()>) {
++    let Some(ref mut val) = opt else { return };
++}
++    "#,
++        );
++    }
++
++    #[test]
++    fn nested_pattern() {
++        check_assist(
++            convert_match_to_let_else,
++            r#"
++//- minicore: option, result
++fn foo(opt: Option<Result<()>>) {
++    let val = $0match opt {
++        Some(Ok(it)) => it,
++        _ => return,
++    };
++}
++    "#,
++            r#"
++fn foo(opt: Option<Result<()>>) {
++    let Some(Ok(val)) = opt else { return };
++}
++    "#,
++        );
++    }
++
++    #[test]
++    fn works_with_any_diverging_block() {
++        check_assist(
++            convert_match_to_let_else,
++            r#"
++//- minicore: option
++fn foo(opt: Option<()>) {
++    loop {
++        let val = $0match opt {
++            Some(it) => it,
++            None => break,
++        };
++    }
++}
++    "#,
++            r#"
++fn foo(opt: Option<()>) {
++    loop {
++        let Some(val) = opt else { break };
++    }
++}
++    "#,
++        );
++
++        check_assist(
++            convert_match_to_let_else,
++            r#"
++//- minicore: option
++fn foo(opt: Option<()>) {
++    loop {
++        let val = $0match opt {
++            Some(it) => it,
++            None => continue,
++        };
++    }
++}
++    "#,
++            r#"
++fn foo(opt: Option<()>) {
++    loop {
++        let Some(val) = opt else { continue };
++    }
++}
++    "#,
++        );
++
++        check_assist(
++            convert_match_to_let_else,
++            r#"
++//- minicore: option
++fn panic() -> ! {}
++
++fn foo(opt: Option<()>) {
++    loop {
++        let val = $0match opt {
++            Some(it) => it,
++            None => panic(),
++        };
++    }
++}
++    "#,
++            r#"
++fn panic() -> ! {}
++
++fn foo(opt: Option<()>) {
++    loop {
++        let Some(val) = opt else { panic() };
++    }
++}
++    "#,
++        );
++    }
++
++    #[test]
++    fn struct_pattern() {
++        check_assist(
++            convert_match_to_let_else,
++            r#"
++//- minicore: option
++struct Point {
++    x: i32,
++    y: i32,
++}
++
++fn foo(opt: Option<Point>) {
++    let val = $0match opt {
++        Some(Point { x: 0, y }) => y,
++        _ => return,
++    };
++}
++    "#,
++            r#"
++struct Point {
++    x: i32,
++    y: i32,
++}
++
++fn foo(opt: Option<Point>) {
++    let Some(Point { x: 0, y: val }) = opt else { return };
++}
++    "#,
++        );
++    }
++
++    #[test]
++    fn renames_whole_binding() {
++        check_assist(
++            convert_match_to_let_else,
++            r#"
++//- minicore: option
++fn foo(opt: Option<i32>) -> Option<i32> {
++    let val = $0match opt {
++        it @ Some(42) => it,
++        _ => return None,
++    };
++    val
++}
++    "#,
++            r#"
++fn foo(opt: Option<i32>) -> Option<i32> {
++    let val @ Some(42) = opt else { return None };
++    val
++}
++    "#,
++        );
++    }
++}
index cb75619ced9c31f46bf0c2a92adfc3902870e67f,0000000000000000000000000000000000000000..b97be34c5f7e48324ff86a7c701dad8d6d96de38
mode 100644,000000..100644
--- /dev/null
@@@ -1,574 -1,0 +1,536 @@@
-                     let match_expr = {
-                         let happy_arm = {
-                             let pat = make::tuple_struct_pat(
-                                 path,
-                                 once(make::ext::simple_ident_pat(make::name("it")).into()),
-                             );
-                             let expr = {
-                                 let path = make::ext::ident_path("it");
-                                 make::expr_path(path)
-                             };
-                             make::match_arm(once(pat.into()), None, expr)
-                         };
-                         let sad_arm = make::match_arm(
-                             // FIXME: would be cool to use `None` or `Err(_)` if appropriate
-                             once(make::wildcard_pat().into()),
-                             None,
-                             early_expression,
-                         );
-                         make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm]))
-                     };
-                     let let_stmt = make::let_stmt(bound_ident, None, Some(match_expr));
-                     let let_stmt = let_stmt.indent(if_indent_level);
-                     let_stmt.syntax().clone_for_update()
 +use std::iter::once;
 +
 +use ide_db::syntax_helpers::node_ext::{is_pattern_cond, single_let};
 +use syntax::{
 +    ast::{
 +        self,
 +        edit::{AstNodeEdit, IndentLevel},
 +        make,
 +    },
 +    ted, AstNode,
 +    SyntaxKind::{FN, LOOP_EXPR, WHILE_EXPR, WHITESPACE},
 +    T,
 +};
 +
 +use crate::{
 +    assist_context::{AssistContext, Assists},
 +    utils::invert_boolean_expression,
 +    AssistId, AssistKind,
 +};
 +
 +// Assist: convert_to_guarded_return
 +//
 +// Replace a large conditional with a guarded return.
 +//
 +// ```
 +// fn main() {
 +//     $0if cond {
 +//         foo();
 +//         bar();
 +//     }
 +// }
 +// ```
 +// ->
 +// ```
 +// fn main() {
 +//     if !cond {
 +//         return;
 +//     }
 +//     foo();
 +//     bar();
 +// }
 +// ```
 +pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    let if_expr: ast::IfExpr = ctx.find_node_at_offset()?;
 +    if if_expr.else_branch().is_some() {
 +        return None;
 +    }
 +
 +    let cond = if_expr.condition()?;
 +
 +    // Check if there is an IfLet that we can handle.
 +    let (if_let_pat, cond_expr) = if is_pattern_cond(cond.clone()) {
 +        let let_ = single_let(cond)?;
 +        match let_.pat() {
 +            Some(ast::Pat::TupleStructPat(pat)) if pat.fields().count() == 1 => {
 +                let path = pat.path()?;
 +                if path.qualifier().is_some() {
 +                    return None;
 +                }
 +
 +                let bound_ident = pat.fields().next().unwrap();
 +                if !ast::IdentPat::can_cast(bound_ident.syntax().kind()) {
 +                    return None;
 +                }
 +
 +                (Some((path, bound_ident)), let_.expr()?)
 +            }
 +            _ => return None, // Unsupported IfLet.
 +        }
 +    } else {
 +        (None, cond)
 +    };
 +
 +    let then_block = if_expr.then_branch()?;
 +    let then_block = then_block.stmt_list()?;
 +
 +    let parent_block = if_expr.syntax().parent()?.ancestors().find_map(ast::BlockExpr::cast)?;
 +
 +    if parent_block.tail_expr()? != if_expr.clone().into() {
 +        return None;
 +    }
 +
 +    // FIXME: This relies on untyped syntax tree and casts to much. It should be
 +    // rewritten to use strongly-typed APIs.
 +
 +    // check for early return and continue
 +    let first_in_then_block = then_block.syntax().first_child()?;
 +    if ast::ReturnExpr::can_cast(first_in_then_block.kind())
 +        || ast::ContinueExpr::can_cast(first_in_then_block.kind())
 +        || first_in_then_block
 +            .children()
 +            .any(|x| ast::ReturnExpr::can_cast(x.kind()) || ast::ContinueExpr::can_cast(x.kind()))
 +    {
 +        return None;
 +    }
 +
 +    let parent_container = parent_block.syntax().parent()?;
 +
 +    let early_expression: ast::Expr = match parent_container.kind() {
 +        WHILE_EXPR | LOOP_EXPR => make::expr_continue(None),
 +        FN => make::expr_return(None),
 +        _ => return None,
 +    };
 +
 +    if then_block.syntax().first_child_or_token().map(|t| t.kind() == T!['{']).is_none() {
 +        return None;
 +    }
 +
 +    then_block.syntax().last_child_or_token().filter(|t| t.kind() == T!['}'])?;
 +
 +    let target = if_expr.syntax().text_range();
 +    acc.add(
 +        AssistId("convert_to_guarded_return", AssistKind::RefactorRewrite),
 +        "Convert to guarded return",
 +        target,
 +        |edit| {
 +            let if_expr = edit.make_mut(if_expr);
 +            let if_indent_level = IndentLevel::from_node(if_expr.syntax());
 +            let replacement = match if_let_pat {
 +                None => {
 +                    // If.
 +                    let new_expr = {
 +                        let then_branch =
 +                            make::block_expr(once(make::expr_stmt(early_expression).into()), None);
 +                        let cond = invert_boolean_expression(cond_expr);
 +                        make::expr_if(cond, then_branch, None).indent(if_indent_level)
 +                    };
 +                    new_expr.syntax().clone_for_update()
 +                }
 +                Some((path, bound_ident)) => {
 +                    // If-let.
-     let n = match n {
-         Some(it) => it,
-         _ => return,
-     };
++                    let pat = make::tuple_struct_pat(path, once(bound_ident));
++                    let let_else_stmt = make::let_else_stmt(
++                        pat.into(),
++                        None,
++                        cond_expr,
++                        ast::make::tail_only_block_expr(early_expression),
++                    );
++                    let let_else_stmt = let_else_stmt.indent(if_indent_level);
++                    let_else_stmt.syntax().clone_for_update()
 +                }
 +            };
 +
 +            let then_block_items = then_block.dedent(IndentLevel(1)).clone_for_update();
 +
 +            let end_of_then = then_block_items.syntax().last_child_or_token().unwrap();
 +            let end_of_then =
 +                if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) {
 +                    end_of_then.prev_sibling_or_token().unwrap()
 +                } else {
 +                    end_of_then
 +                };
 +
 +            let then_statements = replacement
 +                .children_with_tokens()
 +                .chain(
 +                    then_block_items
 +                        .syntax()
 +                        .children_with_tokens()
 +                        .skip(1)
 +                        .take_while(|i| *i != end_of_then),
 +                )
 +                .collect();
 +
 +            ted::replace_with_many(if_expr.syntax(), then_statements)
 +        },
 +    )
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use crate::tests::{check_assist, check_assist_not_applicable};
 +
 +    use super::*;
 +
 +    #[test]
 +    fn convert_inside_fn() {
 +        check_assist(
 +            convert_to_guarded_return,
 +            r#"
 +fn main() {
 +    bar();
 +    if$0 true {
 +        foo();
 +
 +        // comment
 +        bar();
 +    }
 +}
 +"#,
 +            r#"
 +fn main() {
 +    bar();
 +    if false {
 +        return;
 +    }
 +    foo();
 +
 +    // comment
 +    bar();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_let_inside_fn() {
 +        check_assist(
 +            convert_to_guarded_return,
 +            r#"
 +fn main(n: Option<String>) {
 +    bar();
 +    if$0 let Some(n) = n {
 +        foo(n);
 +
 +        // comment
 +        bar();
 +    }
 +}
 +"#,
 +            r#"
 +fn main(n: Option<String>) {
 +    bar();
-     let x = match Err(92) {
-         Ok(it) => it,
-         _ => return,
-     };
++    let Some(n) = n else { return };
 +    foo(n);
 +
 +    // comment
 +    bar();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_if_let_result() {
 +        check_assist(
 +            convert_to_guarded_return,
 +            r#"
 +fn main() {
 +    if$0 let Ok(x) = Err(92) {
 +        foo(x);
 +    }
 +}
 +"#,
 +            r#"
 +fn main() {
-     let n = match n {
-         Some(it) => it,
-         _ => return,
-     };
++    let Ok(x) = Err(92) else { return };
 +    foo(x);
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_let_ok_inside_fn() {
 +        check_assist(
 +            convert_to_guarded_return,
 +            r#"
 +fn main(n: Option<String>) {
 +    bar();
 +    if$0 let Some(n) = n {
 +        foo(n);
 +
 +        // comment
 +        bar();
 +    }
 +}
 +"#,
 +            r#"
 +fn main(n: Option<String>) {
 +    bar();
-     let mut n = match n {
-         Some(it) => it,
-         _ => return,
-     };
++    let Some(n) = n else { return };
 +    foo(n);
 +
 +    // comment
 +    bar();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_let_mut_ok_inside_fn() {
 +        check_assist(
 +            convert_to_guarded_return,
 +            r#"
 +fn main(n: Option<String>) {
 +    bar();
 +    if$0 let Some(mut n) = n {
 +        foo(n);
 +
 +        // comment
 +        bar();
 +    }
 +}
 +"#,
 +            r#"
 +fn main(n: Option<String>) {
 +    bar();
-     let ref n = match n {
-         Some(it) => it,
-         _ => return,
-     };
++    let Some(mut n) = n else { return };
 +    foo(n);
 +
 +    // comment
 +    bar();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_let_ref_ok_inside_fn() {
 +        check_assist(
 +            convert_to_guarded_return,
 +            r#"
 +fn main(n: Option<&str>) {
 +    bar();
 +    if$0 let Some(ref n) = n {
 +        foo(n);
 +
 +        // comment
 +        bar();
 +    }
 +}
 +"#,
 +            r#"
 +fn main(n: Option<&str>) {
 +    bar();
-         let n = match n {
-             Some(it) => it,
-             _ => continue,
-         };
++    let Some(ref n) = n else { return };
 +    foo(n);
 +
 +    // comment
 +    bar();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_inside_while() {
 +        check_assist(
 +            convert_to_guarded_return,
 +            r#"
 +fn main() {
 +    while true {
 +        if$0 true {
 +            foo();
 +            bar();
 +        }
 +    }
 +}
 +"#,
 +            r#"
 +fn main() {
 +    while true {
 +        if false {
 +            continue;
 +        }
 +        foo();
 +        bar();
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_let_inside_while() {
 +        check_assist(
 +            convert_to_guarded_return,
 +            r#"
 +fn main() {
 +    while true {
 +        if$0 let Some(n) = n {
 +            foo(n);
 +            bar();
 +        }
 +    }
 +}
 +"#,
 +            r#"
 +fn main() {
 +    while true {
-         let n = match n {
-             Some(it) => it,
-             _ => continue,
-         };
++        let Some(n) = n else { continue };
 +        foo(n);
 +        bar();
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_inside_loop() {
 +        check_assist(
 +            convert_to_guarded_return,
 +            r#"
 +fn main() {
 +    loop {
 +        if$0 true {
 +            foo();
 +            bar();
 +        }
 +    }
 +}
 +"#,
 +            r#"
 +fn main() {
 +    loop {
 +        if false {
 +            continue;
 +        }
 +        foo();
 +        bar();
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_let_inside_loop() {
 +        check_assist(
 +            convert_to_guarded_return,
 +            r#"
 +fn main() {
 +    loop {
 +        if$0 let Some(n) = n {
 +            foo(n);
 +            bar();
 +        }
 +    }
 +}
 +"#,
 +            r#"
 +fn main() {
 +    loop {
++        let Some(n) = n else { continue };
 +        foo(n);
 +        bar();
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn ignore_already_converted_if() {
 +        check_assist_not_applicable(
 +            convert_to_guarded_return,
 +            r#"
 +fn main() {
 +    if$0 true {
 +        return;
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn ignore_already_converted_loop() {
 +        check_assist_not_applicable(
 +            convert_to_guarded_return,
 +            r#"
 +fn main() {
 +    loop {
 +        if$0 true {
 +            continue;
 +        }
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn ignore_return() {
 +        check_assist_not_applicable(
 +            convert_to_guarded_return,
 +            r#"
 +fn main() {
 +    if$0 true {
 +        return
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn ignore_else_branch() {
 +        check_assist_not_applicable(
 +            convert_to_guarded_return,
 +            r#"
 +fn main() {
 +    if$0 true {
 +        foo();
 +    } else {
 +        bar()
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn ignore_statements_aftert_if() {
 +        check_assist_not_applicable(
 +            convert_to_guarded_return,
 +            r#"
 +fn main() {
 +    if$0 true {
 +        foo();
 +    }
 +    bar();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn ignore_statements_inside_if() {
 +        check_assist_not_applicable(
 +            convert_to_guarded_return,
 +            r#"
 +fn main() {
 +    if false {
 +        if$0 true {
 +            foo();
 +        }
 +    }
 +}
 +"#,
 +        );
 +    }
 +}
index d8f522708460e06622d63ae8b6bcb9d92775c7b5,0000000000000000000000000000000000000000..92e091fca126c9ac3bbfe8da5a8de017f326fec8
mode 100644,000000..100644
--- /dev/null
@@@ -1,840 -1,0 +1,846 @@@
-     fields.enumerate().map(|(i, _)| ast::make::name(&format!("field{}", i + 1))).collect()
 +use either::Either;
 +use ide_db::defs::{Definition, NameRefClass};
 +use syntax::{
 +    ast::{self, AstNode, HasGenericParams, HasVisibility},
 +    match_ast, SyntaxNode,
 +};
 +
 +use crate::{assist_context::SourceChangeBuilder, AssistContext, AssistId, AssistKind, Assists};
 +
 +// Assist: convert_tuple_struct_to_named_struct
 +//
 +// Converts tuple struct to struct with named fields, and analogously for tuple enum variants.
 +//
 +// ```
 +// struct Point$0(f32, f32);
 +//
 +// impl Point {
 +//     pub fn new(x: f32, y: f32) -> Self {
 +//         Point(x, y)
 +//     }
 +//
 +//     pub fn x(&self) -> f32 {
 +//         self.0
 +//     }
 +//
 +//     pub fn y(&self) -> f32 {
 +//         self.1
 +//     }
 +// }
 +// ```
 +// ->
 +// ```
 +// struct Point { field1: f32, field2: f32 }
 +//
 +// impl Point {
 +//     pub fn new(x: f32, y: f32) -> Self {
 +//         Point { field1: x, field2: y }
 +//     }
 +//
 +//     pub fn x(&self) -> f32 {
 +//         self.field1
 +//     }
 +//
 +//     pub fn y(&self) -> f32 {
 +//         self.field2
 +//     }
 +// }
 +// ```
 +pub(crate) fn convert_tuple_struct_to_named_struct(
 +    acc: &mut Assists,
 +    ctx: &AssistContext<'_>,
 +) -> Option<()> {
 +    let strukt = ctx
 +        .find_node_at_offset::<ast::Struct>()
 +        .map(Either::Left)
 +        .or_else(|| ctx.find_node_at_offset::<ast::Variant>().map(Either::Right))?;
 +    let field_list = strukt.as_ref().either(|s| s.field_list(), |v| v.field_list())?;
 +    let tuple_fields = match field_list {
 +        ast::FieldList::TupleFieldList(it) => it,
 +        ast::FieldList::RecordFieldList(_) => return None,
 +    };
 +    let strukt_def = match &strukt {
 +        Either::Left(s) => Either::Left(ctx.sema.to_def(s)?),
 +        Either::Right(v) => Either::Right(ctx.sema.to_def(v)?),
 +    };
 +    let target = strukt.as_ref().either(|s| s.syntax(), |v| v.syntax()).text_range();
 +
 +    acc.add(
 +        AssistId("convert_tuple_struct_to_named_struct", AssistKind::RefactorRewrite),
 +        "Convert to named struct",
 +        target,
 +        |edit| {
 +            let names = generate_names(tuple_fields.fields());
 +            edit_field_references(ctx, edit, tuple_fields.fields(), &names);
 +            edit_struct_references(ctx, edit, strukt_def, &names);
 +            edit_struct_def(ctx, edit, &strukt, tuple_fields, names);
 +        },
 +    )
 +}
 +
 +fn edit_struct_def(
 +    ctx: &AssistContext<'_>,
 +    edit: &mut SourceChangeBuilder,
 +    strukt: &Either<ast::Struct, ast::Variant>,
 +    tuple_fields: ast::TupleFieldList,
 +    names: Vec<ast::Name>,
 +) {
 +    let record_fields = tuple_fields
 +        .fields()
 +        .zip(names)
 +        .filter_map(|(f, name)| Some(ast::make::record_field(f.visibility(), name, f.ty()?)));
 +    let record_fields = ast::make::record_field_list(record_fields);
 +    let tuple_fields_text_range = tuple_fields.syntax().text_range();
 +
 +    edit.edit_file(ctx.file_id());
 +
 +    if let Either::Left(strukt) = strukt {
 +        if let Some(w) = strukt.where_clause() {
 +            edit.delete(w.syntax().text_range());
 +            edit.insert(
 +                tuple_fields_text_range.start(),
 +                ast::make::tokens::single_newline().text(),
 +            );
 +            edit.insert(tuple_fields_text_range.start(), w.syntax().text());
 +            edit.insert(tuple_fields_text_range.start(), ",");
 +            edit.insert(
 +                tuple_fields_text_range.start(),
 +                ast::make::tokens::single_newline().text(),
 +            );
 +        } else {
 +            edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_space().text());
 +        }
 +        if let Some(t) = strukt.semicolon_token() {
 +            edit.delete(t.text_range());
 +        }
 +    } else {
 +        edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_space().text());
 +    }
 +
 +    edit.replace(tuple_fields_text_range, record_fields.to_string());
 +}
 +
 +fn edit_struct_references(
 +    ctx: &AssistContext<'_>,
 +    edit: &mut SourceChangeBuilder,
 +    strukt: Either<hir::Struct, hir::Variant>,
 +    names: &[ast::Name],
 +) {
 +    let strukt_def = match strukt {
 +        Either::Left(s) => Definition::Adt(hir::Adt::Struct(s)),
 +        Either::Right(v) => Definition::Variant(v),
 +    };
 +    let usages = strukt_def.usages(&ctx.sema).include_self_refs().all();
 +
 +    let edit_node = |edit: &mut SourceChangeBuilder, node: SyntaxNode| -> Option<()> {
 +        match_ast! {
 +            match node {
 +                ast::TupleStructPat(tuple_struct_pat) => {
 +                    edit.replace(
 +                        tuple_struct_pat.syntax().text_range(),
 +                        ast::make::record_pat_with_fields(
 +                            tuple_struct_pat.path()?,
 +                            ast::make::record_pat_field_list(tuple_struct_pat.fields().zip(names).map(
 +                                |(pat, name)| {
 +                                    ast::make::record_pat_field(
 +                                        ast::make::name_ref(&name.to_string()),
 +                                        pat,
 +                                    )
 +                                },
 +                            )),
 +                        )
 +                        .to_string(),
 +                    );
 +                },
 +                // for tuple struct creations like Foo(42)
 +                ast::CallExpr(call_expr) => {
 +                    let path = call_expr.syntax().descendants().find_map(ast::PathExpr::cast).and_then(|expr| expr.path())?;
 +
 +                    // this also includes method calls like Foo::new(42), we should skip them
 +                    if let Some(name_ref) = path.segment().and_then(|s| s.name_ref()) {
 +                        match NameRefClass::classify(&ctx.sema, &name_ref) {
 +                            Some(NameRefClass::Definition(Definition::SelfType(_))) => {},
 +                            Some(NameRefClass::Definition(def)) if def == strukt_def => {},
 +                            _ => return None,
 +                        };
 +                    }
 +
 +                    let arg_list = call_expr.syntax().descendants().find_map(ast::ArgList::cast)?;
 +
 +                    edit.replace(
 +                        call_expr.syntax().text_range(),
 +                        ast::make::record_expr(
 +                            path,
 +                            ast::make::record_expr_field_list(arg_list.args().zip(names).map(
 +                                |(expr, name)| {
 +                                    ast::make::record_expr_field(
 +                                        ast::make::name_ref(&name.to_string()),
 +                                        Some(expr),
 +                                    )
 +                                },
 +                            )),
 +                        )
 +                        .to_string(),
 +                    );
 +                },
 +                _ => return None,
 +            }
 +        }
 +        Some(())
 +    };
 +
 +    for (file_id, refs) in usages {
 +        edit.edit_file(file_id);
 +        for r in refs {
 +            for node in r.name.syntax().ancestors() {
 +                if edit_node(edit, node).is_some() {
 +                    break;
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn edit_field_references(
 +    ctx: &AssistContext<'_>,
 +    edit: &mut SourceChangeBuilder,
 +    fields: impl Iterator<Item = ast::TupleField>,
 +    names: &[ast::Name],
 +) {
 +    for (field, name) in fields.zip(names) {
 +        let field = match ctx.sema.to_def(&field) {
 +            Some(it) => it,
 +            None => continue,
 +        };
 +        let def = Definition::Field(field);
 +        let usages = def.usages(&ctx.sema).all();
 +        for (file_id, refs) in usages {
 +            edit.edit_file(file_id);
 +            for r in refs {
 +                if let Some(name_ref) = r.name.as_name_ref() {
 +                    edit.replace(name_ref.syntax().text_range(), name.text());
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn generate_names(fields: impl Iterator<Item = ast::TupleField>) -> Vec<ast::Name> {
++    fields
++        .enumerate()
++        .map(|(i, _)| {
++            let idx = i + 1;
++            ast::make::name(&format!("field{idx}"))
++        })
++        .collect()
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use crate::tests::{check_assist, check_assist_not_applicable};
 +
 +    use super::*;
 +
 +    #[test]
 +    fn not_applicable_other_than_tuple_struct() {
 +        check_assist_not_applicable(
 +            convert_tuple_struct_to_named_struct,
 +            r#"struct Foo$0 { bar: u32 };"#,
 +        );
 +        check_assist_not_applicable(convert_tuple_struct_to_named_struct, r#"struct Foo$0;"#);
 +    }
 +
 +    #[test]
 +    fn convert_simple_struct() {
 +        check_assist(
 +            convert_tuple_struct_to_named_struct,
 +            r#"
 +struct Inner;
 +struct A$0(Inner);
 +
 +impl A {
 +    fn new(inner: Inner) -> A {
 +        A(inner)
 +    }
 +
 +    fn new_with_default() -> A {
 +        A::new(Inner)
 +    }
 +
 +    fn into_inner(self) -> Inner {
 +        self.0
 +    }
 +}"#,
 +            r#"
 +struct Inner;
 +struct A { field1: Inner }
 +
 +impl A {
 +    fn new(inner: Inner) -> A {
 +        A { field1: inner }
 +    }
 +
 +    fn new_with_default() -> A {
 +        A::new(Inner)
 +    }
 +
 +    fn into_inner(self) -> Inner {
 +        self.field1
 +    }
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_struct_referenced_via_self_kw() {
 +        check_assist(
 +            convert_tuple_struct_to_named_struct,
 +            r#"
 +struct Inner;
 +struct A$0(Inner);
 +
 +impl A {
 +    fn new(inner: Inner) -> Self {
 +        Self(inner)
 +    }
 +
 +    fn new_with_default() -> Self {
 +        Self::new(Inner)
 +    }
 +
 +    fn into_inner(self) -> Inner {
 +        self.0
 +    }
 +}"#,
 +            r#"
 +struct Inner;
 +struct A { field1: Inner }
 +
 +impl A {
 +    fn new(inner: Inner) -> Self {
 +        Self { field1: inner }
 +    }
 +
 +    fn new_with_default() -> Self {
 +        Self::new(Inner)
 +    }
 +
 +    fn into_inner(self) -> Inner {
 +        self.field1
 +    }
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_destructured_struct() {
 +        check_assist(
 +            convert_tuple_struct_to_named_struct,
 +            r#"
 +struct Inner;
 +struct A$0(Inner);
 +
 +impl A {
 +    fn into_inner(self) -> Inner {
 +        let A(first) = self;
 +        first
 +    }
 +
 +    fn into_inner_via_self(self) -> Inner {
 +        let Self(first) = self;
 +        first
 +    }
 +}"#,
 +            r#"
 +struct Inner;
 +struct A { field1: Inner }
 +
 +impl A {
 +    fn into_inner(self) -> Inner {
 +        let A { field1: first } = self;
 +        first
 +    }
 +
 +    fn into_inner_via_self(self) -> Inner {
 +        let Self { field1: first } = self;
 +        first
 +    }
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_struct_with_visibility() {
 +        check_assist(
 +            convert_tuple_struct_to_named_struct,
 +            r#"
 +struct A$0(pub u32, pub(crate) u64);
 +
 +impl A {
 +    fn new() -> A {
 +        A(42, 42)
 +    }
 +
 +    fn into_first(self) -> u32 {
 +        self.0
 +    }
 +
 +    fn into_second(self) -> u64 {
 +        self.1
 +    }
 +}"#,
 +            r#"
 +struct A { pub field1: u32, pub(crate) field2: u64 }
 +
 +impl A {
 +    fn new() -> A {
 +        A { field1: 42, field2: 42 }
 +    }
 +
 +    fn into_first(self) -> u32 {
 +        self.field1
 +    }
 +
 +    fn into_second(self) -> u64 {
 +        self.field2
 +    }
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_struct_with_wrapped_references() {
 +        check_assist(
 +            convert_tuple_struct_to_named_struct,
 +            r#"
 +struct Inner$0(u32);
 +struct Outer(Inner);
 +
 +impl Outer {
 +    fn new() -> Self {
 +        Self(Inner(42))
 +    }
 +
 +    fn into_inner(self) -> u32 {
 +        (self.0).0
 +    }
 +
 +    fn into_inner_destructed(self) -> u32 {
 +        let Outer(Inner(x)) = self;
 +        x
 +    }
 +}"#,
 +            r#"
 +struct Inner { field1: u32 }
 +struct Outer(Inner);
 +
 +impl Outer {
 +    fn new() -> Self {
 +        Self(Inner { field1: 42 })
 +    }
 +
 +    fn into_inner(self) -> u32 {
 +        (self.0).field1
 +    }
 +
 +    fn into_inner_destructed(self) -> u32 {
 +        let Outer(Inner { field1: x }) = self;
 +        x
 +    }
 +}"#,
 +        );
 +
 +        check_assist(
 +            convert_tuple_struct_to_named_struct,
 +            r#"
 +struct Inner(u32);
 +struct Outer$0(Inner);
 +
 +impl Outer {
 +    fn new() -> Self {
 +        Self(Inner(42))
 +    }
 +
 +    fn into_inner(self) -> u32 {
 +        (self.0).0
 +    }
 +
 +    fn into_inner_destructed(self) -> u32 {
 +        let Outer(Inner(x)) = self;
 +        x
 +    }
 +}"#,
 +            r#"
 +struct Inner(u32);
 +struct Outer { field1: Inner }
 +
 +impl Outer {
 +    fn new() -> Self {
 +        Self { field1: Inner(42) }
 +    }
 +
 +    fn into_inner(self) -> u32 {
 +        (self.field1).0
 +    }
 +
 +    fn into_inner_destructed(self) -> u32 {
 +        let Outer { field1: Inner(x) } = self;
 +        x
 +    }
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_struct_with_multi_file_references() {
 +        check_assist(
 +            convert_tuple_struct_to_named_struct,
 +            r#"
 +//- /main.rs
 +struct Inner;
 +struct A$0(Inner);
 +
 +mod foo;
 +
 +//- /foo.rs
 +use crate::{A, Inner};
 +fn f() {
 +    let a = A(Inner);
 +}
 +"#,
 +            r#"
 +//- /main.rs
 +struct Inner;
 +struct A { field1: Inner }
 +
 +mod foo;
 +
 +//- /foo.rs
 +use crate::{A, Inner};
 +fn f() {
 +    let a = A { field1: Inner };
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_struct_with_where_clause() {
 +        check_assist(
 +            convert_tuple_struct_to_named_struct,
 +            r#"
 +struct Wrap$0<T>(T)
 +where
 +    T: Display;
 +"#,
 +            r#"
 +struct Wrap<T>
 +where
 +    T: Display,
 +{ field1: T }
 +
 +"#,
 +        );
 +    }
 +    #[test]
 +    fn not_applicable_other_than_tuple_variant() {
 +        check_assist_not_applicable(
 +            convert_tuple_struct_to_named_struct,
 +            r#"enum Enum { Variant$0 { value: usize } };"#,
 +        );
 +        check_assist_not_applicable(
 +            convert_tuple_struct_to_named_struct,
 +            r#"enum Enum { Variant$0 }"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_simple_variant() {
 +        check_assist(
 +            convert_tuple_struct_to_named_struct,
 +            r#"
 +enum A {
 +    $0Variant(usize),
 +}
 +
 +impl A {
 +    fn new(value: usize) -> A {
 +        A::Variant(value)
 +    }
 +
 +    fn new_with_default() -> A {
 +        A::new(Default::default())
 +    }
 +
 +    fn value(self) -> usize {
 +        match self {
 +            A::Variant(value) => value,
 +        }
 +    }
 +}"#,
 +            r#"
 +enum A {
 +    Variant { field1: usize },
 +}
 +
 +impl A {
 +    fn new(value: usize) -> A {
 +        A::Variant { field1: value }
 +    }
 +
 +    fn new_with_default() -> A {
 +        A::new(Default::default())
 +    }
 +
 +    fn value(self) -> usize {
 +        match self {
 +            A::Variant { field1: value } => value,
 +        }
 +    }
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_variant_referenced_via_self_kw() {
 +        check_assist(
 +            convert_tuple_struct_to_named_struct,
 +            r#"
 +enum A {
 +    $0Variant(usize),
 +}
 +
 +impl A {
 +    fn new(value: usize) -> A {
 +        Self::Variant(value)
 +    }
 +
 +    fn new_with_default() -> A {
 +        Self::new(Default::default())
 +    }
 +
 +    fn value(self) -> usize {
 +        match self {
 +            Self::Variant(value) => value,
 +        }
 +    }
 +}"#,
 +            r#"
 +enum A {
 +    Variant { field1: usize },
 +}
 +
 +impl A {
 +    fn new(value: usize) -> A {
 +        Self::Variant { field1: value }
 +    }
 +
 +    fn new_with_default() -> A {
 +        Self::new(Default::default())
 +    }
 +
 +    fn value(self) -> usize {
 +        match self {
 +            Self::Variant { field1: value } => value,
 +        }
 +    }
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_destructured_variant() {
 +        check_assist(
 +            convert_tuple_struct_to_named_struct,
 +            r#"
 +enum A {
 +    $0Variant(usize),
 +}
 +
 +impl A {
 +    fn into_inner(self) -> usize {
 +        let A::Variant(first) = self;
 +        first
 +    }
 +
 +    fn into_inner_via_self(self) -> usize {
 +        let Self::Variant(first) = self;
 +        first
 +    }
 +}"#,
 +            r#"
 +enum A {
 +    Variant { field1: usize },
 +}
 +
 +impl A {
 +    fn into_inner(self) -> usize {
 +        let A::Variant { field1: first } = self;
 +        first
 +    }
 +
 +    fn into_inner_via_self(self) -> usize {
 +        let Self::Variant { field1: first } = self;
 +        first
 +    }
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_variant_with_wrapped_references() {
 +        check_assist(
 +            convert_tuple_struct_to_named_struct,
 +            r#"
 +enum Inner {
 +    $0Variant(usize),
 +}
 +enum Outer {
 +    Variant(Inner),
 +}
 +
 +impl Outer {
 +    fn new() -> Self {
 +        Self::Variant(Inner::Variant(42))
 +    }
 +
 +    fn into_inner_destructed(self) -> u32 {
 +        let Outer::Variant(Inner::Variant(x)) = self;
 +        x
 +    }
 +}"#,
 +            r#"
 +enum Inner {
 +    Variant { field1: usize },
 +}
 +enum Outer {
 +    Variant(Inner),
 +}
 +
 +impl Outer {
 +    fn new() -> Self {
 +        Self::Variant(Inner::Variant { field1: 42 })
 +    }
 +
 +    fn into_inner_destructed(self) -> u32 {
 +        let Outer::Variant(Inner::Variant { field1: x }) = self;
 +        x
 +    }
 +}"#,
 +        );
 +
 +        check_assist(
 +            convert_tuple_struct_to_named_struct,
 +            r#"
 +enum Inner {
 +    Variant(usize),
 +}
 +enum Outer {
 +    $0Variant(Inner),
 +}
 +
 +impl Outer {
 +    fn new() -> Self {
 +        Self::Variant(Inner::Variant(42))
 +    }
 +
 +    fn into_inner_destructed(self) -> u32 {
 +        let Outer::Variant(Inner::Variant(x)) = self;
 +        x
 +    }
 +}"#,
 +            r#"
 +enum Inner {
 +    Variant(usize),
 +}
 +enum Outer {
 +    Variant { field1: Inner },
 +}
 +
 +impl Outer {
 +    fn new() -> Self {
 +        Self::Variant { field1: Inner::Variant(42) }
 +    }
 +
 +    fn into_inner_destructed(self) -> u32 {
 +        let Outer::Variant { field1: Inner::Variant(x) } = self;
 +        x
 +    }
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_variant_with_multi_file_references() {
 +        check_assist(
 +            convert_tuple_struct_to_named_struct,
 +            r#"
 +//- /main.rs
 +struct Inner;
 +enum A {
 +    $0Variant(Inner),
 +}
 +
 +mod foo;
 +
 +//- /foo.rs
 +use crate::{A, Inner};
 +fn f() {
 +    let a = A::Variant(Inner);
 +}
 +"#,
 +            r#"
 +//- /main.rs
 +struct Inner;
 +enum A {
 +    Variant { field1: Inner },
 +}
 +
 +mod foo;
 +
 +//- /foo.rs
 +use crate::{A, Inner};
 +fn f() {
 +    let a = A::Variant { field1: Inner };
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_directly_used_variant() {
 +        check_assist(
 +            convert_tuple_struct_to_named_struct,
 +            r#"
 +//- /main.rs
 +struct Inner;
 +enum A {
 +    $0Variant(Inner),
 +}
 +
 +mod foo;
 +
 +//- /foo.rs
 +use crate::{A::Variant, Inner};
 +fn f() {
 +    let a = Variant(Inner);
 +}
 +"#,
 +            r#"
 +//- /main.rs
 +struct Inner;
 +enum A {
 +    Variant { field1: Inner },
 +}
 +
 +mod foo;
 +
 +//- /foo.rs
 +use crate::{A::Variant, Inner};
 +fn f() {
 +    let a = Variant { field1: Inner };
 +}
 +"#,
 +        );
 +    }
 +}
index 54a7f480a4e46abd5316a3cc719e8c54ede8a69f,0000000000000000000000000000000000000000..b1b0f587cd33d0edc338f93c7cdfcb66d0a7b61c
mode 100644,000000..100644
--- /dev/null
@@@ -1,294 -1,0 +1,294 @@@
-             if let Some(ref pat) = first_arm.pat() {
 +use syntax::ast::{self, AstNode};
 +
 +use crate::{AssistContext, AssistId, AssistKind, Assists};
 +
 +// Assist: convert_two_arm_bool_match_to_matches_macro
 +//
 +// Convert 2-arm match that evaluates to a boolean into the equivalent matches! invocation.
 +//
 +// ```
 +// fn main() {
 +//     match scrutinee$0 {
 +//         Some(val) if val.cond() => true,
 +//         _ => false,
 +//     }
 +// }
 +// ```
 +// ->
 +// ```
 +// fn main() {
 +//     matches!(scrutinee, Some(val) if val.cond())
 +// }
 +// ```
 +pub(crate) fn convert_two_arm_bool_match_to_matches_macro(
 +    acc: &mut Assists,
 +    ctx: &AssistContext<'_>,
 +) -> Option<()> {
 +    let match_expr = ctx.find_node_at_offset::<ast::MatchExpr>()?;
 +    let match_arm_list = match_expr.match_arm_list()?;
 +    let mut arms = match_arm_list.arms();
 +    let first_arm = arms.next()?;
 +    let second_arm = arms.next()?;
 +    if arms.next().is_some() {
 +        cov_mark::hit!(non_two_arm_match);
 +        return None;
 +    }
 +    let first_arm_expr = first_arm.expr();
 +    let second_arm_expr = second_arm.expr();
 +
 +    let invert_matches = if is_bool_literal_expr(&first_arm_expr, true)
 +        && is_bool_literal_expr(&second_arm_expr, false)
 +    {
 +        false
 +    } else if is_bool_literal_expr(&first_arm_expr, false)
 +        && is_bool_literal_expr(&second_arm_expr, true)
 +    {
 +        true
 +    } else {
 +        cov_mark::hit!(non_invert_bool_literal_arms);
 +        return None;
 +    };
 +
 +    let target_range = ctx.sema.original_range(match_expr.syntax()).range;
 +    let expr = match_expr.expr()?;
 +
 +    acc.add(
 +        AssistId("convert_two_arm_bool_match_to_matches_macro", AssistKind::RefactorRewrite),
 +        "Convert to matches!",
 +        target_range,
 +        |builder| {
 +            let mut arm_str = String::new();
-             if let Some(ref guard) = first_arm.guard() {
-                 arm_str += &format!(" {}", &guard.to_string());
++            if let Some(pat) = &first_arm.pat() {
 +                arm_str += &pat.to_string();
 +            }
-                 builder.replace(target_range, format!("!matches!({}, {})", expr, arm_str));
++            if let Some(guard) = &first_arm.guard() {
++                arm_str += &format!(" {guard}");
 +            }
 +            if invert_matches {
-                 builder.replace(target_range, format!("matches!({}, {})", expr, arm_str));
++                builder.replace(target_range, format!("!matches!({expr}, {arm_str})"));
 +            } else {
++                builder.replace(target_range, format!("matches!({expr}, {arm_str})"));
 +            }
 +        },
 +    )
 +}
 +
 +fn is_bool_literal_expr(expr: &Option<ast::Expr>, expect_bool: bool) -> bool {
 +    if let Some(ast::Expr::Literal(lit)) = expr {
 +        if let ast::LiteralKind::Bool(b) = lit.kind() {
 +            return b == expect_bool;
 +        }
 +    }
 +
 +    return false;
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
 +
 +    use super::convert_two_arm_bool_match_to_matches_macro;
 +
 +    #[test]
 +    fn not_applicable_outside_of_range_left() {
 +        check_assist_not_applicable(
 +            convert_two_arm_bool_match_to_matches_macro,
 +            r#"
 +fn foo(a: Option<u32>) -> bool {
 +    $0 match a {
 +        Some(_val) => true,
 +        _ => false
 +    }
 +}
 +        "#,
 +        );
 +    }
 +
 +    #[test]
 +    fn not_applicable_non_two_arm_match() {
 +        cov_mark::check!(non_two_arm_match);
 +        check_assist_not_applicable(
 +            convert_two_arm_bool_match_to_matches_macro,
 +            r#"
 +fn foo(a: Option<u32>) -> bool {
 +    match a$0 {
 +        Some(3) => true,
 +        Some(4) => true,
 +        _ => false
 +    }
 +}
 +        "#,
 +        );
 +    }
 +
 +    #[test]
 +    fn not_applicable_non_bool_literal_arms() {
 +        cov_mark::check!(non_invert_bool_literal_arms);
 +        check_assist_not_applicable(
 +            convert_two_arm_bool_match_to_matches_macro,
 +            r#"
 +fn foo(a: Option<u32>) -> bool {
 +    match a$0 {
 +        Some(val) => val == 3,
 +        _ => false
 +    }
 +}
 +        "#,
 +        );
 +    }
 +    #[test]
 +    fn not_applicable_both_false_arms() {
 +        cov_mark::check!(non_invert_bool_literal_arms);
 +        check_assist_not_applicable(
 +            convert_two_arm_bool_match_to_matches_macro,
 +            r#"
 +fn foo(a: Option<u32>) -> bool {
 +    match a$0 {
 +        Some(val) => false,
 +        _ => false
 +    }
 +}
 +        "#,
 +        );
 +    }
 +
 +    #[test]
 +    fn not_applicable_both_true_arms() {
 +        cov_mark::check!(non_invert_bool_literal_arms);
 +        check_assist_not_applicable(
 +            convert_two_arm_bool_match_to_matches_macro,
 +            r#"
 +fn foo(a: Option<u32>) -> bool {
 +    match a$0 {
 +        Some(val) => true,
 +        _ => true
 +    }
 +}
 +        "#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_simple_case() {
 +        check_assist(
 +            convert_two_arm_bool_match_to_matches_macro,
 +            r#"
 +fn foo(a: Option<u32>) -> bool {
 +    match a$0 {
 +        Some(_val) => true,
 +        _ => false
 +    }
 +}
 +"#,
 +            r#"
 +fn foo(a: Option<u32>) -> bool {
 +    matches!(a, Some(_val))
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_simple_invert_case() {
 +        check_assist(
 +            convert_two_arm_bool_match_to_matches_macro,
 +            r#"
 +fn foo(a: Option<u32>) -> bool {
 +    match a$0 {
 +        Some(_val) => false,
 +        _ => true
 +    }
 +}
 +"#,
 +            r#"
 +fn foo(a: Option<u32>) -> bool {
 +    !matches!(a, Some(_val))
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_with_guard_case() {
 +        check_assist(
 +            convert_two_arm_bool_match_to_matches_macro,
 +            r#"
 +fn foo(a: Option<u32>) -> bool {
 +    match a$0 {
 +        Some(val) if val > 3 => true,
 +        _ => false
 +    }
 +}
 +"#,
 +            r#"
 +fn foo(a: Option<u32>) -> bool {
 +    matches!(a, Some(val) if val > 3)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_enum_match_cases() {
 +        check_assist(
 +            convert_two_arm_bool_match_to_matches_macro,
 +            r#"
 +enum X { A, B }
 +
 +fn foo(a: X) -> bool {
 +    match a$0 {
 +        X::A => true,
 +        _ => false
 +    }
 +}
 +"#,
 +            r#"
 +enum X { A, B }
 +
 +fn foo(a: X) -> bool {
 +    matches!(a, X::A)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_target_simple() {
 +        check_assist_target(
 +            convert_two_arm_bool_match_to_matches_macro,
 +            r#"
 +fn foo(a: Option<u32>) -> bool {
 +    match a$0 {
 +        Some(val) => true,
 +        _ => false
 +    }
 +}
 +"#,
 +            r#"match a {
 +        Some(val) => true,
 +        _ => false
 +    }"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn convert_target_complex() {
 +        check_assist_target(
 +            convert_two_arm_bool_match_to_matches_macro,
 +            r#"
 +enum E { X, Y }
 +
 +fn main() {
 +    match E::X$0 {
 +        E::X => true,
 +        _ => false,
 +    }
 +}
 +"#,
 +            "match E::X {
 +        E::X => true,
 +        _ => false,
 +    }",
 +        );
 +    }
 +}
index dc581ff3bd2c7a85adcfcd4451c8b8a0ed20af57,0000000000000000000000000000000000000000..31c2ce7c1b54ae93a6e16bbf4d34243b1e07d6f6
mode 100644,000000..100644
--- /dev/null
@@@ -1,2147 -1,0 +1,2147 @@@
-     format!("_{}", index)
 +use ide_db::{
 +    assists::{AssistId, AssistKind},
 +    defs::Definition,
 +    search::{FileReference, SearchScope, UsageSearchResult},
 +};
 +use syntax::{
 +    ast::{self, AstNode, FieldExpr, HasName, IdentPat, MethodCallExpr},
 +    TextRange,
 +};
 +
 +use crate::assist_context::{AssistContext, Assists, SourceChangeBuilder};
 +
 +// Assist: destructure_tuple_binding
 +//
 +// Destructures a tuple binding in place.
 +//
 +// ```
 +// fn main() {
 +//     let $0t = (1,2);
 +//     let v = t.0;
 +// }
 +// ```
 +// ->
 +// ```
 +// fn main() {
 +//     let ($0_0, _1) = (1,2);
 +//     let v = _0;
 +// }
 +// ```
 +pub(crate) fn destructure_tuple_binding(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    destructure_tuple_binding_impl(acc, ctx, false)
 +}
 +
 +// And when `with_sub_pattern` enabled (currently disabled):
 +// Assist: destructure_tuple_binding_in_sub_pattern
 +//
 +// Destructures tuple items in sub-pattern (after `@`).
 +//
 +// ```
 +// fn main() {
 +//     let $0t = (1,2);
 +//     let v = t.0;
 +// }
 +// ```
 +// ->
 +// ```
 +// fn main() {
 +//     let t @ ($0_0, _1) = (1,2);
 +//     let v = _0;
 +// }
 +// ```
 +pub(crate) fn destructure_tuple_binding_impl(
 +    acc: &mut Assists,
 +    ctx: &AssistContext<'_>,
 +    with_sub_pattern: bool,
 +) -> Option<()> {
 +    let ident_pat = ctx.find_node_at_offset::<ast::IdentPat>()?;
 +    let data = collect_data(ident_pat, ctx)?;
 +
 +    if with_sub_pattern {
 +        acc.add(
 +            AssistId("destructure_tuple_binding_in_sub_pattern", AssistKind::RefactorRewrite),
 +            "Destructure tuple in sub-pattern",
 +            data.range,
 +            |builder| {
 +                edit_tuple_assignment(ctx, builder, &data, true);
 +                edit_tuple_usages(&data, builder, ctx, true);
 +            },
 +        );
 +    }
 +
 +    acc.add(
 +        AssistId("destructure_tuple_binding", AssistKind::RefactorRewrite),
 +        if with_sub_pattern { "Destructure tuple in place" } else { "Destructure tuple" },
 +        data.range,
 +        |builder| {
 +            edit_tuple_assignment(ctx, builder, &data, false);
 +            edit_tuple_usages(&data, builder, ctx, false);
 +        },
 +    );
 +
 +    Some(())
 +}
 +
 +fn collect_data(ident_pat: IdentPat, ctx: &AssistContext<'_>) -> Option<TupleData> {
 +    if ident_pat.at_token().is_some() {
 +        // Cannot destructure pattern with sub-pattern:
 +        // Only IdentPat can have sub-pattern,
 +        // but not TuplePat (`(a,b)`).
 +        cov_mark::hit!(destructure_tuple_subpattern);
 +        return None;
 +    }
 +
 +    let ty = ctx.sema.type_of_pat(&ident_pat.clone().into())?.adjusted();
 +    let ref_type = if ty.is_mutable_reference() {
 +        Some(RefType::Mutable)
 +    } else if ty.is_reference() {
 +        Some(RefType::ReadOnly)
 +    } else {
 +        None
 +    };
 +    // might be reference
 +    let ty = ty.strip_references();
 +    // must be tuple
 +    let field_types = ty.tuple_fields(ctx.db());
 +    if field_types.is_empty() {
 +        cov_mark::hit!(destructure_tuple_no_tuple);
 +        return None;
 +    }
 +
 +    let name = ident_pat.name()?.to_string();
 +    let range = ident_pat.syntax().text_range();
 +
 +    let usages = ctx.sema.to_def(&ident_pat).map(|def| {
 +        Definition::Local(def)
 +            .usages(&ctx.sema)
 +            .in_scope(SearchScope::single_file(ctx.file_id()))
 +            .all()
 +    });
 +
 +    let field_names = (0..field_types.len())
 +        .map(|i| generate_name(ctx, i, &name, &ident_pat, &usages))
 +        .collect::<Vec<_>>();
 +
 +    Some(TupleData { ident_pat, range, ref_type, field_names, usages })
 +}
 +
 +fn generate_name(
 +    _ctx: &AssistContext<'_>,
 +    index: usize,
 +    _tuple_name: &str,
 +    _ident_pat: &IdentPat,
 +    _usages: &Option<UsageSearchResult>,
 +) -> String {
 +    // FIXME: detect if name already used
-         text.replacen(first_tuple, &format!("$0{}", first_tuple), 1)
++    format!("_{index}")
 +}
 +
 +enum RefType {
 +    ReadOnly,
 +    Mutable,
 +}
 +struct TupleData {
 +    ident_pat: IdentPat,
 +    // name: String,
 +    range: TextRange,
 +    ref_type: Option<RefType>,
 +    field_names: Vec<String>,
 +    // field_types: Vec<Type>,
 +    usages: Option<UsageSearchResult>,
 +}
 +fn edit_tuple_assignment(
 +    ctx: &AssistContext<'_>,
 +    builder: &mut SourceChangeBuilder,
 +    data: &TupleData,
 +    in_sub_pattern: bool,
 +) {
 +    let tuple_pat = {
 +        let original = &data.ident_pat;
 +        let is_ref = original.ref_token().is_some();
 +        let is_mut = original.mut_token().is_some();
 +        let fields = data.field_names.iter().map(|name| {
 +            ast::Pat::from(ast::make::ident_pat(is_ref, is_mut, ast::make::name(name)))
 +        });
 +        ast::make::tuple_pat(fields)
 +    };
 +
 +    let add_cursor = |text: &str| {
 +        // place cursor on first tuple item
 +        let first_tuple = &data.field_names[0];
-         let text = format!(" @ {}", tuple_pat);
++        text.replacen(first_tuple, &format!("$0{first_tuple}"), 1)
 +    };
 +
 +    // with sub_pattern: keep original tuple and add subpattern: `tup @ (_0, _1)`
 +    if in_sub_pattern {
-             (true, true) => format!("(*{})", field_name),
-             (true, false) => format!("*{}", field_name),
-             (false, true) => format!("({})", field_name),
++        let text = format!(" @ {tuple_pat}");
 +        match ctx.config.snippet_cap {
 +            Some(cap) => {
 +                let snip = add_cursor(&text);
 +                builder.insert_snippet(cap, data.range.end(), snip);
 +            }
 +            None => builder.insert(data.range.end(), text),
 +        };
 +    } else {
 +        let text = tuple_pat.to_string();
 +        match ctx.config.snippet_cap {
 +            Some(cap) => {
 +                let snip = add_cursor(&text);
 +                builder.replace_snippet(cap, data.range, snip);
 +            }
 +            None => builder.replace(data.range, text),
 +        };
 +    }
 +}
 +
 +fn edit_tuple_usages(
 +    data: &TupleData,
 +    builder: &mut SourceChangeBuilder,
 +    ctx: &AssistContext<'_>,
 +    in_sub_pattern: bool,
 +) {
 +    if let Some(usages) = data.usages.as_ref() {
 +        for (file_id, refs) in usages.iter() {
 +            builder.edit_file(*file_id);
 +
 +            for r in refs {
 +                edit_tuple_usage(ctx, builder, r, data, in_sub_pattern);
 +            }
 +        }
 +    }
 +}
 +fn edit_tuple_usage(
 +    ctx: &AssistContext<'_>,
 +    builder: &mut SourceChangeBuilder,
 +    usage: &FileReference,
 +    data: &TupleData,
 +    in_sub_pattern: bool,
 +) {
 +    match detect_tuple_index(usage, data) {
 +        Some(index) => edit_tuple_field_usage(ctx, builder, data, index),
 +        None => {
 +            if in_sub_pattern {
 +                cov_mark::hit!(destructure_tuple_call_with_subpattern);
 +                return;
 +            }
 +
 +            // no index access -> make invalid -> requires handling by user
 +            // -> put usage in block comment
 +            //
 +            // Note: For macro invocations this might result in still valid code:
 +            //   When a macro accepts the tuple as argument, as well as no arguments at all,
 +            //   uncommenting the tuple still leaves the macro call working (see `tests::in_macro_call::empty_macro`).
 +            //   But this is an unlikely case. Usually the resulting macro call will become erroneous.
 +            builder.insert(usage.range.start(), "/*");
 +            builder.insert(usage.range.end(), "*/");
 +        }
 +    }
 +}
 +
 +fn edit_tuple_field_usage(
 +    ctx: &AssistContext<'_>,
 +    builder: &mut SourceChangeBuilder,
 +    data: &TupleData,
 +    index: TupleIndex,
 +) {
 +    let field_name = &data.field_names[index.index];
 +
 +    if data.ref_type.is_some() {
 +        let ref_data = handle_ref_field_usage(ctx, &index.field_expr);
 +        builder.replace(ref_data.range, ref_data.format(field_name));
 +    } else {
 +        builder.replace(index.range, field_name);
 +    }
 +}
 +struct TupleIndex {
 +    index: usize,
 +    range: TextRange,
 +    field_expr: FieldExpr,
 +}
 +fn detect_tuple_index(usage: &FileReference, data: &TupleData) -> Option<TupleIndex> {
 +    // usage is IDENT
 +    // IDENT
 +    //  NAME_REF
 +    //   PATH_SEGMENT
 +    //    PATH
 +    //     PATH_EXPR
 +    //      PAREN_EXRP*
 +    //       FIELD_EXPR
 +
 +    let node = usage
 +        .name
 +        .syntax()
 +        .ancestors()
 +        .skip_while(|s| !ast::PathExpr::can_cast(s.kind()))
 +        .skip(1) // PATH_EXPR
 +        .find(|s| !ast::ParenExpr::can_cast(s.kind()))?; // skip parentheses
 +
 +    if let Some(field_expr) = ast::FieldExpr::cast(node) {
 +        let idx = field_expr.name_ref()?.as_tuple_field()?;
 +        if idx < data.field_names.len() {
 +            // special case: in macro call -> range of `field_expr` in applied macro, NOT range in actual file!
 +            if field_expr.syntax().ancestors().any(|a| ast::MacroStmts::can_cast(a.kind())) {
 +                cov_mark::hit!(destructure_tuple_macro_call);
 +
 +                // issue: cannot differentiate between tuple index passed into macro or tuple index as result of macro:
 +                // ```rust
 +                // macro_rules! m {
 +                //     ($t1:expr, $t2:expr) => { $t1; $t2.0 }
 +                // }
 +                // let t = (1,2);
 +                // m!(t.0, t)
 +                // ```
 +                // -> 2 tuple index usages detected!
 +                //
 +                // -> only handle `t`
 +                return None;
 +            }
 +
 +            Some(TupleIndex { index: idx, range: field_expr.syntax().text_range(), field_expr })
 +        } else {
 +            // tuple index out of range
 +            None
 +        }
 +    } else {
 +        None
 +    }
 +}
 +
 +struct RefData {
 +    range: TextRange,
 +    needs_deref: bool,
 +    needs_parentheses: bool,
 +}
 +impl RefData {
 +    fn format(&self, field_name: &str) -> String {
 +        match (self.needs_deref, self.needs_parentheses) {
++            (true, true) => format!("(*{field_name})"),
++            (true, false) => format!("*{field_name}"),
++            (false, true) => format!("({field_name})"),
 +            (false, false) => field_name.to_string(),
 +        }
 +    }
 +}
 +fn handle_ref_field_usage(ctx: &AssistContext<'_>, field_expr: &FieldExpr) -> RefData {
 +    let s = field_expr.syntax();
 +    let mut ref_data =
 +        RefData { range: s.text_range(), needs_deref: true, needs_parentheses: true };
 +
 +    let parent = match s.parent().map(ast::Expr::cast) {
 +        Some(Some(parent)) => parent,
 +        Some(None) => {
 +            ref_data.needs_parentheses = false;
 +            return ref_data;
 +        }
 +        None => return ref_data,
 +    };
 +
 +    match parent {
 +        ast::Expr::ParenExpr(it) => {
 +            // already parens in place -> don't replace
 +            ref_data.needs_parentheses = false;
 +            // there might be a ref outside: `&(t.0)` -> can be removed
 +            if let Some(it) = it.syntax().parent().and_then(ast::RefExpr::cast) {
 +                ref_data.needs_deref = false;
 +                ref_data.range = it.syntax().text_range();
 +            }
 +        }
 +        ast::Expr::RefExpr(it) => {
 +            // `&*` -> cancel each other out
 +            ref_data.needs_deref = false;
 +            ref_data.needs_parentheses = false;
 +            // might be surrounded by parens -> can be removed too
 +            match it.syntax().parent().and_then(ast::ParenExpr::cast) {
 +                Some(parent) => ref_data.range = parent.syntax().text_range(),
 +                None => ref_data.range = it.syntax().text_range(),
 +            };
 +        }
 +        // higher precedence than deref `*`
 +        // https://doc.rust-lang.org/reference/expressions.html#expression-precedence
 +        // -> requires parentheses
 +        ast::Expr::PathExpr(_it) => {}
 +        ast::Expr::MethodCallExpr(it) => {
 +            // `field_expr` is `self_param` (otherwise it would be in `ArgList`)
 +
 +            // test if there's already auto-ref in place (`value` -> `&value`)
 +            // -> no method accepting `self`, but `&self` -> no need for deref
 +            //
 +            // other combinations (`&value` -> `value`, `&&value` -> `&value`, `&value` -> `&&value`) might or might not be able to auto-ref/deref,
 +            // but there might be trait implementations an added `&` might resolve to
 +            // -> ONLY handle auto-ref from `value` to `&value`
 +            fn is_auto_ref(ctx: &AssistContext<'_>, call_expr: &MethodCallExpr) -> bool {
 +                fn impl_(ctx: &AssistContext<'_>, call_expr: &MethodCallExpr) -> Option<bool> {
 +                    let rec = call_expr.receiver()?;
 +                    let rec_ty = ctx.sema.type_of_expr(&rec)?.original();
 +                    // input must be actual value
 +                    if rec_ty.is_reference() {
 +                        return Some(false);
 +                    }
 +
 +                    // doesn't resolve trait impl
 +                    let f = ctx.sema.resolve_method_call(call_expr)?;
 +                    let self_param = f.self_param(ctx.db())?;
 +                    // self must be ref
 +                    match self_param.access(ctx.db()) {
 +                        hir::Access::Shared | hir::Access::Exclusive => Some(true),
 +                        hir::Access::Owned => Some(false),
 +                    }
 +                }
 +                impl_(ctx, call_expr).unwrap_or(false)
 +            }
 +
 +            if is_auto_ref(ctx, &it) {
 +                ref_data.needs_deref = false;
 +                ref_data.needs_parentheses = false;
 +            }
 +        }
 +        ast::Expr::FieldExpr(_it) => {
 +            // `t.0.my_field`
 +            ref_data.needs_deref = false;
 +            ref_data.needs_parentheses = false;
 +        }
 +        ast::Expr::IndexExpr(_it) => {
 +            // `t.0[1]`
 +            ref_data.needs_deref = false;
 +            ref_data.needs_parentheses = false;
 +        }
 +        ast::Expr::TryExpr(_it) => {
 +            // `t.0?`
 +            // requires deref and parens: `(*_0)`
 +        }
 +        // lower precedence than deref `*` -> no parens
 +        _ => {
 +            ref_data.needs_parentheses = false;
 +        }
 +    };
 +
 +    ref_data
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use super::*;
 +
 +    use crate::tests::{check_assist, check_assist_not_applicable};
 +
 +    // Tests for direct tuple destructure:
 +    // `let $0t = (1,2);` -> `let (_0, _1) = (1,2);`
 +
 +    fn assist(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +        destructure_tuple_binding_impl(acc, ctx, false)
 +    }
 +
 +    #[test]
 +    fn dont_trigger_on_unit() {
 +        cov_mark::check!(destructure_tuple_no_tuple);
 +        check_assist_not_applicable(
 +            assist,
 +            r#"
 +fn main() {
 +let $0v = ();
 +}
 +            "#,
 +        )
 +    }
 +    #[test]
 +    fn dont_trigger_on_number() {
 +        cov_mark::check!(destructure_tuple_no_tuple);
 +        check_assist_not_applicable(
 +            assist,
 +            r#"
 +fn main() {
 +let $0v = 32;
 +}
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn destructure_3_tuple() {
 +        check_assist(
 +            assist,
 +            r#"
 +fn main() {
 +    let $0tup = (1,2,3);
 +}
 +            "#,
 +            r#"
 +fn main() {
 +    let ($0_0, _1, _2) = (1,2,3);
 +}
 +            "#,
 +        )
 +    }
 +    #[test]
 +    fn destructure_2_tuple() {
 +        check_assist(
 +            assist,
 +            r#"
 +fn main() {
 +    let $0tup = (1,2);
 +}
 +            "#,
 +            r#"
 +fn main() {
 +    let ($0_0, _1) = (1,2);
 +}
 +            "#,
 +        )
 +    }
 +    #[test]
 +    fn replace_indices() {
 +        check_assist(
 +            assist,
 +            r#"
 +fn main() {
 +    let $0tup = (1,2,3);
 +    let v1 = tup.0;
 +    let v2 = tup.1;
 +    let v3 = tup.2;
 +}
 +            "#,
 +            r#"
 +fn main() {
 +    let ($0_0, _1, _2) = (1,2,3);
 +    let v1 = _0;
 +    let v2 = _1;
 +    let v3 = _2;
 +}
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn replace_usage_in_parentheses() {
 +        check_assist(
 +            assist,
 +            r#"
 +fn main() {
 +    let $0tup = (1,2,3);
 +    let a = (tup).1;
 +    let b = ((tup)).1;
 +}
 +            "#,
 +            r#"
 +fn main() {
 +    let ($0_0, _1, _2) = (1,2,3);
 +    let a = _1;
 +    let b = _1;
 +}
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn handle_function_call() {
 +        check_assist(
 +            assist,
 +            r#"
 +fn main() {
 +    let $0tup = (1,2);
 +    let v = tup.into();
 +}
 +            "#,
 +            r#"
 +fn main() {
 +    let ($0_0, _1) = (1,2);
 +    let v = /*tup*/.into();
 +}
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn handle_invalid_index() {
 +        check_assist(
 +            assist,
 +            r#"
 +fn main() {
 +    let $0tup = (1,2);
 +    let v = tup.3;
 +}
 +            "#,
 +            r#"
 +fn main() {
 +    let ($0_0, _1) = (1,2);
 +    let v = /*tup*/.3;
 +}
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn dont_replace_variable_with_same_name_as_tuple() {
 +        check_assist(
 +            assist,
 +            r#"
 +fn main() {
 +    let tup = (1,2);
 +    let v = tup.1;
 +    let $0tup = (1,2,3);
 +    let v = tup.1;
 +    let tup = (1,2,3);
 +    let v = tup.1;
 +}
 +            "#,
 +            r#"
 +fn main() {
 +    let tup = (1,2);
 +    let v = tup.1;
 +    let ($0_0, _1, _2) = (1,2,3);
 +    let v = _1;
 +    let tup = (1,2,3);
 +    let v = tup.1;
 +}
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn keep_function_call_in_tuple_item() {
 +        check_assist(
 +            assist,
 +            r#"
 +fn main() {
 +    let $0t = ("3.14", 0);
 +    let pi: f32 = t.0.parse().unwrap_or(0.0);
 +}
 +            "#,
 +            r#"
 +fn main() {
 +    let ($0_0, _1) = ("3.14", 0);
 +    let pi: f32 = _0.parse().unwrap_or(0.0);
 +}
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn keep_type() {
 +        check_assist(
 +            assist,
 +            r#"
 +fn main() {
 +    let $0t: (usize, i32) = (1,2);
 +}
 +            "#,
 +            r#"
 +fn main() {
 +    let ($0_0, _1): (usize, i32) = (1,2);
 +}
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn destructure_reference() {
 +        check_assist(
 +            assist,
 +            r#"
 +fn main() {
 +    let t = (1,2);
 +    let $0t = &t;
 +    let v = t.0;
 +}
 +            "#,
 +            r#"
 +fn main() {
 +    let t = (1,2);
 +    let ($0_0, _1) = &t;
 +    let v = *_0;
 +}
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn destructure_multiple_reference() {
 +        check_assist(
 +            assist,
 +            r#"
 +fn main() {
 +    let t = (1,2);
 +    let $0t = &&t;
 +    let v = t.0;
 +}
 +            "#,
 +            r#"
 +fn main() {
 +    let t = (1,2);
 +    let ($0_0, _1) = &&t;
 +    let v = *_0;
 +}
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn keep_reference() {
 +        check_assist(
 +            assist,
 +            r#"
 +fn foo(t: &(usize, usize)) -> usize {
 +    match t {
 +        &$0t => t.0
 +    }
 +}
 +            "#,
 +            r#"
 +fn foo(t: &(usize, usize)) -> usize {
 +    match t {
 +        &($0_0, _1) => _0
 +    }
 +}
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn with_ref() {
 +        check_assist(
 +            assist,
 +            r#"
 +fn main() {
 +    let ref $0t = (1,2);
 +    let v = t.0;
 +}
 +            "#,
 +            r#"
 +fn main() {
 +    let (ref $0_0, ref _1) = (1,2);
 +    let v = *_0;
 +}
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn with_mut() {
 +        check_assist(
 +            assist,
 +            r#"
 +fn main() {
 +    let mut $0t = (1,2);
 +    t.0 = 42;
 +    let v = t.0;
 +}
 +            "#,
 +            r#"
 +fn main() {
 +    let (mut $0_0, mut _1) = (1,2);
 +    _0 = 42;
 +    let v = _0;
 +}
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn with_ref_mut() {
 +        check_assist(
 +            assist,
 +            r#"
 +fn main() {
 +    let ref mut $0t = (1,2);
 +    t.0 = 42;
 +    let v = t.0;
 +}
 +            "#,
 +            r#"
 +fn main() {
 +    let (ref mut $0_0, ref mut _1) = (1,2);
 +    *_0 = 42;
 +    let v = *_0;
 +}
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn dont_trigger_for_non_tuple_reference() {
 +        check_assist_not_applicable(
 +            assist,
 +            r#"
 +fn main() {
 +    let v = 42;
 +    let $0v = &42;
 +}
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn dont_trigger_on_static_tuple() {
 +        check_assist_not_applicable(
 +            assist,
 +            r#"
 +static $0TUP: (usize, usize) = (1,2);
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn dont_trigger_on_wildcard() {
 +        check_assist_not_applicable(
 +            assist,
 +            r#"
 +fn main() {
 +    let $0_ = (1,2);
 +}
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn dont_trigger_in_struct() {
 +        check_assist_not_applicable(
 +            assist,
 +            r#"
 +struct S {
 +    $0tup: (usize, usize),
 +}
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn dont_trigger_in_struct_creation() {
 +        check_assist_not_applicable(
 +            assist,
 +            r#"
 +struct S {
 +    tup: (usize, usize),
 +}
 +fn main() {
 +    let s = S {
 +        $0tup: (1,2),
 +    };
 +}
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn dont_trigger_on_tuple_struct() {
 +        check_assist_not_applicable(
 +            assist,
 +            r#"
 +struct S(usize, usize);
 +fn main() {
 +    let $0s = S(1,2);
 +}
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn dont_trigger_when_subpattern_exists() {
 +        // sub-pattern is only allowed with IdentPat (name), not other patterns (like TuplePat)
 +        cov_mark::check!(destructure_tuple_subpattern);
 +        check_assist_not_applicable(
 +            assist,
 +            r#"
 +fn sum(t: (usize, usize)) -> usize {
 +    match t {
 +        $0t @ (1..=3,1..=3) => t.0 + t.1,
 +        _ => 0,
 +    }
 +}
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn in_subpattern() {
 +        check_assist(
 +            assist,
 +            r#"
 +fn main() {
 +    let t1 @ (_, $0t2) = (1, (2,3));
 +    let v = t1.0 + t2.0 + t2.1;
 +}
 +            "#,
 +            r#"
 +fn main() {
 +    let t1 @ (_, ($0_0, _1)) = (1, (2,3));
 +    let v = t1.0 + _0 + _1;
 +}
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn in_nested_tuple() {
 +        check_assist(
 +            assist,
 +            r#"
 +fn main() {
 +    let ($0tup, v) = ((1,2),3);
 +}
 +            "#,
 +            r#"
 +fn main() {
 +    let (($0_0, _1), v) = ((1,2),3);
 +}
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn in_closure() {
 +        check_assist(
 +            assist,
 +            r#"
 +fn main() {
 +    let $0tup = (1,2,3);
 +    let f = |v| v + tup.1;
 +}
 +            "#,
 +            r#"
 +fn main() {
 +    let ($0_0, _1, _2) = (1,2,3);
 +    let f = |v| v + _1;
 +}
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn in_closure_args() {
 +        check_assist(
 +            assist,
 +            r#"
 +fn main() {
 +    let f = |$0t| t.0 + t.1;
 +    let v = f((1,2));
 +}
 +            "#,
 +            r#"
 +fn main() {
 +    let f = |($0_0, _1)| _0 + _1;
 +    let v = f((1,2));
 +}
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn in_function_args() {
 +        check_assist(
 +            assist,
 +            r#"
 +fn f($0t: (usize, usize)) {
 +    let v = t.0;
 +}
 +            "#,
 +            r#"
 +fn f(($0_0, _1): (usize, usize)) {
 +    let v = _0;
 +}
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn in_if_let() {
 +        check_assist(
 +            assist,
 +            r#"
 +fn f(t: (usize, usize)) {
 +    if let $0t = t {
 +        let v = t.0;
 +    }
 +}
 +            "#,
 +            r#"
 +fn f(t: (usize, usize)) {
 +    if let ($0_0, _1) = t {
 +        let v = _0;
 +    }
 +}
 +            "#,
 +        )
 +    }
 +    #[test]
 +    fn in_if_let_option() {
 +        check_assist(
 +            assist,
 +            r#"
 +//- minicore: option
 +fn f(o: Option<(usize, usize)>) {
 +    if let Some($0t) = o {
 +        let v = t.0;
 +    }
 +}
 +            "#,
 +            r#"
 +fn f(o: Option<(usize, usize)>) {
 +    if let Some(($0_0, _1)) = o {
 +        let v = _0;
 +    }
 +}
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn in_match() {
 +        check_assist(
 +            assist,
 +            r#"
 +fn main() {
 +    match (1,2) {
 +        $0t => t.1,
 +    };
 +}
 +            "#,
 +            r#"
 +fn main() {
 +    match (1,2) {
 +        ($0_0, _1) => _1,
 +    };
 +}
 +            "#,
 +        )
 +    }
 +    #[test]
 +    fn in_match_option() {
 +        check_assist(
 +            assist,
 +            r#"
 +//- minicore: option
 +fn main() {
 +    match Some((1,2)) {
 +        Some($0t) => t.1,
 +        _ => 0,
 +    };
 +}
 +            "#,
 +            r#"
 +fn main() {
 +    match Some((1,2)) {
 +        Some(($0_0, _1)) => _1,
 +        _ => 0,
 +    };
 +}
 +            "#,
 +        )
 +    }
 +    #[test]
 +    fn in_match_reference_option() {
 +        check_assist(
 +            assist,
 +            r#"
 +//- minicore: option
 +fn main() {
 +    let t = (1,2);
 +    match Some(&t) {
 +        Some($0t) => t.1,
 +        _ => 0,
 +    };
 +}
 +            "#,
 +            r#"
 +fn main() {
 +    let t = (1,2);
 +    match Some(&t) {
 +        Some(($0_0, _1)) => *_1,
 +        _ => 0,
 +    };
 +}
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn in_for() {
 +        check_assist(
 +            assist,
 +            r#"
 +//- minicore: iterators
 +fn main() {
 +    for $0t in core::iter::repeat((1,2))  {
 +        let v = t.1;
 +    }
 +}
 +            "#,
 +            r#"
 +fn main() {
 +    for ($0_0, _1) in core::iter::repeat((1,2))  {
 +        let v = _1;
 +    }
 +}
 +            "#,
 +        )
 +    }
 +    #[test]
 +    fn in_for_nested() {
 +        check_assist(
 +            assist,
 +            r#"
 +//- minicore: iterators
 +fn main() {
 +    for (a, $0b) in core::iter::repeat((1,(2,3)))  {
 +        let v = b.1;
 +    }
 +}
 +            "#,
 +            r#"
 +fn main() {
 +    for (a, ($0_0, _1)) in core::iter::repeat((1,(2,3)))  {
 +        let v = _1;
 +    }
 +}
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn not_applicable_on_tuple_usage() {
 +        //Improvement: might be reasonable to allow & implement
 +        check_assist_not_applicable(
 +            assist,
 +            r#"
 +fn main() {
 +    let t = (1,2);
 +    let v = $0t.0;
 +}
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn replace_all() {
 +        check_assist(
 +            assist,
 +            r#"
 +fn main() {
 +    let $0t = (1,2);
 +    let v = t.1;
 +    let s = (t.0 + t.1) / 2;
 +    let f = |v| v + t.0;
 +    let r = f(t.1);
 +    let e = t == (9,0);
 +    let m =
 +      match t {
 +        (_,2) if t.0 > 2 => 1,
 +        _ => 0,
 +      };
 +}
 +            "#,
 +            r#"
 +fn main() {
 +    let ($0_0, _1) = (1,2);
 +    let v = _1;
 +    let s = (_0 + _1) / 2;
 +    let f = |v| v + _0;
 +    let r = f(_1);
 +    let e = /*t*/ == (9,0);
 +    let m =
 +      match /*t*/ {
 +        (_,2) if _0 > 2 => 1,
 +        _ => 0,
 +      };
 +}
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn non_trivial_tuple_assignment() {
 +        check_assist(
 +            assist,
 +            r#"
 +fn main {
 +    let $0t =
 +        if 1 > 2 {
 +            (1,2)
 +        } else {
 +            (5,6)
 +        };
 +    let v1 = t.0;
 +    let v2 =
 +        if t.0 > t.1 {
 +            t.0 - t.1
 +        } else {
 +            t.1 - t.0
 +        };
 +}
 +            "#,
 +            r#"
 +fn main {
 +    let ($0_0, _1) =
 +        if 1 > 2 {
 +            (1,2)
 +        } else {
 +            (5,6)
 +        };
 +    let v1 = _0;
 +    let v2 =
 +        if _0 > _1 {
 +            _0 - _1
 +        } else {
 +            _1 - _0
 +        };
 +}
 +            "#,
 +        )
 +    }
 +
 +    mod assist {
 +        use super::*;
 +        use crate::tests::check_assist_by_label;
 +
 +        fn assist(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +            destructure_tuple_binding_impl(acc, ctx, true)
 +        }
 +        fn in_place_assist(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +            destructure_tuple_binding_impl(acc, ctx, false)
 +        }
 +
 +        pub(crate) fn check_in_place_assist(ra_fixture_before: &str, ra_fixture_after: &str) {
 +            check_assist_by_label(
 +                in_place_assist,
 +                ra_fixture_before,
 +                ra_fixture_after,
 +                // "Destructure tuple in place",
 +                "Destructure tuple",
 +            );
 +        }
 +
 +        pub(crate) fn check_sub_pattern_assist(ra_fixture_before: &str, ra_fixture_after: &str) {
 +            check_assist_by_label(
 +                assist,
 +                ra_fixture_before,
 +                ra_fixture_after,
 +                "Destructure tuple in sub-pattern",
 +            );
 +        }
 +
 +        pub(crate) fn check_both_assists(
 +            ra_fixture_before: &str,
 +            ra_fixture_after_in_place: &str,
 +            ra_fixture_after_in_sub_pattern: &str,
 +        ) {
 +            check_in_place_assist(ra_fixture_before, ra_fixture_after_in_place);
 +            check_sub_pattern_assist(ra_fixture_before, ra_fixture_after_in_sub_pattern);
 +        }
 +    }
 +
 +    /// Tests for destructure of tuple in sub-pattern:
 +    /// `let $0t = (1,2);` -> `let t @ (_0, _1) = (1,2);`
 +    mod sub_pattern {
 +        use super::assist::*;
 +        use super::*;
 +        use crate::tests::check_assist_by_label;
 +
 +        #[test]
 +        fn destructure_in_sub_pattern() {
 +            check_sub_pattern_assist(
 +                r#"
 +#![feature(bindings_after_at)]
 +
 +fn main() {
 +    let $0t = (1,2);
 +}
 +                "#,
 +                r#"
 +#![feature(bindings_after_at)]
 +
 +fn main() {
 +    let t @ ($0_0, _1) = (1,2);
 +}
 +                "#,
 +            )
 +        }
 +
 +        #[test]
 +        fn trigger_both_destructure_tuple_assists() {
 +            fn assist(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +                destructure_tuple_binding_impl(acc, ctx, true)
 +            }
 +            let text = r#"
 +fn main() {
 +    let $0t = (1,2);
 +}
 +            "#;
 +            check_assist_by_label(
 +                assist,
 +                text,
 +                r#"
 +fn main() {
 +    let ($0_0, _1) = (1,2);
 +}
 +            "#,
 +                "Destructure tuple in place",
 +            );
 +            check_assist_by_label(
 +                assist,
 +                text,
 +                r#"
 +fn main() {
 +    let t @ ($0_0, _1) = (1,2);
 +}
 +            "#,
 +                "Destructure tuple in sub-pattern",
 +            );
 +        }
 +
 +        #[test]
 +        fn replace_indices() {
 +            check_sub_pattern_assist(
 +                r#"
 +fn main() {
 +    let $0t = (1,2);
 +    let v1 = t.0;
 +    let v2 = t.1;
 +}
 +                "#,
 +                r#"
 +fn main() {
 +    let t @ ($0_0, _1) = (1,2);
 +    let v1 = _0;
 +    let v2 = _1;
 +}
 +                "#,
 +            )
 +        }
 +
 +        #[test]
 +        fn keep_function_call() {
 +            cov_mark::check!(destructure_tuple_call_with_subpattern);
 +            check_sub_pattern_assist(
 +                r#"
 +fn main() {
 +    let $0t = (1,2);
 +    let v = t.into();
 +}
 +                "#,
 +                r#"
 +fn main() {
 +    let t @ ($0_0, _1) = (1,2);
 +    let v = t.into();
 +}
 +                "#,
 +            )
 +        }
 +
 +        #[test]
 +        fn keep_type() {
 +            check_sub_pattern_assist(
 +                r#"
 +fn main() {
 +    let $0t: (usize, i32) = (1,2);
 +    let v = t.1;
 +    let f = t.into();
 +}
 +                "#,
 +                r#"
 +fn main() {
 +    let t @ ($0_0, _1): (usize, i32) = (1,2);
 +    let v = _1;
 +    let f = t.into();
 +}
 +                "#,
 +            )
 +        }
 +
 +        #[test]
 +        fn in_function_args() {
 +            check_sub_pattern_assist(
 +                r#"
 +fn f($0t: (usize, usize)) {
 +    let v = t.0;
 +    let f = t.into();
 +}
 +                "#,
 +                r#"
 +fn f(t @ ($0_0, _1): (usize, usize)) {
 +    let v = _0;
 +    let f = t.into();
 +}
 +                "#,
 +            )
 +        }
 +
 +        #[test]
 +        fn with_ref() {
 +            check_sub_pattern_assist(
 +                r#"
 +fn main() {
 +    let ref $0t = (1,2);
 +    let v = t.1;
 +    let f = t.into();
 +}
 +                "#,
 +                r#"
 +fn main() {
 +    let ref t @ (ref $0_0, ref _1) = (1,2);
 +    let v = *_1;
 +    let f = t.into();
 +}
 +                "#,
 +            )
 +        }
 +        #[test]
 +        fn with_mut() {
 +            check_sub_pattern_assist(
 +                r#"
 +fn main() {
 +    let mut $0t = (1,2);
 +    let v = t.1;
 +    let f = t.into();
 +}
 +                "#,
 +                r#"
 +fn main() {
 +    let mut t @ (mut $0_0, mut _1) = (1,2);
 +    let v = _1;
 +    let f = t.into();
 +}
 +                "#,
 +            )
 +        }
 +        #[test]
 +        fn with_ref_mut() {
 +            check_sub_pattern_assist(
 +                r#"
 +fn main() {
 +    let ref mut $0t = (1,2);
 +    let v = t.1;
 +    let f = t.into();
 +}
 +                "#,
 +                r#"
 +fn main() {
 +    let ref mut t @ (ref mut $0_0, ref mut _1) = (1,2);
 +    let v = *_1;
 +    let f = t.into();
 +}
 +                "#,
 +            )
 +        }
 +    }
 +
 +    /// Tests for tuple usage in macro call:
 +    /// `println!("{}", t.0)`
 +    mod in_macro_call {
 +        use super::assist::*;
 +
 +        #[test]
 +        fn detect_macro_call() {
 +            cov_mark::check!(destructure_tuple_macro_call);
 +            check_in_place_assist(
 +                r#"
 +macro_rules! m {
 +    ($e:expr) => { "foo"; $e };
 +}
 +
 +fn main() {
 +    let $0t = (1,2);
 +    m!(t.0);
 +}
 +                "#,
 +                r#"
 +macro_rules! m {
 +    ($e:expr) => { "foo"; $e };
 +}
 +
 +fn main() {
 +    let ($0_0, _1) = (1,2);
 +    m!(/*t*/.0);
 +}
 +                "#,
 +            )
 +        }
 +
 +        #[test]
 +        fn tuple_usage() {
 +            check_both_assists(
 +                // leading `"foo"` to ensure `$e` doesn't start at position `0`
 +                r#"
 +macro_rules! m {
 +    ($e:expr) => { "foo"; $e };
 +}
 +
 +fn main() {
 +    let $0t = (1,2);
 +    m!(t);
 +}
 +                "#,
 +                r#"
 +macro_rules! m {
 +    ($e:expr) => { "foo"; $e };
 +}
 +
 +fn main() {
 +    let ($0_0, _1) = (1,2);
 +    m!(/*t*/);
 +}
 +                "#,
 +                r#"
 +macro_rules! m {
 +    ($e:expr) => { "foo"; $e };
 +}
 +
 +fn main() {
 +    let t @ ($0_0, _1) = (1,2);
 +    m!(t);
 +}
 +                "#,
 +            )
 +        }
 +
 +        #[test]
 +        fn tuple_function_usage() {
 +            check_both_assists(
 +                r#"
 +macro_rules! m {
 +    ($e:expr) => { "foo"; $e };
 +}
 +
 +fn main() {
 +    let $0t = (1,2);
 +    m!(t.into());
 +}
 +                "#,
 +                r#"
 +macro_rules! m {
 +    ($e:expr) => { "foo"; $e };
 +}
 +
 +fn main() {
 +    let ($0_0, _1) = (1,2);
 +    m!(/*t*/.into());
 +}
 +                "#,
 +                r#"
 +macro_rules! m {
 +    ($e:expr) => { "foo"; $e };
 +}
 +
 +fn main() {
 +    let t @ ($0_0, _1) = (1,2);
 +    m!(t.into());
 +}
 +                "#,
 +            )
 +        }
 +
 +        #[test]
 +        fn tuple_index_usage() {
 +            check_both_assists(
 +                r#"
 +macro_rules! m {
 +    ($e:expr) => { "foo"; $e };
 +}
 +
 +fn main() {
 +    let $0t = (1,2);
 +    m!(t.0);
 +}
 +                "#,
 +                // FIXME: replace `t.0` with `_0` (cannot detect range of tuple index in macro call)
 +                r#"
 +macro_rules! m {
 +    ($e:expr) => { "foo"; $e };
 +}
 +
 +fn main() {
 +    let ($0_0, _1) = (1,2);
 +    m!(/*t*/.0);
 +}
 +                "#,
 +                // FIXME: replace `t.0` with `_0`
 +                r#"
 +macro_rules! m {
 +    ($e:expr) => { "foo"; $e };
 +}
 +
 +fn main() {
 +    let t @ ($0_0, _1) = (1,2);
 +    m!(t.0);
 +}
 +                "#,
 +            )
 +        }
 +
 +        #[test]
 +        fn tuple_in_parentheses_index_usage() {
 +            check_both_assists(
 +                r#"
 +macro_rules! m {
 +    ($e:expr) => { "foo"; $e };
 +}
 +
 +fn main() {
 +    let $0t = (1,2);
 +    m!((t).0);
 +}
 +                "#,
 +                // FIXME: replace `(t).0` with `_0`
 +                r#"
 +macro_rules! m {
 +    ($e:expr) => { "foo"; $e };
 +}
 +
 +fn main() {
 +    let ($0_0, _1) = (1,2);
 +    m!((/*t*/).0);
 +}
 +                "#,
 +                // FIXME: replace `(t).0` with `_0`
 +                r#"
 +macro_rules! m {
 +    ($e:expr) => { "foo"; $e };
 +}
 +
 +fn main() {
 +    let t @ ($0_0, _1) = (1,2);
 +    m!((t).0);
 +}
 +                "#,
 +            )
 +        }
 +
 +        #[test]
 +        fn empty_macro() {
 +            check_in_place_assist(
 +                r#"
 +macro_rules! m {
 +    () => { "foo" };
 +    ($e:expr) => { $e; "foo" };
 +}
 +
 +fn main() {
 +    let $0t = (1,2);
 +    m!(t);
 +}
 +                "#,
 +                // FIXME: macro allows no arg -> is valid. But assist should result in invalid code
 +                r#"
 +macro_rules! m {
 +    () => { "foo" };
 +    ($e:expr) => { $e; "foo" };
 +}
 +
 +fn main() {
 +    let ($0_0, _1) = (1,2);
 +    m!(/*t*/);
 +}
 +                "#,
 +            )
 +        }
 +
 +        #[test]
 +        fn tuple_index_in_macro() {
 +            check_both_assists(
 +                r#"
 +macro_rules! m {
 +    ($t:expr, $i:expr) => { $t.0 + $i };
 +}
 +
 +fn main() {
 +    let $0t = (1,2);
 +    m!(t, t.0);
 +}
 +                "#,
 +                // FIXME: replace `t.0` in macro call (not IN macro) with `_0`
 +                r#"
 +macro_rules! m {
 +    ($t:expr, $i:expr) => { $t.0 + $i };
 +}
 +
 +fn main() {
 +    let ($0_0, _1) = (1,2);
 +    m!(/*t*/, /*t*/.0);
 +}
 +                "#,
 +                // FIXME: replace `t.0` in macro call with `_0`
 +                r#"
 +macro_rules! m {
 +    ($t:expr, $i:expr) => { $t.0 + $i };
 +}
 +
 +fn main() {
 +    let t @ ($0_0, _1) = (1,2);
 +    m!(t, t.0);
 +}
 +                "#,
 +            )
 +        }
 +    }
 +
 +    mod refs {
 +        use super::assist::*;
 +
 +        #[test]
 +        fn no_ref() {
 +            check_in_place_assist(
 +                r#"
 +fn main() {
 +    let $0t = &(1,2);
 +    let v: i32 = t.0;
 +}
 +                "#,
 +                r#"
 +fn main() {
 +    let ($0_0, _1) = &(1,2);
 +    let v: i32 = *_0;
 +}
 +                "#,
 +            )
 +        }
 +        #[test]
 +        fn no_ref_with_parens() {
 +            check_in_place_assist(
 +                r#"
 +fn main() {
 +    let $0t = &(1,2);
 +    let v: i32 = (t.0);
 +}
 +                "#,
 +                r#"
 +fn main() {
 +    let ($0_0, _1) = &(1,2);
 +    let v: i32 = (*_0);
 +}
 +                "#,
 +            )
 +        }
 +        #[test]
 +        fn with_ref() {
 +            check_in_place_assist(
 +                r#"
 +fn main() {
 +    let $0t = &(1,2);
 +    let v: &i32 = &t.0;
 +}
 +                "#,
 +                r#"
 +fn main() {
 +    let ($0_0, _1) = &(1,2);
 +    let v: &i32 = _0;
 +}
 +                "#,
 +            )
 +        }
 +        #[test]
 +        fn with_ref_in_parens_ref() {
 +            check_in_place_assist(
 +                r#"
 +fn main() {
 +    let $0t = &(1,2);
 +    let v: &i32 = &(t.0);
 +}
 +                "#,
 +                r#"
 +fn main() {
 +    let ($0_0, _1) = &(1,2);
 +    let v: &i32 = _0;
 +}
 +                "#,
 +            )
 +        }
 +        #[test]
 +        fn with_ref_in_ref_parens() {
 +            check_in_place_assist(
 +                r#"
 +fn main() {
 +    let $0t = &(1,2);
 +    let v: &i32 = (&t.0);
 +}
 +                "#,
 +                r#"
 +fn main() {
 +    let ($0_0, _1) = &(1,2);
 +    let v: &i32 = _0;
 +}
 +                "#,
 +            )
 +        }
 +
 +        #[test]
 +        fn deref_and_parentheses() {
 +            // Operator/Expressions with higher precedence than deref (`*`):
 +            // https://doc.rust-lang.org/reference/expressions.html#expression-precedence
 +            // * Path
 +            // * Method call
 +            // * Field expression
 +            // * Function calls, array indexing
 +            // * `?`
 +            check_in_place_assist(
 +                r#"
 +//- minicore: option
 +fn f1(v: i32) {}
 +fn f2(v: &i32) {}
 +trait T {
 +    fn do_stuff(self) {}
 +}
 +impl T for i32 {
 +    fn do_stuff(self) {}
 +}
 +impl T for &i32 {
 +    fn do_stuff(self) {}
 +}
 +struct S4 {
 +    value: i32,
 +}
 +
 +fn foo() -> Option<()> {
 +    let $0t = &(0, (1,"1"), Some(2), [3;3], S4 { value: 4 }, &5);
 +    let v: i32 = t.0;           // deref, no parens
 +    let v: &i32 = &t.0;         // no deref, no parens, remove `&`
 +    f1(t.0);                    // deref, no parens
 +    f2(&t.0);                   // `&*` -> cancel out -> no deref, no parens
 +    // https://github.com/rust-lang/rust-analyzer/issues/1109#issuecomment-658868639
 +    // let v: i32 = t.1.0;      // no deref, no parens
 +    let v: i32 = t.4.value;     // no deref, no parens
 +    t.0.do_stuff();             // deref, parens
 +    let v: i32 = t.2?;          // deref, parens
 +    let v: i32 = t.3[0];        // no deref, no parens
 +    (t.0).do_stuff();           // deref, no additional parens
 +    let v: i32 = *t.5;          // deref (-> 2), no parens
 +
 +    None
 +}
 +                "#,
 +                r#"
 +fn f1(v: i32) {}
 +fn f2(v: &i32) {}
 +trait T {
 +    fn do_stuff(self) {}
 +}
 +impl T for i32 {
 +    fn do_stuff(self) {}
 +}
 +impl T for &i32 {
 +    fn do_stuff(self) {}
 +}
 +struct S4 {
 +    value: i32,
 +}
 +
 +fn foo() -> Option<()> {
 +    let ($0_0, _1, _2, _3, _4, _5) = &(0, (1,"1"), Some(2), [3;3], S4 { value: 4 }, &5);
 +    let v: i32 = *_0;           // deref, no parens
 +    let v: &i32 = _0;         // no deref, no parens, remove `&`
 +    f1(*_0);                    // deref, no parens
 +    f2(_0);                   // `&*` -> cancel out -> no deref, no parens
 +    // https://github.com/rust-lang/rust-analyzer/issues/1109#issuecomment-658868639
 +    // let v: i32 = t.1.0;      // no deref, no parens
 +    let v: i32 = _4.value;     // no deref, no parens
 +    (*_0).do_stuff();             // deref, parens
 +    let v: i32 = (*_2)?;          // deref, parens
 +    let v: i32 = _3[0];        // no deref, no parens
 +    (*_0).do_stuff();           // deref, no additional parens
 +    let v: i32 = **_5;          // deref (-> 2), no parens
 +
 +    None
 +}
 +                "#,
 +            )
 +        }
 +
 +        // ---------
 +        // auto-ref/deref
 +
 +        #[test]
 +        fn self_auto_ref_doesnt_need_deref() {
 +            check_in_place_assist(
 +                r#"
 +#[derive(Clone, Copy)]
 +struct S;
 +impl S {
 +  fn f(&self) {}
 +}
 +
 +fn main() {
 +    let $0t = &(S,2);
 +    let s = t.0.f();
 +}
 +                "#,
 +                r#"
 +#[derive(Clone, Copy)]
 +struct S;
 +impl S {
 +  fn f(&self) {}
 +}
 +
 +fn main() {
 +    let ($0_0, _1) = &(S,2);
 +    let s = _0.f();
 +}
 +                "#,
 +            )
 +        }
 +
 +        #[test]
 +        fn self_owned_requires_deref() {
 +            check_in_place_assist(
 +                r#"
 +#[derive(Clone, Copy)]
 +struct S;
 +impl S {
 +  fn f(self) {}
 +}
 +
 +fn main() {
 +    let $0t = &(S,2);
 +    let s = t.0.f();
 +}
 +                "#,
 +                r#"
 +#[derive(Clone, Copy)]
 +struct S;
 +impl S {
 +  fn f(self) {}
 +}
 +
 +fn main() {
 +    let ($0_0, _1) = &(S,2);
 +    let s = (*_0).f();
 +}
 +                "#,
 +            )
 +        }
 +
 +        #[test]
 +        fn self_auto_ref_in_trait_call_doesnt_require_deref() {
 +            check_in_place_assist(
 +                r#"
 +trait T {
 +    fn f(self);
 +}
 +#[derive(Clone, Copy)]
 +struct S;
 +impl T for &S {
 +    fn f(self) {}
 +}
 +
 +fn main() {
 +    let $0t = &(S,2);
 +    let s = t.0.f();
 +}
 +                "#,
 +                // FIXME: doesn't need deref * parens. But `ctx.sema.resolve_method_call` doesn't resolve trait implementations
 +                r#"
 +trait T {
 +    fn f(self);
 +}
 +#[derive(Clone, Copy)]
 +struct S;
 +impl T for &S {
 +    fn f(self) {}
 +}
 +
 +fn main() {
 +    let ($0_0, _1) = &(S,2);
 +    let s = (*_0).f();
 +}
 +                "#,
 +            )
 +        }
 +        #[test]
 +        fn no_auto_deref_because_of_owned_and_ref_trait_impl() {
 +            check_in_place_assist(
 +                r#"
 +trait T {
 +    fn f(self);
 +}
 +#[derive(Clone, Copy)]
 +struct S;
 +impl T for S {
 +    fn f(self) {}
 +}
 +impl T for &S {
 +    fn f(self) {}
 +}
 +
 +fn main() {
 +    let $0t = &(S,2);
 +    let s = t.0.f();
 +}
 +                "#,
 +                r#"
 +trait T {
 +    fn f(self);
 +}
 +#[derive(Clone, Copy)]
 +struct S;
 +impl T for S {
 +    fn f(self) {}
 +}
 +impl T for &S {
 +    fn f(self) {}
 +}
 +
 +fn main() {
 +    let ($0_0, _1) = &(S,2);
 +    let s = (*_0).f();
 +}
 +                "#,
 +            )
 +        }
 +
 +        #[test]
 +        fn no_outer_parens_when_ref_deref() {
 +            check_in_place_assist(
 +                r#"
 +#[derive(Clone, Copy)]
 +struct S;
 +impl S {
 +    fn do_stuff(&self) -> i32 { 42 }
 +}
 +fn main() {
 +    let $0t = &(S,&S);
 +    let v = (&t.0).do_stuff();
 +}
 +                "#,
 +                r#"
 +#[derive(Clone, Copy)]
 +struct S;
 +impl S {
 +    fn do_stuff(&self) -> i32 { 42 }
 +}
 +fn main() {
 +    let ($0_0, _1) = &(S,&S);
 +    let v = _0.do_stuff();
 +}
 +                "#,
 +            )
 +        }
 +
 +        #[test]
 +        fn auto_ref_deref() {
 +            check_in_place_assist(
 +                r#"
 +#[derive(Clone, Copy)]
 +struct S;
 +impl S {
 +    fn do_stuff(&self) -> i32 { 42 }
 +}
 +fn main() {
 +    let $0t = &(S,&S);
 +    let v = (&t.0).do_stuff();      // no deref, remove parens
 +    // `t.0` gets auto-refed -> no deref needed -> no parens
 +    let v = t.0.do_stuff();         // no deref, no parens
 +    let v = &t.0.do_stuff();        // `&` is for result -> no deref, no parens
 +    // deref: `_1` is `&&S`, but method called is on `&S` -> there might be a method accepting `&&S`
 +    let v = t.1.do_stuff();         // deref, parens
 +}
 +                "#,
 +                r#"
 +#[derive(Clone, Copy)]
 +struct S;
 +impl S {
 +    fn do_stuff(&self) -> i32 { 42 }
 +}
 +fn main() {
 +    let ($0_0, _1) = &(S,&S);
 +    let v = _0.do_stuff();      // no deref, remove parens
 +    // `t.0` gets auto-refed -> no deref needed -> no parens
 +    let v = _0.do_stuff();         // no deref, no parens
 +    let v = &_0.do_stuff();        // `&` is for result -> no deref, no parens
 +    // deref: `_1` is `&&S`, but method called is on `&S` -> there might be a method accepting `&&S`
 +    let v = (*_1).do_stuff();         // deref, parens
 +}
 +                "#,
 +            )
 +        }
 +
 +        #[test]
 +        fn mutable() {
 +            check_in_place_assist(
 +                r#"
 +fn f_owned(v: i32) {}
 +fn f(v: &i32) {}
 +fn f_mut(v: &mut i32) { *v = 42; }
 +
 +fn main() {
 +    let $0t = &mut (1,2);
 +    let v = t.0;
 +    t.0 = 42;
 +    f_owned(t.0);
 +    f(&t.0);
 +    f_mut(&mut t.0);
 +}
 +                "#,
 +                r#"
 +fn f_owned(v: i32) {}
 +fn f(v: &i32) {}
 +fn f_mut(v: &mut i32) { *v = 42; }
 +
 +fn main() {
 +    let ($0_0, _1) = &mut (1,2);
 +    let v = *_0;
 +    *_0 = 42;
 +    f_owned(*_0);
 +    f(_0);
 +    f_mut(_0);
 +}
 +                "#,
 +            )
 +        }
 +
 +        #[test]
 +        fn with_ref_keyword() {
 +            check_in_place_assist(
 +                r#"
 +fn f_owned(v: i32) {}
 +fn f(v: &i32) {}
 +
 +fn main() {
 +    let ref $0t = (1,2);
 +    let v = t.0;
 +    f_owned(t.0);
 +    f(&t.0);
 +}
 +                "#,
 +                r#"
 +fn f_owned(v: i32) {}
 +fn f(v: &i32) {}
 +
 +fn main() {
 +    let (ref $0_0, ref _1) = (1,2);
 +    let v = *_0;
 +    f_owned(*_0);
 +    f(_0);
 +}
 +                "#,
 +            )
 +        }
 +        #[test]
 +        fn with_ref_mut_keywords() {
 +            check_in_place_assist(
 +                r#"
 +fn f_owned(v: i32) {}
 +fn f(v: &i32) {}
 +fn f_mut(v: &mut i32) { *v = 42; }
 +
 +fn main() {
 +    let ref mut $0t = (1,2);
 +    let v = t.0;
 +    t.0 = 42;
 +    f_owned(t.0);
 +    f(&t.0);
 +    f_mut(&mut t.0);
 +}
 +                "#,
 +                r#"
 +fn f_owned(v: i32) {}
 +fn f(v: &i32) {}
 +fn f_mut(v: &mut i32) { *v = 42; }
 +
 +fn main() {
 +    let (ref mut $0_0, ref mut _1) = (1,2);
 +    let v = *_0;
 +    *_0 = 42;
 +    f_owned(*_0);
 +    f(_0);
 +    f_mut(_0);
 +}
 +                "#,
 +            )
 +        }
 +    }
 +}
index d6c8ea785f84aa87f1b2904e06ddcdab103eafc1,0000000000000000000000000000000000000000..0605883584922ee329ff9fcc879561a18b7a7c98
mode 100644,000000..100644
--- /dev/null
@@@ -1,5334 -1,0 +1,5326 @@@
-         name = format!("{}{}", &default_name, counter)
 +use std::iter;
 +
 +use ast::make;
 +use either::Either;
 +use hir::{
 +    HasSource, HirDisplay, InFile, Local, ModuleDef, PathResolution, Semantics, TypeInfo, TypeParam,
 +};
 +use ide_db::{
 +    defs::{Definition, NameRefClass},
 +    famous_defs::FamousDefs,
 +    helpers::mod_path_to_ast,
 +    imports::insert_use::{insert_use, ImportScope},
 +    search::{FileReference, ReferenceCategory, SearchScope},
 +    syntax_helpers::node_ext::{preorder_expr, walk_expr, walk_pat, walk_patterns_in_expr},
 +    FxIndexSet, RootDatabase,
 +};
 +use itertools::Itertools;
 +use stdx::format_to;
 +use syntax::{
 +    ast::{
 +        self,
 +        edit::{AstNodeEdit, IndentLevel},
 +        AstNode, HasGenericParams,
 +    },
 +    match_ast, ted, SyntaxElement,
 +    SyntaxKind::{self, COMMENT},
 +    SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, WalkEvent, T,
 +};
 +
 +use crate::{
 +    assist_context::{AssistContext, Assists, TreeMutator},
 +    utils::generate_impl_text,
 +    AssistId,
 +};
 +
 +// Assist: extract_function
 +//
 +// Extracts selected statements and comments into new function.
 +//
 +// ```
 +// fn main() {
 +//     let n = 1;
 +//     $0let m = n + 2;
 +//     // calculate
 +//     let k = m + n;$0
 +//     let g = 3;
 +// }
 +// ```
 +// ->
 +// ```
 +// fn main() {
 +//     let n = 1;
 +//     fun_name(n);
 +//     let g = 3;
 +// }
 +//
 +// fn $0fun_name(n: i32) {
 +//     let m = n + 2;
 +//     // calculate
 +//     let k = m + n;
 +// }
 +// ```
 +pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    let range = ctx.selection_trimmed();
 +    if range.is_empty() {
 +        return None;
 +    }
 +
 +    let node = ctx.covering_element();
 +    if node.kind() == COMMENT {
 +        cov_mark::hit!(extract_function_in_comment_is_not_applicable);
 +        return None;
 +    }
 +
 +    let node = match node {
 +        syntax::NodeOrToken::Node(n) => n,
 +        syntax::NodeOrToken::Token(t) => t.parent()?,
 +    };
 +
 +    let body = extraction_target(&node, range)?;
 +    let container_info = body.analyze_container(&ctx.sema)?;
 +
 +    let (locals_used, self_param) = body.analyze(&ctx.sema);
 +
 +    let anchor = if self_param.is_some() { Anchor::Method } else { Anchor::Freestanding };
 +    let insert_after = node_to_insert_after(&body, anchor)?;
 +    let semantics_scope = ctx.sema.scope(&insert_after)?;
 +    let module = semantics_scope.module();
 +
 +    let ret_ty = body.return_ty(ctx)?;
 +    let control_flow = body.external_control_flow(ctx, &container_info)?;
 +    let ret_values = body.ret_values(ctx, node.parent().as_ref().unwrap_or(&node));
 +
 +    let target_range = body.text_range();
 +
 +    let scope = ImportScope::find_insert_use_container(&node, &ctx.sema)?;
 +
 +    acc.add(
 +        AssistId("extract_function", crate::AssistKind::RefactorExtract),
 +        "Extract into function",
 +        target_range,
 +        move |builder| {
 +            let outliving_locals: Vec<_> = ret_values.collect();
 +            if stdx::never!(!outliving_locals.is_empty() && !ret_ty.is_unit()) {
 +                // We should not have variables that outlive body if we have expression block
 +                return;
 +            }
 +
 +            let params =
 +                body.extracted_function_params(ctx, &container_info, locals_used.iter().copied());
 +
 +            let extracted_from_trait_impl = body.extracted_from_trait_impl();
 +
 +            let name = make_function_name(&semantics_scope);
 +
 +            let fun = Function {
 +                name,
 +                self_param,
 +                params,
 +                control_flow,
 +                ret_ty,
 +                body,
 +                outliving_locals,
 +                mods: container_info,
 +            };
 +
 +            let new_indent = IndentLevel::from_node(&insert_after);
 +            let old_indent = fun.body.indent_level();
 +
 +            builder.replace(target_range, make_call(ctx, &fun, old_indent));
 +
 +            let fn_def = match fun.self_param_adt(ctx) {
 +                Some(adt) if extracted_from_trait_impl => {
 +                    let fn_def = format_function(ctx, module, &fun, old_indent, new_indent + 1);
 +                    generate_impl_text(&adt, &fn_def).replace("{\n\n", "{")
 +                }
 +                _ => format_function(ctx, module, &fun, old_indent, new_indent),
 +            };
 +
 +            if fn_def.contains("ControlFlow") {
 +                let scope = match scope {
 +                    ImportScope::File(it) => ImportScope::File(builder.make_mut(it)),
 +                    ImportScope::Module(it) => ImportScope::Module(builder.make_mut(it)),
 +                    ImportScope::Block(it) => ImportScope::Block(builder.make_mut(it)),
 +                };
 +
 +                let control_flow_enum =
 +                    FamousDefs(&ctx.sema, module.krate()).core_ops_ControlFlow();
 +
 +                if let Some(control_flow_enum) = control_flow_enum {
 +                    let mod_path = module.find_use_path_prefixed(
 +                        ctx.sema.db,
 +                        ModuleDef::from(control_flow_enum),
 +                        ctx.config.insert_use.prefix_kind,
 +                        ctx.config.prefer_no_std,
 +                    );
 +
 +                    if let Some(mod_path) = mod_path {
 +                        insert_use(&scope, mod_path_to_ast(&mod_path), &ctx.config.insert_use);
 +                    }
 +                }
 +            }
 +
 +            let insert_offset = insert_after.text_range().end();
 +
 +            match ctx.config.snippet_cap {
 +                Some(cap) => builder.insert_snippet(cap, insert_offset, fn_def),
 +                None => builder.insert(insert_offset, fn_def),
 +            };
 +        },
 +    )
 +}
 +
 +fn make_function_name(semantics_scope: &hir::SemanticsScope<'_>) -> ast::NameRef {
 +    let mut names_in_scope = vec![];
 +    semantics_scope.process_all_names(&mut |name, _| names_in_scope.push(name.to_string()));
 +
 +    let default_name = "fun_name";
 +
 +    let mut name = default_name.to_string();
 +    let mut counter = 0;
 +    while names_in_scope.contains(&name) {
 +        counter += 1;
-             format_to!(buf, "let {}{} = ", mut_modifier(var), var.local.name(ctx.db()))
++        name = format!("{default_name}{counter}")
 +    }
 +    make::name_ref(&name)
 +}
 +
 +/// Try to guess what user wants to extract
 +///
 +/// We have basically have two cases:
 +/// * We want whole node, like `loop {}`, `2 + 2`, `{ let n = 1; }` exprs.
 +///   Then we can use `ast::Expr`
 +/// * We want a few statements for a block. E.g.
 +///   ```rust,no_run
 +///   fn foo() -> i32 {
 +///     let m = 1;
 +///     $0
 +///     let n = 2;
 +///     let k = 3;
 +///     k + n
 +///     $0
 +///   }
 +///   ```
 +///
 +fn extraction_target(node: &SyntaxNode, selection_range: TextRange) -> Option<FunctionBody> {
 +    if let Some(stmt) = ast::Stmt::cast(node.clone()) {
 +        return match stmt {
 +            ast::Stmt::Item(_) => None,
 +            ast::Stmt::ExprStmt(_) | ast::Stmt::LetStmt(_) => Some(FunctionBody::from_range(
 +                node.parent().and_then(ast::StmtList::cast)?,
 +                node.text_range(),
 +            )),
 +        };
 +    }
 +
 +    // Covering element returned the parent block of one or multiple statements that have been selected
 +    if let Some(stmt_list) = ast::StmtList::cast(node.clone()) {
 +        if let Some(block_expr) = stmt_list.syntax().parent().and_then(ast::BlockExpr::cast) {
 +            if block_expr.syntax().text_range() == selection_range {
 +                return FunctionBody::from_expr(block_expr.into());
 +            }
 +        }
 +
 +        // Extract the full statements.
 +        return Some(FunctionBody::from_range(stmt_list, selection_range));
 +    }
 +
 +    let expr = ast::Expr::cast(node.clone())?;
 +    // A node got selected fully
 +    if node.text_range() == selection_range {
 +        return FunctionBody::from_expr(expr);
 +    }
 +
 +    node.ancestors().find_map(ast::Expr::cast).and_then(FunctionBody::from_expr)
 +}
 +
 +#[derive(Debug)]
 +struct Function {
 +    name: ast::NameRef,
 +    self_param: Option<ast::SelfParam>,
 +    params: Vec<Param>,
 +    control_flow: ControlFlow,
 +    ret_ty: RetType,
 +    body: FunctionBody,
 +    outliving_locals: Vec<OutlivedLocal>,
 +    mods: ContainerInfo,
 +}
 +
 +#[derive(Debug)]
 +struct Param {
 +    var: Local,
 +    ty: hir::Type,
 +    move_local: bool,
 +    requires_mut: bool,
 +    is_copy: bool,
 +}
 +
 +#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 +enum ParamKind {
 +    Value,
 +    MutValue,
 +    SharedRef,
 +    MutRef,
 +}
 +
 +#[derive(Debug, Eq, PartialEq)]
 +enum FunType {
 +    Unit,
 +    Single(hir::Type),
 +    Tuple(Vec<hir::Type>),
 +}
 +
 +/// Where to put extracted function definition
 +#[derive(Debug)]
 +enum Anchor {
 +    /// Extract free function and put right after current top-level function
 +    Freestanding,
 +    /// Extract method and put right after current function in the impl-block
 +    Method,
 +}
 +
 +// FIXME: ControlFlow and ContainerInfo both track some function modifiers, feels like these two should
 +// probably be merged somehow.
 +#[derive(Debug)]
 +struct ControlFlow {
 +    kind: Option<FlowKind>,
 +    is_async: bool,
 +    is_unsafe: bool,
 +}
 +
 +/// The thing whose expression we are extracting from. Can be a function, const, static, const arg, ...
 +#[derive(Clone, Debug)]
 +struct ContainerInfo {
 +    is_const: bool,
 +    is_in_tail: bool,
 +    parent_loop: Option<SyntaxNode>,
 +    /// The function's return type, const's type etc.
 +    ret_type: Option<hir::Type>,
 +    generic_param_lists: Vec<ast::GenericParamList>,
 +    where_clauses: Vec<ast::WhereClause>,
 +}
 +
 +/// Control flow that is exported from extracted function
 +///
 +/// E.g.:
 +/// ```rust,no_run
 +/// loop {
 +///     $0
 +///     if 42 == 42 {
 +///         break;
 +///     }
 +///     $0
 +/// }
 +/// ```
 +#[derive(Debug, Clone)]
 +enum FlowKind {
 +    /// Return with value (`return $expr;`)
 +    Return(Option<ast::Expr>),
 +    Try {
 +        kind: TryKind,
 +    },
 +    /// Break with label and value (`break 'label $expr;`)
 +    Break(Option<ast::Lifetime>, Option<ast::Expr>),
 +    /// Continue with label (`continue 'label;`)
 +    Continue(Option<ast::Lifetime>),
 +}
 +
 +#[derive(Debug, Clone)]
 +enum TryKind {
 +    Option,
 +    Result { ty: hir::Type },
 +}
 +
 +#[derive(Debug)]
 +enum RetType {
 +    Expr(hir::Type),
 +    Stmt,
 +}
 +
 +impl RetType {
 +    fn is_unit(&self) -> bool {
 +        match self {
 +            RetType::Expr(ty) => ty.is_unit(),
 +            RetType::Stmt => true,
 +        }
 +    }
 +}
 +
 +/// Semantically same as `ast::Expr`, but preserves identity when using only part of the Block
 +/// This is the future function body, the part that is being extracted.
 +#[derive(Debug)]
 +enum FunctionBody {
 +    Expr(ast::Expr),
 +    Span { parent: ast::StmtList, text_range: TextRange },
 +}
 +
 +#[derive(Debug)]
 +struct OutlivedLocal {
 +    local: Local,
 +    mut_usage_outside_body: bool,
 +}
 +
 +/// Container of local variable usages
 +///
 +/// Semanticall same as `UsageSearchResult`, but provides more convenient interface
 +struct LocalUsages(ide_db::search::UsageSearchResult);
 +
 +impl LocalUsages {
 +    fn find_local_usages(ctx: &AssistContext<'_>, var: Local) -> Self {
 +        Self(
 +            Definition::Local(var)
 +                .usages(&ctx.sema)
 +                .in_scope(SearchScope::single_file(ctx.file_id()))
 +                .all(),
 +        )
 +    }
 +
 +    fn iter(&self) -> impl Iterator<Item = &FileReference> + '_ {
 +        self.0.iter().flat_map(|(_, rs)| rs)
 +    }
 +}
 +
 +impl Function {
 +    fn return_type(&self, ctx: &AssistContext<'_>) -> FunType {
 +        match &self.ret_ty {
 +            RetType::Expr(ty) if ty.is_unit() => FunType::Unit,
 +            RetType::Expr(ty) => FunType::Single(ty.clone()),
 +            RetType::Stmt => match self.outliving_locals.as_slice() {
 +                [] => FunType::Unit,
 +                [var] => FunType::Single(var.local.ty(ctx.db())),
 +                vars => {
 +                    let types = vars.iter().map(|v| v.local.ty(ctx.db())).collect();
 +                    FunType::Tuple(types)
 +                }
 +            },
 +        }
 +    }
 +
 +    fn self_param_adt(&self, ctx: &AssistContext<'_>) -> Option<ast::Adt> {
 +        let self_param = self.self_param.as_ref()?;
 +        let def = ctx.sema.to_def(self_param)?;
 +        let adt = def.ty(ctx.db()).strip_references().as_adt()?;
 +        let InFile { file_id: _, value } = adt.source(ctx.db())?;
 +        Some(value)
 +    }
 +}
 +
 +impl ParamKind {
 +    fn is_ref(&self) -> bool {
 +        matches!(self, ParamKind::SharedRef | ParamKind::MutRef)
 +    }
 +}
 +
 +impl Param {
 +    fn kind(&self) -> ParamKind {
 +        match (self.move_local, self.requires_mut, self.is_copy) {
 +            (false, true, _) => ParamKind::MutRef,
 +            (false, false, false) => ParamKind::SharedRef,
 +            (true, true, _) => ParamKind::MutValue,
 +            (_, false, _) => ParamKind::Value,
 +        }
 +    }
 +
 +    fn to_arg(&self, ctx: &AssistContext<'_>) -> ast::Expr {
 +        let var = path_expr_from_local(ctx, self.var);
 +        match self.kind() {
 +            ParamKind::Value | ParamKind::MutValue => var,
 +            ParamKind::SharedRef => make::expr_ref(var, false),
 +            ParamKind::MutRef => make::expr_ref(var, true),
 +        }
 +    }
 +
 +    fn to_param(&self, ctx: &AssistContext<'_>, module: hir::Module) -> ast::Param {
 +        let var = self.var.name(ctx.db()).to_string();
 +        let var_name = make::name(&var);
 +        let pat = match self.kind() {
 +            ParamKind::MutValue => make::ident_pat(false, true, var_name),
 +            ParamKind::Value | ParamKind::SharedRef | ParamKind::MutRef => {
 +                make::ext::simple_ident_pat(var_name)
 +            }
 +        };
 +
 +        let ty = make_ty(&self.ty, ctx, module);
 +        let ty = match self.kind() {
 +            ParamKind::Value | ParamKind::MutValue => ty,
 +            ParamKind::SharedRef => make::ty_ref(ty, false),
 +            ParamKind::MutRef => make::ty_ref(ty, true),
 +        };
 +
 +        make::param(pat.into(), ty)
 +    }
 +}
 +
 +impl TryKind {
 +    fn of_ty(ty: hir::Type, ctx: &AssistContext<'_>) -> Option<TryKind> {
 +        if ty.is_unknown() {
 +            // We favour Result for `expr?`
 +            return Some(TryKind::Result { ty });
 +        }
 +        let adt = ty.as_adt()?;
 +        let name = adt.name(ctx.db());
 +        // FIXME: use lang items to determine if it is std type or user defined
 +        //        E.g. if user happens to define type named `Option`, we would have false positive
 +        match name.to_string().as_str() {
 +            "Option" => Some(TryKind::Option),
 +            "Result" => Some(TryKind::Result { ty }),
 +            _ => None,
 +        }
 +    }
 +}
 +
 +impl FlowKind {
 +    fn make_result_handler(&self, expr: Option<ast::Expr>) -> ast::Expr {
 +        match self {
 +            FlowKind::Return(_) => make::expr_return(expr),
 +            FlowKind::Break(label, _) => make::expr_break(label.clone(), expr),
 +            FlowKind::Try { .. } => {
 +                stdx::never!("cannot have result handler with try");
 +                expr.unwrap_or_else(|| make::expr_return(None))
 +            }
 +            FlowKind::Continue(label) => {
 +                stdx::always!(expr.is_none(), "continue with value is not possible");
 +                make::expr_continue(label.clone())
 +            }
 +        }
 +    }
 +
 +    fn expr_ty(&self, ctx: &AssistContext<'_>) -> Option<hir::Type> {
 +        match self {
 +            FlowKind::Return(Some(expr)) | FlowKind::Break(_, Some(expr)) => {
 +                ctx.sema.type_of_expr(expr).map(TypeInfo::adjusted)
 +            }
 +            FlowKind::Try { .. } => {
 +                stdx::never!("try does not have defined expr_ty");
 +                None
 +            }
 +            _ => None,
 +        }
 +    }
 +}
 +
 +impl FunctionBody {
 +    fn parent(&self) -> Option<SyntaxNode> {
 +        match self {
 +            FunctionBody::Expr(expr) => expr.syntax().parent(),
 +            FunctionBody::Span { parent, .. } => Some(parent.syntax().clone()),
 +        }
 +    }
 +
 +    fn node(&self) -> &SyntaxNode {
 +        match self {
 +            FunctionBody::Expr(e) => e.syntax(),
 +            FunctionBody::Span { parent, .. } => parent.syntax(),
 +        }
 +    }
 +
 +    fn extracted_from_trait_impl(&self) -> bool {
 +        match self.node().ancestors().find_map(ast::Impl::cast) {
 +            Some(c) => return c.trait_().is_some(),
 +            None => false,
 +        }
 +    }
 +
 +    fn descendants(&self) -> impl Iterator<Item = SyntaxNode> {
 +        match self {
 +            FunctionBody::Expr(expr) => expr.syntax().descendants(),
 +            FunctionBody::Span { parent, .. } => parent.syntax().descendants(),
 +        }
 +    }
 +
 +    fn descendant_paths(&self) -> impl Iterator<Item = ast::Path> {
 +        self.descendants().filter_map(|node| {
 +            match_ast! {
 +                match node {
 +                    ast::Path(it) => Some(it),
 +                    _ => None
 +                }
 +            }
 +        })
 +    }
 +
 +    fn from_expr(expr: ast::Expr) -> Option<Self> {
 +        match expr {
 +            ast::Expr::BreakExpr(it) => it.expr().map(Self::Expr),
 +            ast::Expr::ReturnExpr(it) => it.expr().map(Self::Expr),
 +            ast::Expr::BlockExpr(it) if !it.is_standalone() => None,
 +            expr => Some(Self::Expr(expr)),
 +        }
 +    }
 +
 +    fn from_range(parent: ast::StmtList, selected: TextRange) -> FunctionBody {
 +        let full_body = parent.syntax().children_with_tokens();
 +
 +        let mut text_range = full_body
 +            .filter(|it| ast::Stmt::can_cast(it.kind()) || it.kind() == COMMENT)
 +            .map(|element| element.text_range())
 +            .filter(|&range| selected.intersect(range).filter(|it| !it.is_empty()).is_some())
 +            .reduce(|acc, stmt| acc.cover(stmt));
 +
 +        if let Some(tail_range) = parent
 +            .tail_expr()
 +            .map(|it| it.syntax().text_range())
 +            .filter(|&it| selected.intersect(it).is_some())
 +        {
 +            text_range = Some(match text_range {
 +                Some(text_range) => text_range.cover(tail_range),
 +                None => tail_range,
 +            });
 +        }
 +        Self::Span { parent, text_range: text_range.unwrap_or(selected) }
 +    }
 +
 +    fn indent_level(&self) -> IndentLevel {
 +        match &self {
 +            FunctionBody::Expr(expr) => IndentLevel::from_node(expr.syntax()),
 +            FunctionBody::Span { parent, .. } => IndentLevel::from_node(parent.syntax()) + 1,
 +        }
 +    }
 +
 +    fn tail_expr(&self) -> Option<ast::Expr> {
 +        match &self {
 +            FunctionBody::Expr(expr) => Some(expr.clone()),
 +            FunctionBody::Span { parent, text_range } => {
 +                let tail_expr = parent.tail_expr()?;
 +                text_range.contains_range(tail_expr.syntax().text_range()).then(|| tail_expr)
 +            }
 +        }
 +    }
 +
 +    fn walk_expr(&self, cb: &mut dyn FnMut(ast::Expr)) {
 +        match self {
 +            FunctionBody::Expr(expr) => walk_expr(expr, cb),
 +            FunctionBody::Span { parent, text_range } => {
 +                parent
 +                    .statements()
 +                    .filter(|stmt| text_range.contains_range(stmt.syntax().text_range()))
 +                    .filter_map(|stmt| match stmt {
 +                        ast::Stmt::ExprStmt(expr_stmt) => expr_stmt.expr(),
 +                        ast::Stmt::Item(_) => None,
 +                        ast::Stmt::LetStmt(stmt) => stmt.initializer(),
 +                    })
 +                    .for_each(|expr| walk_expr(&expr, cb));
 +                if let Some(expr) = parent
 +                    .tail_expr()
 +                    .filter(|it| text_range.contains_range(it.syntax().text_range()))
 +                {
 +                    walk_expr(&expr, cb);
 +                }
 +            }
 +        }
 +    }
 +
 +    fn preorder_expr(&self, cb: &mut dyn FnMut(WalkEvent<ast::Expr>) -> bool) {
 +        match self {
 +            FunctionBody::Expr(expr) => preorder_expr(expr, cb),
 +            FunctionBody::Span { parent, text_range } => {
 +                parent
 +                    .statements()
 +                    .filter(|stmt| text_range.contains_range(stmt.syntax().text_range()))
 +                    .filter_map(|stmt| match stmt {
 +                        ast::Stmt::ExprStmt(expr_stmt) => expr_stmt.expr(),
 +                        ast::Stmt::Item(_) => None,
 +                        ast::Stmt::LetStmt(stmt) => stmt.initializer(),
 +                    })
 +                    .for_each(|expr| preorder_expr(&expr, cb));
 +                if let Some(expr) = parent
 +                    .tail_expr()
 +                    .filter(|it| text_range.contains_range(it.syntax().text_range()))
 +                {
 +                    preorder_expr(&expr, cb);
 +                }
 +            }
 +        }
 +    }
 +
 +    fn walk_pat(&self, cb: &mut dyn FnMut(ast::Pat)) {
 +        match self {
 +            FunctionBody::Expr(expr) => walk_patterns_in_expr(expr, cb),
 +            FunctionBody::Span { parent, text_range } => {
 +                parent
 +                    .statements()
 +                    .filter(|stmt| text_range.contains_range(stmt.syntax().text_range()))
 +                    .for_each(|stmt| match stmt {
 +                        ast::Stmt::ExprStmt(expr_stmt) => {
 +                            if let Some(expr) = expr_stmt.expr() {
 +                                walk_patterns_in_expr(&expr, cb)
 +                            }
 +                        }
 +                        ast::Stmt::Item(_) => (),
 +                        ast::Stmt::LetStmt(stmt) => {
 +                            if let Some(pat) = stmt.pat() {
 +                                walk_pat(&pat, cb);
 +                            }
 +                            if let Some(expr) = stmt.initializer() {
 +                                walk_patterns_in_expr(&expr, cb);
 +                            }
 +                        }
 +                    });
 +                if let Some(expr) = parent
 +                    .tail_expr()
 +                    .filter(|it| text_range.contains_range(it.syntax().text_range()))
 +                {
 +                    walk_patterns_in_expr(&expr, cb);
 +                }
 +            }
 +        }
 +    }
 +
 +    fn text_range(&self) -> TextRange {
 +        match self {
 +            FunctionBody::Expr(expr) => expr.syntax().text_range(),
 +            &FunctionBody::Span { text_range, .. } => text_range,
 +        }
 +    }
 +
 +    fn contains_range(&self, range: TextRange) -> bool {
 +        self.text_range().contains_range(range)
 +    }
 +
 +    fn precedes_range(&self, range: TextRange) -> bool {
 +        self.text_range().end() <= range.start()
 +    }
 +
 +    fn contains_node(&self, node: &SyntaxNode) -> bool {
 +        self.contains_range(node.text_range())
 +    }
 +}
 +
 +impl FunctionBody {
 +    /// Analyzes a function body, returning the used local variables that are referenced in it as well as
 +    /// whether it contains an await expression.
 +    fn analyze(
 +        &self,
 +        sema: &Semantics<'_, RootDatabase>,
 +    ) -> (FxIndexSet<Local>, Option<ast::SelfParam>) {
 +        let mut self_param = None;
 +        let mut res = FxIndexSet::default();
 +        let mut cb = |name_ref: Option<_>| {
 +            let local_ref =
 +                match name_ref.and_then(|name_ref| NameRefClass::classify(sema, &name_ref)) {
 +                    Some(
 +                        NameRefClass::Definition(Definition::Local(local_ref))
 +                        | NameRefClass::FieldShorthand { local_ref, field_ref: _ },
 +                    ) => local_ref,
 +                    _ => return,
 +                };
 +            let InFile { file_id, value } = local_ref.source(sema.db);
 +            // locals defined inside macros are not relevant to us
 +            if !file_id.is_macro() {
 +                match value {
 +                    Either::Right(it) => {
 +                        self_param.replace(it);
 +                    }
 +                    Either::Left(_) => {
 +                        res.insert(local_ref);
 +                    }
 +                }
 +            }
 +        };
 +        self.walk_expr(&mut |expr| match expr {
 +            ast::Expr::PathExpr(path_expr) => {
 +                cb(path_expr.path().and_then(|it| it.as_single_name_ref()))
 +            }
 +            ast::Expr::ClosureExpr(closure_expr) => {
 +                if let Some(body) = closure_expr.body() {
 +                    body.syntax().descendants().map(ast::NameRef::cast).for_each(|it| cb(it));
 +                }
 +            }
 +            ast::Expr::MacroExpr(expr) => {
 +                if let Some(tt) = expr.macro_call().and_then(|call| call.token_tree()) {
 +                    tt.syntax()
 +                        .children_with_tokens()
 +                        .flat_map(SyntaxElement::into_token)
 +                        .filter(|it| it.kind() == SyntaxKind::IDENT)
 +                        .flat_map(|t| sema.descend_into_macros(t))
 +                        .for_each(|t| cb(t.parent().and_then(ast::NameRef::cast)));
 +                }
 +            }
 +            _ => (),
 +        });
 +        (res, self_param)
 +    }
 +
 +    fn analyze_container(&self, sema: &Semantics<'_, RootDatabase>) -> Option<ContainerInfo> {
 +        let mut ancestors = self.parent()?.ancestors();
 +        let infer_expr_opt = |expr| sema.type_of_expr(&expr?).map(TypeInfo::adjusted);
 +        let mut parent_loop = None;
 +        let mut set_parent_loop = |loop_: &dyn ast::HasLoopBody| {
 +            if loop_
 +                .loop_body()
 +                .map_or(false, |it| it.syntax().text_range().contains_range(self.text_range()))
 +            {
 +                parent_loop.get_or_insert(loop_.syntax().clone());
 +            }
 +        };
 +
 +        let (is_const, expr, ty) = loop {
 +            let anc = ancestors.next()?;
 +            break match_ast! {
 +                match anc {
 +                    ast::ClosureExpr(closure) => (false, closure.body(), infer_expr_opt(closure.body())),
 +                    ast::BlockExpr(block_expr) => {
 +                        let (constness, block) = match block_expr.modifier() {
 +                            Some(ast::BlockModifier::Const(_)) => (true, block_expr),
 +                            Some(ast::BlockModifier::Try(_)) => (false, block_expr),
 +                            Some(ast::BlockModifier::Label(label)) if label.lifetime().is_some() => (false, block_expr),
 +                            _ => continue,
 +                        };
 +                        let expr = Some(ast::Expr::BlockExpr(block));
 +                        (constness, expr.clone(), infer_expr_opt(expr))
 +                    },
 +                    ast::Fn(fn_) => {
 +                        let func = sema.to_def(&fn_)?;
 +                        let mut ret_ty = func.ret_type(sema.db);
 +                        if func.is_async(sema.db) {
 +                            if let Some(async_ret) = func.async_ret_type(sema.db) {
 +                                ret_ty = async_ret;
 +                            }
 +                        }
 +                        (fn_.const_token().is_some(), fn_.body().map(ast::Expr::BlockExpr), Some(ret_ty))
 +                    },
 +                    ast::Static(statik) => {
 +                        (true, statik.body(), Some(sema.to_def(&statik)?.ty(sema.db)))
 +                    },
 +                    ast::ConstArg(ca) => {
 +                        (true, ca.expr(), infer_expr_opt(ca.expr()))
 +                    },
 +                    ast::Const(konst) => {
 +                        (true, konst.body(), Some(sema.to_def(&konst)?.ty(sema.db)))
 +                    },
 +                    ast::ConstParam(cp) => {
 +                        (true, cp.default_val(), Some(sema.to_def(&cp)?.ty(sema.db)))
 +                    },
 +                    ast::ConstBlockPat(cbp) => {
 +                        let expr = cbp.block_expr().map(ast::Expr::BlockExpr);
 +                        (true, expr.clone(), infer_expr_opt(expr))
 +                    },
 +                    ast::Variant(__) => return None,
 +                    ast::Meta(__) => return None,
 +                    ast::LoopExpr(it) => {
 +                        set_parent_loop(&it);
 +                        continue;
 +                    },
 +                    ast::ForExpr(it) => {
 +                        set_parent_loop(&it);
 +                        continue;
 +                    },
 +                    ast::WhileExpr(it) => {
 +                        set_parent_loop(&it);
 +                        continue;
 +                    },
 +                    _ => continue,
 +                }
 +            };
 +        };
 +        let container_tail = match expr? {
 +            ast::Expr::BlockExpr(block) => block.tail_expr(),
 +            expr => Some(expr),
 +        };
 +        let is_in_tail =
 +            container_tail.zip(self.tail_expr()).map_or(false, |(container_tail, body_tail)| {
 +                container_tail.syntax().text_range().contains_range(body_tail.syntax().text_range())
 +            });
 +
 +        let parent = self.parent()?;
 +        let parents = generic_parents(&parent);
 +        let generic_param_lists = parents.iter().filter_map(|it| it.generic_param_list()).collect();
 +        let where_clauses = parents.iter().filter_map(|it| it.where_clause()).collect();
 +
 +        Some(ContainerInfo {
 +            is_in_tail,
 +            is_const,
 +            parent_loop,
 +            ret_type: ty,
 +            generic_param_lists,
 +            where_clauses,
 +        })
 +    }
 +
 +    fn return_ty(&self, ctx: &AssistContext<'_>) -> Option<RetType> {
 +        match self.tail_expr() {
 +            Some(expr) => ctx.sema.type_of_expr(&expr).map(TypeInfo::original).map(RetType::Expr),
 +            None => Some(RetType::Stmt),
 +        }
 +    }
 +
 +    /// Local variables defined inside `body` that are accessed outside of it
 +    fn ret_values<'a>(
 +        &self,
 +        ctx: &'a AssistContext<'_>,
 +        parent: &SyntaxNode,
 +    ) -> impl Iterator<Item = OutlivedLocal> + 'a {
 +        let parent = parent.clone();
 +        let range = self.text_range();
 +        locals_defined_in_body(&ctx.sema, self)
 +            .into_iter()
 +            .filter_map(move |local| local_outlives_body(ctx, range, local, &parent))
 +    }
 +
 +    /// Analyses the function body for external control flow.
 +    fn external_control_flow(
 +        &self,
 +        ctx: &AssistContext<'_>,
 +        container_info: &ContainerInfo,
 +    ) -> Option<ControlFlow> {
 +        let mut ret_expr = None;
 +        let mut try_expr = None;
 +        let mut break_expr = None;
 +        let mut continue_expr = None;
 +        let mut is_async = false;
 +        let mut _is_unsafe = false;
 +
 +        let mut unsafe_depth = 0;
 +        let mut loop_depth = 0;
 +
 +        self.preorder_expr(&mut |expr| {
 +            let expr = match expr {
 +                WalkEvent::Enter(e) => e,
 +                WalkEvent::Leave(expr) => {
 +                    match expr {
 +                        ast::Expr::LoopExpr(_)
 +                        | ast::Expr::ForExpr(_)
 +                        | ast::Expr::WhileExpr(_) => loop_depth -= 1,
 +                        ast::Expr::BlockExpr(block_expr) if block_expr.unsafe_token().is_some() => {
 +                            unsafe_depth -= 1
 +                        }
 +                        _ => (),
 +                    }
 +                    return false;
 +                }
 +            };
 +            match expr {
 +                ast::Expr::LoopExpr(_) | ast::Expr::ForExpr(_) | ast::Expr::WhileExpr(_) => {
 +                    loop_depth += 1;
 +                }
 +                ast::Expr::BlockExpr(block_expr) if block_expr.unsafe_token().is_some() => {
 +                    unsafe_depth += 1
 +                }
 +                ast::Expr::ReturnExpr(it) => {
 +                    ret_expr = Some(it);
 +                }
 +                ast::Expr::TryExpr(it) => {
 +                    try_expr = Some(it);
 +                }
 +                ast::Expr::BreakExpr(it) if loop_depth == 0 => {
 +                    break_expr = Some(it);
 +                }
 +                ast::Expr::ContinueExpr(it) if loop_depth == 0 => {
 +                    continue_expr = Some(it);
 +                }
 +                ast::Expr::AwaitExpr(_) => is_async = true,
 +                // FIXME: Do unsafe analysis on expression, sem highlighting knows this so we should be able
 +                // to just lift that out of there
 +                // expr if unsafe_depth ==0 && expr.is_unsafe => is_unsafe = true,
 +                _ => {}
 +            }
 +            false
 +        });
 +
 +        let kind = match (try_expr, ret_expr, break_expr, continue_expr) {
 +            (Some(_), _, None, None) => {
 +                let ret_ty = container_info.ret_type.clone()?;
 +                let kind = TryKind::of_ty(ret_ty, ctx)?;
 +
 +                Some(FlowKind::Try { kind })
 +            }
 +            (Some(_), _, _, _) => {
 +                cov_mark::hit!(external_control_flow_try_and_bc);
 +                return None;
 +            }
 +            (None, Some(r), None, None) => Some(FlowKind::Return(r.expr())),
 +            (None, Some(_), _, _) => {
 +                cov_mark::hit!(external_control_flow_return_and_bc);
 +                return None;
 +            }
 +            (None, None, Some(_), Some(_)) => {
 +                cov_mark::hit!(external_control_flow_break_and_continue);
 +                return None;
 +            }
 +            (None, None, Some(b), None) => Some(FlowKind::Break(b.lifetime(), b.expr())),
 +            (None, None, None, Some(c)) => Some(FlowKind::Continue(c.lifetime())),
 +            (None, None, None, None) => None,
 +        };
 +
 +        Some(ControlFlow { kind, is_async, is_unsafe: _is_unsafe })
 +    }
 +
 +    /// find variables that should be extracted as params
 +    ///
 +    /// Computes additional info that affects param type and mutability
 +    fn extracted_function_params(
 +        &self,
 +        ctx: &AssistContext<'_>,
 +        container_info: &ContainerInfo,
 +        locals: impl Iterator<Item = Local>,
 +    ) -> Vec<Param> {
 +        locals
 +            .map(|local| (local, local.source(ctx.db())))
 +            .filter(|(_, src)| is_defined_outside_of_body(ctx, self, src))
 +            .filter_map(|(local, src)| match src.value {
 +                Either::Left(src) => Some((local, src)),
 +                Either::Right(_) => {
 +                    stdx::never!(false, "Local::is_self returned false, but source is SelfParam");
 +                    None
 +                }
 +            })
 +            .map(|(var, src)| {
 +                let usages = LocalUsages::find_local_usages(ctx, var);
 +                let ty = var.ty(ctx.db());
 +
 +                let defined_outside_parent_loop = container_info
 +                    .parent_loop
 +                    .as_ref()
 +                    .map_or(true, |it| it.text_range().contains_range(src.syntax().text_range()));
 +
 +                let is_copy = ty.is_copy(ctx.db());
 +                let has_usages = self.has_usages_after_body(&usages);
 +                let requires_mut =
 +                    !ty.is_mutable_reference() && has_exclusive_usages(ctx, &usages, self);
 +                // We can move the value into the function call if it's not used after the call,
 +                // if the var is not used but defined outside a loop we are extracting from we can't move it either
 +                // as the function will reuse it in the next iteration.
 +                let move_local = (!has_usages && defined_outside_parent_loop) || ty.is_reference();
 +                Param { var, ty, move_local, requires_mut, is_copy }
 +            })
 +            .collect()
 +    }
 +
 +    fn has_usages_after_body(&self, usages: &LocalUsages) -> bool {
 +        usages.iter().any(|reference| self.precedes_range(reference.range))
 +    }
 +}
 +
 +enum GenericParent {
 +    Fn(ast::Fn),
 +    Impl(ast::Impl),
 +    Trait(ast::Trait),
 +}
 +
 +impl GenericParent {
 +    fn generic_param_list(&self) -> Option<ast::GenericParamList> {
 +        match self {
 +            GenericParent::Fn(fn_) => fn_.generic_param_list(),
 +            GenericParent::Impl(impl_) => impl_.generic_param_list(),
 +            GenericParent::Trait(trait_) => trait_.generic_param_list(),
 +        }
 +    }
 +
 +    fn where_clause(&self) -> Option<ast::WhereClause> {
 +        match self {
 +            GenericParent::Fn(fn_) => fn_.where_clause(),
 +            GenericParent::Impl(impl_) => impl_.where_clause(),
 +            GenericParent::Trait(trait_) => trait_.where_clause(),
 +        }
 +    }
 +}
 +
 +/// Search `parent`'s ancestors for items with potentially applicable generic parameters
 +fn generic_parents(parent: &SyntaxNode) -> Vec<GenericParent> {
 +    let mut list = Vec::new();
 +    if let Some(parent_item) = parent.ancestors().find_map(ast::Item::cast) {
 +        match parent_item {
 +            ast::Item::Fn(ref fn_) => {
 +                if let Some(parent_parent) = parent_item
 +                    .syntax()
 +                    .parent()
 +                    .and_then(|it| it.parent())
 +                    .and_then(ast::Item::cast)
 +                {
 +                    match parent_parent {
 +                        ast::Item::Impl(impl_) => list.push(GenericParent::Impl(impl_)),
 +                        ast::Item::Trait(trait_) => list.push(GenericParent::Trait(trait_)),
 +                        _ => (),
 +                    }
 +                }
 +                list.push(GenericParent::Fn(fn_.clone()));
 +            }
 +            _ => (),
 +        }
 +    }
 +    list
 +}
 +
 +/// checks if relevant var is used with `&mut` access inside body
 +fn has_exclusive_usages(
 +    ctx: &AssistContext<'_>,
 +    usages: &LocalUsages,
 +    body: &FunctionBody,
 +) -> bool {
 +    usages
 +        .iter()
 +        .filter(|reference| body.contains_range(reference.range))
 +        .any(|reference| reference_is_exclusive(reference, body, ctx))
 +}
 +
 +/// checks if this reference requires `&mut` access inside node
 +fn reference_is_exclusive(
 +    reference: &FileReference,
 +    node: &dyn HasTokenAtOffset,
 +    ctx: &AssistContext<'_>,
 +) -> bool {
 +    // we directly modify variable with set: `n = 0`, `n += 1`
 +    if reference.category == Some(ReferenceCategory::Write) {
 +        return true;
 +    }
 +
 +    // we take `&mut` reference to variable: `&mut v`
 +    let path = match path_element_of_reference(node, reference) {
 +        Some(path) => path,
 +        None => return false,
 +    };
 +
 +    expr_require_exclusive_access(ctx, &path).unwrap_or(false)
 +}
 +
 +/// checks if this expr requires `&mut` access, recurses on field access
 +fn expr_require_exclusive_access(ctx: &AssistContext<'_>, expr: &ast::Expr) -> Option<bool> {
 +    if let ast::Expr::MacroExpr(_) = expr {
 +        // FIXME: expand macro and check output for mutable usages of the variable?
 +        return None;
 +    }
 +
 +    let parent = expr.syntax().parent()?;
 +
 +    if let Some(bin_expr) = ast::BinExpr::cast(parent.clone()) {
 +        if matches!(bin_expr.op_kind()?, ast::BinaryOp::Assignment { .. }) {
 +            return Some(bin_expr.lhs()?.syntax() == expr.syntax());
 +        }
 +        return Some(false);
 +    }
 +
 +    if let Some(ref_expr) = ast::RefExpr::cast(parent.clone()) {
 +        return Some(ref_expr.mut_token().is_some());
 +    }
 +
 +    if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) {
 +        let func = ctx.sema.resolve_method_call(&method_call)?;
 +        let self_param = func.self_param(ctx.db())?;
 +        let access = self_param.access(ctx.db());
 +
 +        return Some(matches!(access, hir::Access::Exclusive));
 +    }
 +
 +    if let Some(field) = ast::FieldExpr::cast(parent) {
 +        return expr_require_exclusive_access(ctx, &field.into());
 +    }
 +
 +    Some(false)
 +}
 +
 +trait HasTokenAtOffset {
 +    fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset<SyntaxToken>;
 +}
 +
 +impl HasTokenAtOffset for SyntaxNode {
 +    fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset<SyntaxToken> {
 +        SyntaxNode::token_at_offset(self, offset)
 +    }
 +}
 +
 +impl HasTokenAtOffset for FunctionBody {
 +    fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset<SyntaxToken> {
 +        match self {
 +            FunctionBody::Expr(expr) => expr.syntax().token_at_offset(offset),
 +            FunctionBody::Span { parent, text_range } => {
 +                match parent.syntax().token_at_offset(offset) {
 +                    TokenAtOffset::None => TokenAtOffset::None,
 +                    TokenAtOffset::Single(t) => {
 +                        if text_range.contains_range(t.text_range()) {
 +                            TokenAtOffset::Single(t)
 +                        } else {
 +                            TokenAtOffset::None
 +                        }
 +                    }
 +                    TokenAtOffset::Between(a, b) => {
 +                        match (
 +                            text_range.contains_range(a.text_range()),
 +                            text_range.contains_range(b.text_range()),
 +                        ) {
 +                            (true, true) => TokenAtOffset::Between(a, b),
 +                            (true, false) => TokenAtOffset::Single(a),
 +                            (false, true) => TokenAtOffset::Single(b),
 +                            (false, false) => TokenAtOffset::None,
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +/// find relevant `ast::Expr` for reference
 +///
 +/// # Preconditions
 +///
 +/// `node` must cover `reference`, that is `node.text_range().contains_range(reference.range)`
 +fn path_element_of_reference(
 +    node: &dyn HasTokenAtOffset,
 +    reference: &FileReference,
 +) -> Option<ast::Expr> {
 +    let token = node.token_at_offset(reference.range.start()).right_biased().or_else(|| {
 +        stdx::never!(false, "cannot find token at variable usage: {:?}", reference);
 +        None
 +    })?;
 +    let path = token.parent_ancestors().find_map(ast::Expr::cast).or_else(|| {
 +        stdx::never!(false, "cannot find path parent of variable usage: {:?}", token);
 +        None
 +    })?;
 +    stdx::always!(
 +        matches!(path, ast::Expr::PathExpr(_) | ast::Expr::MacroExpr(_)),
 +        "unexpected expression type for variable usage: {:?}",
 +        path
 +    );
 +    Some(path)
 +}
 +
 +/// list local variables defined inside `body`
 +fn locals_defined_in_body(
 +    sema: &Semantics<'_, RootDatabase>,
 +    body: &FunctionBody,
 +) -> FxIndexSet<Local> {
 +    // FIXME: this doesn't work well with macros
 +    //        see https://github.com/rust-lang/rust-analyzer/pull/7535#discussion_r570048550
 +    let mut res = FxIndexSet::default();
 +    body.walk_pat(&mut |pat| {
 +        if let ast::Pat::IdentPat(pat) = pat {
 +            if let Some(local) = sema.to_def(&pat) {
 +                res.insert(local);
 +            }
 +        }
 +    });
 +    res
 +}
 +
 +/// Returns usage details if local variable is used after(outside of) body
 +fn local_outlives_body(
 +    ctx: &AssistContext<'_>,
 +    body_range: TextRange,
 +    local: Local,
 +    parent: &SyntaxNode,
 +) -> Option<OutlivedLocal> {
 +    let usages = LocalUsages::find_local_usages(ctx, local);
 +    let mut has_mut_usages = false;
 +    let mut any_outlives = false;
 +    for usage in usages.iter() {
 +        if body_range.end() <= usage.range.start() {
 +            has_mut_usages |= reference_is_exclusive(usage, parent, ctx);
 +            any_outlives |= true;
 +            if has_mut_usages {
 +                break; // no need to check more elements we have all the info we wanted
 +            }
 +        }
 +    }
 +    if !any_outlives {
 +        return None;
 +    }
 +    Some(OutlivedLocal { local, mut_usage_outside_body: has_mut_usages })
 +}
 +
 +/// checks if the relevant local was defined before(outside of) body
 +fn is_defined_outside_of_body(
 +    ctx: &AssistContext<'_>,
 +    body: &FunctionBody,
 +    src: &hir::InFile<Either<ast::IdentPat, ast::SelfParam>>,
 +) -> bool {
 +    src.file_id.original_file(ctx.db()) == ctx.file_id()
 +        && !body.contains_node(either_syntax(&src.value))
 +}
 +
 +fn either_syntax(value: &Either<ast::IdentPat, ast::SelfParam>) -> &SyntaxNode {
 +    match value {
 +        Either::Left(pat) => pat.syntax(),
 +        Either::Right(it) => it.syntax(),
 +    }
 +}
 +
 +/// find where to put extracted function definition
 +///
 +/// Function should be put right after returned node
 +fn node_to_insert_after(body: &FunctionBody, anchor: Anchor) -> Option<SyntaxNode> {
 +    let node = body.node();
 +    let mut ancestors = node.ancestors().peekable();
 +    let mut last_ancestor = None;
 +    while let Some(next_ancestor) = ancestors.next() {
 +        match next_ancestor.kind() {
 +            SyntaxKind::SOURCE_FILE => break,
 +            SyntaxKind::ITEM_LIST if !matches!(anchor, Anchor::Freestanding) => continue,
 +            SyntaxKind::ITEM_LIST => {
 +                if ancestors.peek().map(SyntaxNode::kind) == Some(SyntaxKind::MODULE) {
 +                    break;
 +                }
 +            }
 +            SyntaxKind::ASSOC_ITEM_LIST if !matches!(anchor, Anchor::Method) => continue,
 +            SyntaxKind::ASSOC_ITEM_LIST if body.extracted_from_trait_impl() => continue,
 +            SyntaxKind::ASSOC_ITEM_LIST => {
 +                if ancestors.peek().map(SyntaxNode::kind) == Some(SyntaxKind::IMPL) {
 +                    break;
 +                }
 +            }
 +            _ => (),
 +        }
 +        last_ancestor = Some(next_ancestor);
 +    }
 +    last_ancestor
 +}
 +
 +fn make_call(ctx: &AssistContext<'_>, fun: &Function, indent: IndentLevel) -> String {
 +    let ret_ty = fun.return_type(ctx);
 +
 +    let args = make::arg_list(fun.params.iter().map(|param| param.to_arg(ctx)));
 +    let name = fun.name.clone();
 +    let mut call_expr = if fun.self_param.is_some() {
 +        let self_arg = make::expr_path(make::ext::ident_path("self"));
 +        make::expr_method_call(self_arg, name, args)
 +    } else {
 +        let func = make::expr_path(make::path_unqualified(make::path_segment(name)));
 +        make::expr_call(func, args)
 +    };
 +
 +    let handler = FlowHandler::from_ret_ty(fun, &ret_ty);
 +
 +    if fun.control_flow.is_async {
 +        call_expr = make::expr_await(call_expr);
 +    }
 +    let expr = handler.make_call_expr(call_expr).indent(indent);
 +
 +    let mut_modifier = |var: &OutlivedLocal| if var.mut_usage_outside_body { "mut " } else { "" };
 +
 +    let mut buf = String::new();
 +    match fun.outliving_locals.as_slice() {
 +        [] => {}
 +        [var] => {
-                 f(&format_args!("{}{}", mut_modifier(local), local.local.name(ctx.db())))
++            let modifier = mut_modifier(var);
++            let name = var.local.name(ctx.db());
++            format_to!(buf, "let {modifier}{name} = ")
 +        }
 +        vars => {
 +            buf.push_str("let (");
 +            let bindings = vars.iter().format_with(", ", |local, f| {
-             format_to!(buf, "{}", bindings);
++                let modifier = mut_modifier(local);
++                let name = local.local.name(ctx.db());
++                f(&format_args!("{modifier}{name}"))
 +            });
-     format_to!(buf, "{}", expr);
++            format_to!(buf, "{bindings}");
 +            buf.push_str(") = ");
 +        }
 +    }
 +
-         Some(_) => format_to!(
-             fn_def,
-             "\n\n{}{}{}{}fn $0{}",
-             new_indent,
-             const_kw,
-             async_kw,
-             unsafe_kw,
-             fun.name,
-         ),
-         None => format_to!(
-             fn_def,
-             "\n\n{}{}{}{}fn {}",
-             new_indent,
-             const_kw,
-             async_kw,
-             unsafe_kw,
-             fun.name,
-         ),
++    format_to!(buf, "{expr}");
 +    let insert_comma = fun
 +        .body
 +        .parent()
 +        .and_then(ast::MatchArm::cast)
 +        .map_or(false, |it| it.comma_token().is_none());
 +    if insert_comma {
 +        buf.push(',');
 +    } else if fun.ret_ty.is_unit() && (!fun.outliving_locals.is_empty() || !expr.is_block_like()) {
 +        buf.push(';');
 +    }
 +    buf
 +}
 +
 +enum FlowHandler {
 +    None,
 +    Try { kind: TryKind },
 +    If { action: FlowKind },
 +    IfOption { action: FlowKind },
 +    MatchOption { none: FlowKind },
 +    MatchResult { err: FlowKind },
 +}
 +
 +impl FlowHandler {
 +    fn from_ret_ty(fun: &Function, ret_ty: &FunType) -> FlowHandler {
 +        match &fun.control_flow.kind {
 +            None => FlowHandler::None,
 +            Some(flow_kind) => {
 +                let action = flow_kind.clone();
 +                if *ret_ty == FunType::Unit {
 +                    match flow_kind {
 +                        FlowKind::Return(None)
 +                        | FlowKind::Break(_, None)
 +                        | FlowKind::Continue(_) => FlowHandler::If { action },
 +                        FlowKind::Return(_) | FlowKind::Break(_, _) => {
 +                            FlowHandler::IfOption { action }
 +                        }
 +                        FlowKind::Try { kind } => FlowHandler::Try { kind: kind.clone() },
 +                    }
 +                } else {
 +                    match flow_kind {
 +                        FlowKind::Return(None)
 +                        | FlowKind::Break(_, None)
 +                        | FlowKind::Continue(_) => FlowHandler::MatchOption { none: action },
 +                        FlowKind::Return(_) | FlowKind::Break(_, _) => {
 +                            FlowHandler::MatchResult { err: action }
 +                        }
 +                        FlowKind::Try { kind } => FlowHandler::Try { kind: kind.clone() },
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    fn make_call_expr(&self, call_expr: ast::Expr) -> ast::Expr {
 +        match self {
 +            FlowHandler::None => call_expr,
 +            FlowHandler::Try { kind: _ } => make::expr_try(call_expr),
 +            FlowHandler::If { action } => {
 +                let action = action.make_result_handler(None);
 +                let stmt = make::expr_stmt(action);
 +                let block = make::block_expr(iter::once(stmt.into()), None);
 +                let controlflow_break_path = make::path_from_text("ControlFlow::Break");
 +                let condition = make::expr_let(
 +                    make::tuple_struct_pat(
 +                        controlflow_break_path,
 +                        iter::once(make::wildcard_pat().into()),
 +                    )
 +                    .into(),
 +                    call_expr,
 +                );
 +                make::expr_if(condition.into(), block, None)
 +            }
 +            FlowHandler::IfOption { action } => {
 +                let path = make::ext::ident_path("Some");
 +                let value_pat = make::ext::simple_ident_pat(make::name("value"));
 +                let pattern = make::tuple_struct_pat(path, iter::once(value_pat.into()));
 +                let cond = make::expr_let(pattern.into(), call_expr);
 +                let value = make::expr_path(make::ext::ident_path("value"));
 +                let action_expr = action.make_result_handler(Some(value));
 +                let action_stmt = make::expr_stmt(action_expr);
 +                let then = make::block_expr(iter::once(action_stmt.into()), None);
 +                make::expr_if(cond.into(), then, None)
 +            }
 +            FlowHandler::MatchOption { none } => {
 +                let some_name = "value";
 +
 +                let some_arm = {
 +                    let path = make::ext::ident_path("Some");
 +                    let value_pat = make::ext::simple_ident_pat(make::name(some_name));
 +                    let pat = make::tuple_struct_pat(path, iter::once(value_pat.into()));
 +                    let value = make::expr_path(make::ext::ident_path(some_name));
 +                    make::match_arm(iter::once(pat.into()), None, value)
 +                };
 +                let none_arm = {
 +                    let path = make::ext::ident_path("None");
 +                    let pat = make::path_pat(path);
 +                    make::match_arm(iter::once(pat), None, none.make_result_handler(None))
 +                };
 +                let arms = make::match_arm_list(vec![some_arm, none_arm]);
 +                make::expr_match(call_expr, arms)
 +            }
 +            FlowHandler::MatchResult { err } => {
 +                let ok_name = "value";
 +                let err_name = "value";
 +
 +                let ok_arm = {
 +                    let path = make::ext::ident_path("Ok");
 +                    let value_pat = make::ext::simple_ident_pat(make::name(ok_name));
 +                    let pat = make::tuple_struct_pat(path, iter::once(value_pat.into()));
 +                    let value = make::expr_path(make::ext::ident_path(ok_name));
 +                    make::match_arm(iter::once(pat.into()), None, value)
 +                };
 +                let err_arm = {
 +                    let path = make::ext::ident_path("Err");
 +                    let value_pat = make::ext::simple_ident_pat(make::name(err_name));
 +                    let pat = make::tuple_struct_pat(path, iter::once(value_pat.into()));
 +                    let value = make::expr_path(make::ext::ident_path(err_name));
 +                    make::match_arm(
 +                        iter::once(pat.into()),
 +                        None,
 +                        err.make_result_handler(Some(value)),
 +                    )
 +                };
 +                let arms = make::match_arm_list(vec![ok_arm, err_arm]);
 +                make::expr_match(call_expr, arms)
 +            }
 +        }
 +    }
 +}
 +
 +fn path_expr_from_local(ctx: &AssistContext<'_>, var: Local) -> ast::Expr {
 +    let name = var.name(ctx.db()).to_string();
 +    make::expr_path(make::ext::ident_path(&name))
 +}
 +
 +fn format_function(
 +    ctx: &AssistContext<'_>,
 +    module: hir::Module,
 +    fun: &Function,
 +    old_indent: IndentLevel,
 +    new_indent: IndentLevel,
 +) -> String {
 +    let mut fn_def = String::new();
++
++    let fun_name = &fun.name;
 +    let params = fun.make_param_list(ctx, module);
 +    let ret_ty = fun.make_ret_ty(ctx, module);
 +    let body = make_body(ctx, old_indent, new_indent, fun);
 +    let const_kw = if fun.mods.is_const { "const " } else { "" };
 +    let async_kw = if fun.control_flow.is_async { "async " } else { "" };
 +    let unsafe_kw = if fun.control_flow.is_unsafe { "unsafe " } else { "" };
 +    let (generic_params, where_clause) = make_generic_params_and_where_clause(ctx, fun);
++
++    format_to!(fn_def, "\n\n{new_indent}{const_kw}{async_kw}{unsafe_kw}");
 +    match ctx.config.snippet_cap {
-         format_to!(fn_def, "{}", generic_params);
++        Some(_) => format_to!(fn_def, "fn $0{fun_name}"),
++        None => format_to!(fn_def, "fn {fun_name}"),
 +    }
 +
 +    if let Some(generic_params) = generic_params {
-     format_to!(fn_def, "{}", params);
++        format_to!(fn_def, "{generic_params}");
 +    }
 +
-         format_to!(fn_def, " {}", ret_ty);
++    format_to!(fn_def, "{params}");
 +
 +    if let Some(ret_ty) = ret_ty {
-         format_to!(fn_def, " {}", where_clause);
++        format_to!(fn_def, " {ret_ty}");
 +    }
 +
 +    if let Some(where_clause) = where_clause {
-     format_to!(fn_def, " {}", body);
++        format_to!(fn_def, " {where_clause}");
 +    }
 +
++    format_to!(fn_def, " {body}");
 +
 +    fn_def
 +}
 +
 +fn make_generic_params_and_where_clause(
 +    ctx: &AssistContext<'_>,
 +    fun: &Function,
 +) -> (Option<ast::GenericParamList>, Option<ast::WhereClause>) {
 +    let used_type_params = fun.type_params(ctx);
 +
 +    let generic_param_list = make_generic_param_list(ctx, fun, &used_type_params);
 +    let where_clause = make_where_clause(ctx, fun, &used_type_params);
 +
 +    (generic_param_list, where_clause)
 +}
 +
 +fn make_generic_param_list(
 +    ctx: &AssistContext<'_>,
 +    fun: &Function,
 +    used_type_params: &[TypeParam],
 +) -> Option<ast::GenericParamList> {
 +    let mut generic_params = fun
 +        .mods
 +        .generic_param_lists
 +        .iter()
 +        .flat_map(|parent_params| {
 +            parent_params
 +                .generic_params()
 +                .filter(|param| param_is_required(ctx, param, used_type_params))
 +        })
 +        .peekable();
 +
 +    if generic_params.peek().is_some() {
 +        Some(make::generic_param_list(generic_params))
 +    } else {
 +        None
 +    }
 +}
 +
 +fn param_is_required(
 +    ctx: &AssistContext<'_>,
 +    param: &ast::GenericParam,
 +    used_type_params: &[TypeParam],
 +) -> bool {
 +    match param {
 +        ast::GenericParam::ConstParam(_) | ast::GenericParam::LifetimeParam(_) => false,
 +        ast::GenericParam::TypeParam(type_param) => match &ctx.sema.to_def(type_param) {
 +            Some(def) => used_type_params.contains(def),
 +            _ => false,
 +        },
 +    }
 +}
 +
 +fn make_where_clause(
 +    ctx: &AssistContext<'_>,
 +    fun: &Function,
 +    used_type_params: &[TypeParam],
 +) -> Option<ast::WhereClause> {
 +    let mut predicates = fun
 +        .mods
 +        .where_clauses
 +        .iter()
 +        .flat_map(|parent_where_clause| {
 +            parent_where_clause
 +                .predicates()
 +                .filter(|pred| pred_is_required(ctx, pred, used_type_params))
 +        })
 +        .peekable();
 +
 +    if predicates.peek().is_some() {
 +        Some(make::where_clause(predicates))
 +    } else {
 +        None
 +    }
 +}
 +
 +fn pred_is_required(
 +    ctx: &AssistContext<'_>,
 +    pred: &ast::WherePred,
 +    used_type_params: &[TypeParam],
 +) -> bool {
 +    match resolved_type_param(ctx, pred) {
 +        Some(it) => used_type_params.contains(&it),
 +        None => false,
 +    }
 +}
 +
 +fn resolved_type_param(ctx: &AssistContext<'_>, pred: &ast::WherePred) -> Option<TypeParam> {
 +    let path = match pred.ty()? {
 +        ast::Type::PathType(path_type) => path_type.path(),
 +        _ => None,
 +    }?;
 +
 +    match ctx.sema.resolve_path(&path)? {
 +        PathResolution::TypeParam(type_param) => Some(type_param),
 +        _ => None,
 +    }
 +}
 +
 +impl Function {
 +    /// Collect all the `TypeParam`s used in the `body` and `params`.
 +    fn type_params(&self, ctx: &AssistContext<'_>) -> Vec<TypeParam> {
 +        let type_params_in_descendant_paths =
 +            self.body.descendant_paths().filter_map(|it| match ctx.sema.resolve_path(&it) {
 +                Some(PathResolution::TypeParam(type_param)) => Some(type_param),
 +                _ => None,
 +            });
 +        let type_params_in_params = self.params.iter().filter_map(|p| p.ty.as_type_param(ctx.db()));
 +        type_params_in_descendant_paths.chain(type_params_in_params).collect()
 +    }
 +
 +    fn make_param_list(&self, ctx: &AssistContext<'_>, module: hir::Module) -> ast::ParamList {
 +        let self_param = self.self_param.clone();
 +        let params = self.params.iter().map(|param| param.to_param(ctx, module));
 +        make::param_list(self_param, params)
 +    }
 +
 +    fn make_ret_ty(&self, ctx: &AssistContext<'_>, module: hir::Module) -> Option<ast::RetType> {
 +        let fun_ty = self.return_type(ctx);
 +        let handler = if self.mods.is_in_tail {
 +            FlowHandler::None
 +        } else {
 +            FlowHandler::from_ret_ty(self, &fun_ty)
 +        };
 +        let ret_ty = match &handler {
 +            FlowHandler::None => {
 +                if matches!(fun_ty, FunType::Unit) {
 +                    return None;
 +                }
 +                fun_ty.make_ty(ctx, module)
 +            }
 +            FlowHandler::Try { kind: TryKind::Option } => {
 +                make::ext::ty_option(fun_ty.make_ty(ctx, module))
 +            }
 +            FlowHandler::Try { kind: TryKind::Result { ty: parent_ret_ty } } => {
 +                let handler_ty = parent_ret_ty
 +                    .type_arguments()
 +                    .nth(1)
 +                    .map(|ty| make_ty(&ty, ctx, module))
 +                    .unwrap_or_else(make::ty_placeholder);
 +                make::ext::ty_result(fun_ty.make_ty(ctx, module), handler_ty)
 +            }
 +            FlowHandler::If { .. } => make::ty("ControlFlow<()>"),
 +            FlowHandler::IfOption { action } => {
 +                let handler_ty = action
 +                    .expr_ty(ctx)
 +                    .map(|ty| make_ty(&ty, ctx, module))
 +                    .unwrap_or_else(make::ty_placeholder);
 +                make::ext::ty_option(handler_ty)
 +            }
 +            FlowHandler::MatchOption { .. } => make::ext::ty_option(fun_ty.make_ty(ctx, module)),
 +            FlowHandler::MatchResult { err } => {
 +                let handler_ty = err
 +                    .expr_ty(ctx)
 +                    .map(|ty| make_ty(&ty, ctx, module))
 +                    .unwrap_or_else(make::ty_placeholder);
 +                make::ext::ty_result(fun_ty.make_ty(ctx, module), handler_ty)
 +            }
 +        };
 +        Some(make::ret_type(ret_ty))
 +    }
 +}
 +
 +impl FunType {
 +    fn make_ty(&self, ctx: &AssistContext<'_>, module: hir::Module) -> ast::Type {
 +        match self {
 +            FunType::Unit => make::ty_unit(),
 +            FunType::Single(ty) => make_ty(ty, ctx, module),
 +            FunType::Tuple(types) => match types.as_slice() {
 +                [] => {
 +                    stdx::never!("tuple type with 0 elements");
 +                    make::ty_unit()
 +                }
 +                [ty] => {
 +                    stdx::never!("tuple type with 1 element");
 +                    make_ty(ty, ctx, module)
 +                }
 +                types => {
 +                    let types = types.iter().map(|ty| make_ty(ty, ctx, module));
 +                    make::ty_tuple(types)
 +                }
 +            },
 +        }
 +    }
 +}
 +
 +fn make_body(
 +    ctx: &AssistContext<'_>,
 +    old_indent: IndentLevel,
 +    new_indent: IndentLevel,
 +    fun: &Function,
 +) -> ast::BlockExpr {
 +    let ret_ty = fun.return_type(ctx);
 +    let handler = if fun.mods.is_in_tail {
 +        FlowHandler::None
 +    } else {
 +        FlowHandler::from_ret_ty(fun, &ret_ty)
 +    };
 +
 +    let block = match &fun.body {
 +        FunctionBody::Expr(expr) => {
 +            let expr = rewrite_body_segment(ctx, &fun.params, &handler, expr.syntax());
 +            let expr = ast::Expr::cast(expr).unwrap();
 +            match expr {
 +                ast::Expr::BlockExpr(block) => {
 +                    // If the extracted expression is itself a block, there is no need to wrap it inside another block.
 +                    let block = block.dedent(old_indent);
 +                    // Recreate the block for formatting consistency with other extracted functions.
 +                    make::block_expr(block.statements(), block.tail_expr())
 +                }
 +                _ => {
 +                    let expr = expr.dedent(old_indent).indent(IndentLevel(1));
 +
 +                    make::block_expr(Vec::new(), Some(expr))
 +                }
 +            }
 +        }
 +        FunctionBody::Span { parent, text_range } => {
 +            let mut elements: Vec<_> = parent
 +                .syntax()
 +                .children_with_tokens()
 +                .filter(|it| text_range.contains_range(it.text_range()))
 +                .map(|it| match &it {
 +                    syntax::NodeOrToken::Node(n) => syntax::NodeOrToken::Node(
 +                        rewrite_body_segment(ctx, &fun.params, &handler, n),
 +                    ),
 +                    _ => it,
 +                })
 +                .collect();
 +
 +            let mut tail_expr = match &elements.last() {
 +                Some(syntax::NodeOrToken::Node(node)) if ast::Expr::can_cast(node.kind()) => {
 +                    ast::Expr::cast(node.clone())
 +                }
 +                _ => None,
 +            };
 +
 +            match tail_expr {
 +                Some(_) => {
 +                    elements.pop();
 +                }
 +                None => match fun.outliving_locals.as_slice() {
 +                    [] => {}
 +                    [var] => {
 +                        tail_expr = Some(path_expr_from_local(ctx, var.local));
 +                    }
 +                    vars => {
 +                        let exprs = vars.iter().map(|var| path_expr_from_local(ctx, var.local));
 +                        let expr = make::expr_tuple(exprs);
 +                        tail_expr = Some(expr);
 +                    }
 +                },
 +            };
 +
 +            let body_indent = IndentLevel(1);
 +            let elements = elements
 +                .into_iter()
 +                .map(|node_or_token| match &node_or_token {
 +                    syntax::NodeOrToken::Node(node) => match ast::Stmt::cast(node.clone()) {
 +                        Some(stmt) => {
 +                            let indented = stmt.dedent(old_indent).indent(body_indent);
 +                            let ast_node = indented.syntax().clone_subtree();
 +                            syntax::NodeOrToken::Node(ast_node)
 +                        }
 +                        _ => node_or_token,
 +                    },
 +                    _ => node_or_token,
 +                })
 +                .collect::<Vec<SyntaxElement>>();
 +            let tail_expr = tail_expr.map(|expr| expr.dedent(old_indent).indent(body_indent));
 +
 +            make::hacky_block_expr_with_comments(elements, tail_expr)
 +        }
 +    };
 +
 +    let block = match &handler {
 +        FlowHandler::None => block,
 +        FlowHandler::Try { kind } => {
 +            let block = with_default_tail_expr(block, make::expr_unit());
 +            map_tail_expr(block, |tail_expr| {
 +                let constructor = match kind {
 +                    TryKind::Option => "Some",
 +                    TryKind::Result { .. } => "Ok",
 +                };
 +                let func = make::expr_path(make::ext::ident_path(constructor));
 +                let args = make::arg_list(iter::once(tail_expr));
 +                make::expr_call(func, args)
 +            })
 +        }
 +        FlowHandler::If { .. } => {
 +            let controlflow_continue = make::expr_call(
 +                make::expr_path(make::path_from_text("ControlFlow::Continue")),
 +                make::arg_list(iter::once(make::expr_unit())),
 +            );
 +            with_tail_expr(block, controlflow_continue)
 +        }
 +        FlowHandler::IfOption { .. } => {
 +            let none = make::expr_path(make::ext::ident_path("None"));
 +            with_tail_expr(block, none)
 +        }
 +        FlowHandler::MatchOption { .. } => map_tail_expr(block, |tail_expr| {
 +            let some = make::expr_path(make::ext::ident_path("Some"));
 +            let args = make::arg_list(iter::once(tail_expr));
 +            make::expr_call(some, args)
 +        }),
 +        FlowHandler::MatchResult { .. } => map_tail_expr(block, |tail_expr| {
 +            let ok = make::expr_path(make::ext::ident_path("Ok"));
 +            let args = make::arg_list(iter::once(tail_expr));
 +            make::expr_call(ok, args)
 +        }),
 +    };
 +
 +    block.indent(new_indent)
 +}
 +
 +fn map_tail_expr(block: ast::BlockExpr, f: impl FnOnce(ast::Expr) -> ast::Expr) -> ast::BlockExpr {
 +    let tail_expr = match block.tail_expr() {
 +        Some(tail_expr) => tail_expr,
 +        None => return block,
 +    };
 +    make::block_expr(block.statements(), Some(f(tail_expr)))
 +}
 +
 +fn with_default_tail_expr(block: ast::BlockExpr, tail_expr: ast::Expr) -> ast::BlockExpr {
 +    match block.tail_expr() {
 +        Some(_) => block,
 +        None => make::block_expr(block.statements(), Some(tail_expr)),
 +    }
 +}
 +
 +fn with_tail_expr(block: ast::BlockExpr, tail_expr: ast::Expr) -> ast::BlockExpr {
 +    let stmt_tail = block.tail_expr().map(|expr| make::expr_stmt(expr).into());
 +    let stmts = block.statements().chain(stmt_tail);
 +    make::block_expr(stmts, Some(tail_expr))
 +}
 +
 +fn format_type(ty: &hir::Type, ctx: &AssistContext<'_>, module: hir::Module) -> String {
 +    ty.display_source_code(ctx.db(), module.into()).ok().unwrap_or_else(|| "_".to_string())
 +}
 +
 +fn make_ty(ty: &hir::Type, ctx: &AssistContext<'_>, module: hir::Module) -> ast::Type {
 +    let ty_str = format_type(ty, ctx, module);
 +    make::ty(&ty_str)
 +}
 +
 +fn rewrite_body_segment(
 +    ctx: &AssistContext<'_>,
 +    params: &[Param],
 +    handler: &FlowHandler,
 +    syntax: &SyntaxNode,
 +) -> SyntaxNode {
 +    let syntax = fix_param_usages(ctx, params, syntax);
 +    update_external_control_flow(handler, &syntax);
 +    syntax
 +}
 +
 +/// change all usages to account for added `&`/`&mut` for some params
 +fn fix_param_usages(ctx: &AssistContext<'_>, params: &[Param], syntax: &SyntaxNode) -> SyntaxNode {
 +    let mut usages_for_param: Vec<(&Param, Vec<ast::Expr>)> = Vec::new();
 +
 +    let tm = TreeMutator::new(syntax);
 +
 +    for param in params {
 +        if !param.kind().is_ref() {
 +            continue;
 +        }
 +
 +        let usages = LocalUsages::find_local_usages(ctx, param.var);
 +        let usages = usages
 +            .iter()
 +            .filter(|reference| syntax.text_range().contains_range(reference.range))
 +            .filter_map(|reference| path_element_of_reference(syntax, reference))
 +            .map(|expr| tm.make_mut(&expr));
 +
 +        usages_for_param.push((param, usages.collect()));
 +    }
 +
 +    let res = tm.make_syntax_mut(syntax);
 +
 +    for (param, usages) in usages_for_param {
 +        for usage in usages {
 +            match usage.syntax().ancestors().skip(1).find_map(ast::Expr::cast) {
 +                Some(ast::Expr::MethodCallExpr(_) | ast::Expr::FieldExpr(_)) => {
 +                    // do nothing
 +                }
 +                Some(ast::Expr::RefExpr(node))
 +                    if param.kind() == ParamKind::MutRef && node.mut_token().is_some() =>
 +                {
 +                    ted::replace(node.syntax(), node.expr().unwrap().syntax());
 +                }
 +                Some(ast::Expr::RefExpr(node))
 +                    if param.kind() == ParamKind::SharedRef && node.mut_token().is_none() =>
 +                {
 +                    ted::replace(node.syntax(), node.expr().unwrap().syntax());
 +                }
 +                Some(_) | None => {
 +                    let p = &make::expr_prefix(T![*], usage.clone()).clone_for_update();
 +                    ted::replace(usage.syntax(), p.syntax())
 +                }
 +            }
 +        }
 +    }
 +
 +    res
 +}
 +
 +fn update_external_control_flow(handler: &FlowHandler, syntax: &SyntaxNode) {
 +    let mut nested_loop = None;
 +    let mut nested_scope = None;
 +    for event in syntax.preorder() {
 +        match event {
 +            WalkEvent::Enter(e) => match e.kind() {
 +                SyntaxKind::LOOP_EXPR | SyntaxKind::WHILE_EXPR | SyntaxKind::FOR_EXPR => {
 +                    if nested_loop.is_none() {
 +                        nested_loop = Some(e.clone());
 +                    }
 +                }
 +                SyntaxKind::FN
 +                | SyntaxKind::CONST
 +                | SyntaxKind::STATIC
 +                | SyntaxKind::IMPL
 +                | SyntaxKind::MODULE => {
 +                    if nested_scope.is_none() {
 +                        nested_scope = Some(e.clone());
 +                    }
 +                }
 +                _ => {}
 +            },
 +            WalkEvent::Leave(e) => {
 +                if nested_scope.is_none() {
 +                    if let Some(expr) = ast::Expr::cast(e.clone()) {
 +                        match expr {
 +                            ast::Expr::ReturnExpr(return_expr) if nested_scope.is_none() => {
 +                                let expr = return_expr.expr();
 +                                if let Some(replacement) = make_rewritten_flow(handler, expr) {
 +                                    ted::replace(return_expr.syntax(), replacement.syntax())
 +                                }
 +                            }
 +                            ast::Expr::BreakExpr(break_expr) if nested_loop.is_none() => {
 +                                let expr = break_expr.expr();
 +                                if let Some(replacement) = make_rewritten_flow(handler, expr) {
 +                                    ted::replace(break_expr.syntax(), replacement.syntax())
 +                                }
 +                            }
 +                            ast::Expr::ContinueExpr(continue_expr) if nested_loop.is_none() => {
 +                                if let Some(replacement) = make_rewritten_flow(handler, None) {
 +                                    ted::replace(continue_expr.syntax(), replacement.syntax())
 +                                }
 +                            }
 +                            _ => {
 +                                // do nothing
 +                            }
 +                        }
 +                    }
 +                }
 +
 +                if nested_loop.as_ref() == Some(&e) {
 +                    nested_loop = None;
 +                }
 +                if nested_scope.as_ref() == Some(&e) {
 +                    nested_scope = None;
 +                }
 +            }
 +        };
 +    }
 +}
 +
 +fn make_rewritten_flow(handler: &FlowHandler, arg_expr: Option<ast::Expr>) -> Option<ast::Expr> {
 +    let value = match handler {
 +        FlowHandler::None | FlowHandler::Try { .. } => return None,
 +        FlowHandler::If { .. } => make::expr_call(
 +            make::expr_path(make::path_from_text("ControlFlow::Break")),
 +            make::arg_list(iter::once(make::expr_unit())),
 +        ),
 +        FlowHandler::IfOption { .. } => {
 +            let expr = arg_expr.unwrap_or_else(|| make::expr_tuple(Vec::new()));
 +            let args = make::arg_list(iter::once(expr));
 +            make::expr_call(make::expr_path(make::ext::ident_path("Some")), args)
 +        }
 +        FlowHandler::MatchOption { .. } => make::expr_path(make::ext::ident_path("None")),
 +        FlowHandler::MatchResult { .. } => {
 +            let expr = arg_expr.unwrap_or_else(|| make::expr_tuple(Vec::new()));
 +            let args = make::arg_list(iter::once(expr));
 +            make::expr_call(make::expr_path(make::ext::ident_path("Err")), args)
 +        }
 +    };
 +    Some(make::expr_return(Some(value)).clone_for_update())
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use crate::tests::{check_assist, check_assist_not_applicable};
 +
 +    use super::*;
 +
 +    #[test]
 +    fn no_args_from_binary_expr() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() {
 +    foo($01 + 1$0);
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    foo(fun_name());
 +}
 +
 +fn $0fun_name() -> i32 {
 +    1 + 1
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn no_args_from_binary_expr_in_module() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +mod bar {
 +    fn foo() {
 +        foo($01 + 1$0);
 +    }
 +}
 +"#,
 +            r#"
 +mod bar {
 +    fn foo() {
 +        foo(fun_name());
 +    }
 +
 +    fn $0fun_name() -> i32 {
 +        1 + 1
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn no_args_from_binary_expr_indented() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() {
 +    $0{ 1 + 1 }$0;
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    fun_name();
 +}
 +
 +fn $0fun_name() -> i32 {
 +    1 + 1
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn no_args_from_stmt_with_last_expr() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() -> i32 {
 +    let k = 1;
 +    $0let m = 1;
 +    m + 1$0
 +}
 +"#,
 +            r#"
 +fn foo() -> i32 {
 +    let k = 1;
 +    fun_name()
 +}
 +
 +fn $0fun_name() -> i32 {
 +    let m = 1;
 +    m + 1
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn no_args_from_stmt_unit() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() {
 +    let k = 3;
 +    $0let m = 1;
 +    let n = m + 1;$0
 +    let g = 5;
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    let k = 3;
 +    fun_name();
 +    let g = 5;
 +}
 +
 +fn $0fun_name() {
 +    let m = 1;
 +    let n = m + 1;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn no_args_if() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() {
 +    $0if true { }$0
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    fun_name();
 +}
 +
 +fn $0fun_name() {
 +    if true { }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn no_args_if_else() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() -> i32 {
 +    $0if true { 1 } else { 2 }$0
 +}
 +"#,
 +            r#"
 +fn foo() -> i32 {
 +    fun_name()
 +}
 +
 +fn $0fun_name() -> i32 {
 +    if true { 1 } else { 2 }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn no_args_if_let_else() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() -> i32 {
 +    $0if let true = false { 1 } else { 2 }$0
 +}
 +"#,
 +            r#"
 +fn foo() -> i32 {
 +    fun_name()
 +}
 +
 +fn $0fun_name() -> i32 {
 +    if let true = false { 1 } else { 2 }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn no_args_match() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() -> i32 {
 +    $0match true {
 +        true => 1,
 +        false => 2,
 +    }$0
 +}
 +"#,
 +            r#"
 +fn foo() -> i32 {
 +    fun_name()
 +}
 +
 +fn $0fun_name() -> i32 {
 +    match true {
 +        true => 1,
 +        false => 2,
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn no_args_while() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() {
 +    $0while true { }$0
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    fun_name();
 +}
 +
 +fn $0fun_name() {
 +    while true { }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn no_args_for() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() {
 +    $0for v in &[0, 1] { }$0
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    fun_name();
 +}
 +
 +fn $0fun_name() {
 +    for v in &[0, 1] { }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn no_args_from_loop_unit() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() {
 +    $0loop {
 +        let m = 1;
 +    }$0
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    fun_name()
 +}
 +
 +fn $0fun_name() -> ! {
 +    loop {
 +        let m = 1;
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn no_args_from_loop_with_return() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() {
 +    let v = $0loop {
 +        let m = 1;
 +        break m;
 +    }$0;
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    let v = fun_name();
 +}
 +
 +fn $0fun_name() -> i32 {
 +    loop {
 +        let m = 1;
 +        break m;
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn no_args_from_match() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() {
 +    let v: i32 = $0match Some(1) {
 +        Some(x) => x,
 +        None => 0,
 +    }$0;
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    let v: i32 = fun_name();
 +}
 +
 +fn $0fun_name() -> i32 {
 +    match Some(1) {
 +        Some(x) => x,
 +        None => 0,
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn extract_partial_block_single_line() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() {
 +    let n = 1;
 +    let mut v = $0n * n;$0
 +    v += 1;
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    let n = 1;
 +    let mut v = fun_name(n);
 +    v += 1;
 +}
 +
 +fn $0fun_name(n: i32) -> i32 {
 +    let mut v = n * n;
 +    v
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn extract_partial_block() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() {
 +    let m = 2;
 +    let n = 1;
 +    let mut v = m $0* n;
 +    let mut w = 3;$0
 +    v += 1;
 +    w += 1;
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    let m = 2;
 +    let n = 1;
 +    let (mut v, mut w) = fun_name(m, n);
 +    v += 1;
 +    w += 1;
 +}
 +
 +fn $0fun_name(m: i32, n: i32) -> (i32, i32) {
 +    let mut v = m * n;
 +    let mut w = 3;
 +    (v, w)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn argument_form_expr() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() -> u32 {
 +    let n = 2;
 +    $0n+2$0
 +}
 +"#,
 +            r#"
 +fn foo() -> u32 {
 +    let n = 2;
 +    fun_name(n)
 +}
 +
 +fn $0fun_name(n: u32) -> u32 {
 +    n+2
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn argument_used_twice_form_expr() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() -> u32 {
 +    let n = 2;
 +    $0n+n$0
 +}
 +"#,
 +            r#"
 +fn foo() -> u32 {
 +    let n = 2;
 +    fun_name(n)
 +}
 +
 +fn $0fun_name(n: u32) -> u32 {
 +    n+n
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn two_arguments_form_expr() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() -> u32 {
 +    let n = 2;
 +    let m = 3;
 +    $0n+n*m$0
 +}
 +"#,
 +            r#"
 +fn foo() -> u32 {
 +    let n = 2;
 +    let m = 3;
 +    fun_name(n, m)
 +}
 +
 +fn $0fun_name(n: u32, m: u32) -> u32 {
 +    n+n*m
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn argument_and_locals() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() -> u32 {
 +    let n = 2;
 +    $0let m = 1;
 +    n + m$0
 +}
 +"#,
 +            r#"
 +fn foo() -> u32 {
 +    let n = 2;
 +    fun_name(n)
 +}
 +
 +fn $0fun_name(n: u32) -> u32 {
 +    let m = 1;
 +    n + m
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn in_comment_is_not_applicable() {
 +        cov_mark::check!(extract_function_in_comment_is_not_applicable);
 +        check_assist_not_applicable(extract_function, r"fn main() { 1 + /* $0comment$0 */ 1; }");
 +    }
 +
 +    #[test]
 +    fn part_of_expr_stmt() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() {
 +    $01$0 + 1;
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    fun_name() + 1;
 +}
 +
 +fn $0fun_name() -> i32 {
 +    1
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn function_expr() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() {
 +    $0bar(1 + 1)$0
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    fun_name();
 +}
 +
 +fn $0fun_name() {
 +    bar(1 + 1)
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn extract_from_nested() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn main() {
 +    let x = true;
 +    let tuple = match x {
 +        true => ($02 + 2$0, true)
 +        _ => (0, false)
 +    };
 +}
 +"#,
 +            r#"
 +fn main() {
 +    let x = true;
 +    let tuple = match x {
 +        true => (fun_name(), true)
 +        _ => (0, false)
 +    };
 +}
 +
 +fn $0fun_name() -> i32 {
 +    2 + 2
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn param_from_closure() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn main() {
 +    let lambda = |x: u32| $0x * 2$0;
 +}
 +"#,
 +            r#"
 +fn main() {
 +    let lambda = |x: u32| fun_name(x);
 +}
 +
 +fn $0fun_name(x: u32) -> u32 {
 +    x * 2
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn extract_return_stmt() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() -> u32 {
 +    $0return 2 + 2$0;
 +}
 +"#,
 +            r#"
 +fn foo() -> u32 {
 +    return fun_name();
 +}
 +
 +fn $0fun_name() -> u32 {
 +    2 + 2
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn does_not_add_extra_whitespace() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() -> u32 {
 +
 +
 +    $0return 2 + 2$0;
 +}
 +"#,
 +            r#"
 +fn foo() -> u32 {
 +
 +
 +    return fun_name();
 +}
 +
 +fn $0fun_name() -> u32 {
 +    2 + 2
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn break_stmt() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn main() {
 +    let result = loop {
 +        $0break 2 + 2$0;
 +    };
 +}
 +"#,
 +            r#"
 +fn main() {
 +    let result = loop {
 +        break fun_name();
 +    };
 +}
 +
 +fn $0fun_name() -> i32 {
 +    2 + 2
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn extract_cast() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn main() {
 +    let v = $00f32 as u32$0;
 +}
 +"#,
 +            r#"
 +fn main() {
 +    let v = fun_name();
 +}
 +
 +fn $0fun_name() -> u32 {
 +    0f32 as u32
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn return_not_applicable() {
 +        check_assist_not_applicable(extract_function, r"fn foo() { $0return$0; } ");
 +    }
 +
 +    #[test]
 +    fn method_to_freestanding() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +struct S;
 +
 +impl S {
 +    fn foo(&self) -> i32 {
 +        $01+1$0
 +    }
 +}
 +"#,
 +            r#"
 +struct S;
 +
 +impl S {
 +    fn foo(&self) -> i32 {
 +        fun_name()
 +    }
 +}
 +
 +fn $0fun_name() -> i32 {
 +    1+1
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn method_with_reference() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +struct S { f: i32 };
 +
 +impl S {
 +    fn foo(&self) -> i32 {
 +        $0self.f+self.f$0
 +    }
 +}
 +"#,
 +            r#"
 +struct S { f: i32 };
 +
 +impl S {
 +    fn foo(&self) -> i32 {
 +        self.fun_name()
 +    }
 +
 +    fn $0fun_name(&self) -> i32 {
 +        self.f+self.f
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn method_with_mut() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +struct S { f: i32 };
 +
 +impl S {
 +    fn foo(&mut self) {
 +        $0self.f += 1;$0
 +    }
 +}
 +"#,
 +            r#"
 +struct S { f: i32 };
 +
 +impl S {
 +    fn foo(&mut self) {
 +        self.fun_name();
 +    }
 +
 +    fn $0fun_name(&mut self) {
 +        self.f += 1;
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn variable_defined_inside_and_used_after_no_ret() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() {
 +    let n = 1;
 +    $0let k = n * n;$0
 +    let m = k + 1;
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    let n = 1;
 +    let k = fun_name(n);
 +    let m = k + 1;
 +}
 +
 +fn $0fun_name(n: i32) -> i32 {
 +    let k = n * n;
 +    k
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn variable_defined_inside_and_used_after_mutably_no_ret() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() {
 +    let n = 1;
 +    $0let mut k = n * n;$0
 +    k += 1;
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    let n = 1;
 +    let mut k = fun_name(n);
 +    k += 1;
 +}
 +
 +fn $0fun_name(n: i32) -> i32 {
 +    let mut k = n * n;
 +    k
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn two_variables_defined_inside_and_used_after_no_ret() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() {
 +    let n = 1;
 +    $0let k = n * n;
 +    let m = k + 2;$0
 +    let h = k + m;
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    let n = 1;
 +    let (k, m) = fun_name(n);
 +    let h = k + m;
 +}
 +
 +fn $0fun_name(n: i32) -> (i32, i32) {
 +    let k = n * n;
 +    let m = k + 2;
 +    (k, m)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn multi_variables_defined_inside_and_used_after_mutably_no_ret() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() {
 +    let n = 1;
 +    $0let mut k = n * n;
 +    let mut m = k + 2;
 +    let mut o = m + 3;
 +    o += 1;$0
 +    k += o;
 +    m = 1;
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    let n = 1;
 +    let (mut k, mut m, o) = fun_name(n);
 +    k += o;
 +    m = 1;
 +}
 +
 +fn $0fun_name(n: i32) -> (i32, i32, i32) {
 +    let mut k = n * n;
 +    let mut m = k + 2;
 +    let mut o = m + 3;
 +    o += 1;
 +    (k, m, o)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn nontrivial_patterns_define_variables() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +struct Counter(i32);
 +fn foo() {
 +    $0let Counter(n) = Counter(0);$0
 +    let m = n;
 +}
 +"#,
 +            r#"
 +struct Counter(i32);
 +fn foo() {
 +    let n = fun_name();
 +    let m = n;
 +}
 +
 +fn $0fun_name() -> i32 {
 +    let Counter(n) = Counter(0);
 +    n
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn struct_with_two_fields_pattern_define_variables() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +struct Counter { n: i32, m: i32 };
 +fn foo() {
 +    $0let Counter { n, m: k } = Counter { n: 1, m: 2 };$0
 +    let h = n + k;
 +}
 +"#,
 +            r#"
 +struct Counter { n: i32, m: i32 };
 +fn foo() {
 +    let (n, k) = fun_name();
 +    let h = n + k;
 +}
 +
 +fn $0fun_name() -> (i32, i32) {
 +    let Counter { n, m: k } = Counter { n: 1, m: 2 };
 +    (n, k)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn mut_var_from_outer_scope() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() {
 +    let mut n = 1;
 +    $0n += 1;$0
 +    let m = n + 1;
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    let mut n = 1;
 +    fun_name(&mut n);
 +    let m = n + 1;
 +}
 +
 +fn $0fun_name(n: &mut i32) {
 +    *n += 1;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn mut_field_from_outer_scope() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +struct C { n: i32 }
 +fn foo() {
 +    let mut c = C { n: 0 };
 +    $0c.n += 1;$0
 +    let m = c.n + 1;
 +}
 +"#,
 +            r#"
 +struct C { n: i32 }
 +fn foo() {
 +    let mut c = C { n: 0 };
 +    fun_name(&mut c);
 +    let m = c.n + 1;
 +}
 +
 +fn $0fun_name(c: &mut C) {
 +    c.n += 1;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn mut_nested_field_from_outer_scope() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +struct P { n: i32}
 +struct C { p: P }
 +fn foo() {
 +    let mut c = C { p: P { n: 0 } };
 +    let mut v = C { p: P { n: 0 } };
 +    let u = C { p: P { n: 0 } };
 +    $0c.p.n += u.p.n;
 +    let r = &mut v.p.n;$0
 +    let m = c.p.n + v.p.n + u.p.n;
 +}
 +"#,
 +            r#"
 +struct P { n: i32}
 +struct C { p: P }
 +fn foo() {
 +    let mut c = C { p: P { n: 0 } };
 +    let mut v = C { p: P { n: 0 } };
 +    let u = C { p: P { n: 0 } };
 +    fun_name(&mut c, &u, &mut v);
 +    let m = c.p.n + v.p.n + u.p.n;
 +}
 +
 +fn $0fun_name(c: &mut C, u: &C, v: &mut C) {
 +    c.p.n += u.p.n;
 +    let r = &mut v.p.n;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn mut_param_many_usages_stmt() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn bar(k: i32) {}
 +trait I: Copy {
 +    fn succ(&self) -> Self;
 +    fn inc(&mut self) -> Self { let v = self.succ(); *self = v; v }
 +}
 +impl I for i32 {
 +    fn succ(&self) -> Self { *self + 1 }
 +}
 +fn foo() {
 +    let mut n = 1;
 +    $0n += n;
 +    bar(n);
 +    bar(n+1);
 +    bar(n*n);
 +    bar(&n);
 +    n.inc();
 +    let v = &mut n;
 +    *v = v.succ();
 +    n.succ();$0
 +    let m = n + 1;
 +}
 +"#,
 +            r#"
 +fn bar(k: i32) {}
 +trait I: Copy {
 +    fn succ(&self) -> Self;
 +    fn inc(&mut self) -> Self { let v = self.succ(); *self = v; v }
 +}
 +impl I for i32 {
 +    fn succ(&self) -> Self { *self + 1 }
 +}
 +fn foo() {
 +    let mut n = 1;
 +    fun_name(&mut n);
 +    let m = n + 1;
 +}
 +
 +fn $0fun_name(n: &mut i32) {
 +    *n += *n;
 +    bar(*n);
 +    bar(*n+1);
 +    bar(*n**n);
 +    bar(&*n);
 +    n.inc();
 +    let v = n;
 +    *v = v.succ();
 +    n.succ();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn mut_param_many_usages_expr() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn bar(k: i32) {}
 +trait I: Copy {
 +    fn succ(&self) -> Self;
 +    fn inc(&mut self) -> Self { let v = self.succ(); *self = v; v }
 +}
 +impl I for i32 {
 +    fn succ(&self) -> Self { *self + 1 }
 +}
 +fn foo() {
 +    let mut n = 1;
 +    $0{
 +        n += n;
 +        bar(n);
 +        bar(n+1);
 +        bar(n*n);
 +        bar(&n);
 +        n.inc();
 +        let v = &mut n;
 +        *v = v.succ();
 +        n.succ();
 +    }$0
 +    let m = n + 1;
 +}
 +"#,
 +            r#"
 +fn bar(k: i32) {}
 +trait I: Copy {
 +    fn succ(&self) -> Self;
 +    fn inc(&mut self) -> Self { let v = self.succ(); *self = v; v }
 +}
 +impl I for i32 {
 +    fn succ(&self) -> Self { *self + 1 }
 +}
 +fn foo() {
 +    let mut n = 1;
 +    fun_name(&mut n);
 +    let m = n + 1;
 +}
 +
 +fn $0fun_name(n: &mut i32) {
 +    *n += *n;
 +    bar(*n);
 +    bar(*n+1);
 +    bar(*n**n);
 +    bar(&*n);
 +    n.inc();
 +    let v = n;
 +    *v = v.succ();
 +    n.succ();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn mut_param_by_value() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() {
 +    let mut n = 1;
 +    $0n += 1;$0
 +}
 +"#,
 +            r"
 +fn foo() {
 +    let mut n = 1;
 +    fun_name(n);
 +}
 +
 +fn $0fun_name(mut n: i32) {
 +    n += 1;
 +}
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn mut_param_because_of_mut_ref() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() {
 +    let mut n = 1;
 +    $0let v = &mut n;
 +    *v += 1;$0
 +    let k = n;
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    let mut n = 1;
 +    fun_name(&mut n);
 +    let k = n;
 +}
 +
 +fn $0fun_name(n: &mut i32) {
 +    let v = n;
 +    *v += 1;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn mut_param_by_value_because_of_mut_ref() {
 +        check_assist(
 +            extract_function,
 +            r"
 +fn foo() {
 +    let mut n = 1;
 +    $0let v = &mut n;
 +    *v += 1;$0
 +}
 +",
 +            r#"
 +fn foo() {
 +    let mut n = 1;
 +    fun_name(n);
 +}
 +
 +fn $0fun_name(mut n: i32) {
 +    let v = &mut n;
 +    *v += 1;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn mut_method_call() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +trait I {
 +    fn inc(&mut self);
 +}
 +impl I for i32 {
 +    fn inc(&mut self) { *self += 1 }
 +}
 +fn foo() {
 +    let mut n = 1;
 +    $0n.inc();$0
 +}
 +"#,
 +            r#"
 +trait I {
 +    fn inc(&mut self);
 +}
 +impl I for i32 {
 +    fn inc(&mut self) { *self += 1 }
 +}
 +fn foo() {
 +    let mut n = 1;
 +    fun_name(n);
 +}
 +
 +fn $0fun_name(mut n: i32) {
 +    n.inc();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn shared_method_call() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +trait I {
 +    fn succ(&self);
 +}
 +impl I for i32 {
 +    fn succ(&self) { *self + 1 }
 +}
 +fn foo() {
 +    let mut n = 1;
 +    $0n.succ();$0
 +}
 +"#,
 +            r"
 +trait I {
 +    fn succ(&self);
 +}
 +impl I for i32 {
 +    fn succ(&self) { *self + 1 }
 +}
 +fn foo() {
 +    let mut n = 1;
 +    fun_name(n);
 +}
 +
 +fn $0fun_name(n: i32) {
 +    n.succ();
 +}
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn mut_method_call_with_other_receiver() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +trait I {
 +    fn inc(&mut self, n: i32);
 +}
 +impl I for i32 {
 +    fn inc(&mut self, n: i32) { *self += n }
 +}
 +fn foo() {
 +    let mut n = 1;
 +    $0let mut m = 2;
 +    m.inc(n);$0
 +}
 +"#,
 +            r"
 +trait I {
 +    fn inc(&mut self, n: i32);
 +}
 +impl I for i32 {
 +    fn inc(&mut self, n: i32) { *self += n }
 +}
 +fn foo() {
 +    let mut n = 1;
 +    fun_name(n);
 +}
 +
 +fn $0fun_name(n: i32) {
 +    let mut m = 2;
 +    m.inc(n);
 +}
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn non_copy_without_usages_after() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +struct Counter(i32);
 +fn foo() {
 +    let c = Counter(0);
 +    $0let n = c.0;$0
 +}
 +"#,
 +            r"
 +struct Counter(i32);
 +fn foo() {
 +    let c = Counter(0);
 +    fun_name(c);
 +}
 +
 +fn $0fun_name(c: Counter) {
 +    let n = c.0;
 +}
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn non_copy_used_after() {
 +        check_assist(
 +            extract_function,
 +            r"
 +struct Counter(i32);
 +fn foo() {
 +    let c = Counter(0);
 +    $0let n = c.0;$0
 +    let m = c.0;
 +}
 +",
 +            r#"
 +struct Counter(i32);
 +fn foo() {
 +    let c = Counter(0);
 +    fun_name(&c);
 +    let m = c.0;
 +}
 +
 +fn $0fun_name(c: &Counter) {
 +    let n = c.0;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn copy_used_after() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +//- minicore: copy
 +fn foo() {
 +    let n = 0;
 +    $0let m = n;$0
 +    let k = n;
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    let n = 0;
 +    fun_name(n);
 +    let k = n;
 +}
 +
 +fn $0fun_name(n: i32) {
 +    let m = n;
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn copy_custom_used_after() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +//- minicore: copy, derive
 +#[derive(Clone, Copy)]
 +struct Counter(i32);
 +fn foo() {
 +    let c = Counter(0);
 +    $0let n = c.0;$0
 +    let m = c.0;
 +}
 +"#,
 +            r#"
 +#[derive(Clone, Copy)]
 +struct Counter(i32);
 +fn foo() {
 +    let c = Counter(0);
 +    fun_name(c);
 +    let m = c.0;
 +}
 +
 +fn $0fun_name(c: Counter) {
 +    let n = c.0;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn indented_stmts() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() {
 +    if true {
 +        loop {
 +            $0let n = 1;
 +            let m = 2;$0
 +        }
 +    }
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    if true {
 +        loop {
 +            fun_name();
 +        }
 +    }
 +}
 +
 +fn $0fun_name() {
 +    let n = 1;
 +    let m = 2;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn indented_stmts_inside_mod() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +mod bar {
 +    fn foo() {
 +        if true {
 +            loop {
 +                $0let n = 1;
 +                let m = 2;$0
 +            }
 +        }
 +    }
 +}
 +"#,
 +            r#"
 +mod bar {
 +    fn foo() {
 +        if true {
 +            loop {
 +                fun_name();
 +            }
 +        }
 +    }
 +
 +    fn $0fun_name() {
 +        let n = 1;
 +        let m = 2;
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn break_loop() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +//- minicore: option
 +fn foo() {
 +    loop {
 +        let n = 1;
 +        $0let m = n + 1;
 +        break;
 +        let k = 2;$0
 +        let h = 1 + k;
 +    }
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    loop {
 +        let n = 1;
 +        let k = match fun_name(n) {
 +            Some(value) => value,
 +            None => break,
 +        };
 +        let h = 1 + k;
 +    }
 +}
 +
 +fn $0fun_name(n: i32) -> Option<i32> {
 +    let m = n + 1;
 +    return None;
 +    let k = 2;
 +    Some(k)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn return_to_parent() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +//- minicore: copy, result
 +fn foo() -> i64 {
 +    let n = 1;
 +    $0let m = n + 1;
 +    return 1;
 +    let k = 2;$0
 +    (n + k) as i64
 +}
 +"#,
 +            r#"
 +fn foo() -> i64 {
 +    let n = 1;
 +    let k = match fun_name(n) {
 +        Ok(value) => value,
 +        Err(value) => return value,
 +    };
 +    (n + k) as i64
 +}
 +
 +fn $0fun_name(n: i32) -> Result<i32, i64> {
 +    let m = n + 1;
 +    return Err(1);
 +    let k = 2;
 +    Ok(k)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn break_and_continue() {
 +        cov_mark::check!(external_control_flow_break_and_continue);
 +        check_assist_not_applicable(
 +            extract_function,
 +            r#"
 +fn foo() {
 +    loop {
 +        let n = 1;
 +        $0let m = n + 1;
 +        break;
 +        let k = 2;
 +        continue;
 +        let k = k + 1;$0
 +        let r = n + k;
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn return_and_break() {
 +        cov_mark::check!(external_control_flow_return_and_bc);
 +        check_assist_not_applicable(
 +            extract_function,
 +            r#"
 +fn foo() {
 +    loop {
 +        let n = 1;
 +        $0let m = n + 1;
 +        break;
 +        let k = 2;
 +        return;
 +        let k = k + 1;$0
 +        let r = n + k;
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn break_loop_with_if() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +//- minicore: try
 +fn foo() {
 +    loop {
 +        let mut n = 1;
 +        $0let m = n + 1;
 +        break;
 +        n += m;$0
 +        let h = 1 + n;
 +    }
 +}
 +"#,
 +            r#"
 +use core::ops::ControlFlow;
 +
 +fn foo() {
 +    loop {
 +        let mut n = 1;
 +        if let ControlFlow::Break(_) = fun_name(&mut n) {
 +            break;
 +        }
 +        let h = 1 + n;
 +    }
 +}
 +
 +fn $0fun_name(n: &mut i32) -> ControlFlow<()> {
 +    let m = *n + 1;
 +    return ControlFlow::Break(());
 +    *n += m;
 +    ControlFlow::Continue(())
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn break_loop_nested() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +//- minicore: try
 +fn foo() {
 +    loop {
 +        let mut n = 1;
 +        $0let m = n + 1;
 +        if m == 42 {
 +            break;
 +        }$0
 +        let h = 1;
 +    }
 +}
 +"#,
 +            r#"
 +use core::ops::ControlFlow;
 +
 +fn foo() {
 +    loop {
 +        let mut n = 1;
 +        if let ControlFlow::Break(_) = fun_name(n) {
 +            break;
 +        }
 +        let h = 1;
 +    }
 +}
 +
 +fn $0fun_name(n: i32) -> ControlFlow<()> {
 +    let m = n + 1;
 +    if m == 42 {
 +        return ControlFlow::Break(());
 +    }
 +    ControlFlow::Continue(())
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn break_loop_nested_labeled() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +//- minicore: try
 +fn foo() {
 +    'bar: loop {
 +        loop {
 +            $0break 'bar;$0
 +        }
 +    }
 +}
 +"#,
 +            r#"
 +use core::ops::ControlFlow;
 +
 +fn foo() {
 +    'bar: loop {
 +        loop {
 +            if let ControlFlow::Break(_) = fun_name() {
 +                break 'bar;
 +            }
 +        }
 +    }
 +}
 +
 +fn $0fun_name() -> ControlFlow<()> {
 +    return ControlFlow::Break(());
 +    ControlFlow::Continue(())
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn continue_loop_nested_labeled() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +//- minicore: try
 +fn foo() {
 +    'bar: loop {
 +        loop {
 +            $0continue 'bar;$0
 +        }
 +    }
 +}
 +"#,
 +            r#"
 +use core::ops::ControlFlow;
 +
 +fn foo() {
 +    'bar: loop {
 +        loop {
 +            if let ControlFlow::Break(_) = fun_name() {
 +                continue 'bar;
 +            }
 +        }
 +    }
 +}
 +
 +fn $0fun_name() -> ControlFlow<()> {
 +    return ControlFlow::Break(());
 +    ControlFlow::Continue(())
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn return_from_nested_loop() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() {
 +    loop {
 +        let n = 1;$0
 +        let k = 1;
 +        loop {
 +            return;
 +        }
 +        let m = k + 1;$0
 +        let h = 1 + m;
 +    }
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    loop {
 +        let n = 1;
 +        let m = match fun_name() {
 +            Some(value) => value,
 +            None => return,
 +        };
 +        let h = 1 + m;
 +    }
 +}
 +
 +fn $0fun_name() -> Option<i32> {
 +    let k = 1;
 +    loop {
 +        return None;
 +    }
 +    let m = k + 1;
 +    Some(m)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn break_from_nested_loop() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() {
 +    loop {
 +        let n = 1;
 +        $0let k = 1;
 +        loop {
 +            break;
 +        }
 +        let m = k + 1;$0
 +        let h = 1 + m;
 +    }
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    loop {
 +        let n = 1;
 +        let m = fun_name();
 +        let h = 1 + m;
 +    }
 +}
 +
 +fn $0fun_name() -> i32 {
 +    let k = 1;
 +    loop {
 +        break;
 +    }
 +    let m = k + 1;
 +    m
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn break_from_nested_and_outer_loops() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() {
 +    loop {
 +        let n = 1;
 +        $0let k = 1;
 +        loop {
 +            break;
 +        }
 +        if k == 42 {
 +            break;
 +        }
 +        let m = k + 1;$0
 +        let h = 1 + m;
 +    }
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    loop {
 +        let n = 1;
 +        let m = match fun_name() {
 +            Some(value) => value,
 +            None => break,
 +        };
 +        let h = 1 + m;
 +    }
 +}
 +
 +fn $0fun_name() -> Option<i32> {
 +    let k = 1;
 +    loop {
 +        break;
 +    }
 +    if k == 42 {
 +        return None;
 +    }
 +    let m = k + 1;
 +    Some(m)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn return_from_nested_fn() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() {
 +    loop {
 +        let n = 1;
 +        $0let k = 1;
 +        fn test() {
 +            return;
 +        }
 +        let m = k + 1;$0
 +        let h = 1 + m;
 +    }
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    loop {
 +        let n = 1;
 +        let m = fun_name();
 +        let h = 1 + m;
 +    }
 +}
 +
 +fn $0fun_name() -> i32 {
 +    let k = 1;
 +    fn test() {
 +        return;
 +    }
 +    let m = k + 1;
 +    m
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn break_with_value() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() -> i32 {
 +    loop {
 +        let n = 1;
 +        $0let k = 1;
 +        if k == 42 {
 +            break 3;
 +        }
 +        let m = k + 1;$0
 +        let h = 1;
 +    }
 +}
 +"#,
 +            r#"
 +fn foo() -> i32 {
 +    loop {
 +        let n = 1;
 +        if let Some(value) = fun_name() {
 +            break value;
 +        }
 +        let h = 1;
 +    }
 +}
 +
 +fn $0fun_name() -> Option<i32> {
 +    let k = 1;
 +    if k == 42 {
 +        return Some(3);
 +    }
 +    let m = k + 1;
 +    None
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn break_with_value_and_label() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() -> i32 {
 +    'bar: loop {
 +        let n = 1;
 +        $0let k = 1;
 +        if k == 42 {
 +            break 'bar 4;
 +        }
 +        let m = k + 1;$0
 +        let h = 1;
 +    }
 +}
 +"#,
 +            r#"
 +fn foo() -> i32 {
 +    'bar: loop {
 +        let n = 1;
 +        if let Some(value) = fun_name() {
 +            break 'bar value;
 +        }
 +        let h = 1;
 +    }
 +}
 +
 +fn $0fun_name() -> Option<i32> {
 +    let k = 1;
 +    if k == 42 {
 +        return Some(4);
 +    }
 +    let m = k + 1;
 +    None
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn break_with_value_and_return() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() -> i64 {
 +    loop {
 +        let n = 1;$0
 +        let k = 1;
 +        if k == 42 {
 +            break 3;
 +        }
 +        let m = k + 1;$0
 +        let h = 1 + m;
 +    }
 +}
 +"#,
 +            r#"
 +fn foo() -> i64 {
 +    loop {
 +        let n = 1;
 +        let m = match fun_name() {
 +            Ok(value) => value,
 +            Err(value) => break value,
 +        };
 +        let h = 1 + m;
 +    }
 +}
 +
 +fn $0fun_name() -> Result<i32, i64> {
 +    let k = 1;
 +    if k == 42 {
 +        return Err(3);
 +    }
 +    let m = k + 1;
 +    Ok(m)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn try_option() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +//- minicore: option
 +fn bar() -> Option<i32> { None }
 +fn foo() -> Option<()> {
 +    let n = bar()?;
 +    $0let k = foo()?;
 +    let m = k + 1;$0
 +    let h = 1 + m;
 +    Some(())
 +}
 +"#,
 +            r#"
 +fn bar() -> Option<i32> { None }
 +fn foo() -> Option<()> {
 +    let n = bar()?;
 +    let m = fun_name()?;
 +    let h = 1 + m;
 +    Some(())
 +}
 +
 +fn $0fun_name() -> Option<i32> {
 +    let k = foo()?;
 +    let m = k + 1;
 +    Some(m)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn try_option_unit() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +//- minicore: option
 +fn foo() -> Option<()> {
 +    let n = 1;
 +    $0let k = foo()?;
 +    let m = k + 1;$0
 +    let h = 1 + n;
 +    Some(())
 +}
 +"#,
 +            r#"
 +fn foo() -> Option<()> {
 +    let n = 1;
 +    fun_name()?;
 +    let h = 1 + n;
 +    Some(())
 +}
 +
 +fn $0fun_name() -> Option<()> {
 +    let k = foo()?;
 +    let m = k + 1;
 +    Some(())
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn try_result() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +//- minicore: result
 +fn foo() -> Result<(), i64> {
 +    let n = 1;
 +    $0let k = foo()?;
 +    let m = k + 1;$0
 +    let h = 1 + m;
 +    Ok(())
 +}
 +"#,
 +            r#"
 +fn foo() -> Result<(), i64> {
 +    let n = 1;
 +    let m = fun_name()?;
 +    let h = 1 + m;
 +    Ok(())
 +}
 +
 +fn $0fun_name() -> Result<i32, i64> {
 +    let k = foo()?;
 +    let m = k + 1;
 +    Ok(m)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn try_option_with_return() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +//- minicore: option
 +fn foo() -> Option<()> {
 +    let n = 1;
 +    $0let k = foo()?;
 +    if k == 42 {
 +        return None;
 +    }
 +    let m = k + 1;$0
 +    let h = 1 + m;
 +    Some(())
 +}
 +"#,
 +            r#"
 +fn foo() -> Option<()> {
 +    let n = 1;
 +    let m = fun_name()?;
 +    let h = 1 + m;
 +    Some(())
 +}
 +
 +fn $0fun_name() -> Option<i32> {
 +    let k = foo()?;
 +    if k == 42 {
 +        return None;
 +    }
 +    let m = k + 1;
 +    Some(m)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn try_result_with_return() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +//- minicore: result
 +fn foo() -> Result<(), i64> {
 +    let n = 1;
 +    $0let k = foo()?;
 +    if k == 42 {
 +        return Err(1);
 +    }
 +    let m = k + 1;$0
 +    let h = 1 + m;
 +    Ok(())
 +}
 +"#,
 +            r#"
 +fn foo() -> Result<(), i64> {
 +    let n = 1;
 +    let m = fun_name()?;
 +    let h = 1 + m;
 +    Ok(())
 +}
 +
 +fn $0fun_name() -> Result<i32, i64> {
 +    let k = foo()?;
 +    if k == 42 {
 +        return Err(1);
 +    }
 +    let m = k + 1;
 +    Ok(m)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn try_and_break() {
 +        cov_mark::check!(external_control_flow_try_and_bc);
 +        check_assist_not_applicable(
 +            extract_function,
 +            r#"
 +//- minicore: option
 +fn foo() -> Option<()> {
 +    loop {
 +        let n = Some(1);
 +        $0let m = n? + 1;
 +        break;
 +        let k = 2;
 +        let k = k + 1;$0
 +        let r = n + k;
 +    }
 +    Some(())
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn try_and_return_ok() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +//- minicore: result
 +fn foo() -> Result<(), i64> {
 +    let n = 1;
 +    $0let k = foo()?;
 +    if k == 42 {
 +        return Ok(1);
 +    }
 +    let m = k + 1;$0
 +    let h = 1 + m;
 +    Ok(())
 +}
 +"#,
 +            r#"
 +fn foo() -> Result<(), i64> {
 +    let n = 1;
 +    let m = fun_name()?;
 +    let h = 1 + m;
 +    Ok(())
 +}
 +
 +fn $0fun_name() -> Result<i32, i64> {
 +    let k = foo()?;
 +    if k == 42 {
 +        return Ok(1);
 +    }
 +    let m = k + 1;
 +    Ok(m)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn param_usage_in_macro() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +macro_rules! m {
 +    ($val:expr) => { $val };
 +}
 +
 +fn foo() {
 +    let n = 1;
 +    $0let k = n * m!(n);$0
 +    let m = k + 1;
 +}
 +"#,
 +            r#"
 +macro_rules! m {
 +    ($val:expr) => { $val };
 +}
 +
 +fn foo() {
 +    let n = 1;
 +    let k = fun_name(n);
 +    let m = k + 1;
 +}
 +
 +fn $0fun_name(n: i32) -> i32 {
 +    let k = n * m!(n);
 +    k
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn extract_with_await() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +//- minicore: future
 +fn main() {
 +    $0some_function().await;$0
 +}
 +
 +async fn some_function() {
 +
 +}
 +"#,
 +            r#"
 +fn main() {
 +    fun_name().await;
 +}
 +
 +async fn $0fun_name() {
 +    some_function().await;
 +}
 +
 +async fn some_function() {
 +
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn extract_with_await_and_result_not_producing_match_expr() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +//- minicore: future, result
 +async fn foo() -> Result<(), ()> {
 +    $0async {}.await;
 +    Err(())?$0
 +}
 +"#,
 +            r#"
 +async fn foo() -> Result<(), ()> {
 +    fun_name().await?
 +}
 +
 +async fn $0fun_name() -> Result<(), ()> {
 +    async {}.await;
 +    Err(())?
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn extract_with_await_and_result_producing_match_expr() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +//- minicore: future
 +async fn foo() -> i32 {
 +    loop {
 +        let n = 1;$0
 +        let k = async { 1 }.await;
 +        if k == 42 {
 +            break 3;
 +        }
 +        let m = k + 1;$0
 +        let h = 1 + m;
 +    }
 +}
 +"#,
 +            r#"
 +async fn foo() -> i32 {
 +    loop {
 +        let n = 1;
 +        let m = match fun_name().await {
 +            Ok(value) => value,
 +            Err(value) => break value,
 +        };
 +        let h = 1 + m;
 +    }
 +}
 +
 +async fn $0fun_name() -> Result<i32, i32> {
 +    let k = async { 1 }.await;
 +    if k == 42 {
 +        return Err(3);
 +    }
 +    let m = k + 1;
 +    Ok(m)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn extract_with_await_in_args() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +//- minicore: future
 +fn main() {
 +    $0function_call("a", some_function().await);$0
 +}
 +
 +async fn some_function() {
 +
 +}
 +"#,
 +            r#"
 +fn main() {
 +    fun_name().await;
 +}
 +
 +async fn $0fun_name() {
 +    function_call("a", some_function().await);
 +}
 +
 +async fn some_function() {
 +
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn extract_does_not_extract_standalone_blocks() {
 +        check_assist_not_applicable(
 +            extract_function,
 +            r#"
 +fn main() $0{}$0
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn extract_adds_comma_for_match_arm() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn main() {
 +    match 6 {
 +        100 => $0{ 100 }$0
 +        _ => 0,
 +    };
 +}
 +"#,
 +            r#"
 +fn main() {
 +    match 6 {
 +        100 => fun_name(),
 +        _ => 0,
 +    };
 +}
 +
 +fn $0fun_name() -> i32 {
 +    100
 +}
 +"#,
 +        );
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn main() {
 +    match 6 {
 +        100 => $0{ 100 }$0,
 +        _ => 0,
 +    };
 +}
 +"#,
 +            r#"
 +fn main() {
 +    match 6 {
 +        100 => fun_name(),
 +        _ => 0,
 +    };
 +}
 +
 +fn $0fun_name() -> i32 {
 +    100
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn extract_does_not_tear_comments_apart() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() {
 +    /*$0*/
 +    foo();
 +    foo();
 +    /*$0*/
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    fun_name();
 +}
 +
 +fn $0fun_name() {
 +    /**/
 +    foo();
 +    foo();
 +    /**/
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn extract_does_not_tear_body_apart() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() {
 +    $0foo();
 +}$0
 +"#,
 +            r#"
 +fn foo() {
 +    fun_name();
 +}
 +
 +fn $0fun_name() {
 +    foo();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn extract_does_not_wrap_res_in_res() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +//- minicore: result
 +fn foo() -> Result<(), i64> {
 +    $0Result::<i32, i64>::Ok(0)?;
 +    Ok(())$0
 +}
 +"#,
 +            r#"
 +fn foo() -> Result<(), i64> {
 +    fun_name()?
 +}
 +
 +fn $0fun_name() -> Result<(), i64> {
 +    Result::<i32, i64>::Ok(0)?;
 +    Ok(())
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn extract_knows_const() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +const fn foo() {
 +    $0()$0
 +}
 +"#,
 +            r#"
 +const fn foo() {
 +    fun_name();
 +}
 +
 +const fn $0fun_name() {
 +    ()
 +}
 +"#,
 +        );
 +        check_assist(
 +            extract_function,
 +            r#"
 +const FOO: () = {
 +    $0()$0
 +};
 +"#,
 +            r#"
 +const FOO: () = {
 +    fun_name();
 +};
 +
 +const fn $0fun_name() {
 +    ()
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn extract_does_not_move_outer_loop_vars() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() {
 +    let mut x = 5;
 +    for _ in 0..10 {
 +        $0x += 1;$0
 +    }
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    let mut x = 5;
 +    for _ in 0..10 {
 +        fun_name(&mut x);
 +    }
 +}
 +
 +fn $0fun_name(x: &mut i32) {
 +    *x += 1;
 +}
 +"#,
 +        );
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() {
 +    for _ in 0..10 {
 +        let mut x = 5;
 +        $0x += 1;$0
 +    }
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    for _ in 0..10 {
 +        let mut x = 5;
 +        fun_name(x);
 +    }
 +}
 +
 +fn $0fun_name(mut x: i32) {
 +    x += 1;
 +}
 +"#,
 +        );
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() {
 +    loop {
 +        let mut x = 5;
 +        for _ in 0..10 {
 +            $0x += 1;$0
 +        }
 +    }
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    loop {
 +        let mut x = 5;
 +        for _ in 0..10 {
 +            fun_name(&mut x);
 +        }
 +    }
 +}
 +
 +fn $0fun_name(x: &mut i32) {
 +    *x += 1;
 +}
 +"#,
 +        );
 +    }
 +
 +    // regression test for #9822
 +    #[test]
 +    fn extract_mut_ref_param_has_no_mut_binding_in_loop() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +struct Foo;
 +impl Foo {
 +    fn foo(&mut self) {}
 +}
 +fn foo() {
 +    let mut x = Foo;
 +    while false {
 +        let y = &mut x;
 +        $0y.foo();$0
 +    }
 +    let z = x;
 +}
 +"#,
 +            r#"
 +struct Foo;
 +impl Foo {
 +    fn foo(&mut self) {}
 +}
 +fn foo() {
 +    let mut x = Foo;
 +    while false {
 +        let y = &mut x;
 +        fun_name(y);
 +    }
 +    let z = x;
 +}
 +
 +fn $0fun_name(y: &mut Foo) {
 +    y.foo();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn extract_with_macro_arg() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +macro_rules! m {
 +    ($val:expr) => { $val };
 +}
 +fn main() {
 +    let bar = "bar";
 +    $0m!(bar);$0
 +}
 +"#,
 +            r#"
 +macro_rules! m {
 +    ($val:expr) => { $val };
 +}
 +fn main() {
 +    let bar = "bar";
 +    fun_name(bar);
 +}
 +
 +fn $0fun_name(bar: &str) {
 +    m!(bar);
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn unresolveable_types_default_to_placeholder() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn foo() {
 +    let a = __unresolved;
 +    let _ = $0{a}$0;
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    let a = __unresolved;
 +    let _ = fun_name(a);
 +}
 +
 +fn $0fun_name(a: _) -> _ {
 +    a
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn reference_mutable_param_with_further_usages() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +pub struct Foo {
 +    field: u32,
 +}
 +
 +pub fn testfn(arg: &mut Foo) {
 +    $0arg.field = 8;$0
 +    // Simulating access after the extracted portion
 +    arg.field = 16;
 +}
 +"#,
 +            r#"
 +pub struct Foo {
 +    field: u32,
 +}
 +
 +pub fn testfn(arg: &mut Foo) {
 +    fun_name(arg);
 +    // Simulating access after the extracted portion
 +    arg.field = 16;
 +}
 +
 +fn $0fun_name(arg: &mut Foo) {
 +    arg.field = 8;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn reference_mutable_param_without_further_usages() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +pub struct Foo {
 +    field: u32,
 +}
 +
 +pub fn testfn(arg: &mut Foo) {
 +    $0arg.field = 8;$0
 +}
 +"#,
 +            r#"
 +pub struct Foo {
 +    field: u32,
 +}
 +
 +pub fn testfn(arg: &mut Foo) {
 +    fun_name(arg);
 +}
 +
 +fn $0fun_name(arg: &mut Foo) {
 +    arg.field = 8;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn extract_function_copies_comment_at_start() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn func() {
 +    let i = 0;
 +    $0// comment here!
 +    let x = 0;$0
 +}
 +"#,
 +            r#"
 +fn func() {
 +    let i = 0;
 +    fun_name();
 +}
 +
 +fn $0fun_name() {
 +    // comment here!
 +    let x = 0;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn extract_function_copies_comment_in_between() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn func() {
 +    let i = 0;$0
 +    let a = 0;
 +    // comment here!
 +    let x = 0;$0
 +}
 +"#,
 +            r#"
 +fn func() {
 +    let i = 0;
 +    fun_name();
 +}
 +
 +fn $0fun_name() {
 +    let a = 0;
 +    // comment here!
 +    let x = 0;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn extract_function_copies_comment_at_end() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn func() {
 +    let i = 0;
 +    $0let x = 0;
 +    // comment here!$0
 +}
 +"#,
 +            r#"
 +fn func() {
 +    let i = 0;
 +    fun_name();
 +}
 +
 +fn $0fun_name() {
 +    let x = 0;
 +    // comment here!
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn extract_function_copies_comment_indented() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn func() {
 +    let i = 0;
 +    $0let x = 0;
 +    while(true) {
 +        // comment here!
 +    }$0
 +}
 +"#,
 +            r#"
 +fn func() {
 +    let i = 0;
 +    fun_name();
 +}
 +
 +fn $0fun_name() {
 +    let x = 0;
 +    while(true) {
 +        // comment here!
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    // FIXME: we do want to preserve whitespace
 +    #[test]
 +    fn extract_function_does_not_preserve_whitespace() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn func() {
 +    let i = 0;
 +    $0let a = 0;
 +
 +    let x = 0;$0
 +}
 +"#,
 +            r#"
 +fn func() {
 +    let i = 0;
 +    fun_name();
 +}
 +
 +fn $0fun_name() {
 +    let a = 0;
 +    let x = 0;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn extract_function_long_form_comment() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn func() {
 +    let i = 0;
 +    $0/* a comment */
 +    let x = 0;$0
 +}
 +"#,
 +            r#"
 +fn func() {
 +    let i = 0;
 +    fun_name();
 +}
 +
 +fn $0fun_name() {
 +    /* a comment */
 +    let x = 0;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn it_should_not_generate_duplicate_function_names() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn fun_name() {
 +    $0let x = 0;$0
 +}
 +"#,
 +            r#"
 +fn fun_name() {
 +    fun_name1();
 +}
 +
 +fn $0fun_name1() {
 +    let x = 0;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn should_increment_suffix_until_it_finds_space() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn fun_name1() {
 +    let y = 0;
 +}
 +
 +fn fun_name() {
 +    $0let x = 0;$0
 +}
 +"#,
 +            r#"
 +fn fun_name1() {
 +    let y = 0;
 +}
 +
 +fn fun_name() {
 +    fun_name2();
 +}
 +
 +fn $0fun_name2() {
 +    let x = 0;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn extract_method_from_trait_impl() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +struct Struct(i32);
 +trait Trait {
 +    fn bar(&self) -> i32;
 +}
 +
 +impl Trait for Struct {
 +    fn bar(&self) -> i32 {
 +        $0self.0 + 2$0
 +    }
 +}
 +"#,
 +            r#"
 +struct Struct(i32);
 +trait Trait {
 +    fn bar(&self) -> i32;
 +}
 +
 +impl Trait for Struct {
 +    fn bar(&self) -> i32 {
 +        self.fun_name()
 +    }
 +}
 +
 +impl Struct {
 +    fn $0fun_name(&self) -> i32 {
 +        self.0 + 2
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn closure_arguments() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn parent(factor: i32) {
 +    let v = &[1, 2, 3];
 +
 +    $0v.iter().map(|it| it * factor);$0
 +}
 +"#,
 +            r#"
 +fn parent(factor: i32) {
 +    let v = &[1, 2, 3];
 +
 +    fun_name(v, factor);
 +}
 +
 +fn $0fun_name(v: &[i32; 3], factor: i32) {
 +    v.iter().map(|it| it * factor);
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn preserve_generics() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn func<T: Debug>(i: T) {
 +    $0foo(i);$0
 +}
 +"#,
 +            r#"
 +fn func<T: Debug>(i: T) {
 +    fun_name(i);
 +}
 +
 +fn $0fun_name<T: Debug>(i: T) {
 +    foo(i);
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn preserve_generics_from_body() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn func<T: Default>() -> T {
 +    $0T::default()$0
 +}
 +"#,
 +            r#"
 +fn func<T: Default>() -> T {
 +    fun_name()
 +}
 +
 +fn $0fun_name<T: Default>() -> T {
 +    T::default()
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn filter_unused_generics() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn func<T: Debug, U: Copy>(i: T, u: U) {
 +    bar(u);
 +    $0foo(i);$0
 +}
 +"#,
 +            r#"
 +fn func<T: Debug, U: Copy>(i: T, u: U) {
 +    bar(u);
 +    fun_name(i);
 +}
 +
 +fn $0fun_name<T: Debug>(i: T) {
 +    foo(i);
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn empty_generic_param_list() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn func<T: Debug>(t: T, i: u32) {
 +    bar(t);
 +    $0foo(i);$0
 +}
 +"#,
 +            r#"
 +fn func<T: Debug>(t: T, i: u32) {
 +    bar(t);
 +    fun_name(i);
 +}
 +
 +fn $0fun_name(i: u32) {
 +    foo(i);
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn preserve_where_clause() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn func<T>(i: T) where T: Debug {
 +    $0foo(i);$0
 +}
 +"#,
 +            r#"
 +fn func<T>(i: T) where T: Debug {
 +    fun_name(i);
 +}
 +
 +fn $0fun_name<T>(i: T) where T: Debug {
 +    foo(i);
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn filter_unused_where_clause() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +fn func<T, U>(i: T, u: U) where T: Debug, U: Copy {
 +    bar(u);
 +    $0foo(i);$0
 +}
 +"#,
 +            r#"
 +fn func<T, U>(i: T, u: U) where T: Debug, U: Copy {
 +    bar(u);
 +    fun_name(i);
 +}
 +
 +fn $0fun_name<T>(i: T) where T: Debug {
 +    foo(i);
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn nested_generics() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +struct Struct<T: Into<i32>>(T);
 +impl <T: Into<i32> + Copy> Struct<T> {
 +    fn func<V: Into<i32>>(&self, v: V) -> i32 {
 +        let t = self.0;
 +        $0t.into() + v.into()$0
 +    }
 +}
 +"#,
 +            r#"
 +struct Struct<T: Into<i32>>(T);
 +impl <T: Into<i32> + Copy> Struct<T> {
 +    fn func<V: Into<i32>>(&self, v: V) -> i32 {
 +        let t = self.0;
 +        fun_name(t, v)
 +    }
 +}
 +
 +fn $0fun_name<T: Into<i32> + Copy, V: Into<i32>>(t: T, v: V) -> i32 {
 +    t.into() + v.into()
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn filters_unused_nested_generics() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +struct Struct<T: Into<i32>, U: Debug>(T, U);
 +impl <T: Into<i32> + Copy, U: Debug> Struct<T, U> {
 +    fn func<V: Into<i32>>(&self, v: V) -> i32 {
 +        let t = self.0;
 +        $0t.into() + v.into()$0
 +    }
 +}
 +"#,
 +            r#"
 +struct Struct<T: Into<i32>, U: Debug>(T, U);
 +impl <T: Into<i32> + Copy, U: Debug> Struct<T, U> {
 +    fn func<V: Into<i32>>(&self, v: V) -> i32 {
 +        let t = self.0;
 +        fun_name(t, v)
 +    }
 +}
 +
 +fn $0fun_name<T: Into<i32> + Copy, V: Into<i32>>(t: T, v: V) -> i32 {
 +    t.into() + v.into()
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn nested_where_clauses() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +struct Struct<T>(T) where T: Into<i32>;
 +impl <T> Struct<T> where T: Into<i32> + Copy {
 +    fn func<V>(&self, v: V) -> i32 where V: Into<i32> {
 +        let t = self.0;
 +        $0t.into() + v.into()$0
 +    }
 +}
 +"#,
 +            r#"
 +struct Struct<T>(T) where T: Into<i32>;
 +impl <T> Struct<T> where T: Into<i32> + Copy {
 +    fn func<V>(&self, v: V) -> i32 where V: Into<i32> {
 +        let t = self.0;
 +        fun_name(t, v)
 +    }
 +}
 +
 +fn $0fun_name<T, V>(t: T, v: V) -> i32 where T: Into<i32> + Copy, V: Into<i32> {
 +    t.into() + v.into()
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn filters_unused_nested_where_clauses() {
 +        check_assist(
 +            extract_function,
 +            r#"
 +struct Struct<T, U>(T, U) where T: Into<i32>, U: Debug;
 +impl <T, U> Struct<T, U> where T: Into<i32> + Copy, U: Debug {
 +    fn func<V>(&self, v: V) -> i32 where V: Into<i32> {
 +        let t = self.0;
 +        $0t.into() + v.into()$0
 +    }
 +}
 +"#,
 +            r#"
 +struct Struct<T, U>(T, U) where T: Into<i32>, U: Debug;
 +impl <T, U> Struct<T, U> where T: Into<i32> + Copy, U: Debug {
 +    fn func<V>(&self, v: V) -> i32 where V: Into<i32> {
 +        let t = self.0;
 +        fun_name(t, v)
 +    }
 +}
 +
 +fn $0fun_name<T, V>(t: T, v: V) -> i32 where T: Into<i32> + Copy, V: Into<i32> {
 +    t.into() + v.into()
 +}
 +"#,
 +        );
 +    }
 +}
index 897980c6650497132360baa4833ee68bcb5858a1,0000000000000000000000000000000000000000..56834394aebaa8fbe7a125dc647280aa58920628
mode 100644,000000..100644
--- /dev/null
@@@ -1,1770 -1,0 +1,1769 @@@
-                 format_to!(indented_item, "{}{}", new_item_indent, item.to_string());
 +use std::{
 +    collections::{HashMap, HashSet},
 +    iter,
 +};
 +
 +use hir::{HasSource, ModuleSource};
 +use ide_db::{
 +    assists::{AssistId, AssistKind},
 +    base_db::FileId,
 +    defs::{Definition, NameClass, NameRefClass},
 +    search::{FileReference, SearchScope},
 +};
 +use stdx::format_to;
 +use syntax::{
 +    algo::find_node_at_range,
 +    ast::{
 +        self,
 +        edit::{AstNodeEdit, IndentLevel},
 +        make, HasName, HasVisibility,
 +    },
 +    match_ast, ted, AstNode, SourceFile,
 +    SyntaxKind::{self, WHITESPACE},
 +    SyntaxNode, TextRange,
 +};
 +
 +use crate::{AssistContext, Assists};
 +
 +use super::remove_unused_param::range_to_remove;
 +
 +// Assist: extract_module
 +//
 +// Extracts a selected region as separate module. All the references, visibility and imports are
 +// resolved.
 +//
 +// ```
 +// $0fn foo(name: i32) -> i32 {
 +//     name + 1
 +// }$0
 +//
 +// fn bar(name: i32) -> i32 {
 +//     name + 2
 +// }
 +// ```
 +// ->
 +// ```
 +// mod modname {
 +//     pub(crate) fn foo(name: i32) -> i32 {
 +//         name + 1
 +//     }
 +// }
 +//
 +// fn bar(name: i32) -> i32 {
 +//     name + 2
 +// }
 +// ```
 +pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    if ctx.has_empty_selection() {
 +        return None;
 +    }
 +
 +    let node = ctx.covering_element();
 +    let node = match node {
 +        syntax::NodeOrToken::Node(n) => n,
 +        syntax::NodeOrToken::Token(t) => t.parent()?,
 +    };
 +
 +    //If the selection is inside impl block, we need to place new module outside impl block,
 +    //as impl blocks cannot contain modules
 +
 +    let mut impl_parent: Option<ast::Impl> = None;
 +    let mut impl_child_count: usize = 0;
 +    if let Some(parent_assoc_list) = node.parent() {
 +        if let Some(parent_impl) = parent_assoc_list.parent() {
 +            if let Some(impl_) = ast::Impl::cast(parent_impl) {
 +                impl_child_count = parent_assoc_list.children().count();
 +                impl_parent = Some(impl_);
 +            }
 +        }
 +    }
 +
 +    let mut curr_parent_module: Option<ast::Module> = None;
 +    if let Some(mod_syn_opt) = node.ancestors().find(|it| ast::Module::can_cast(it.kind())) {
 +        curr_parent_module = ast::Module::cast(mod_syn_opt);
 +    }
 +
 +    let mut module = extract_target(&node, ctx.selection_trimmed())?;
 +    if module.body_items.is_empty() {
 +        return None;
 +    }
 +
 +    let old_item_indent = module.body_items[0].indent_level();
 +
 +    acc.add(
 +        AssistId("extract_module", AssistKind::RefactorExtract),
 +        "Extract Module",
 +        module.text_range,
 +        |builder| {
 +            //This takes place in three steps:
 +            //
 +            //- Firstly, we will update the references(usages) e.g. converting a
 +            //  function call bar() to modname::bar(), and similarly for other items
 +            //
 +            //- Secondly, changing the visibility of each item inside the newly selected module
 +            //  i.e. making a fn a() {} to pub(crate) fn a() {}
 +            //
 +            //- Thirdly, resolving all the imports this includes removing paths from imports
 +            //  outside the module, shifting/cloning them inside new module, or shifting the imports, or making
 +            //  new import statements
 +
 +            //We are getting item usages and record_fields together, record_fields
 +            //for change_visibility and usages for first point mentioned above in the process
 +            let (usages_to_be_processed, record_fields) = module.get_usages_and_record_fields(ctx);
 +
 +            let import_paths_to_be_removed = module.resolve_imports(curr_parent_module, ctx);
 +            module.change_visibility(record_fields);
 +
 +            let mut body_items: Vec<String> = Vec::new();
 +            let mut items_to_be_processed: Vec<ast::Item> = module.body_items.clone();
 +            let mut new_item_indent = old_item_indent + 1;
 +
 +            if impl_parent.is_some() {
 +                new_item_indent = old_item_indent + 2;
 +            } else {
 +                items_to_be_processed = [module.use_items.clone(), items_to_be_processed].concat();
 +            }
 +
 +            for item in items_to_be_processed {
 +                let item = item.indent(IndentLevel(1));
 +                let mut indented_item = String::new();
-                     format_to!(
-                         impl_body_def,
-                         "{}impl {} {{\n{}\n{}}}",
-                         old_item_indent + 1,
-                         self_ty.to_string(),
-                         body,
-                         old_item_indent + 1
-                     );
++                format_to!(indented_item, "{new_item_indent}{item}");
 +                body_items.push(indented_item);
 +            }
 +
 +            let mut body = body_items.join("\n\n");
 +
 +            if let Some(impl_) = &impl_parent {
 +                let mut impl_body_def = String::new();
 +
 +                if let Some(self_ty) = impl_.self_ty() {
-                         let mut indented_item = String::new();
-                         format_to!(indented_item, "{}{}", old_item_indent + 1, item.to_string());
-                         body = format!("{}\n\n{}", indented_item, body);
++                    {
++                        let impl_indent = old_item_indent + 1;
++                        format_to!(
++                            impl_body_def,
++                            "{impl_indent}impl {self_ty} {{\n{body}\n{impl_indent}}}",
++                        );
++                    }
 +                    body = impl_body_def;
 +
 +                    // Add the import for enum/struct corresponding to given impl block
 +                    module.make_use_stmt_of_node_with_super(self_ty.syntax());
 +                    for item in module.use_items {
-             format_to!(module_def, "mod {} {{\n{}\n{}}}", module.name, body, old_item_indent);
++                        let item_indent = old_item_indent + 1;
++                        body = format!("{item_indent}{item}\n\n{body}");
 +                    }
 +                }
 +            }
 +
 +            let mut module_def = String::new();
 +
-                 builder.insert(impl_.syntax().text_range().end(), format!("\n\n{}", module_def));
++            let module_name = module.name;
++            format_to!(module_def, "mod {module_name} {{\n{body}\n{old_item_indent}}}");
 +
 +            let mut usages_to_be_updated_for_curr_file = vec![];
 +            for usages_to_be_updated_for_file in usages_to_be_processed {
 +                if usages_to_be_updated_for_file.0 == ctx.file_id() {
 +                    usages_to_be_updated_for_curr_file = usages_to_be_updated_for_file.1;
 +                    continue;
 +                }
 +                builder.edit_file(usages_to_be_updated_for_file.0);
 +                for usage_to_be_processed in usages_to_be_updated_for_file.1 {
 +                    builder.replace(usage_to_be_processed.0, usage_to_be_processed.1)
 +                }
 +            }
 +
 +            builder.edit_file(ctx.file_id());
 +            for usage_to_be_processed in usages_to_be_updated_for_curr_file {
 +                builder.replace(usage_to_be_processed.0, usage_to_be_processed.1)
 +            }
 +
 +            for import_path_text_range in import_paths_to_be_removed {
 +                builder.delete(import_path_text_range);
 +            }
 +
 +            if let Some(impl_) = impl_parent {
 +                // Remove complete impl block if it has only one child (as such it will be empty
 +                // after deleting that child)
 +                let node_to_be_removed = if impl_child_count == 1 {
 +                    impl_.syntax()
 +                } else {
 +                    //Remove selected node
 +                    &node
 +                };
 +
 +                builder.delete(node_to_be_removed.text_range());
 +                // Remove preceding indentation from node
 +                if let Some(range) = indent_range_before_given_node(node_to_be_removed) {
 +                    builder.delete(range);
 +                }
 +
-                         format!("{}::{}", self.name, name_ref),
++                builder.insert(impl_.syntax().text_range().end(), format!("\n\n{module_def}"));
 +            } else {
 +                builder.replace(module.text_range, module_def)
 +            }
 +        },
 +    )
 +}
 +
 +#[derive(Debug)]
 +struct Module {
 +    text_range: TextRange,
 +    name: &'static str,
 +    /// All items except use items.
 +    body_items: Vec<ast::Item>,
 +    /// Use items are kept separately as they help when the selection is inside an impl block,
 +    /// we can directly take these items and keep them outside generated impl block inside
 +    /// generated module.
 +    use_items: Vec<ast::Item>,
 +}
 +
 +fn extract_target(node: &SyntaxNode, selection_range: TextRange) -> Option<Module> {
 +    let selected_nodes = node
 +        .children()
 +        .filter(|node| selection_range.contains_range(node.text_range()))
 +        .chain(iter::once(node.clone()));
 +    let (use_items, body_items) = selected_nodes
 +        .filter_map(ast::Item::cast)
 +        .partition(|item| matches!(item, ast::Item::Use(..)));
 +
 +    Some(Module { text_range: selection_range, name: "modname", body_items, use_items })
 +}
 +
 +impl Module {
 +    fn get_usages_and_record_fields(
 +        &self,
 +        ctx: &AssistContext<'_>,
 +    ) -> (HashMap<FileId, Vec<(TextRange, String)>>, Vec<SyntaxNode>) {
 +        let mut adt_fields = Vec::new();
 +        let mut refs: HashMap<FileId, Vec<(TextRange, String)>> = HashMap::new();
 +
 +        //Here impl is not included as each item inside impl will be tied to the parent of
 +        //implementing block(a struct, enum, etc), if the parent is in selected module, it will
 +        //get updated by ADT section given below or if it is not, then we dont need to do any operation
 +        for item in &self.body_items {
 +            match_ast! {
 +                match (item.syntax()) {
 +                    ast::Adt(it) => {
 +                        if let Some( nod ) = ctx.sema.to_def(&it) {
 +                            let node_def = Definition::Adt(nod);
 +                            self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs);
 +
 +                            //Enum Fields are not allowed to explicitly specify pub, it is implied
 +                            match it {
 +                                ast::Adt::Struct(x) => {
 +                                    if let Some(field_list) = x.field_list() {
 +                                        match field_list {
 +                                            ast::FieldList::RecordFieldList(record_field_list) => {
 +                                                record_field_list.fields().for_each(|record_field| {
 +                                                    adt_fields.push(record_field.syntax().clone());
 +                                                });
 +                                            },
 +                                            ast::FieldList::TupleFieldList(tuple_field_list) => {
 +                                                tuple_field_list.fields().for_each(|tuple_field| {
 +                                                    adt_fields.push(tuple_field.syntax().clone());
 +                                                });
 +                                            },
 +                                        }
 +                                    }
 +                                },
 +                                ast::Adt::Union(x) => {
 +                                        if let Some(record_field_list) = x.record_field_list() {
 +                                            record_field_list.fields().for_each(|record_field| {
 +                                                    adt_fields.push(record_field.syntax().clone());
 +                                            });
 +                                        }
 +                                },
 +                                ast::Adt::Enum(_) => {},
 +                            }
 +                        }
 +                    },
 +                    ast::TypeAlias(it) => {
 +                        if let Some( nod ) = ctx.sema.to_def(&it) {
 +                            let node_def = Definition::TypeAlias(nod);
 +                            self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs);
 +                        }
 +                    },
 +                    ast::Const(it) => {
 +                        if let Some( nod ) = ctx.sema.to_def(&it) {
 +                            let node_def = Definition::Const(nod);
 +                            self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs);
 +                        }
 +                    },
 +                    ast::Static(it) => {
 +                        if let Some( nod ) = ctx.sema.to_def(&it) {
 +                            let node_def = Definition::Static(nod);
 +                            self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs);
 +                        }
 +                    },
 +                    ast::Fn(it) => {
 +                        if let Some( nod ) = ctx.sema.to_def(&it) {
 +                            let node_def = Definition::Function(nod);
 +                            self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs);
 +                        }
 +                    },
 +                    ast::Macro(it) => {
 +                        if let Some(nod) = ctx.sema.to_def(&it) {
 +                            self.expand_and_group_usages_file_wise(ctx, Definition::Macro(nod), &mut refs);
 +                        }
 +                    },
 +                    _ => (),
 +                }
 +            }
 +        }
 +
 +        (refs, adt_fields)
 +    }
 +
 +    fn expand_and_group_usages_file_wise(
 +        &self,
 +        ctx: &AssistContext<'_>,
 +        node_def: Definition,
 +        refs_in_files: &mut HashMap<FileId, Vec<(TextRange, String)>>,
 +    ) {
 +        for (file_id, references) in node_def.usages(&ctx.sema).all() {
 +            let source_file = ctx.sema.parse(file_id);
 +            let usages_in_file = references
 +                .into_iter()
 +                .filter_map(|usage| self.get_usage_to_be_processed(&source_file, usage));
 +            refs_in_files.entry(file_id).or_default().extend(usages_in_file);
 +        }
 +    }
 +
 +    fn get_usage_to_be_processed(
 +        &self,
 +        source_file: &SourceFile,
 +        FileReference { range, name, .. }: FileReference,
 +    ) -> Option<(TextRange, String)> {
 +        let path: ast::Path = find_node_at_range(source_file.syntax(), range)?;
 +
 +        for desc in path.syntax().descendants() {
 +            if desc.to_string() == name.syntax().to_string()
 +                && !self.text_range.contains_range(desc.text_range())
 +            {
 +                if let Some(name_ref) = ast::NameRef::cast(desc) {
++                    let mod_name = self.name;
 +                    return Some((
 +                        name_ref.syntax().text_range(),
++                        format!("{mod_name}::{name_ref}"),
 +                    ));
 +                }
 +            }
 +        }
 +
 +        None
 +    }
 +
 +    fn change_visibility(&mut self, record_fields: Vec<SyntaxNode>) {
 +        let (mut replacements, record_field_parents, impls) =
 +            get_replacements_for_visibilty_change(&mut self.body_items, false);
 +
 +        let mut impl_items: Vec<ast::Item> = impls
 +            .into_iter()
 +            .flat_map(|impl_| impl_.syntax().descendants())
 +            .filter_map(ast::Item::cast)
 +            .collect();
 +
 +        let (mut impl_item_replacements, _, _) =
 +            get_replacements_for_visibilty_change(&mut impl_items, true);
 +
 +        replacements.append(&mut impl_item_replacements);
 +
 +        for (_, field_owner) in record_field_parents {
 +            for desc in field_owner.descendants().filter_map(ast::RecordField::cast) {
 +                let is_record_field_present =
 +                    record_fields.clone().into_iter().any(|x| x.to_string() == desc.to_string());
 +                if is_record_field_present {
 +                    replacements.push((desc.visibility(), desc.syntax().clone()));
 +                }
 +            }
 +        }
 +
 +        for (vis, syntax) in replacements {
 +            let item = syntax.children_with_tokens().find(|node_or_token| {
 +                match node_or_token.kind() {
 +                    // We're skipping comments, doc comments, and attribute macros that may precede the keyword
 +                    // that the visibility should be placed before.
 +                    SyntaxKind::COMMENT | SyntaxKind::ATTR | SyntaxKind::WHITESPACE => false,
 +                    _ => true,
 +                }
 +            });
 +
 +            add_change_vis(vis, item);
 +        }
 +    }
 +
 +    fn resolve_imports(
 +        &mut self,
 +        curr_parent_module: Option<ast::Module>,
 +        ctx: &AssistContext<'_>,
 +    ) -> Vec<TextRange> {
 +        let mut import_paths_to_be_removed: Vec<TextRange> = vec![];
 +        let mut node_set: HashSet<String> = HashSet::new();
 +
 +        for item in self.body_items.clone() {
 +            for x in item.syntax().descendants() {
 +                if let Some(name) = ast::Name::cast(x.clone()) {
 +                    if let Some(name_classify) = NameClass::classify(&ctx.sema, &name) {
 +                        //Necessary to avoid two same names going through
 +                        if !node_set.contains(&name.syntax().to_string()) {
 +                            node_set.insert(name.syntax().to_string());
 +                            let def_opt: Option<Definition> = match name_classify {
 +                                NameClass::Definition(def) => Some(def),
 +                                _ => None,
 +                            };
 +
 +                            if let Some(def) = def_opt {
 +                                if let Some(import_path) = self
 +                                    .process_names_and_namerefs_for_import_resolve(
 +                                        def,
 +                                        name.syntax(),
 +                                        &curr_parent_module,
 +                                        ctx,
 +                                    )
 +                                {
 +                                    check_intersection_and_push(
 +                                        &mut import_paths_to_be_removed,
 +                                        import_path,
 +                                    );
 +                                }
 +                            }
 +                        }
 +                    }
 +                }
 +
 +                if let Some(name_ref) = ast::NameRef::cast(x) {
 +                    if let Some(name_classify) = NameRefClass::classify(&ctx.sema, &name_ref) {
 +                        //Necessary to avoid two same names going through
 +                        if !node_set.contains(&name_ref.syntax().to_string()) {
 +                            node_set.insert(name_ref.syntax().to_string());
 +                            let def_opt: Option<Definition> = match name_classify {
 +                                NameRefClass::Definition(def) => Some(def),
 +                                _ => None,
 +                            };
 +
 +                            if let Some(def) = def_opt {
 +                                if let Some(import_path) = self
 +                                    .process_names_and_namerefs_for_import_resolve(
 +                                        def,
 +                                        name_ref.syntax(),
 +                                        &curr_parent_module,
 +                                        ctx,
 +                                    )
 +                                {
 +                                    check_intersection_and_push(
 +                                        &mut import_paths_to_be_removed,
 +                                        import_path,
 +                                    );
 +                                }
 +                            }
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +
 +        import_paths_to_be_removed
 +    }
 +
 +    fn process_names_and_namerefs_for_import_resolve(
 +        &mut self,
 +        def: Definition,
 +        node_syntax: &SyntaxNode,
 +        curr_parent_module: &Option<ast::Module>,
 +        ctx: &AssistContext<'_>,
 +    ) -> Option<TextRange> {
 +        //We only need to find in the current file
 +        let selection_range = ctx.selection_trimmed();
 +        let curr_file_id = ctx.file_id();
 +        let search_scope = SearchScope::single_file(curr_file_id);
 +        let usage_res = def.usages(&ctx.sema).in_scope(search_scope).all();
 +        let file = ctx.sema.parse(curr_file_id);
 +
 +        let mut exists_inside_sel = false;
 +        let mut exists_outside_sel = false;
 +        for (_, refs) in usage_res.iter() {
 +            let mut non_use_nodes_itr = refs.iter().filter_map(|x| {
 +                if find_node_at_range::<ast::Use>(file.syntax(), x.range).is_none() {
 +                    let path_opt = find_node_at_range::<ast::Path>(file.syntax(), x.range);
 +                    return path_opt;
 +                }
 +
 +                None
 +            });
 +
 +            if non_use_nodes_itr
 +                .clone()
 +                .any(|x| !selection_range.contains_range(x.syntax().text_range()))
 +            {
 +                exists_outside_sel = true;
 +            }
 +            if non_use_nodes_itr.any(|x| selection_range.contains_range(x.syntax().text_range())) {
 +                exists_inside_sel = true;
 +            }
 +        }
 +
 +        let source_exists_outside_sel_in_same_mod = does_source_exists_outside_sel_in_same_mod(
 +            def,
 +            ctx,
 +            curr_parent_module,
 +            selection_range,
 +            curr_file_id,
 +        );
 +
 +        let use_stmt_opt: Option<ast::Use> = usage_res.into_iter().find_map(|(file_id, refs)| {
 +            if file_id == curr_file_id {
 +                refs.into_iter()
 +                    .rev()
 +                    .find_map(|fref| find_node_at_range(file.syntax(), fref.range))
 +            } else {
 +                None
 +            }
 +        });
 +
 +        let mut use_tree_str_opt: Option<Vec<ast::Path>> = None;
 +        //Exists inside and outside selection
 +        // - Use stmt for item is present -> get the use_tree_str and reconstruct the path in new
 +        // module
 +        // - Use stmt for item is not present ->
 +        //If it is not found, the definition is either ported inside new module or it stays
 +        //outside:
 +        //- Def is inside: Nothing to import
 +        //- Def is outside: Import it inside with super
 +
 +        //Exists inside selection but not outside -> Check for the import of it in original module,
 +        //get the use_tree_str, reconstruct the use stmt in new module
 +
 +        let mut import_path_to_be_removed: Option<TextRange> = None;
 +        if exists_inside_sel && exists_outside_sel {
 +            //Changes to be made only inside new module
 +
 +            //If use_stmt exists, find the use_tree_str, reconstruct it inside new module
 +            //If not, insert a use stmt with super and the given nameref
 +            if let Some((use_tree_str, _)) =
 +                self.process_use_stmt_for_import_resolve(use_stmt_opt, node_syntax)
 +            {
 +                use_tree_str_opt = Some(use_tree_str);
 +            } else if source_exists_outside_sel_in_same_mod {
 +                //Considered only after use_stmt is not present
 +                //source_exists_outside_sel_in_same_mod | exists_outside_sel(exists_inside_sel =
 +                //true for all cases)
 +                // false | false -> Do nothing
 +                // false | true -> If source is in selection -> nothing to do, If source is outside
 +                // mod -> ust_stmt transversal
 +                // true  | false -> super import insertion
 +                // true  | true -> super import insertion
 +                self.make_use_stmt_of_node_with_super(node_syntax);
 +            }
 +        } else if exists_inside_sel && !exists_outside_sel {
 +            //Changes to be made inside new module, and remove import from outside
 +
 +            if let Some((mut use_tree_str, text_range_opt)) =
 +                self.process_use_stmt_for_import_resolve(use_stmt_opt, node_syntax)
 +            {
 +                if let Some(text_range) = text_range_opt {
 +                    import_path_to_be_removed = Some(text_range);
 +                }
 +
 +                if source_exists_outside_sel_in_same_mod {
 +                    if let Some(first_path_in_use_tree) = use_tree_str.last() {
 +                        let first_path_in_use_tree_str = first_path_in_use_tree.to_string();
 +                        if !first_path_in_use_tree_str.contains("super")
 +                            && !first_path_in_use_tree_str.contains("crate")
 +                        {
 +                            let super_path = make::ext::ident_path("super");
 +                            use_tree_str.push(super_path);
 +                        }
 +                    }
 +                }
 +
 +                use_tree_str_opt = Some(use_tree_str);
 +            } else if source_exists_outside_sel_in_same_mod {
 +                self.make_use_stmt_of_node_with_super(node_syntax);
 +            }
 +        }
 +
 +        if let Some(use_tree_str) = use_tree_str_opt {
 +            let mut use_tree_str = use_tree_str;
 +            use_tree_str.reverse();
 +
 +            if !(!exists_outside_sel && exists_inside_sel && source_exists_outside_sel_in_same_mod)
 +            {
 +                if let Some(first_path_in_use_tree) = use_tree_str.first() {
 +                    let first_path_in_use_tree_str = first_path_in_use_tree.to_string();
 +                    if first_path_in_use_tree_str.contains("super") {
 +                        let super_path = make::ext::ident_path("super");
 +                        use_tree_str.insert(0, super_path)
 +                    }
 +                }
 +            }
 +
 +            let use_ =
 +                make::use_(None, make::use_tree(make::join_paths(use_tree_str), None, None, false));
 +            let item = ast::Item::from(use_);
 +            self.use_items.insert(0, item);
 +        }
 +
 +        import_path_to_be_removed
 +    }
 +
 +    fn make_use_stmt_of_node_with_super(&mut self, node_syntax: &SyntaxNode) -> ast::Item {
 +        let super_path = make::ext::ident_path("super");
 +        let node_path = make::ext::ident_path(&node_syntax.to_string());
 +        let use_ = make::use_(
 +            None,
 +            make::use_tree(make::join_paths(vec![super_path, node_path]), None, None, false),
 +        );
 +
 +        let item = ast::Item::from(use_);
 +        self.use_items.insert(0, item.clone());
 +        item
 +    }
 +
 +    fn process_use_stmt_for_import_resolve(
 +        &self,
 +        use_stmt_opt: Option<ast::Use>,
 +        node_syntax: &SyntaxNode,
 +    ) -> Option<(Vec<ast::Path>, Option<TextRange>)> {
 +        if let Some(use_stmt) = use_stmt_opt {
 +            for desc in use_stmt.syntax().descendants() {
 +                if let Some(path_seg) = ast::PathSegment::cast(desc) {
 +                    if path_seg.syntax().to_string() == node_syntax.to_string() {
 +                        let mut use_tree_str = vec![path_seg.parent_path()];
 +                        get_use_tree_paths_from_path(path_seg.parent_path(), &mut use_tree_str);
 +                        for ancs in path_seg.syntax().ancestors() {
 +                            //Here we are looking for use_tree with same string value as node
 +                            //passed above as the range_to_remove function looks for a comma and
 +                            //then includes it in the text range to remove it. But the comma only
 +                            //appears at the use_tree level
 +                            if let Some(use_tree) = ast::UseTree::cast(ancs) {
 +                                if use_tree.syntax().to_string() == node_syntax.to_string() {
 +                                    return Some((
 +                                        use_tree_str,
 +                                        Some(range_to_remove(use_tree.syntax())),
 +                                    ));
 +                                }
 +                            }
 +                        }
 +
 +                        return Some((use_tree_str, None));
 +                    }
 +                }
 +            }
 +        }
 +
 +        None
 +    }
 +}
 +
 +fn check_intersection_and_push(
 +    import_paths_to_be_removed: &mut Vec<TextRange>,
 +    import_path: TextRange,
 +) {
 +    if import_paths_to_be_removed.len() > 0 {
 +        // Text ranges received here for imports are extended to the
 +        // next/previous comma which can cause intersections among them
 +        // and later deletion of these can cause panics similar
 +        // to reported in #11766. So to mitigate it, we
 +        // check for intersection between all current members
 +        // and if it exists we combine both text ranges into
 +        // one
 +        let r = import_paths_to_be_removed
 +            .into_iter()
 +            .position(|it| it.intersect(import_path).is_some());
 +        match r {
 +            Some(it) => {
 +                import_paths_to_be_removed[it] = import_paths_to_be_removed[it].cover(import_path)
 +            }
 +            None => import_paths_to_be_removed.push(import_path),
 +        }
 +    } else {
 +        import_paths_to_be_removed.push(import_path);
 +    }
 +}
 +
 +fn does_source_exists_outside_sel_in_same_mod(
 +    def: Definition,
 +    ctx: &AssistContext<'_>,
 +    curr_parent_module: &Option<ast::Module>,
 +    selection_range: TextRange,
 +    curr_file_id: FileId,
 +) -> bool {
 +    let mut source_exists_outside_sel_in_same_mod = false;
 +    match def {
 +        Definition::Module(x) => {
 +            let source = x.definition_source(ctx.db());
 +            let have_same_parent;
 +            if let Some(ast_module) = &curr_parent_module {
 +                if let Some(hir_module) = x.parent(ctx.db()) {
 +                    have_same_parent =
 +                        compare_hir_and_ast_module(ast_module, hir_module, ctx).is_some();
 +                } else {
 +                    let source_file_id = source.file_id.original_file(ctx.db());
 +                    have_same_parent = source_file_id == curr_file_id;
 +                }
 +            } else {
 +                let source_file_id = source.file_id.original_file(ctx.db());
 +                have_same_parent = source_file_id == curr_file_id;
 +            }
 +
 +            if have_same_parent {
 +                match source.value {
 +                    ModuleSource::Module(module_) => {
 +                        source_exists_outside_sel_in_same_mod =
 +                            !selection_range.contains_range(module_.syntax().text_range());
 +                    }
 +                    _ => {}
 +                }
 +            }
 +        }
 +        Definition::Function(x) => {
 +            if let Some(source) = x.source(ctx.db()) {
 +                let have_same_parent = if let Some(ast_module) = &curr_parent_module {
 +                    compare_hir_and_ast_module(ast_module, x.module(ctx.db()), ctx).is_some()
 +                } else {
 +                    let source_file_id = source.file_id.original_file(ctx.db());
 +                    source_file_id == curr_file_id
 +                };
 +
 +                if have_same_parent {
 +                    source_exists_outside_sel_in_same_mod =
 +                        !selection_range.contains_range(source.value.syntax().text_range());
 +                }
 +            }
 +        }
 +        Definition::Adt(x) => {
 +            if let Some(source) = x.source(ctx.db()) {
 +                let have_same_parent = if let Some(ast_module) = &curr_parent_module {
 +                    compare_hir_and_ast_module(ast_module, x.module(ctx.db()), ctx).is_some()
 +                } else {
 +                    let source_file_id = source.file_id.original_file(ctx.db());
 +                    source_file_id == curr_file_id
 +                };
 +
 +                if have_same_parent {
 +                    source_exists_outside_sel_in_same_mod =
 +                        !selection_range.contains_range(source.value.syntax().text_range());
 +                }
 +            }
 +        }
 +        Definition::Variant(x) => {
 +            if let Some(source) = x.source(ctx.db()) {
 +                let have_same_parent = if let Some(ast_module) = &curr_parent_module {
 +                    compare_hir_and_ast_module(ast_module, x.module(ctx.db()), ctx).is_some()
 +                } else {
 +                    let source_file_id = source.file_id.original_file(ctx.db());
 +                    source_file_id == curr_file_id
 +                };
 +
 +                if have_same_parent {
 +                    source_exists_outside_sel_in_same_mod =
 +                        !selection_range.contains_range(source.value.syntax().text_range());
 +                }
 +            }
 +        }
 +        Definition::Const(x) => {
 +            if let Some(source) = x.source(ctx.db()) {
 +                let have_same_parent = if let Some(ast_module) = &curr_parent_module {
 +                    compare_hir_and_ast_module(ast_module, x.module(ctx.db()), ctx).is_some()
 +                } else {
 +                    let source_file_id = source.file_id.original_file(ctx.db());
 +                    source_file_id == curr_file_id
 +                };
 +
 +                if have_same_parent {
 +                    source_exists_outside_sel_in_same_mod =
 +                        !selection_range.contains_range(source.value.syntax().text_range());
 +                }
 +            }
 +        }
 +        Definition::Static(x) => {
 +            if let Some(source) = x.source(ctx.db()) {
 +                let have_same_parent = if let Some(ast_module) = &curr_parent_module {
 +                    compare_hir_and_ast_module(ast_module, x.module(ctx.db()), ctx).is_some()
 +                } else {
 +                    let source_file_id = source.file_id.original_file(ctx.db());
 +                    source_file_id == curr_file_id
 +                };
 +
 +                if have_same_parent {
 +                    source_exists_outside_sel_in_same_mod =
 +                        !selection_range.contains_range(source.value.syntax().text_range());
 +                }
 +            }
 +        }
 +        Definition::Trait(x) => {
 +            if let Some(source) = x.source(ctx.db()) {
 +                let have_same_parent = if let Some(ast_module) = &curr_parent_module {
 +                    compare_hir_and_ast_module(ast_module, x.module(ctx.db()), ctx).is_some()
 +                } else {
 +                    let source_file_id = source.file_id.original_file(ctx.db());
 +                    source_file_id == curr_file_id
 +                };
 +
 +                if have_same_parent {
 +                    source_exists_outside_sel_in_same_mod =
 +                        !selection_range.contains_range(source.value.syntax().text_range());
 +                }
 +            }
 +        }
 +        Definition::TypeAlias(x) => {
 +            if let Some(source) = x.source(ctx.db()) {
 +                let have_same_parent = if let Some(ast_module) = &curr_parent_module {
 +                    compare_hir_and_ast_module(ast_module, x.module(ctx.db()), ctx).is_some()
 +                } else {
 +                    let source_file_id = source.file_id.original_file(ctx.db());
 +                    source_file_id == curr_file_id
 +                };
 +
 +                if have_same_parent {
 +                    source_exists_outside_sel_in_same_mod =
 +                        !selection_range.contains_range(source.value.syntax().text_range());
 +                }
 +            }
 +        }
 +        _ => {}
 +    }
 +
 +    source_exists_outside_sel_in_same_mod
 +}
 +
 +fn get_replacements_for_visibilty_change(
 +    items: &mut [ast::Item],
 +    is_clone_for_updated: bool,
 +) -> (
 +    Vec<(Option<ast::Visibility>, SyntaxNode)>,
 +    Vec<(Option<ast::Visibility>, SyntaxNode)>,
 +    Vec<ast::Impl>,
 +) {
 +    let mut replacements = Vec::new();
 +    let mut record_field_parents = Vec::new();
 +    let mut impls = Vec::new();
 +
 +    for item in items {
 +        if !is_clone_for_updated {
 +            *item = item.clone_for_update();
 +        }
 +        //Use stmts are ignored
 +        match item {
 +            ast::Item::Const(it) => replacements.push((it.visibility(), it.syntax().clone())),
 +            ast::Item::Enum(it) => replacements.push((it.visibility(), it.syntax().clone())),
 +            ast::Item::ExternCrate(it) => replacements.push((it.visibility(), it.syntax().clone())),
 +            ast::Item::Fn(it) => replacements.push((it.visibility(), it.syntax().clone())),
 +            //Associated item's visibility should not be changed
 +            ast::Item::Impl(it) if it.for_token().is_none() => impls.push(it.clone()),
 +            ast::Item::MacroDef(it) => replacements.push((it.visibility(), it.syntax().clone())),
 +            ast::Item::Module(it) => replacements.push((it.visibility(), it.syntax().clone())),
 +            ast::Item::Static(it) => replacements.push((it.visibility(), it.syntax().clone())),
 +            ast::Item::Struct(it) => {
 +                replacements.push((it.visibility(), it.syntax().clone()));
 +                record_field_parents.push((it.visibility(), it.syntax().clone()));
 +            }
 +            ast::Item::Trait(it) => replacements.push((it.visibility(), it.syntax().clone())),
 +            ast::Item::TypeAlias(it) => replacements.push((it.visibility(), it.syntax().clone())),
 +            ast::Item::Union(it) => {
 +                replacements.push((it.visibility(), it.syntax().clone()));
 +                record_field_parents.push((it.visibility(), it.syntax().clone()));
 +            }
 +            _ => (),
 +        }
 +    }
 +
 +    (replacements, record_field_parents, impls)
 +}
 +
 +fn get_use_tree_paths_from_path(
 +    path: ast::Path,
 +    use_tree_str: &mut Vec<ast::Path>,
 +) -> Option<&mut Vec<ast::Path>> {
 +    path.syntax().ancestors().filter(|x| x.to_string() != path.to_string()).find_map(|x| {
 +        if let Some(use_tree) = ast::UseTree::cast(x) {
 +            if let Some(upper_tree_path) = use_tree.path() {
 +                if upper_tree_path.to_string() != path.to_string() {
 +                    use_tree_str.push(upper_tree_path.clone());
 +                    get_use_tree_paths_from_path(upper_tree_path, use_tree_str);
 +                    return Some(use_tree);
 +                }
 +            }
 +        }
 +        None
 +    })?;
 +
 +    Some(use_tree_str)
 +}
 +
 +fn add_change_vis(vis: Option<ast::Visibility>, node_or_token_opt: Option<syntax::SyntaxElement>) {
 +    if vis.is_none() {
 +        if let Some(node_or_token) = node_or_token_opt {
 +            let pub_crate_vis = make::visibility_pub_crate().clone_for_update();
 +            ted::insert(ted::Position::before(node_or_token), pub_crate_vis.syntax());
 +        }
 +    }
 +}
 +
 +fn compare_hir_and_ast_module(
 +    ast_module: &ast::Module,
 +    hir_module: hir::Module,
 +    ctx: &AssistContext<'_>,
 +) -> Option<()> {
 +    let hir_mod_name = hir_module.name(ctx.db())?;
 +    let ast_mod_name = ast_module.name()?;
 +    if hir_mod_name.to_string() != ast_mod_name.to_string() {
 +        return None;
 +    }
 +
 +    Some(())
 +}
 +
 +fn indent_range_before_given_node(node: &SyntaxNode) -> Option<TextRange> {
 +    node.siblings_with_tokens(syntax::Direction::Prev)
 +        .find(|x| x.kind() == WHITESPACE)
 +        .map(|x| x.text_range())
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use crate::tests::{check_assist, check_assist_not_applicable};
 +
 +    use super::*;
 +
 +    #[test]
 +    fn test_not_applicable_without_selection() {
 +        check_assist_not_applicable(
 +            extract_module,
 +            r"
 +$0pub struct PublicStruct {
 +    field: i32,
 +}
 +            ",
 +        )
 +    }
 +
 +    #[test]
 +    fn test_extract_module() {
 +        check_assist(
 +            extract_module,
 +            r"
 +            mod thirdpartycrate {
 +                pub mod nest {
 +                    pub struct SomeType;
 +                    pub struct SomeType2;
 +                }
 +                pub struct SomeType1;
 +            }
 +
 +            mod bar {
 +                use crate::thirdpartycrate::{nest::{SomeType, SomeType2}, SomeType1};
 +
 +                pub struct PublicStruct {
 +                    field: PrivateStruct,
 +                    field1: SomeType1,
 +                }
 +
 +                impl PublicStruct {
 +                    pub fn new() -> Self {
 +                        Self { field: PrivateStruct::new(), field1: SomeType1 }
 +                    }
 +                }
 +
 +                fn foo() {
 +                    let _s = PrivateStruct::new();
 +                    let _a = bar();
 +                }
 +
 +$0struct PrivateStruct {
 +    inner: SomeType,
 +}
 +
 +pub struct PrivateStruct1 {
 +    pub inner: i32,
 +}
 +
 +impl PrivateStruct {
 +    fn new() -> Self {
 +         PrivateStruct { inner: SomeType }
 +    }
 +}
 +
 +fn bar() -> i32 {
 +    2
 +}$0
 +            }
 +            ",
 +            r"
 +            mod thirdpartycrate {
 +                pub mod nest {
 +                    pub struct SomeType;
 +                    pub struct SomeType2;
 +                }
 +                pub struct SomeType1;
 +            }
 +
 +            mod bar {
 +                use crate::thirdpartycrate::{nest::{SomeType2}, SomeType1};
 +
 +                pub struct PublicStruct {
 +                    field: modname::PrivateStruct,
 +                    field1: SomeType1,
 +                }
 +
 +                impl PublicStruct {
 +                    pub fn new() -> Self {
 +                        Self { field: modname::PrivateStruct::new(), field1: SomeType1 }
 +                    }
 +                }
 +
 +                fn foo() {
 +                    let _s = modname::PrivateStruct::new();
 +                    let _a = modname::bar();
 +                }
 +
 +mod modname {
 +    use crate::thirdpartycrate::nest::SomeType;
 +
 +    pub(crate) struct PrivateStruct {
 +        pub(crate) inner: SomeType,
 +    }
 +
 +    pub struct PrivateStruct1 {
 +        pub inner: i32,
 +    }
 +
 +    impl PrivateStruct {
 +        pub(crate) fn new() -> Self {
 +             PrivateStruct { inner: SomeType }
 +        }
 +    }
 +
 +    pub(crate) fn bar() -> i32 {
 +        2
 +    }
 +}
 +            }
 +            ",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_module_for_function_only() {
 +        check_assist(
 +            extract_module,
 +            r"
 +$0fn foo(name: i32) -> i32 {
 +    name + 1
 +}$0
 +
 +                fn bar(name: i32) -> i32 {
 +                    name + 2
 +                }
 +            ",
 +            r"
 +mod modname {
 +    pub(crate) fn foo(name: i32) -> i32 {
 +        name + 1
 +    }
 +}
 +
 +                fn bar(name: i32) -> i32 {
 +                    name + 2
 +                }
 +            ",
 +        )
 +    }
 +
 +    #[test]
 +    fn test_extract_module_for_impl_having_corresponding_adt_in_selection() {
 +        check_assist(
 +            extract_module,
 +            r"
 +            mod impl_play {
 +$0struct A {}
 +
 +impl A {
 +    pub fn new_a() -> i32 {
 +        2
 +    }
 +}$0
 +
 +                fn a() {
 +                    let _a = A::new_a();
 +                }
 +            }
 +            ",
 +            r"
 +            mod impl_play {
 +mod modname {
 +    pub(crate) struct A {}
 +
 +    impl A {
 +        pub fn new_a() -> i32 {
 +            2
 +        }
 +    }
 +}
 +
 +                fn a() {
 +                    let _a = modname::A::new_a();
 +                }
 +            }
 +            ",
 +        )
 +    }
 +
 +    #[test]
 +    fn test_import_resolve_when_its_only_inside_selection() {
 +        check_assist(
 +            extract_module,
 +            r"
 +            mod foo {
 +                pub struct PrivateStruct;
 +                pub struct PrivateStruct1;
 +            }
 +
 +            mod bar {
 +                use super::foo::{PrivateStruct, PrivateStruct1};
 +
 +$0struct Strukt {
 +    field: PrivateStruct,
 +}$0
 +
 +                struct Strukt1 {
 +                    field: PrivateStruct1,
 +                }
 +            }
 +            ",
 +            r"
 +            mod foo {
 +                pub struct PrivateStruct;
 +                pub struct PrivateStruct1;
 +            }
 +
 +            mod bar {
 +                use super::foo::{PrivateStruct1};
 +
 +mod modname {
 +    use super::super::foo::PrivateStruct;
 +
 +    pub(crate) struct Strukt {
 +        pub(crate) field: PrivateStruct,
 +    }
 +}
 +
 +                struct Strukt1 {
 +                    field: PrivateStruct1,
 +                }
 +            }
 +            ",
 +        )
 +    }
 +
 +    #[test]
 +    fn test_import_resolve_when_its_inside_and_outside_selection_and_source_not_in_same_mod() {
 +        check_assist(
 +            extract_module,
 +            r"
 +            mod foo {
 +                pub struct PrivateStruct;
 +            }
 +
 +            mod bar {
 +                use super::foo::PrivateStruct;
 +
 +$0struct Strukt {
 +    field: PrivateStruct,
 +}$0
 +
 +                struct Strukt1 {
 +                    field: PrivateStruct,
 +                }
 +            }
 +            ",
 +            r"
 +            mod foo {
 +                pub struct PrivateStruct;
 +            }
 +
 +            mod bar {
 +                use super::foo::PrivateStruct;
 +
 +mod modname {
 +    use super::super::foo::PrivateStruct;
 +
 +    pub(crate) struct Strukt {
 +        pub(crate) field: PrivateStruct,
 +    }
 +}
 +
 +                struct Strukt1 {
 +                    field: PrivateStruct,
 +                }
 +            }
 +            ",
 +        )
 +    }
 +
 +    #[test]
 +    fn test_import_resolve_when_its_inside_and_outside_selection_and_source_is_in_same_mod() {
 +        check_assist(
 +            extract_module,
 +            r"
 +            mod bar {
 +                pub struct PrivateStruct;
 +
 +$0struct Strukt {
 +   field: PrivateStruct,
 +}$0
 +
 +                struct Strukt1 {
 +                    field: PrivateStruct,
 +                }
 +            }
 +            ",
 +            r"
 +            mod bar {
 +                pub struct PrivateStruct;
 +
 +mod modname {
 +    use super::PrivateStruct;
 +
 +    pub(crate) struct Strukt {
 +       pub(crate) field: PrivateStruct,
 +    }
 +}
 +
 +                struct Strukt1 {
 +                    field: PrivateStruct,
 +                }
 +            }
 +            ",
 +        )
 +    }
 +
 +    #[test]
 +    fn test_extract_module_for_correspoding_adt_of_impl_present_in_same_mod_but_not_in_selection() {
 +        check_assist(
 +            extract_module,
 +            r"
 +            mod impl_play {
 +                struct A {}
 +
 +$0impl A {
 +    pub fn new_a() -> i32 {
 +        2
 +    }
 +}$0
 +
 +                fn a() {
 +                    let _a = A::new_a();
 +                }
 +            }
 +            ",
 +            r"
 +            mod impl_play {
 +                struct A {}
 +
 +mod modname {
 +    use super::A;
 +
 +    impl A {
 +        pub fn new_a() -> i32 {
 +            2
 +        }
 +    }
 +}
 +
 +                fn a() {
 +                    let _a = A::new_a();
 +                }
 +            }
 +            ",
 +        )
 +    }
 +
 +    #[test]
 +    fn test_extract_module_for_impl_not_having_corresponding_adt_in_selection_and_not_in_same_mod_but_with_super(
 +    ) {
 +        check_assist(
 +            extract_module,
 +            r"
 +            mod foo {
 +                pub struct A {}
 +            }
 +            mod impl_play {
 +                use super::foo::A;
 +
 +$0impl A {
 +    pub fn new_a() -> i32 {
 +        2
 +    }
 +}$0
 +
 +                fn a() {
 +                    let _a = A::new_a();
 +                }
 +            }
 +            ",
 +            r"
 +            mod foo {
 +                pub struct A {}
 +            }
 +            mod impl_play {
 +                use super::foo::A;
 +
 +mod modname {
 +    use super::super::foo::A;
 +
 +    impl A {
 +        pub fn new_a() -> i32 {
 +            2
 +        }
 +    }
 +}
 +
 +                fn a() {
 +                    let _a = A::new_a();
 +                }
 +            }
 +            ",
 +        )
 +    }
 +
 +    #[test]
 +    fn test_import_resolve_for_trait_bounds_on_function() {
 +        check_assist(
 +            extract_module,
 +            r"
 +            mod impl_play2 {
 +                trait JustATrait {}
 +
 +$0struct A {}
 +
 +fn foo<T: JustATrait>(arg: T) -> T {
 +    arg
 +}
 +
 +impl JustATrait for A {}
 +
 +fn bar() {
 +    let a = A {};
 +    foo(a);
 +}$0
 +            }
 +            ",
 +            r"
 +            mod impl_play2 {
 +                trait JustATrait {}
 +
 +mod modname {
 +    use super::JustATrait;
 +
 +    pub(crate) struct A {}
 +
 +    pub(crate) fn foo<T: JustATrait>(arg: T) -> T {
 +        arg
 +    }
 +
 +    impl JustATrait for A {}
 +
 +    pub(crate) fn bar() {
 +        let a = A {};
 +        foo(a);
 +    }
 +}
 +            }
 +            ",
 +        )
 +    }
 +
 +    #[test]
 +    fn test_extract_module_for_module() {
 +        check_assist(
 +            extract_module,
 +            r"
 +            mod impl_play2 {
 +$0mod impl_play {
 +    pub struct A {}
 +}$0
 +            }
 +            ",
 +            r"
 +            mod impl_play2 {
 +mod modname {
 +    pub(crate) mod impl_play {
 +        pub struct A {}
 +    }
 +}
 +            }
 +            ",
 +        )
 +    }
 +
 +    #[test]
 +    fn test_extract_module_with_multiple_files() {
 +        check_assist(
 +            extract_module,
 +            r"
 +            //- /main.rs
 +            mod foo;
 +
 +            use foo::PrivateStruct;
 +
 +            pub struct Strukt {
 +                field: PrivateStruct,
 +            }
 +
 +            fn main() {
 +                $0struct Strukt1 {
 +                    field: Strukt,
 +                }$0
 +            }
 +            //- /foo.rs
 +            pub struct PrivateStruct;
 +            ",
 +            r"
 +            mod foo;
 +
 +            use foo::PrivateStruct;
 +
 +            pub struct Strukt {
 +                field: PrivateStruct,
 +            }
 +
 +            fn main() {
 +                mod modname {
 +                    use super::Strukt;
 +
 +                    pub(crate) struct Strukt1 {
 +                        pub(crate) field: Strukt,
 +                    }
 +                }
 +            }
 +            ",
 +        )
 +    }
 +
 +    #[test]
 +    fn test_extract_module_macro_rules() {
 +        check_assist(
 +            extract_module,
 +            r"
 +$0macro_rules! m {
 +    () => {};
 +}$0
 +m! {}
 +            ",
 +            r"
 +mod modname {
 +    macro_rules! m {
 +        () => {};
 +    }
 +}
 +modname::m! {}
 +            ",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_do_not_apply_visibility_modifier_to_trait_impl_items() {
 +        check_assist(
 +            extract_module,
 +            r"
 +            trait ATrait {
 +                fn function();
 +            }
 +
 +            struct A {}
 +
 +$0impl ATrait for A {
 +    fn function() {}
 +}$0
 +            ",
 +            r"
 +            trait ATrait {
 +                fn function();
 +            }
 +
 +            struct A {}
 +
 +mod modname {
 +    use super::A;
 +
 +    use super::ATrait;
 +
 +    impl ATrait for A {
 +        fn function() {}
 +    }
 +}
 +            ",
 +        )
 +    }
 +
 +    #[test]
 +    fn test_if_inside_impl_block_generate_module_outside() {
 +        check_assist(
 +            extract_module,
 +            r"
 +            struct A {}
 +
 +            impl A {
 +$0fn foo() {}$0
 +                fn bar() {}
 +            }
 +        ",
 +            r"
 +            struct A {}
 +
 +            impl A {
 +                fn bar() {}
 +            }
 +
 +mod modname {
 +    use super::A;
 +
 +    impl A {
 +        pub(crate) fn foo() {}
 +    }
 +}
 +        ",
 +        )
 +    }
 +
 +    #[test]
 +    fn test_if_inside_impl_block_generate_module_outside_but_impl_block_having_one_child() {
 +        check_assist(
 +            extract_module,
 +            r"
 +            struct A {}
 +            struct B {}
 +
 +            impl A {
 +$0fn foo(x: B) {}$0
 +            }
 +        ",
 +            r"
 +            struct A {}
 +            struct B {}
 +
 +mod modname {
 +    use super::B;
 +
 +    use super::A;
 +
 +    impl A {
 +        pub(crate) fn foo(x: B) {}
 +    }
 +}
 +        ",
 +        )
 +    }
 +
 +    #[test]
 +    fn test_issue_11766() {
 +        //https://github.com/rust-lang/rust-analyzer/issues/11766
 +        check_assist(
 +            extract_module,
 +            r"
 +            mod x {
 +                pub struct Foo;
 +                pub struct Bar;
 +            }
 +
 +            use x::{Bar, Foo};
 +
 +            $0type A = (Foo, Bar);$0
 +        ",
 +            r"
 +            mod x {
 +                pub struct Foo;
 +                pub struct Bar;
 +            }
 +
 +            use x::{};
 +
 +            mod modname {
 +                use super::x::Bar;
 +
 +                use super::x::Foo;
 +
 +                pub(crate) type A = (Foo, Bar);
 +            }
 +        ",
 +        )
 +    }
 +
 +    #[test]
 +    fn test_issue_12790() {
 +        check_assist(
 +            extract_module,
 +            r"
 +            $0/// A documented function
 +            fn documented_fn() {}
 +
 +            // A commented function with a #[] attribute macro
 +            #[cfg(test)]
 +            fn attribute_fn() {}
 +
 +            // A normally commented function
 +            fn normal_fn() {}
 +
 +            /// A documented Struct
 +            struct DocumentedStruct {
 +                // Normal field
 +                x: i32,
 +
 +                /// Documented field
 +                y: i32,
 +
 +                // Macroed field
 +                #[cfg(test)]
 +                z: i32,
 +            }
 +
 +            // A macroed Struct
 +            #[cfg(test)]
 +            struct MacroedStruct {
 +                // Normal field
 +                x: i32,
 +
 +                /// Documented field
 +                y: i32,
 +
 +                // Macroed field
 +                #[cfg(test)]
 +                z: i32,
 +            }
 +
 +            // A normal Struct
 +            struct NormalStruct {
 +                // Normal field
 +                x: i32,
 +
 +                /// Documented field
 +                y: i32,
 +
 +                // Macroed field
 +                #[cfg(test)]
 +                z: i32,
 +            }
 +
 +            /// A documented type
 +            type DocumentedType = i32;
 +
 +            // A macroed type
 +            #[cfg(test)]
 +            type MacroedType = i32;
 +
 +            /// A module to move
 +            mod module {}
 +
 +            /// An impl to move
 +            impl NormalStruct {
 +                /// A method
 +                fn new() {}
 +            }
 +
 +            /// A documented trait
 +            trait DocTrait {
 +                /// Inner function
 +                fn doc() {}
 +            }
 +
 +            /// An enum
 +            enum DocumentedEnum {
 +                /// A variant
 +                A,
 +                /// Another variant
 +                B { x: i32, y: i32 }
 +            }
 +
 +            /// Documented const
 +            const MY_CONST: i32 = 0;$0
 +        ",
 +            r"
 +            mod modname {
 +                /// A documented function
 +                pub(crate) fn documented_fn() {}
 +
 +                // A commented function with a #[] attribute macro
 +                #[cfg(test)]
 +                pub(crate) fn attribute_fn() {}
 +
 +                // A normally commented function
 +                pub(crate) fn normal_fn() {}
 +
 +                /// A documented Struct
 +                pub(crate) struct DocumentedStruct {
 +                    // Normal field
 +                    pub(crate) x: i32,
 +
 +                    /// Documented field
 +                    pub(crate) y: i32,
 +
 +                    // Macroed field
 +                    #[cfg(test)]
 +                    pub(crate) z: i32,
 +                }
 +
 +                // A macroed Struct
 +                #[cfg(test)]
 +                pub(crate) struct MacroedStruct {
 +                    // Normal field
 +                    pub(crate) x: i32,
 +
 +                    /// Documented field
 +                    pub(crate) y: i32,
 +
 +                    // Macroed field
 +                    #[cfg(test)]
 +                    pub(crate) z: i32,
 +                }
 +
 +                // A normal Struct
 +                pub(crate) struct NormalStruct {
 +                    // Normal field
 +                    pub(crate) x: i32,
 +
 +                    /// Documented field
 +                    pub(crate) y: i32,
 +
 +                    // Macroed field
 +                    #[cfg(test)]
 +                    pub(crate) z: i32,
 +                }
 +
 +                /// A documented type
 +                pub(crate) type DocumentedType = i32;
 +
 +                // A macroed type
 +                #[cfg(test)]
 +                pub(crate) type MacroedType = i32;
 +
 +                /// A module to move
 +                pub(crate) mod module {}
 +
 +                /// An impl to move
 +                impl NormalStruct {
 +                    /// A method
 +                    pub(crate) fn new() {}
 +                }
 +
 +                /// A documented trait
 +                pub(crate) trait DocTrait {
 +                    /// Inner function
 +                    fn doc() {}
 +                }
 +
 +                /// An enum
 +                pub(crate) enum DocumentedEnum {
 +                    /// A variant
 +                    A,
 +                    /// Another variant
 +                    B { x: i32, y: i32 }
 +                }
 +
 +                /// Documented const
 +                pub(crate) const MY_CONST: i32 = 0;
 +            }
 +        ",
 +        )
 +    }
 +}
index 970e948dfd930f4226be3c9697f0e19dead8d044,0000000000000000000000000000000000000000..b4e10667b07abe4a4bf196973bdcadd98018fe92
mode 100644,000000..100644
--- /dev/null
@@@ -1,1068 -1,0 +1,1072 @@@
-     let ty = generics
 +use std::iter;
 +
 +use either::Either;
 +use hir::{Module, ModuleDef, Name, Variant};
 +use ide_db::{
 +    defs::Definition,
 +    helpers::mod_path_to_ast,
 +    imports::insert_use::{insert_use, ImportScope, InsertUseConfig},
 +    search::FileReference,
 +    FxHashSet, RootDatabase,
 +};
 +use itertools::Itertools;
 +use syntax::{
 +    ast::{
 +        self, edit::IndentLevel, edit_in_place::Indent, make, AstNode, HasAttrs, HasGenericParams,
 +        HasName, HasVisibility,
 +    },
 +    match_ast, ted, SyntaxElement,
 +    SyntaxKind::*,
 +    SyntaxNode, T,
 +};
 +
 +use crate::{assist_context::SourceChangeBuilder, AssistContext, AssistId, AssistKind, Assists};
 +
 +// Assist: extract_struct_from_enum_variant
 +//
 +// Extracts a struct from enum variant.
 +//
 +// ```
 +// enum A { $0One(u32, u32) }
 +// ```
 +// ->
 +// ```
 +// struct One(u32, u32);
 +//
 +// enum A { One(One) }
 +// ```
 +pub(crate) fn extract_struct_from_enum_variant(
 +    acc: &mut Assists,
 +    ctx: &AssistContext<'_>,
 +) -> Option<()> {
 +    let variant = ctx.find_node_at_offset::<ast::Variant>()?;
 +    let field_list = extract_field_list_if_applicable(&variant)?;
 +
 +    let variant_name = variant.name()?;
 +    let variant_hir = ctx.sema.to_def(&variant)?;
 +    if existing_definition(ctx.db(), &variant_name, &variant_hir) {
 +        cov_mark::hit!(test_extract_enum_not_applicable_if_struct_exists);
 +        return None;
 +    }
 +
 +    let enum_ast = variant.parent_enum();
 +    let enum_hir = ctx.sema.to_def(&enum_ast)?;
 +    let target = variant.syntax().text_range();
 +    acc.add(
 +        AssistId("extract_struct_from_enum_variant", AssistKind::RefactorRewrite),
 +        "Extract struct from enum variant",
 +        target,
 +        |builder| {
 +            let variant_hir_name = variant_hir.name(ctx.db());
 +            let enum_module_def = ModuleDef::from(enum_hir);
 +            let usages = Definition::Variant(variant_hir).usages(&ctx.sema).all();
 +
 +            let mut visited_modules_set = FxHashSet::default();
 +            let current_module = enum_hir.module(ctx.db());
 +            visited_modules_set.insert(current_module);
 +            // record file references of the file the def resides in, we only want to swap to the edited file in the builder once
 +            let mut def_file_references = None;
 +            for (file_id, references) in usages {
 +                if file_id == ctx.file_id() {
 +                    def_file_references = Some(references);
 +                    continue;
 +                }
 +                builder.edit_file(file_id);
 +                let processed = process_references(
 +                    ctx,
 +                    builder,
 +                    &mut visited_modules_set,
 +                    &enum_module_def,
 +                    &variant_hir_name,
 +                    references,
 +                );
 +                processed.into_iter().for_each(|(path, node, import)| {
 +                    apply_references(ctx.config.insert_use, path, node, import)
 +                });
 +            }
 +            builder.edit_file(ctx.file_id());
 +
 +            let variant = builder.make_mut(variant.clone());
 +            if let Some(references) = def_file_references {
 +                let processed = process_references(
 +                    ctx,
 +                    builder,
 +                    &mut visited_modules_set,
 +                    &enum_module_def,
 +                    &variant_hir_name,
 +                    references,
 +                );
 +                processed.into_iter().for_each(|(path, node, import)| {
 +                    apply_references(ctx.config.insert_use, path, node, import)
 +                });
 +            }
 +
 +            let generic_params = enum_ast
 +                .generic_param_list()
 +                .and_then(|known_generics| extract_generic_params(&known_generics, &field_list));
 +            let generics = generic_params.as_ref().map(|generics| generics.clone_for_update());
 +            let def =
 +                create_struct_def(variant_name.clone(), &variant, &field_list, generics, &enum_ast);
 +
 +            let enum_ast = variant.parent_enum();
 +            let indent = enum_ast.indent_level();
 +            def.reindent_to(indent);
 +
 +            ted::insert_all(
 +                ted::Position::before(enum_ast.syntax()),
 +                vec![
 +                    def.syntax().clone().into(),
 +                    make::tokens::whitespace(&format!("\n\n{indent}")).into(),
 +                ],
 +            );
 +
 +            update_variant(&variant, generic_params.map(|g| g.clone_for_update()));
 +        },
 +    )
 +}
 +
 +fn extract_field_list_if_applicable(
 +    variant: &ast::Variant,
 +) -> Option<Either<ast::RecordFieldList, ast::TupleFieldList>> {
 +    match variant.kind() {
 +        ast::StructKind::Record(field_list) if field_list.fields().next().is_some() => {
 +            Some(Either::Left(field_list))
 +        }
 +        ast::StructKind::Tuple(field_list) if field_list.fields().count() > 1 => {
 +            Some(Either::Right(field_list))
 +        }
 +        _ => None,
 +    }
 +}
 +
 +fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &Variant) -> bool {
 +    variant
 +        .parent_enum(db)
 +        .module(db)
 +        .scope(db, None)
 +        .into_iter()
 +        .filter(|(_, def)| match def {
 +            // only check type-namespace
 +            hir::ScopeDef::ModuleDef(def) => matches!(
 +                def,
 +                ModuleDef::Module(_)
 +                    | ModuleDef::Adt(_)
 +                    | ModuleDef::Variant(_)
 +                    | ModuleDef::Trait(_)
 +                    | ModuleDef::TypeAlias(_)
 +                    | ModuleDef::BuiltinType(_)
 +            ),
 +            _ => false,
 +        })
 +        .any(|(name, _)| name.to_string() == variant_name.to_string())
 +}
 +
 +fn extract_generic_params(
 +    known_generics: &ast::GenericParamList,
 +    field_list: &Either<ast::RecordFieldList, ast::TupleFieldList>,
 +) -> Option<ast::GenericParamList> {
 +    let mut generics = known_generics.generic_params().map(|param| (param, false)).collect_vec();
 +
 +    let tagged_one = match field_list {
 +        Either::Left(field_list) => field_list
 +            .fields()
 +            .filter_map(|f| f.ty())
 +            .fold(false, |tagged, ty| tag_generics_in_variant(&ty, &mut generics) || tagged),
 +        Either::Right(field_list) => field_list
 +            .fields()
 +            .filter_map(|f| f.ty())
 +            .fold(false, |tagged, ty| tag_generics_in_variant(&ty, &mut generics) || tagged),
 +    };
 +
 +    let generics = generics.into_iter().filter_map(|(param, tag)| tag.then(|| param));
 +    tagged_one.then(|| make::generic_param_list(generics))
 +}
 +
 +fn tag_generics_in_variant(ty: &ast::Type, generics: &mut [(ast::GenericParam, bool)]) -> bool {
 +    let mut tagged_one = false;
 +
 +    for token in ty.syntax().descendants_with_tokens().filter_map(SyntaxElement::into_token) {
 +        for (param, tag) in generics.iter_mut().filter(|(_, tag)| !tag) {
 +            match param {
 +                ast::GenericParam::LifetimeParam(lt)
 +                    if matches!(token.kind(), T![lifetime_ident]) =>
 +                {
 +                    if let Some(lt) = lt.lifetime() {
 +                        if lt.text().as_str() == token.text() {
 +                            *tag = true;
 +                            tagged_one = true;
 +                            break;
 +                        }
 +                    }
 +                }
 +                param if matches!(token.kind(), T![ident]) => {
 +                    if match param {
 +                        ast::GenericParam::ConstParam(konst) => konst
 +                            .name()
 +                            .map(|name| name.text().as_str() == token.text())
 +                            .unwrap_or_default(),
 +                        ast::GenericParam::TypeParam(ty) => ty
 +                            .name()
 +                            .map(|name| name.text().as_str() == token.text())
 +                            .unwrap_or_default(),
 +                        ast::GenericParam::LifetimeParam(lt) => lt
 +                            .lifetime()
 +                            .map(|lt| lt.text().as_str() == token.text())
 +                            .unwrap_or_default(),
 +                    } {
 +                        *tag = true;
 +                        tagged_one = true;
 +                        break;
 +                    }
 +                }
 +                _ => (),
 +            }
 +        }
 +    }
 +
 +    tagged_one
 +}
 +
 +fn create_struct_def(
 +    name: ast::Name,
 +    variant: &ast::Variant,
 +    field_list: &Either<ast::RecordFieldList, ast::TupleFieldList>,
 +    generics: Option<ast::GenericParamList>,
 +    enum_: &ast::Enum,
 +) -> ast::Struct {
 +    let enum_vis = enum_.visibility();
 +
 +    let insert_vis = |node: &'_ SyntaxNode, vis: &'_ SyntaxNode| {
 +        let vis = vis.clone_for_update();
 +        ted::insert(ted::Position::before(node), vis);
 +    };
 +
 +    // for fields without any existing visibility, use visibility of enum
 +    let field_list: ast::FieldList = match field_list {
 +        Either::Left(field_list) => {
 +            let field_list = field_list.clone_for_update();
 +
 +            if let Some(vis) = &enum_vis {
 +                field_list
 +                    .fields()
 +                    .filter(|field| field.visibility().is_none())
 +                    .filter_map(|field| field.name())
 +                    .for_each(|it| insert_vis(it.syntax(), vis.syntax()));
 +            }
 +
 +            field_list.into()
 +        }
 +        Either::Right(field_list) => {
 +            let field_list = field_list.clone_for_update();
 +
 +            if let Some(vis) = &enum_vis {
 +                field_list
 +                    .fields()
 +                    .filter(|field| field.visibility().is_none())
 +                    .filter_map(|field| field.ty())
 +                    .for_each(|it| insert_vis(it.syntax(), vis.syntax()));
 +            }
 +
 +            field_list.into()
 +        }
 +    };
 +    field_list.reindent_to(IndentLevel::single());
 +
 +    let strukt = make::struct_(enum_vis, name, generics, field_list).clone_for_update();
 +
 +    // take comments from variant
 +    ted::insert_all(
 +        ted::Position::first_child_of(strukt.syntax()),
 +        take_all_comments(variant.syntax()),
 +    );
 +
 +    // copy attributes from enum
 +    ted::insert_all(
 +        ted::Position::first_child_of(strukt.syntax()),
 +        enum_
 +            .attrs()
 +            .flat_map(|it| {
 +                vec![it.syntax().clone_for_update().into(), make::tokens::single_newline().into()]
 +            })
 +            .collect(),
 +    );
 +
 +    strukt
 +}
 +
 +fn update_variant(variant: &ast::Variant, generics: Option<ast::GenericParamList>) -> Option<()> {
 +    let name = variant.name()?;
-         .map(|generics| make::ty(&format!("{}{}", &name.text(), generics.to_generic_args())))
-         .unwrap_or_else(|| make::ty(&name.text()));
++    let generic_args = generics
 +        .filter(|generics| generics.generic_params().count() > 0)
++        .map(|generics| generics.to_generic_args());
++    // FIXME: replace with a `ast::make` constructor
++    let ty = match generic_args {
++        Some(generic_args) => make::ty(&format!("{name}{generic_args}")),
++        None => make::ty(&name.text()),
++    };
 +
 +    // change from a record to a tuple field list
 +    let tuple_field = make::tuple_field(None, ty);
 +    let field_list = make::tuple_field_list(iter::once(tuple_field)).clone_for_update();
 +    ted::replace(variant.field_list()?.syntax(), field_list.syntax());
 +
 +    // remove any ws after the name
 +    if let Some(ws) = name
 +        .syntax()
 +        .siblings_with_tokens(syntax::Direction::Next)
 +        .find_map(|tok| tok.into_token().filter(|tok| tok.kind() == WHITESPACE))
 +    {
 +        ted::remove(SyntaxElement::Token(ws));
 +    }
 +
 +    Some(())
 +}
 +
 +// Note: this also detaches whitespace after comments,
 +// since `SyntaxNode::splice_children` (and by extension `ted::insert_all_raw`)
 +// detaches nodes. If we only took the comments, we'd leave behind the old whitespace.
 +fn take_all_comments(node: &SyntaxNode) -> Vec<SyntaxElement> {
 +    let mut remove_next_ws = false;
 +    node.children_with_tokens()
 +        .filter_map(move |child| match child.kind() {
 +            COMMENT => {
 +                remove_next_ws = true;
 +                child.detach();
 +                Some(child)
 +            }
 +            WHITESPACE if remove_next_ws => {
 +                remove_next_ws = false;
 +                child.detach();
 +                Some(make::tokens::single_newline().into())
 +            }
 +            _ => {
 +                remove_next_ws = false;
 +                None
 +            }
 +        })
 +        .collect()
 +}
 +
 +fn apply_references(
 +    insert_use_cfg: InsertUseConfig,
 +    segment: ast::PathSegment,
 +    node: SyntaxNode,
 +    import: Option<(ImportScope, hir::ModPath)>,
 +) {
 +    if let Some((scope, path)) = import {
 +        insert_use(&scope, mod_path_to_ast(&path), &insert_use_cfg);
 +    }
 +    // deep clone to prevent cycle
 +    let path = make::path_from_segments(iter::once(segment.clone_subtree()), false);
 +    ted::insert_raw(ted::Position::before(segment.syntax()), path.clone_for_update().syntax());
 +    ted::insert_raw(ted::Position::before(segment.syntax()), make::token(T!['(']));
 +    ted::insert_raw(ted::Position::after(&node), make::token(T![')']));
 +}
 +
 +fn process_references(
 +    ctx: &AssistContext<'_>,
 +    builder: &mut SourceChangeBuilder,
 +    visited_modules: &mut FxHashSet<Module>,
 +    enum_module_def: &ModuleDef,
 +    variant_hir_name: &Name,
 +    refs: Vec<FileReference>,
 +) -> Vec<(ast::PathSegment, SyntaxNode, Option<(ImportScope, hir::ModPath)>)> {
 +    // we have to recollect here eagerly as we are about to edit the tree we need to calculate the changes
 +    // and corresponding nodes up front
 +    refs.into_iter()
 +        .flat_map(|reference| {
 +            let (segment, scope_node, module) = reference_to_node(&ctx.sema, reference)?;
 +            let segment = builder.make_mut(segment);
 +            let scope_node = builder.make_syntax_mut(scope_node);
 +            if !visited_modules.contains(&module) {
 +                let mod_path = module.find_use_path_prefixed(
 +                    ctx.sema.db,
 +                    *enum_module_def,
 +                    ctx.config.insert_use.prefix_kind,
 +                    ctx.config.prefer_no_std,
 +                );
 +                if let Some(mut mod_path) = mod_path {
 +                    mod_path.pop_segment();
 +                    mod_path.push_segment(variant_hir_name.clone());
 +                    let scope = ImportScope::find_insert_use_container(&scope_node, &ctx.sema)?;
 +                    visited_modules.insert(module);
 +                    return Some((segment, scope_node, Some((scope, mod_path))));
 +                }
 +            }
 +            Some((segment, scope_node, None))
 +        })
 +        .collect()
 +}
 +
 +fn reference_to_node(
 +    sema: &hir::Semantics<'_, RootDatabase>,
 +    reference: FileReference,
 +) -> Option<(ast::PathSegment, SyntaxNode, hir::Module)> {
 +    let segment =
 +        reference.name.as_name_ref()?.syntax().parent().and_then(ast::PathSegment::cast)?;
 +    let parent = segment.parent_path().syntax().parent()?;
 +    let expr_or_pat = match_ast! {
 +        match parent {
 +            ast::PathExpr(_it) => parent.parent()?,
 +            ast::RecordExpr(_it) => parent,
 +            ast::TupleStructPat(_it) => parent,
 +            ast::RecordPat(_it) => parent,
 +            _ => return None,
 +        }
 +    };
 +    let module = sema.scope(&expr_or_pat)?.module();
 +    Some((segment, expr_or_pat, module))
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use crate::tests::{check_assist, check_assist_not_applicable};
 +
 +    use super::*;
 +
 +    #[test]
 +    fn test_extract_struct_several_fields_tuple() {
 +        check_assist(
 +            extract_struct_from_enum_variant,
 +            "enum A { $0One(u32, u32) }",
 +            r#"struct One(u32, u32);
 +
 +enum A { One(One) }"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_struct_several_fields_named() {
 +        check_assist(
 +            extract_struct_from_enum_variant,
 +            "enum A { $0One { foo: u32, bar: u32 } }",
 +            r#"struct One{ foo: u32, bar: u32 }
 +
 +enum A { One(One) }"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_struct_one_field_named() {
 +        check_assist(
 +            extract_struct_from_enum_variant,
 +            "enum A { $0One { foo: u32 } }",
 +            r#"struct One{ foo: u32 }
 +
 +enum A { One(One) }"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_struct_carries_over_generics() {
 +        check_assist(
 +            extract_struct_from_enum_variant,
 +            r"enum En<T> { Var { a: T$0 } }",
 +            r#"struct Var<T>{ a: T }
 +
 +enum En<T> { Var(Var<T>) }"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_struct_carries_over_attributes() {
 +        check_assist(
 +            extract_struct_from_enum_variant,
 +            r#"
 +#[derive(Debug)]
 +#[derive(Clone)]
 +enum Enum { Variant{ field: u32$0 } }"#,
 +            r#"
 +#[derive(Debug)]
 +#[derive(Clone)]
 +struct Variant{ field: u32 }
 +
 +#[derive(Debug)]
 +#[derive(Clone)]
 +enum Enum { Variant(Variant) }"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_struct_indent_to_parent_enum() {
 +        check_assist(
 +            extract_struct_from_enum_variant,
 +            r#"
 +enum Enum {
 +    Variant {
 +        field: u32$0
 +    }
 +}"#,
 +            r#"
 +struct Variant{
 +    field: u32
 +}
 +
 +enum Enum {
 +    Variant(Variant)
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_struct_indent_to_parent_enum_in_mod() {
 +        check_assist(
 +            extract_struct_from_enum_variant,
 +            r#"
 +mod indenting {
 +    enum Enum {
 +        Variant {
 +            field: u32$0
 +        }
 +    }
 +}"#,
 +            r#"
 +mod indenting {
 +    struct Variant{
 +        field: u32
 +    }
 +
 +    enum Enum {
 +        Variant(Variant)
 +    }
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_struct_keep_comments_and_attrs_one_field_named() {
 +        check_assist(
 +            extract_struct_from_enum_variant,
 +            r#"
 +enum A {
 +    $0One {
 +        // leading comment
 +        /// doc comment
 +        #[an_attr]
 +        foo: u32
 +        // trailing comment
 +    }
 +}"#,
 +            r#"
 +struct One{
 +    // leading comment
 +    /// doc comment
 +    #[an_attr]
 +    foo: u32
 +    // trailing comment
 +}
 +
 +enum A {
 +    One(One)
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_struct_keep_comments_and_attrs_several_fields_named() {
 +        check_assist(
 +            extract_struct_from_enum_variant,
 +            r#"
 +enum A {
 +    $0One {
 +        // comment
 +        /// doc
 +        #[attr]
 +        foo: u32,
 +        // comment
 +        #[attr]
 +        /// doc
 +        bar: u32
 +    }
 +}"#,
 +            r#"
 +struct One{
 +    // comment
 +    /// doc
 +    #[attr]
 +    foo: u32,
 +    // comment
 +    #[attr]
 +    /// doc
 +    bar: u32
 +}
 +
 +enum A {
 +    One(One)
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_struct_keep_comments_and_attrs_several_fields_tuple() {
 +        check_assist(
 +            extract_struct_from_enum_variant,
 +            "enum A { $0One(/* comment */ #[attr] u32, /* another */ u32 /* tail */) }",
 +            r#"
 +struct One(/* comment */ #[attr] u32, /* another */ u32 /* tail */);
 +
 +enum A { One(One) }"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_struct_move_struct_variant_comments() {
 +        check_assist(
 +            extract_struct_from_enum_variant,
 +            r#"
 +enum A {
 +    /* comment */
 +    // other
 +    /// comment
 +    #[attr]
 +    $0One {
 +        a: u32
 +    }
 +}"#,
 +            r#"
 +/* comment */
 +// other
 +/// comment
 +struct One{
 +    a: u32
 +}
 +
 +enum A {
 +    #[attr]
 +    One(One)
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_struct_move_tuple_variant_comments() {
 +        check_assist(
 +            extract_struct_from_enum_variant,
 +            r#"
 +enum A {
 +    /* comment */
 +    // other
 +    /// comment
 +    #[attr]
 +    $0One(u32, u32)
 +}"#,
 +            r#"
 +/* comment */
 +// other
 +/// comment
 +struct One(u32, u32);
 +
 +enum A {
 +    #[attr]
 +    One(One)
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_struct_keep_existing_visibility_named() {
 +        check_assist(
 +            extract_struct_from_enum_variant,
 +            "enum A { $0One{ a: u32, pub(crate) b: u32, pub(super) c: u32, d: u32 } }",
 +            r#"
 +struct One{ a: u32, pub(crate) b: u32, pub(super) c: u32, d: u32 }
 +
 +enum A { One(One) }"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_struct_keep_existing_visibility_tuple() {
 +        check_assist(
 +            extract_struct_from_enum_variant,
 +            "enum A { $0One(u32, pub(crate) u32, pub(super) u32, u32) }",
 +            r#"
 +struct One(u32, pub(crate) u32, pub(super) u32, u32);
 +
 +enum A { One(One) }"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_enum_variant_name_value_namespace() {
 +        check_assist(
 +            extract_struct_from_enum_variant,
 +            r#"const One: () = ();
 +enum A { $0One(u32, u32) }"#,
 +            r#"const One: () = ();
 +struct One(u32, u32);
 +
 +enum A { One(One) }"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_struct_no_visibility() {
 +        check_assist(
 +            extract_struct_from_enum_variant,
 +            "enum A { $0One(u32, u32) }",
 +            r#"
 +struct One(u32, u32);
 +
 +enum A { One(One) }"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_struct_pub_visibility() {
 +        check_assist(
 +            extract_struct_from_enum_variant,
 +            "pub enum A { $0One(u32, u32) }",
 +            r#"
 +pub struct One(pub u32, pub u32);
 +
 +pub enum A { One(One) }"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_struct_pub_in_mod_visibility() {
 +        check_assist(
 +            extract_struct_from_enum_variant,
 +            "pub(in something) enum A { $0One{ a: u32, b: u32 } }",
 +            r#"
 +pub(in something) struct One{ pub(in something) a: u32, pub(in something) b: u32 }
 +
 +pub(in something) enum A { One(One) }"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_struct_pub_crate_visibility() {
 +        check_assist(
 +            extract_struct_from_enum_variant,
 +            "pub(crate) enum A { $0One{ a: u32, b: u32, c: u32 } }",
 +            r#"
 +pub(crate) struct One{ pub(crate) a: u32, pub(crate) b: u32, pub(crate) c: u32 }
 +
 +pub(crate) enum A { One(One) }"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_struct_with_complex_imports() {
 +        check_assist(
 +            extract_struct_from_enum_variant,
 +            r#"mod my_mod {
 +    fn another_fn() {
 +        let m = my_other_mod::MyEnum::MyField(1, 1);
 +    }
 +
 +    pub mod my_other_mod {
 +        fn another_fn() {
 +            let m = MyEnum::MyField(1, 1);
 +        }
 +
 +        pub enum MyEnum {
 +            $0MyField(u8, u8),
 +        }
 +    }
 +}
 +
 +fn another_fn() {
 +    let m = my_mod::my_other_mod::MyEnum::MyField(1, 1);
 +}"#,
 +            r#"use my_mod::my_other_mod::MyField;
 +
 +mod my_mod {
 +    use self::my_other_mod::MyField;
 +
 +    fn another_fn() {
 +        let m = my_other_mod::MyEnum::MyField(MyField(1, 1));
 +    }
 +
 +    pub mod my_other_mod {
 +        fn another_fn() {
 +            let m = MyEnum::MyField(MyField(1, 1));
 +        }
 +
 +        pub struct MyField(pub u8, pub u8);
 +
 +        pub enum MyEnum {
 +            MyField(MyField),
 +        }
 +    }
 +}
 +
 +fn another_fn() {
 +    let m = my_mod::my_other_mod::MyEnum::MyField(MyField(1, 1));
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn extract_record_fix_references() {
 +        check_assist(
 +            extract_struct_from_enum_variant,
 +            r#"
 +enum E {
 +    $0V { i: i32, j: i32 }
 +}
 +
 +fn f() {
 +    let E::V { i, j } = E::V { i: 9, j: 2 };
 +}
 +"#,
 +            r#"
 +struct V{ i: i32, j: i32 }
 +
 +enum E {
 +    V(V)
 +}
 +
 +fn f() {
 +    let E::V(V { i, j }) = E::V(V { i: 9, j: 2 });
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn extract_record_fix_references2() {
 +        check_assist(
 +            extract_struct_from_enum_variant,
 +            r#"
 +enum E {
 +    $0V(i32, i32)
 +}
 +
 +fn f() {
 +    let E::V(i, j) = E::V(9, 2);
 +}
 +"#,
 +            r#"
 +struct V(i32, i32);
 +
 +enum E {
 +    V(V)
 +}
 +
 +fn f() {
 +    let E::V(V(i, j)) = E::V(V(9, 2));
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn test_several_files() {
 +        check_assist(
 +            extract_struct_from_enum_variant,
 +            r#"
 +//- /main.rs
 +enum E {
 +    $0V(i32, i32)
 +}
 +mod foo;
 +
 +//- /foo.rs
 +use crate::E;
 +fn f() {
 +    let e = E::V(9, 2);
 +}
 +"#,
 +            r#"
 +//- /main.rs
 +struct V(i32, i32);
 +
 +enum E {
 +    V(V)
 +}
 +mod foo;
 +
 +//- /foo.rs
 +use crate::{E, V};
 +fn f() {
 +    let e = E::V(V(9, 2));
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn test_several_files_record() {
 +        check_assist(
 +            extract_struct_from_enum_variant,
 +            r#"
 +//- /main.rs
 +enum E {
 +    $0V { i: i32, j: i32 }
 +}
 +mod foo;
 +
 +//- /foo.rs
 +use crate::E;
 +fn f() {
 +    let e = E::V { i: 9, j: 2 };
 +}
 +"#,
 +            r#"
 +//- /main.rs
 +struct V{ i: i32, j: i32 }
 +
 +enum E {
 +    V(V)
 +}
 +mod foo;
 +
 +//- /foo.rs
 +use crate::{E, V};
 +fn f() {
 +    let e = E::V(V { i: 9, j: 2 });
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn test_extract_struct_record_nested_call_exp() {
 +        check_assist(
 +            extract_struct_from_enum_variant,
 +            r#"
 +enum A { $0One { a: u32, b: u32 } }
 +
 +struct B(A);
 +
 +fn foo() {
 +    let _ = B(A::One { a: 1, b: 2 });
 +}
 +"#,
 +            r#"
 +struct One{ a: u32, b: u32 }
 +
 +enum A { One(One) }
 +
 +struct B(A);
 +
 +fn foo() {
 +    let _ = B(A::One(One { a: 1, b: 2 }));
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_enum_not_applicable_for_element_with_no_fields() {
 +        check_assist_not_applicable(extract_struct_from_enum_variant, r#"enum A { $0One }"#);
 +    }
 +
 +    #[test]
 +    fn test_extract_enum_not_applicable_if_struct_exists() {
 +        cov_mark::check!(test_extract_enum_not_applicable_if_struct_exists);
 +        check_assist_not_applicable(
 +            extract_struct_from_enum_variant,
 +            r#"
 +struct One;
 +enum A { $0One(u8, u32) }
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_not_applicable_one_field() {
 +        check_assist_not_applicable(extract_struct_from_enum_variant, r"enum A { $0One(u32) }");
 +    }
 +
 +    #[test]
 +    fn test_extract_not_applicable_no_field_tuple() {
 +        check_assist_not_applicable(extract_struct_from_enum_variant, r"enum A { $0None() }");
 +    }
 +
 +    #[test]
 +    fn test_extract_not_applicable_no_field_named() {
 +        check_assist_not_applicable(extract_struct_from_enum_variant, r"enum A { $0None {} }");
 +    }
 +
 +    #[test]
 +    fn test_extract_struct_only_copies_needed_generics() {
 +        check_assist(
 +            extract_struct_from_enum_variant,
 +            r#"
 +enum X<'a, 'b, 'x> {
 +    $0A { a: &'a &'x mut () },
 +    B { b: &'b () },
 +    C { c: () },
 +}
 +"#,
 +            r#"
 +struct A<'a, 'x>{ a: &'a &'x mut () }
 +
 +enum X<'a, 'b, 'x> {
 +    A(A<'a, 'x>),
 +    B { b: &'b () },
 +    C { c: () },
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_struct_with_liftime_type_const() {
 +        check_assist(
 +            extract_struct_from_enum_variant,
 +            r#"
 +enum X<'b, T, V, const C: usize> {
 +    $0A { a: T, b: X<'b>, c: [u8; C] },
 +    D { d: V },
 +}
 +"#,
 +            r#"
 +struct A<'b, T, const C: usize>{ a: T, b: X<'b>, c: [u8; C] }
 +
 +enum X<'b, T, V, const C: usize> {
 +    A(A<'b, T, C>),
 +    D { d: V },
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_struct_without_generics() {
 +        check_assist(
 +            extract_struct_from_enum_variant,
 +            r#"
 +enum X<'a, 'b> {
 +    A { a: &'a () },
 +    B { b: &'b () },
 +    $0C { c: () },
 +}
 +"#,
 +            r#"
 +struct C{ c: () }
 +
 +enum X<'a, 'b> {
 +    A { a: &'a () },
 +    B { b: &'b () },
 +    C(C),
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_struct_keeps_trait_bounds() {
 +        check_assist(
 +            extract_struct_from_enum_variant,
 +            r#"
 +enum En<T: TraitT, V: TraitV> {
 +    $0A { a: T },
 +    B { b: V },
 +}
 +"#,
 +            r#"
 +struct A<T: TraitT>{ a: T }
 +
 +enum En<T: TraitT, V: TraitV> {
 +    A(A<T>),
 +    B { b: V },
 +}
 +"#,
 +        );
 +    }
 +}
index 03aa8601d14e1ef3e34207da916dd4574d175bbf,0000000000000000000000000000000000000000..3116935fc5e7590c81b86632cc0ba618fc9b4ea1
mode 100644,000000..100644
--- /dev/null
@@@ -1,404 -1,0 +1,392 @@@
- use itertools::Itertools;
 +use either::Either;
 +use ide_db::syntax_helpers::node_ext::walk_ty;
-     ast::{self, edit::IndentLevel, AstNode, HasGenericParams, HasName},
 +use syntax::{
-             let replacement = if !generics.is_empty() {
-                 format!(
-                     "Type<{}>",
-                     generics.iter().format_with(", ", |generic, f| {
-                         match generic {
-                             ast::GenericParam::ConstParam(cp) => f(&cp.name().unwrap()),
-                             ast::GenericParam::LifetimeParam(lp) => f(&lp.lifetime().unwrap()),
-                             ast::GenericParam::TypeParam(tp) => f(&tp.name().unwrap()),
-                         }
-                     })
-                 )
-             } else {
-                 String::from("Type")
-             };
++    ast::{self, edit::IndentLevel, make, AstNode, HasGenericParams, HasName},
 +    match_ast,
 +};
 +
 +use crate::{AssistContext, AssistId, AssistKind, Assists};
 +
 +// Assist: extract_type_alias
 +//
 +// Extracts the selected type as a type alias.
 +//
 +// ```
 +// struct S {
 +//     field: $0(u8, u8, u8)$0,
 +// }
 +// ```
 +// ->
 +// ```
 +// type $0Type = (u8, u8, u8);
 +//
 +// struct S {
 +//     field: Type,
 +// }
 +// ```
 +pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    if ctx.has_empty_selection() {
 +        return None;
 +    }
 +
 +    let ty = ctx.find_node_at_range::<ast::Type>()?;
 +    let item = ty.syntax().ancestors().find_map(ast::Item::cast)?;
 +    let assoc_owner = item.syntax().ancestors().nth(2).and_then(|it| {
 +        match_ast! {
 +            match it {
 +                ast::Trait(tr) => Some(Either::Left(tr)),
 +                ast::Impl(impl_) => Some(Either::Right(impl_)),
 +                _ => None,
 +            }
 +        }
 +    });
 +    let node = assoc_owner.as_ref().map_or_else(
 +        || item.syntax(),
 +        |impl_| impl_.as_ref().either(AstNode::syntax, AstNode::syntax),
 +    );
 +    let insert_pos = node.text_range().start();
 +    let target = ty.syntax().text_range();
 +
 +    acc.add(
 +        AssistId("extract_type_alias", AssistKind::RefactorExtract),
 +        "Extract type as type alias",
 +        target,
 +        |builder| {
 +            let mut known_generics = match item.generic_param_list() {
 +                Some(it) => it.generic_params().collect(),
 +                None => Vec::new(),
 +            };
 +            if let Some(it) = assoc_owner.as_ref().and_then(|it| match it {
 +                Either::Left(it) => it.generic_param_list(),
 +                Either::Right(it) => it.generic_param_list(),
 +            }) {
 +                known_generics.extend(it.generic_params());
 +            }
 +            let generics = collect_used_generics(&ty, &known_generics);
++            let generic_params =
++                generics.map(|it| make::generic_param_list(it.into_iter().cloned()));
 +
-             let generics = if !generics.is_empty() {
-                 format!("<{}>", generics.iter().format(", "))
-             } else {
-                 String::new()
-             };
++            let ty_args = generic_params
++                .as_ref()
++                .map_or(String::new(), |it| it.to_generic_args().to_string());
++            let replacement = format!("Type{ty_args}");
 +            builder.replace(target, replacement);
 +
 +            let indent = IndentLevel::from_node(node);
-                         format!("type $0Type{} = {};\n\n{}", generics, ty, indent),
++            let generic_params = generic_params.map_or(String::new(), |it| it.to_string());
 +            match ctx.config.snippet_cap {
 +                Some(cap) => {
 +                    builder.insert_snippet(
 +                        cap,
 +                        insert_pos,
-                         format!("type Type{} = {};\n\n{}", generics, ty, indent),
++                        format!("type $0Type{generic_params} = {ty};\n\n{indent}"),
 +                    );
 +                }
 +                None => {
 +                    builder.insert(
 +                        insert_pos,
- ) -> Vec<&'gp ast::GenericParam> {
++                        format!("type Type{generic_params} = {ty};\n\n{indent}"),
 +                    );
 +                }
 +            }
 +        },
 +    )
 +}
 +
 +fn collect_used_generics<'gp>(
 +    ty: &ast::Type,
 +    known_generics: &'gp [ast::GenericParam],
-     generics
++) -> Option<Vec<&'gp ast::GenericParam>> {
 +    // can't use a closure -> closure here cause lifetime inference fails for that
 +    fn find_lifetime(text: &str) -> impl Fn(&&ast::GenericParam) -> bool + '_ {
 +        move |gp: &&ast::GenericParam| match gp {
 +            ast::GenericParam::LifetimeParam(lp) => {
 +                lp.lifetime().map_or(false, |lt| lt.text() == text)
 +            }
 +            _ => false,
 +        }
 +    }
 +
 +    let mut generics = Vec::new();
 +    walk_ty(ty, &mut |ty| match ty {
 +        ast::Type::PathType(ty) => {
 +            if let Some(path) = ty.path() {
 +                if let Some(name_ref) = path.as_single_name_ref() {
 +                    if let Some(param) = known_generics.iter().find(|gp| {
 +                        match gp {
 +                            ast::GenericParam::ConstParam(cp) => cp.name(),
 +                            ast::GenericParam::TypeParam(tp) => tp.name(),
 +                            _ => None,
 +                        }
 +                        .map_or(false, |n| n.text() == name_ref.text())
 +                    }) {
 +                        generics.push(param);
 +                    }
 +                }
 +                generics.extend(
 +                    path.segments()
 +                        .filter_map(|seg| seg.generic_arg_list())
 +                        .flat_map(|it| it.generic_args())
 +                        .filter_map(|it| match it {
 +                            ast::GenericArg::LifetimeArg(lt) => {
 +                                let lt = lt.lifetime()?;
 +                                known_generics.iter().find(find_lifetime(&lt.text()))
 +                            }
 +                            _ => None,
 +                        }),
 +                );
 +            }
 +        }
 +        ast::Type::ImplTraitType(impl_ty) => {
 +            if let Some(it) = impl_ty.type_bound_list() {
 +                generics.extend(
 +                    it.bounds()
 +                        .filter_map(|it| it.lifetime())
 +                        .filter_map(|lt| known_generics.iter().find(find_lifetime(&lt.text()))),
 +                );
 +            }
 +        }
 +        ast::Type::DynTraitType(dyn_ty) => {
 +            if let Some(it) = dyn_ty.type_bound_list() {
 +                generics.extend(
 +                    it.bounds()
 +                        .filter_map(|it| it.lifetime())
 +                        .filter_map(|lt| known_generics.iter().find(find_lifetime(&lt.text()))),
 +                );
 +            }
 +        }
 +        ast::Type::RefType(ref_) => generics.extend(
 +            ref_.lifetime().and_then(|lt| known_generics.iter().find(find_lifetime(&lt.text()))),
 +        ),
 +        ast::Type::ArrayType(ar) => {
 +            if let Some(expr) = ar.expr() {
 +                if let ast::Expr::PathExpr(p) = expr {
 +                    if let Some(path) = p.path() {
 +                        if let Some(name_ref) = path.as_single_name_ref() {
 +                            if let Some(param) = known_generics.iter().find(|gp| {
 +                                if let ast::GenericParam::ConstParam(cp) = gp {
 +                                    cp.name().map_or(false, |n| n.text() == name_ref.text())
 +                                } else {
 +                                    false
 +                                }
 +                            }) {
 +                                generics.push(param);
 +                            }
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +        _ => (),
 +    });
 +    // stable resort to lifetime, type, const
 +    generics.sort_by_key(|gp| match gp {
 +        ast::GenericParam::ConstParam(_) => 2,
 +        ast::GenericParam::LifetimeParam(_) => 0,
 +        ast::GenericParam::TypeParam(_) => 1,
 +    });
++
++    Some(generics).filter(|it| it.len() > 0)
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use crate::tests::{check_assist, check_assist_not_applicable};
 +
 +    use super::*;
 +
 +    #[test]
 +    fn test_not_applicable_without_selection() {
 +        check_assist_not_applicable(
 +            extract_type_alias,
 +            r"
 +struct S {
 +    field: $0(u8, u8, u8),
 +}
 +            ",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_simple_types() {
 +        check_assist(
 +            extract_type_alias,
 +            r"
 +struct S {
 +    field: $0u8$0,
 +}
 +            ",
 +            r#"
 +type $0Type = u8;
 +
 +struct S {
 +    field: Type,
 +}
 +            "#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_generic_type_arg() {
 +        check_assist(
 +            extract_type_alias,
 +            r"
 +fn generic<T>() {}
 +
 +fn f() {
 +    generic::<$0()$0>();
 +}
 +            ",
 +            r#"
 +fn generic<T>() {}
 +
 +type $0Type = ();
 +
 +fn f() {
 +    generic::<Type>();
 +}
 +            "#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_inner_type_arg() {
 +        check_assist(
 +            extract_type_alias,
 +            r"
 +struct Vec<T> {}
 +struct S {
 +    v: Vec<Vec<$0Vec<u8>$0>>,
 +}
 +            ",
 +            r#"
 +struct Vec<T> {}
 +type $0Type = Vec<u8>;
 +
 +struct S {
 +    v: Vec<Vec<Type>>,
 +}
 +            "#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_inner_type() {
 +        check_assist(
 +            extract_type_alias,
 +            r"
 +struct S {
 +    field: ($0u8$0,),
 +}
 +            ",
 +            r#"
 +type $0Type = u8;
 +
 +struct S {
 +    field: (Type,),
 +}
 +            "#,
 +        );
 +    }
 +
 +    #[test]
 +    fn extract_from_impl_or_trait() {
 +        // When invoked in an impl/trait, extracted type alias should be placed next to the
 +        // impl/trait, not inside.
 +        check_assist(
 +            extract_type_alias,
 +            r#"
 +impl S {
 +    fn f() -> $0(u8, u8)$0 {}
 +}
 +            "#,
 +            r#"
 +type $0Type = (u8, u8);
 +
 +impl S {
 +    fn f() -> Type {}
 +}
 +            "#,
 +        );
 +        check_assist(
 +            extract_type_alias,
 +            r#"
 +trait Tr {
 +    fn f() -> $0(u8, u8)$0 {}
 +}
 +            "#,
 +            r#"
 +type $0Type = (u8, u8);
 +
 +trait Tr {
 +    fn f() -> Type {}
 +}
 +            "#,
 +        );
 +    }
 +
 +    #[test]
 +    fn indentation() {
 +        check_assist(
 +            extract_type_alias,
 +            r#"
 +mod m {
 +    fn f() -> $0u8$0 {}
 +}
 +            "#,
 +            r#"
 +mod m {
 +    type $0Type = u8;
 +
 +    fn f() -> Type {}
 +}
 +            "#,
 +        );
 +    }
 +
 +    #[test]
 +    fn generics() {
 +        check_assist(
 +            extract_type_alias,
 +            r#"
 +struct Struct<const C: usize>;
 +impl<'outer, Outer, const OUTER: usize> () {
 +    fn func<'inner, Inner, const INNER: usize>(_: $0&(Struct<INNER>, Struct<OUTER>, Outer, &'inner (), Inner, &'outer ())$0) {}
 +}
 +"#,
 +            r#"
 +struct Struct<const C: usize>;
 +type $0Type<'inner, 'outer, Outer, Inner, const INNER: usize, const OUTER: usize> = &(Struct<INNER>, Struct<OUTER>, Outer, &'inner (), Inner, &'outer ());
 +
 +impl<'outer, Outer, const OUTER: usize> () {
 +    fn func<'inner, Inner, const INNER: usize>(_: Type<'inner, 'outer, Outer, Inner, INNER, OUTER>) {}
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn issue_11197() {
 +        check_assist(
 +            extract_type_alias,
 +            r#"
 +struct Foo<T, const N: usize>
 +where
 +    [T; N]: Sized,
 +{
 +    arr: $0[T; N]$0,
 +}
 +            "#,
 +            r#"
 +type $0Type<T, const N: usize> = [T; N];
 +
 +struct Foo<T, const N: usize>
 +where
 +    [T; N]: Sized,
 +{
 +    arr: Type<T, N>,
 +}
 +            "#,
 +        );
 +    }
 +}
index 3596b6f82381b5ff1d6127ed162ca27b58aa972e,0000000000000000000000000000000000000000..a738deffb95b302c5d02e23fc1199bb025329792
mode 100644,000000..100644
--- /dev/null
@@@ -1,1279 -1,0 +1,1279 @@@
-                     format_to!(buf, "let {}{} = {}", var_modifier, var_name, reference_modifier)
 +use stdx::format_to;
 +use syntax::{
 +    ast::{self, AstNode},
 +    NodeOrToken,
 +    SyntaxKind::{
 +        BLOCK_EXPR, BREAK_EXPR, CLOSURE_EXPR, COMMENT, LOOP_EXPR, MATCH_ARM, MATCH_GUARD,
 +        PATH_EXPR, RETURN_EXPR,
 +    },
 +    SyntaxNode,
 +};
 +
 +use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists};
 +
 +// Assist: extract_variable
 +//
 +// Extracts subexpression into a variable.
 +//
 +// ```
 +// fn main() {
 +//     $0(1 + 2)$0 * 4;
 +// }
 +// ```
 +// ->
 +// ```
 +// fn main() {
 +//     let $0var_name = (1 + 2);
 +//     var_name * 4;
 +// }
 +// ```
 +pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    if ctx.has_empty_selection() {
 +        return None;
 +    }
 +
 +    let node = match ctx.covering_element() {
 +        NodeOrToken::Node(it) => it,
 +        NodeOrToken::Token(it) if it.kind() == COMMENT => {
 +            cov_mark::hit!(extract_var_in_comment_is_not_applicable);
 +            return None;
 +        }
 +        NodeOrToken::Token(it) => it.parent()?,
 +    };
 +    let node = node.ancestors().take_while(|anc| anc.text_range() == node.text_range()).last()?;
 +    let to_extract = node
 +        .descendants()
 +        .take_while(|it| ctx.selection_trimmed().contains_range(it.text_range()))
 +        .find_map(valid_target_expr)?;
 +
 +    if let Some(ty_info) = ctx.sema.type_of_expr(&to_extract) {
 +        if ty_info.adjusted().is_unit() {
 +            return None;
 +        }
 +    }
 +
 +    let reference_modifier = match get_receiver_type(ctx, &to_extract) {
 +        Some(receiver_type) if receiver_type.is_mutable_reference() => "&mut ",
 +        Some(receiver_type) if receiver_type.is_reference() => "&",
 +        _ => "",
 +    };
 +
 +    let parent_ref_expr = to_extract.syntax().parent().and_then(ast::RefExpr::cast);
 +    let var_modifier = match parent_ref_expr {
 +        Some(expr) if expr.mut_token().is_some() => "mut ",
 +        _ => "",
 +    };
 +
 +    let anchor = Anchor::from(&to_extract)?;
 +    let indent = anchor.syntax().prev_sibling_or_token()?.as_token()?.clone();
 +    let target = to_extract.syntax().text_range();
 +    acc.add(
 +        AssistId("extract_variable", AssistKind::RefactorExtract),
 +        "Extract into variable",
 +        target,
 +        move |edit| {
 +            let field_shorthand =
 +                match to_extract.syntax().parent().and_then(ast::RecordExprField::cast) {
 +                    Some(field) => field.name_ref(),
 +                    None => None,
 +                };
 +
 +            let mut buf = String::new();
 +
 +            let var_name = match &field_shorthand {
 +                Some(it) => it.to_string(),
 +                None => suggest_name::for_variable(&to_extract, &ctx.sema),
 +            };
 +            let expr_range = match &field_shorthand {
 +                Some(it) => it.syntax().text_range().cover(to_extract.syntax().text_range()),
 +                None => to_extract.syntax().text_range(),
 +            };
 +
 +            match anchor {
 +                Anchor::Before(_) | Anchor::Replace(_) => {
-                     format_to!(buf, "{{ let {} = {}", var_name, reference_modifier)
++                    format_to!(buf, "let {var_modifier}{var_name} = {reference_modifier}")
 +                }
 +                Anchor::WrapInBlock(_) => {
-             format_to!(buf, "{}", to_extract.syntax());
++                    format_to!(buf, "{{ let {var_name} = {reference_modifier}")
 +                }
 +            };
-                             &format!("let {}{}", var_modifier, var_name),
-                             &format!("let {}$0{}", var_modifier, var_name),
++            format_to!(buf, "{to_extract}");
 +
 +            if let Anchor::Replace(stmt) = anchor {
 +                cov_mark::hit!(test_extract_var_expr_stmt);
 +                if stmt.semicolon_token().is_none() {
 +                    buf.push(';');
 +                }
 +                match ctx.config.snippet_cap {
 +                    Some(cap) => {
 +                        let snip = buf.replace(
-                         &format!("let {}{}", var_modifier, var_name),
-                         &format!("let {}$0{}", var_modifier, var_name),
++                            &format!("let {var_modifier}{var_name}"),
++                            &format!("let {var_modifier}$0{var_name}"),
 +                        );
 +                        edit.replace_snippet(cap, expr_range, snip)
 +                    }
 +                    None => edit.replace(expr_range, buf),
 +                }
 +                return;
 +            }
 +
 +            buf.push(';');
 +
 +            // We want to maintain the indent level,
 +            // but we do not want to duplicate possible
 +            // extra newlines in the indent block
 +            let text = indent.text();
 +            if text.starts_with('\n') {
 +                buf.push('\n');
 +                buf.push_str(text.trim_start_matches('\n'));
 +            } else {
 +                buf.push_str(text);
 +            }
 +
 +            edit.replace(expr_range, var_name.clone());
 +            let offset = anchor.syntax().text_range().start();
 +            match ctx.config.snippet_cap {
 +                Some(cap) => {
 +                    let snip = buf.replace(
++                        &format!("let {var_modifier}{var_name}"),
++                        &format!("let {var_modifier}$0{var_name}"),
 +                    );
 +                    edit.insert_snippet(cap, offset, snip)
 +                }
 +                None => edit.insert(offset, buf),
 +            }
 +
 +            if let Anchor::WrapInBlock(_) = anchor {
 +                edit.insert(anchor.syntax().text_range().end(), " }");
 +            }
 +        },
 +    )
 +}
 +
 +/// Check whether the node is a valid expression which can be extracted to a variable.
 +/// In general that's true for any expression, but in some cases that would produce invalid code.
 +fn valid_target_expr(node: SyntaxNode) -> Option<ast::Expr> {
 +    match node.kind() {
 +        PATH_EXPR | LOOP_EXPR => None,
 +        BREAK_EXPR => ast::BreakExpr::cast(node).and_then(|e| e.expr()),
 +        RETURN_EXPR => ast::ReturnExpr::cast(node).and_then(|e| e.expr()),
 +        BLOCK_EXPR => {
 +            ast::BlockExpr::cast(node).filter(|it| it.is_standalone()).map(ast::Expr::from)
 +        }
 +        _ => ast::Expr::cast(node),
 +    }
 +}
 +
 +fn get_receiver_type(ctx: &AssistContext<'_>, expression: &ast::Expr) -> Option<hir::Type> {
 +    let receiver = get_receiver(expression.clone())?;
 +    Some(ctx.sema.type_of_expr(&receiver)?.original())
 +}
 +
 +/// In the expression `a.b.c.x()`, find `a`
 +fn get_receiver(expression: ast::Expr) -> Option<ast::Expr> {
 +    match expression {
 +        ast::Expr::FieldExpr(field) if field.expr().is_some() => {
 +            let nested_expression = &field.expr()?;
 +            get_receiver(nested_expression.to_owned())
 +        }
 +        _ => Some(expression),
 +    }
 +}
 +
 +#[derive(Debug)]
 +enum Anchor {
 +    Before(SyntaxNode),
 +    Replace(ast::ExprStmt),
 +    WrapInBlock(SyntaxNode),
 +}
 +
 +impl Anchor {
 +    fn from(to_extract: &ast::Expr) -> Option<Anchor> {
 +        to_extract
 +            .syntax()
 +            .ancestors()
 +            .take_while(|it| !ast::Item::can_cast(it.kind()) || ast::MacroCall::can_cast(it.kind()))
 +            .find_map(|node| {
 +                if ast::MacroCall::can_cast(node.kind()) {
 +                    return None;
 +                }
 +                if let Some(expr) =
 +                    node.parent().and_then(ast::StmtList::cast).and_then(|it| it.tail_expr())
 +                {
 +                    if expr.syntax() == &node {
 +                        cov_mark::hit!(test_extract_var_last_expr);
 +                        return Some(Anchor::Before(node));
 +                    }
 +                }
 +
 +                if let Some(parent) = node.parent() {
 +                    if parent.kind() == CLOSURE_EXPR {
 +                        cov_mark::hit!(test_extract_var_in_closure_no_block);
 +                        return Some(Anchor::WrapInBlock(node));
 +                    }
 +                    if parent.kind() == MATCH_ARM {
 +                        if node.kind() == MATCH_GUARD {
 +                            cov_mark::hit!(test_extract_var_in_match_guard);
 +                        } else {
 +                            cov_mark::hit!(test_extract_var_in_match_arm_no_block);
 +                            return Some(Anchor::WrapInBlock(node));
 +                        }
 +                    }
 +                }
 +
 +                if let Some(stmt) = ast::Stmt::cast(node.clone()) {
 +                    if let ast::Stmt::ExprStmt(stmt) = stmt {
 +                        if stmt.expr().as_ref() == Some(to_extract) {
 +                            return Some(Anchor::Replace(stmt));
 +                        }
 +                    }
 +                    return Some(Anchor::Before(node));
 +                }
 +                None
 +            })
 +    }
 +
 +    fn syntax(&self) -> &SyntaxNode {
 +        match self {
 +            Anchor::Before(it) | Anchor::WrapInBlock(it) => it,
 +            Anchor::Replace(stmt) => stmt.syntax(),
 +        }
 +    }
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
 +
 +    use super::*;
 +
 +    #[test]
 +    fn test_extract_var_simple() {
 +        check_assist(
 +            extract_variable,
 +            r#"
 +fn foo() {
 +    foo($01 + 1$0);
 +}"#,
 +            r#"
 +fn foo() {
 +    let $0var_name = 1 + 1;
 +    foo(var_name);
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn extract_var_in_comment_is_not_applicable() {
 +        cov_mark::check!(extract_var_in_comment_is_not_applicable);
 +        check_assist_not_applicable(extract_variable, "fn main() { 1 + /* $0comment$0 */ 1; }");
 +    }
 +
 +    #[test]
 +    fn test_extract_var_expr_stmt() {
 +        cov_mark::check!(test_extract_var_expr_stmt);
 +        check_assist(
 +            extract_variable,
 +            r#"
 +fn foo() {
 +  $0  1 + 1$0;
 +}"#,
 +            r#"
 +fn foo() {
 +    let $0var_name = 1 + 1;
 +}"#,
 +        );
 +        check_assist(
 +            extract_variable,
 +            r"
 +fn foo() {
 +    $0{ let x = 0; x }$0
 +    something_else();
 +}",
 +            r"
 +fn foo() {
 +    let $0var_name = { let x = 0; x };
 +    something_else();
 +}",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_var_part_of_expr_stmt() {
 +        check_assist(
 +            extract_variable,
 +            r"
 +fn foo() {
 +    $01$0 + 1;
 +}",
 +            r"
 +fn foo() {
 +    let $0var_name = 1;
 +    var_name + 1;
 +}",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_var_last_expr() {
 +        cov_mark::check!(test_extract_var_last_expr);
 +        check_assist(
 +            extract_variable,
 +            r#"
 +fn foo() {
 +    bar($01 + 1$0)
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    let $0var_name = 1 + 1;
 +    bar(var_name)
 +}
 +"#,
 +        );
 +        check_assist(
 +            extract_variable,
 +            r#"
 +fn foo() -> i32 {
 +    $0bar(1 + 1)$0
 +}
 +
 +fn bar(i: i32) -> i32 {
 +    i
 +}
 +"#,
 +            r#"
 +fn foo() -> i32 {
 +    let $0bar = bar(1 + 1);
 +    bar
 +}
 +
 +fn bar(i: i32) -> i32 {
 +    i
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn test_extract_var_in_match_arm_no_block() {
 +        cov_mark::check!(test_extract_var_in_match_arm_no_block);
 +        check_assist(
 +            extract_variable,
 +            r#"
 +fn main() {
 +    let x = true;
 +    let tuple = match x {
 +        true => ($02 + 2$0, true)
 +        _ => (0, false)
 +    };
 +}
 +"#,
 +            r#"
 +fn main() {
 +    let x = true;
 +    let tuple = match x {
 +        true => { let $0var_name = 2 + 2; (var_name, true) }
 +        _ => (0, false)
 +    };
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_var_in_match_arm_with_block() {
 +        check_assist(
 +            extract_variable,
 +            r#"
 +fn main() {
 +    let x = true;
 +    let tuple = match x {
 +        true => {
 +            let y = 1;
 +            ($02 + y$0, true)
 +        }
 +        _ => (0, false)
 +    };
 +}
 +"#,
 +            r#"
 +fn main() {
 +    let x = true;
 +    let tuple = match x {
 +        true => {
 +            let y = 1;
 +            let $0var_name = 2 + y;
 +            (var_name, true)
 +        }
 +        _ => (0, false)
 +    };
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_var_in_match_guard() {
 +        cov_mark::check!(test_extract_var_in_match_guard);
 +        check_assist(
 +            extract_variable,
 +            r#"
 +fn main() {
 +    match () {
 +        () if $010 > 0$0 => 1
 +        _ => 2
 +    };
 +}
 +"#,
 +            r#"
 +fn main() {
 +    let $0var_name = 10 > 0;
 +    match () {
 +        () if var_name => 1
 +        _ => 2
 +    };
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_var_in_closure_no_block() {
 +        cov_mark::check!(test_extract_var_in_closure_no_block);
 +        check_assist(
 +            extract_variable,
 +            r#"
 +fn main() {
 +    let lambda = |x: u32| $0x * 2$0;
 +}
 +"#,
 +            r#"
 +fn main() {
 +    let lambda = |x: u32| { let $0var_name = x * 2; var_name };
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_var_in_closure_with_block() {
 +        check_assist(
 +            extract_variable,
 +            r#"
 +fn main() {
 +    let lambda = |x: u32| { $0x * 2$0 };
 +}
 +"#,
 +            r#"
 +fn main() {
 +    let lambda = |x: u32| { let $0var_name = x * 2; var_name };
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_var_path_simple() {
 +        check_assist(
 +            extract_variable,
 +            "
 +fn main() {
 +    let o = $0Some(true)$0;
 +}
 +",
 +            "
 +fn main() {
 +    let $0var_name = Some(true);
 +    let o = var_name;
 +}
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_var_path_method() {
 +        check_assist(
 +            extract_variable,
 +            "
 +fn main() {
 +    let v = $0bar.foo()$0;
 +}
 +",
 +            "
 +fn main() {
 +    let $0foo = bar.foo();
 +    let v = foo;
 +}
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_var_return() {
 +        check_assist(
 +            extract_variable,
 +            "
 +fn foo() -> u32 {
 +    $0return 2 + 2$0;
 +}
 +",
 +            "
 +fn foo() -> u32 {
 +    let $0var_name = 2 + 2;
 +    return var_name;
 +}
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_var_does_not_add_extra_whitespace() {
 +        check_assist(
 +            extract_variable,
 +            "
 +fn foo() -> u32 {
 +
 +
 +    $0return 2 + 2$0;
 +}
 +",
 +            "
 +fn foo() -> u32 {
 +
 +
 +    let $0var_name = 2 + 2;
 +    return var_name;
 +}
 +",
 +        );
 +
 +        check_assist(
 +            extract_variable,
 +            "
 +fn foo() -> u32 {
 +
 +        $0return 2 + 2$0;
 +}
 +",
 +            "
 +fn foo() -> u32 {
 +
 +        let $0var_name = 2 + 2;
 +        return var_name;
 +}
 +",
 +        );
 +
 +        check_assist(
 +            extract_variable,
 +            "
 +fn foo() -> u32 {
 +    let foo = 1;
 +
 +    // bar
 +
 +
 +    $0return 2 + 2$0;
 +}
 +",
 +            "
 +fn foo() -> u32 {
 +    let foo = 1;
 +
 +    // bar
 +
 +
 +    let $0var_name = 2 + 2;
 +    return var_name;
 +}
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_var_break() {
 +        check_assist(
 +            extract_variable,
 +            "
 +fn main() {
 +    let result = loop {
 +        $0break 2 + 2$0;
 +    };
 +}
 +",
 +            "
 +fn main() {
 +    let result = loop {
 +        let $0var_name = 2 + 2;
 +        break var_name;
 +    };
 +}
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_var_for_cast() {
 +        check_assist(
 +            extract_variable,
 +            "
 +fn main() {
 +    let v = $00f32 as u32$0;
 +}
 +",
 +            "
 +fn main() {
 +    let $0var_name = 0f32 as u32;
 +    let v = var_name;
 +}
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn extract_var_field_shorthand() {
 +        check_assist(
 +            extract_variable,
 +            r#"
 +struct S {
 +    foo: i32
 +}
 +
 +fn main() {
 +    S { foo: $01 + 1$0 }
 +}
 +"#,
 +            r#"
 +struct S {
 +    foo: i32
 +}
 +
 +fn main() {
 +    let $0foo = 1 + 1;
 +    S { foo }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn extract_var_name_from_type() {
 +        check_assist(
 +            extract_variable,
 +            r#"
 +struct Test(i32);
 +
 +fn foo() -> Test {
 +    $0{ Test(10) }$0
 +}
 +"#,
 +            r#"
 +struct Test(i32);
 +
 +fn foo() -> Test {
 +    let $0test = { Test(10) };
 +    test
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn extract_var_name_from_parameter() {
 +        check_assist(
 +            extract_variable,
 +            r#"
 +fn bar(test: u32, size: u32)
 +
 +fn foo() {
 +    bar(1, $01+1$0);
 +}
 +"#,
 +            r#"
 +fn bar(test: u32, size: u32)
 +
 +fn foo() {
 +    let $0size = 1+1;
 +    bar(1, size);
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn extract_var_parameter_name_has_precedence_over_type() {
 +        check_assist(
 +            extract_variable,
 +            r#"
 +struct TextSize(u32);
 +fn bar(test: u32, size: TextSize)
 +
 +fn foo() {
 +    bar(1, $0{ TextSize(1+1) }$0);
 +}
 +"#,
 +            r#"
 +struct TextSize(u32);
 +fn bar(test: u32, size: TextSize)
 +
 +fn foo() {
 +    let $0size = { TextSize(1+1) };
 +    bar(1, size);
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn extract_var_name_from_function() {
 +        check_assist(
 +            extract_variable,
 +            r#"
 +fn is_required(test: u32, size: u32) -> bool
 +
 +fn foo() -> bool {
 +    $0is_required(1, 2)$0
 +}
 +"#,
 +            r#"
 +fn is_required(test: u32, size: u32) -> bool
 +
 +fn foo() -> bool {
 +    let $0is_required = is_required(1, 2);
 +    is_required
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn extract_var_name_from_method() {
 +        check_assist(
 +            extract_variable,
 +            r#"
 +struct S;
 +impl S {
 +    fn bar(&self, n: u32) -> u32 { n }
 +}
 +
 +fn foo() -> u32 {
 +    $0S.bar(1)$0
 +}
 +"#,
 +            r#"
 +struct S;
 +impl S {
 +    fn bar(&self, n: u32) -> u32 { n }
 +}
 +
 +fn foo() -> u32 {
 +    let $0bar = S.bar(1);
 +    bar
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn extract_var_name_from_method_param() {
 +        check_assist(
 +            extract_variable,
 +            r#"
 +struct S;
 +impl S {
 +    fn bar(&self, n: u32, size: u32) { n }
 +}
 +
 +fn foo() {
 +    S.bar($01 + 1$0, 2)
 +}
 +"#,
 +            r#"
 +struct S;
 +impl S {
 +    fn bar(&self, n: u32, size: u32) { n }
 +}
 +
 +fn foo() {
 +    let $0n = 1 + 1;
 +    S.bar(n, 2)
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn extract_var_name_from_ufcs_method_param() {
 +        check_assist(
 +            extract_variable,
 +            r#"
 +struct S;
 +impl S {
 +    fn bar(&self, n: u32, size: u32) { n }
 +}
 +
 +fn foo() {
 +    S::bar(&S, $01 + 1$0, 2)
 +}
 +"#,
 +            r#"
 +struct S;
 +impl S {
 +    fn bar(&self, n: u32, size: u32) { n }
 +}
 +
 +fn foo() {
 +    let $0n = 1 + 1;
 +    S::bar(&S, n, 2)
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn extract_var_parameter_name_has_precedence_over_function() {
 +        check_assist(
 +            extract_variable,
 +            r#"
 +fn bar(test: u32, size: u32)
 +
 +fn foo() {
 +    bar(1, $0symbol_size(1, 2)$0);
 +}
 +"#,
 +            r#"
 +fn bar(test: u32, size: u32)
 +
 +fn foo() {
 +    let $0size = symbol_size(1, 2);
 +    bar(1, size);
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn extract_macro_call() {
 +        check_assist(
 +            extract_variable,
 +            r"
 +struct Vec;
 +macro_rules! vec {
 +    () => {Vec}
 +}
 +fn main() {
 +    let _ = $0vec![]$0;
 +}
 +",
 +            r"
 +struct Vec;
 +macro_rules! vec {
 +    () => {Vec}
 +}
 +fn main() {
 +    let $0vec = vec![];
 +    let _ = vec;
 +}
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_var_for_return_not_applicable() {
 +        check_assist_not_applicable(extract_variable, "fn foo() { $0return$0; } ");
 +    }
 +
 +    #[test]
 +    fn test_extract_var_for_break_not_applicable() {
 +        check_assist_not_applicable(extract_variable, "fn main() { loop { $0break$0; }; }");
 +    }
 +
 +    #[test]
 +    fn test_extract_var_unit_expr_not_applicable() {
 +        check_assist_not_applicable(
 +            extract_variable,
 +            r#"
 +fn foo() {
 +    let mut i = 3;
 +    $0if i >= 0 {
 +        i += 1;
 +    } else {
 +        i -= 1;
 +    }$0
 +}"#,
 +        );
 +    }
 +
 +    // FIXME: This is not quite correct, but good enough(tm) for the sorting heuristic
 +    #[test]
 +    fn extract_var_target() {
 +        check_assist_target(extract_variable, "fn foo() -> u32 { $0return 2 + 2$0; }", "2 + 2");
 +
 +        check_assist_target(
 +            extract_variable,
 +            "
 +fn main() {
 +    let x = true;
 +    let tuple = match x {
 +        true => ($02 + 2$0, true)
 +        _ => (0, false)
 +    };
 +}
 +",
 +            "2 + 2",
 +        );
 +    }
 +
 +    #[test]
 +    fn extract_var_no_block_body() {
 +        check_assist_not_applicable(
 +            extract_variable,
 +            r"
 +const X: usize = $0100$0;
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_var_mutable_reference_parameter() {
 +        check_assist(
 +            extract_variable,
 +            r#"
 +struct S {
 +    vec: Vec<u8>
 +}
 +
 +fn foo(s: &mut S) {
 +    $0s.vec$0.push(0);
 +}"#,
 +            r#"
 +struct S {
 +    vec: Vec<u8>
 +}
 +
 +fn foo(s: &mut S) {
 +    let $0vec = &mut s.vec;
 +    vec.push(0);
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_var_mutable_reference_parameter_deep_nesting() {
 +        check_assist(
 +            extract_variable,
 +            r#"
 +struct Y {
 +    field: X
 +}
 +struct X {
 +    field: S
 +}
 +struct S {
 +    vec: Vec<u8>
 +}
 +
 +fn foo(f: &mut Y) {
 +    $0f.field.field.vec$0.push(0);
 +}"#,
 +            r#"
 +struct Y {
 +    field: X
 +}
 +struct X {
 +    field: S
 +}
 +struct S {
 +    vec: Vec<u8>
 +}
 +
 +fn foo(f: &mut Y) {
 +    let $0vec = &mut f.field.field.vec;
 +    vec.push(0);
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_var_reference_parameter() {
 +        check_assist(
 +            extract_variable,
 +            r#"
 +struct X;
 +
 +impl X {
 +    fn do_thing(&self) {
 +
 +    }
 +}
 +
 +struct S {
 +    sub: X
 +}
 +
 +fn foo(s: &S) {
 +    $0s.sub$0.do_thing();
 +}"#,
 +            r#"
 +struct X;
 +
 +impl X {
 +    fn do_thing(&self) {
 +
 +    }
 +}
 +
 +struct S {
 +    sub: X
 +}
 +
 +fn foo(s: &S) {
 +    let $0x = &s.sub;
 +    x.do_thing();
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_var_reference_parameter_deep_nesting() {
 +        check_assist(
 +            extract_variable,
 +            r#"
 +struct Z;
 +impl Z {
 +    fn do_thing(&self) {
 +
 +    }
 +}
 +
 +struct Y {
 +    field: Z
 +}
 +
 +struct X {
 +    field: Y
 +}
 +
 +struct S {
 +    sub: X
 +}
 +
 +fn foo(s: &S) {
 +    $0s.sub.field.field$0.do_thing();
 +}"#,
 +            r#"
 +struct Z;
 +impl Z {
 +    fn do_thing(&self) {
 +
 +    }
 +}
 +
 +struct Y {
 +    field: Z
 +}
 +
 +struct X {
 +    field: Y
 +}
 +
 +struct S {
 +    sub: X
 +}
 +
 +fn foo(s: &S) {
 +    let $0z = &s.sub.field.field;
 +    z.do_thing();
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_var_regular_parameter() {
 +        check_assist(
 +            extract_variable,
 +            r#"
 +struct X;
 +
 +impl X {
 +    fn do_thing(&self) {
 +
 +    }
 +}
 +
 +struct S {
 +    sub: X
 +}
 +
 +fn foo(s: S) {
 +    $0s.sub$0.do_thing();
 +}"#,
 +            r#"
 +struct X;
 +
 +impl X {
 +    fn do_thing(&self) {
 +
 +    }
 +}
 +
 +struct S {
 +    sub: X
 +}
 +
 +fn foo(s: S) {
 +    let $0x = s.sub;
 +    x.do_thing();
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_var_mutable_reference_local() {
 +        check_assist(
 +            extract_variable,
 +            r#"
 +struct X;
 +
 +struct S {
 +    sub: X
 +}
 +
 +impl S {
 +    fn new() -> S {
 +        S {
 +            sub: X::new()
 +        }
 +    }
 +}
 +
 +impl X {
 +    fn new() -> X {
 +        X { }
 +    }
 +    fn do_thing(&self) {
 +
 +    }
 +}
 +
 +
 +fn foo() {
 +    let local = &mut S::new();
 +    $0local.sub$0.do_thing();
 +}"#,
 +            r#"
 +struct X;
 +
 +struct S {
 +    sub: X
 +}
 +
 +impl S {
 +    fn new() -> S {
 +        S {
 +            sub: X::new()
 +        }
 +    }
 +}
 +
 +impl X {
 +    fn new() -> X {
 +        X { }
 +    }
 +    fn do_thing(&self) {
 +
 +    }
 +}
 +
 +
 +fn foo() {
 +    let local = &mut S::new();
 +    let $0x = &mut local.sub;
 +    x.do_thing();
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_var_reference_local() {
 +        check_assist(
 +            extract_variable,
 +            r#"
 +struct X;
 +
 +struct S {
 +    sub: X
 +}
 +
 +impl S {
 +    fn new() -> S {
 +        S {
 +            sub: X::new()
 +        }
 +    }
 +}
 +
 +impl X {
 +    fn new() -> X {
 +        X { }
 +    }
 +    fn do_thing(&self) {
 +
 +    }
 +}
 +
 +
 +fn foo() {
 +    let local = &S::new();
 +    $0local.sub$0.do_thing();
 +}"#,
 +            r#"
 +struct X;
 +
 +struct S {
 +    sub: X
 +}
 +
 +impl S {
 +    fn new() -> S {
 +        S {
 +            sub: X::new()
 +        }
 +    }
 +}
 +
 +impl X {
 +    fn new() -> X {
 +        X { }
 +    }
 +    fn do_thing(&self) {
 +
 +    }
 +}
 +
 +
 +fn foo() {
 +    let local = &S::new();
 +    let $0x = &local.sub;
 +    x.do_thing();
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_extract_var_for_mutable_borrow() {
 +        check_assist(
 +            extract_variable,
 +            r#"
 +fn foo() {
 +    let v = &mut $00$0;
 +}"#,
 +            r#"
 +fn foo() {
 +    let mut $0var_name = 0;
 +    let v = &mut var_name;
 +}"#,
 +        );
 +    }
 +}
index b33846f546653e9095520861ea3d4fa819803a91,0000000000000000000000000000000000000000..8764543028706f9d37d068397d283d525b029d77
mode 100644,000000..100644
--- /dev/null
@@@ -1,606 -1,0 +1,606 @@@
-         None => format!("Change visibility to {}", missing_visibility),
-         Some(name) => format!("Change visibility of {} to {}", name, missing_visibility),
 +use hir::{db::HirDatabase, HasSource, HasVisibility, PathResolution};
 +use ide_db::base_db::FileId;
 +use syntax::{
 +    ast::{self, HasVisibility as _},
 +    AstNode, TextRange, TextSize,
 +};
 +
 +use crate::{utils::vis_offset, AssistContext, AssistId, AssistKind, Assists};
 +
 +// FIXME: this really should be a fix for diagnostic, rather than an assist.
 +
 +// Assist: fix_visibility
 +//
 +// Makes inaccessible item public.
 +//
 +// ```
 +// mod m {
 +//     fn frobnicate() {}
 +// }
 +// fn main() {
 +//     m::frobnicate$0() {}
 +// }
 +// ```
 +// ->
 +// ```
 +// mod m {
 +//     $0pub(crate) fn frobnicate() {}
 +// }
 +// fn main() {
 +//     m::frobnicate() {}
 +// }
 +// ```
 +pub(crate) fn fix_visibility(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    add_vis_to_referenced_module_def(acc, ctx)
 +        .or_else(|| add_vis_to_referenced_record_field(acc, ctx))
 +}
 +
 +fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    let path: ast::Path = ctx.find_node_at_offset()?;
 +    let path_res = ctx.sema.resolve_path(&path)?;
 +    let def = match path_res {
 +        PathResolution::Def(def) => def,
 +        _ => return None,
 +    };
 +
 +    let current_module = ctx.sema.scope(path.syntax())?.module();
 +    let target_module = def.module(ctx.db())?;
 +
 +    if def.visibility(ctx.db()).is_visible_from(ctx.db(), current_module.into()) {
 +        return None;
 +    };
 +
 +    let (offset, current_visibility, target, target_file, target_name) =
 +        target_data_for_def(ctx.db(), def)?;
 +
 +    let missing_visibility =
 +        if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" };
 +
 +    let assist_label = match target_name {
-                     format!("$0{}", missing_visibility),
++        None => format!("Change visibility to {missing_visibility}"),
++        Some(name) => format!("Change visibility of {name} to {missing_visibility}"),
 +    };
 +
 +    acc.add(AssistId("fix_visibility", AssistKind::QuickFix), assist_label, target, |builder| {
 +        builder.edit_file(target_file);
 +        match ctx.config.snippet_cap {
 +            Some(cap) => match current_visibility {
 +                Some(current_visibility) => builder.replace_snippet(
 +                    cap,
 +                    current_visibility.syntax().text_range(),
-                 None => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)),
++                    format!("$0{missing_visibility}"),
 +                ),
-                 None => builder.insert(offset, format!("{} ", missing_visibility)),
++                None => builder.insert_snippet(cap, offset, format!("$0{missing_visibility} ")),
 +            },
 +            None => match current_visibility {
 +                Some(current_visibility) => {
 +                    builder.replace(current_visibility.syntax().text_range(), missing_visibility)
 +                }
-         format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility);
++                None => builder.insert(offset, format!("{missing_visibility} ")),
 +            },
 +        }
 +    })
 +}
 +
 +fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    let record_field: ast::RecordExprField = ctx.find_node_at_offset()?;
 +    let (record_field_def, _, _) = ctx.sema.resolve_record_field(&record_field)?;
 +
 +    let current_module = ctx.sema.scope(record_field.syntax())?.module();
 +    let visibility = record_field_def.visibility(ctx.db());
 +    if visibility.is_visible_from(ctx.db(), current_module.into()) {
 +        return None;
 +    }
 +
 +    let parent = record_field_def.parent_def(ctx.db());
 +    let parent_name = parent.name(ctx.db());
 +    let target_module = parent.module(ctx.db());
 +
 +    let in_file_source = record_field_def.source(ctx.db())?;
 +    let (offset, current_visibility, target) = match in_file_source.value {
 +        hir::FieldSource::Named(it) => {
 +            let s = it.syntax();
 +            (vis_offset(s), it.visibility(), s.text_range())
 +        }
 +        hir::FieldSource::Pos(it) => {
 +            let s = it.syntax();
 +            (vis_offset(s), it.visibility(), s.text_range())
 +        }
 +    };
 +
 +    let missing_visibility =
 +        if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" };
 +    let target_file = in_file_source.file_id.original_file(ctx.db());
 +
 +    let target_name = record_field_def.name(ctx.db());
 +    let assist_label =
-                     format!("$0{}", missing_visibility),
++        format!("Change visibility of {parent_name}.{target_name} to {missing_visibility}");
 +
 +    acc.add(AssistId("fix_visibility", AssistKind::QuickFix), assist_label, target, |builder| {
 +        builder.edit_file(target_file);
 +        match ctx.config.snippet_cap {
 +            Some(cap) => match current_visibility {
 +                Some(current_visibility) => builder.replace_snippet(
 +                    cap,
 +                    current_visibility.syntax().text_range(),
-                 None => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)),
++                    format!("$0{missing_visibility}"),
 +                ),
-                 None => builder.insert(offset, format!("{} ", missing_visibility)),
++                None => builder.insert_snippet(cap, offset, format!("$0{missing_visibility} ")),
 +            },
 +            None => match current_visibility {
 +                Some(current_visibility) => {
 +                    builder.replace(current_visibility.syntax().text_range(), missing_visibility)
 +                }
++                None => builder.insert(offset, format!("{missing_visibility} ")),
 +            },
 +        }
 +    })
 +}
 +
 +fn target_data_for_def(
 +    db: &dyn HirDatabase,
 +    def: hir::ModuleDef,
 +) -> Option<(TextSize, Option<ast::Visibility>, TextRange, FileId, Option<hir::Name>)> {
 +    fn offset_target_and_file_id<S, Ast>(
 +        db: &dyn HirDatabase,
 +        x: S,
 +    ) -> Option<(TextSize, Option<ast::Visibility>, TextRange, FileId)>
 +    where
 +        S: HasSource<Ast = Ast>,
 +        Ast: AstNode + ast::HasVisibility,
 +    {
 +        let source = x.source(db)?;
 +        let in_file_syntax = source.syntax();
 +        let file_id = in_file_syntax.file_id;
 +        let syntax = in_file_syntax.value;
 +        let current_visibility = source.value.visibility();
 +        Some((
 +            vis_offset(syntax),
 +            current_visibility,
 +            syntax.text_range(),
 +            file_id.original_file(db.upcast()),
 +        ))
 +    }
 +
 +    let target_name;
 +    let (offset, current_visibility, target, target_file) = match def {
 +        hir::ModuleDef::Function(f) => {
 +            target_name = Some(f.name(db));
 +            offset_target_and_file_id(db, f)?
 +        }
 +        hir::ModuleDef::Adt(adt) => {
 +            target_name = Some(adt.name(db));
 +            match adt {
 +                hir::Adt::Struct(s) => offset_target_and_file_id(db, s)?,
 +                hir::Adt::Union(u) => offset_target_and_file_id(db, u)?,
 +                hir::Adt::Enum(e) => offset_target_and_file_id(db, e)?,
 +            }
 +        }
 +        hir::ModuleDef::Const(c) => {
 +            target_name = c.name(db);
 +            offset_target_and_file_id(db, c)?
 +        }
 +        hir::ModuleDef::Static(s) => {
 +            target_name = Some(s.name(db));
 +            offset_target_and_file_id(db, s)?
 +        }
 +        hir::ModuleDef::Trait(t) => {
 +            target_name = Some(t.name(db));
 +            offset_target_and_file_id(db, t)?
 +        }
 +        hir::ModuleDef::TypeAlias(t) => {
 +            target_name = Some(t.name(db));
 +            offset_target_and_file_id(db, t)?
 +        }
 +        hir::ModuleDef::Module(m) => {
 +            target_name = m.name(db);
 +            let in_file_source = m.declaration_source(db)?;
 +            let file_id = in_file_source.file_id.original_file(db.upcast());
 +            let syntax = in_file_source.value.syntax();
 +            (vis_offset(syntax), in_file_source.value.visibility(), syntax.text_range(), file_id)
 +        }
 +        // FIXME
 +        hir::ModuleDef::Macro(_) => return None,
 +        // Enum variants can't be private, we can't modify builtin types
 +        hir::ModuleDef::Variant(_) | hir::ModuleDef::BuiltinType(_) => return None,
 +    };
 +
 +    Some((offset, current_visibility, target, target_file, target_name))
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use crate::tests::{check_assist, check_assist_not_applicable};
 +
 +    use super::*;
 +
 +    #[test]
 +    fn fix_visibility_of_fn() {
 +        check_assist(
 +            fix_visibility,
 +            r"mod foo { fn foo() {} }
 +              fn main() { foo::foo$0() } ",
 +            r"mod foo { $0pub(crate) fn foo() {} }
 +              fn main() { foo::foo() } ",
 +        );
 +        check_assist_not_applicable(
 +            fix_visibility,
 +            r"mod foo { pub fn foo() {} }
 +              fn main() { foo::foo$0() } ",
 +        )
 +    }
 +
 +    #[test]
 +    fn fix_visibility_of_adt_in_submodule() {
 +        check_assist(
 +            fix_visibility,
 +            r"mod foo { struct Foo; }
 +              fn main() { foo::Foo$0 } ",
 +            r"mod foo { $0pub(crate) struct Foo; }
 +              fn main() { foo::Foo } ",
 +        );
 +        check_assist_not_applicable(
 +            fix_visibility,
 +            r"mod foo { pub struct Foo; }
 +              fn main() { foo::Foo$0 } ",
 +        );
 +        check_assist(
 +            fix_visibility,
 +            r"mod foo { enum Foo; }
 +              fn main() { foo::Foo$0 } ",
 +            r"mod foo { $0pub(crate) enum Foo; }
 +              fn main() { foo::Foo } ",
 +        );
 +        check_assist_not_applicable(
 +            fix_visibility,
 +            r"mod foo { pub enum Foo; }
 +              fn main() { foo::Foo$0 } ",
 +        );
 +        check_assist(
 +            fix_visibility,
 +            r"mod foo { union Foo; }
 +              fn main() { foo::Foo$0 } ",
 +            r"mod foo { $0pub(crate) union Foo; }
 +              fn main() { foo::Foo } ",
 +        );
 +        check_assist_not_applicable(
 +            fix_visibility,
 +            r"mod foo { pub union Foo; }
 +              fn main() { foo::Foo$0 } ",
 +        );
 +    }
 +
 +    #[test]
 +    fn fix_visibility_of_adt_in_other_file() {
 +        check_assist(
 +            fix_visibility,
 +            r"
 +//- /main.rs
 +mod foo;
 +fn main() { foo::Foo$0 }
 +
 +//- /foo.rs
 +struct Foo;
 +",
 +            r"$0pub(crate) struct Foo;
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn fix_visibility_of_struct_field() {
 +        check_assist(
 +            fix_visibility,
 +            r"mod foo { pub struct Foo { bar: (), } }
 +              fn main() { foo::Foo { $0bar: () }; } ",
 +            r"mod foo { pub struct Foo { $0pub(crate) bar: (), } }
 +              fn main() { foo::Foo { bar: () }; } ",
 +        );
 +        check_assist(
 +            fix_visibility,
 +            r"
 +//- /lib.rs
 +mod foo;
 +fn main() { foo::Foo { $0bar: () }; }
 +//- /foo.rs
 +pub struct Foo { bar: () }
 +",
 +            r"pub struct Foo { $0pub(crate) bar: () }
 +",
 +        );
 +        check_assist_not_applicable(
 +            fix_visibility,
 +            r"mod foo { pub struct Foo { pub bar: (), } }
 +              fn main() { foo::Foo { $0bar: () }; } ",
 +        );
 +        check_assist_not_applicable(
 +            fix_visibility,
 +            r"
 +//- /lib.rs
 +mod foo;
 +fn main() { foo::Foo { $0bar: () }; }
 +//- /foo.rs
 +pub struct Foo { pub bar: () }
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn fix_visibility_of_enum_variant_field() {
 +        // Enum variants, as well as their fields, always get the enum's visibility. In fact, rustc
 +        // rejects any visibility specifiers on them, so this assist should never fire on them.
 +        check_assist_not_applicable(
 +            fix_visibility,
 +            r"mod foo { pub enum Foo { Bar { bar: () } } }
 +              fn main() { foo::Foo::Bar { $0bar: () }; } ",
 +        );
 +        check_assist_not_applicable(
 +            fix_visibility,
 +            r"
 +//- /lib.rs
 +mod foo;
 +fn main() { foo::Foo::Bar { $0bar: () }; }
 +//- /foo.rs
 +pub enum Foo { Bar { bar: () } }
 +",
 +        );
 +        check_assist_not_applicable(
 +            fix_visibility,
 +            r"mod foo { pub struct Foo { pub bar: (), } }
 +              fn main() { foo::Foo { $0bar: () }; } ",
 +        );
 +        check_assist_not_applicable(
 +            fix_visibility,
 +            r"
 +//- /lib.rs
 +mod foo;
 +fn main() { foo::Foo { $0bar: () }; }
 +//- /foo.rs
 +pub struct Foo { pub bar: () }
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn fix_visibility_of_union_field() {
 +        check_assist(
 +            fix_visibility,
 +            r"mod foo { pub union Foo { bar: (), } }
 +              fn main() { foo::Foo { $0bar: () }; } ",
 +            r"mod foo { pub union Foo { $0pub(crate) bar: (), } }
 +              fn main() { foo::Foo { bar: () }; } ",
 +        );
 +        check_assist(
 +            fix_visibility,
 +            r"
 +//- /lib.rs
 +mod foo;
 +fn main() { foo::Foo { $0bar: () }; }
 +//- /foo.rs
 +pub union Foo { bar: () }
 +",
 +            r"pub union Foo { $0pub(crate) bar: () }
 +",
 +        );
 +        check_assist_not_applicable(
 +            fix_visibility,
 +            r"mod foo { pub union Foo { pub bar: (), } }
 +              fn main() { foo::Foo { $0bar: () }; } ",
 +        );
 +        check_assist_not_applicable(
 +            fix_visibility,
 +            r"
 +//- /lib.rs
 +mod foo;
 +fn main() { foo::Foo { $0bar: () }; }
 +//- /foo.rs
 +pub union Foo { pub bar: () }
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn fix_visibility_of_const() {
 +        check_assist(
 +            fix_visibility,
 +            r"mod foo { const FOO: () = (); }
 +              fn main() { foo::FOO$0 } ",
 +            r"mod foo { $0pub(crate) const FOO: () = (); }
 +              fn main() { foo::FOO } ",
 +        );
 +        check_assist_not_applicable(
 +            fix_visibility,
 +            r"mod foo { pub const FOO: () = (); }
 +              fn main() { foo::FOO$0 } ",
 +        );
 +    }
 +
 +    #[test]
 +    fn fix_visibility_of_static() {
 +        check_assist(
 +            fix_visibility,
 +            r"mod foo { static FOO: () = (); }
 +              fn main() { foo::FOO$0 } ",
 +            r"mod foo { $0pub(crate) static FOO: () = (); }
 +              fn main() { foo::FOO } ",
 +        );
 +        check_assist_not_applicable(
 +            fix_visibility,
 +            r"mod foo { pub static FOO: () = (); }
 +              fn main() { foo::FOO$0 } ",
 +        );
 +    }
 +
 +    #[test]
 +    fn fix_visibility_of_trait() {
 +        check_assist(
 +            fix_visibility,
 +            r"mod foo { trait Foo { fn foo(&self) {} } }
 +              fn main() { let x: &dyn foo::$0Foo; } ",
 +            r"mod foo { $0pub(crate) trait Foo { fn foo(&self) {} } }
 +              fn main() { let x: &dyn foo::Foo; } ",
 +        );
 +        check_assist_not_applicable(
 +            fix_visibility,
 +            r"mod foo { pub trait Foo { fn foo(&self) {} } }
 +              fn main() { let x: &dyn foo::Foo$0; } ",
 +        );
 +    }
 +
 +    #[test]
 +    fn fix_visibility_of_type_alias() {
 +        check_assist(
 +            fix_visibility,
 +            r"mod foo { type Foo = (); }
 +              fn main() { let x: foo::Foo$0; } ",
 +            r"mod foo { $0pub(crate) type Foo = (); }
 +              fn main() { let x: foo::Foo; } ",
 +        );
 +        check_assist_not_applicable(
 +            fix_visibility,
 +            r"mod foo { pub type Foo = (); }
 +              fn main() { let x: foo::Foo$0; } ",
 +        );
 +    }
 +
 +    #[test]
 +    fn fix_visibility_of_module() {
 +        check_assist(
 +            fix_visibility,
 +            r"mod foo { mod bar { fn bar() {} } }
 +              fn main() { foo::bar$0::bar(); } ",
 +            r"mod foo { $0pub(crate) mod bar { fn bar() {} } }
 +              fn main() { foo::bar::bar(); } ",
 +        );
 +
 +        check_assist(
 +            fix_visibility,
 +            r"
 +//- /main.rs
 +mod foo;
 +fn main() { foo::bar$0::baz(); }
 +
 +//- /foo.rs
 +mod bar {
 +    pub fn baz() {}
 +}
 +",
 +            r"$0pub(crate) mod bar {
 +    pub fn baz() {}
 +}
 +",
 +        );
 +
 +        check_assist_not_applicable(
 +            fix_visibility,
 +            r"mod foo { pub mod bar { pub fn bar() {} } }
 +              fn main() { foo::bar$0::bar(); } ",
 +        );
 +    }
 +
 +    #[test]
 +    fn fix_visibility_of_inline_module_in_other_file() {
 +        check_assist(
 +            fix_visibility,
 +            r"
 +//- /main.rs
 +mod foo;
 +fn main() { foo::bar$0::baz(); }
 +
 +//- /foo.rs
 +mod bar;
 +//- /foo/bar.rs
 +pub fn baz() {}
 +",
 +            r"$0pub(crate) mod bar;
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn fix_visibility_of_module_declaration_in_other_file() {
 +        check_assist(
 +            fix_visibility,
 +            r"
 +//- /main.rs
 +mod foo;
 +fn main() { foo::bar$0>::baz(); }
 +
 +//- /foo.rs
 +mod bar {
 +    pub fn baz() {}
 +}
 +",
 +            r"$0pub(crate) mod bar {
 +    pub fn baz() {}
 +}
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn adds_pub_when_target_is_in_another_crate() {
 +        check_assist(
 +            fix_visibility,
 +            r"
 +//- /main.rs crate:a deps:foo
 +foo::Bar$0
 +//- /lib.rs crate:foo
 +struct Bar;
 +",
 +            r"$0pub struct Bar;
 +",
 +        )
 +    }
 +
 +    #[test]
 +    fn replaces_pub_crate_with_pub() {
 +        check_assist(
 +            fix_visibility,
 +            r"
 +//- /main.rs crate:a deps:foo
 +foo::Bar$0
 +//- /lib.rs crate:foo
 +pub(crate) struct Bar;
 +",
 +            r"$0pub struct Bar;
 +",
 +        );
 +        check_assist(
 +            fix_visibility,
 +            r"
 +//- /main.rs crate:a deps:foo
 +fn main() {
 +    foo::Foo { $0bar: () };
 +}
 +//- /lib.rs crate:foo
 +pub struct Foo { pub(crate) bar: () }
 +",
 +            r"pub struct Foo { $0pub bar: () }
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn fix_visibility_of_reexport() {
 +        // FIXME: broken test, this should fix visibility of the re-export
 +        // rather than the struct.
 +        check_assist(
 +            fix_visibility,
 +            r#"
 +mod foo {
 +    use bar::Baz;
 +    mod bar { pub(super) struct Baz; }
 +}
 +foo::Baz$0
 +"#,
 +            r#"
 +mod foo {
 +    use bar::Baz;
 +    mod bar { $0pub(crate) struct Baz; }
 +}
 +foo::Baz
 +"#,
 +        )
 +    }
 +}
index bdd3cf4f06c253692d3e3989075ec4cd56e4ba15,0000000000000000000000000000000000000000..c9aa41c845ad5c0b0a964cb6d0d059167dd1d416
mode 100644,000000..100644
--- /dev/null
@@@ -1,337 -1,0 +1,348 @@@
-     let fn_name = format!("{}_{}", fn_name_prefix, &to_lower_snake_case(&variant_name.text()));
 +use ide_db::assists::GroupLabel;
 +use itertools::Itertools;
 +use stdx::to_lower_snake_case;
 +use syntax::ast::HasVisibility;
 +use syntax::ast::{self, AstNode, HasName};
 +
 +use crate::{
 +    utils::{add_method_to_adt, find_struct_impl},
 +    AssistContext, AssistId, AssistKind, Assists,
 +};
 +
 +// Assist: generate_enum_try_into_method
 +//
 +// Generate a `try_into_` method for this enum variant.
 +//
 +// ```
 +// enum Value {
 +//  Number(i32),
 +//  Text(String)$0,
 +// }
 +// ```
 +// ->
 +// ```
 +// enum Value {
 +//  Number(i32),
 +//  Text(String),
 +// }
 +//
 +// impl Value {
 +//     fn try_into_text(self) -> Result<String, Self> {
 +//         if let Self::Text(v) = self {
 +//             Ok(v)
 +//         } else {
 +//             Err(self)
 +//         }
 +//     }
 +// }
 +// ```
 +pub(crate) fn generate_enum_try_into_method(
 +    acc: &mut Assists,
 +    ctx: &AssistContext<'_>,
 +) -> Option<()> {
 +    generate_enum_projection_method(
 +        acc,
 +        ctx,
 +        "generate_enum_try_into_method",
 +        "Generate a `try_into_` method for this enum variant",
 +        ProjectionProps {
 +            fn_name_prefix: "try_into",
 +            self_param: "self",
 +            return_prefix: "Result<",
 +            return_suffix: ", Self>",
 +            happy_case: "Ok",
 +            sad_case: "Err(self)",
 +        },
 +    )
 +}
 +
 +// Assist: generate_enum_as_method
 +//
 +// Generate an `as_` method for this enum variant.
 +//
 +// ```
 +// enum Value {
 +//  Number(i32),
 +//  Text(String)$0,
 +// }
 +// ```
 +// ->
 +// ```
 +// enum Value {
 +//  Number(i32),
 +//  Text(String),
 +// }
 +//
 +// impl Value {
 +//     fn as_text(&self) -> Option<&String> {
 +//         if let Self::Text(v) = self {
 +//             Some(v)
 +//         } else {
 +//             None
 +//         }
 +//     }
 +// }
 +// ```
 +pub(crate) fn generate_enum_as_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    generate_enum_projection_method(
 +        acc,
 +        ctx,
 +        "generate_enum_as_method",
 +        "Generate an `as_` method for this enum variant",
 +        ProjectionProps {
 +            fn_name_prefix: "as",
 +            self_param: "&self",
 +            return_prefix: "Option<&",
 +            return_suffix: ">",
 +            happy_case: "Some",
 +            sad_case: "None",
 +        },
 +    )
 +}
 +
 +struct ProjectionProps {
 +    fn_name_prefix: &'static str,
 +    self_param: &'static str,
 +    return_prefix: &'static str,
 +    return_suffix: &'static str,
 +    happy_case: &'static str,
 +    sad_case: &'static str,
 +}
 +
 +fn generate_enum_projection_method(
 +    acc: &mut Assists,
 +    ctx: &AssistContext<'_>,
 +    assist_id: &'static str,
 +    assist_description: &str,
 +    props: ProjectionProps,
 +) -> Option<()> {
 +    let ProjectionProps {
 +        fn_name_prefix,
 +        self_param,
 +        return_prefix,
 +        return_suffix,
 +        happy_case,
 +        sad_case,
 +    } = props;
++
 +    let variant = ctx.find_node_at_offset::<ast::Variant>()?;
 +    let variant_name = variant.name()?;
 +    let parent_enum = ast::Adt::Enum(variant.parent_enum());
 +
 +    let (pattern_suffix, field_type, bound_name) = match variant.kind() {
 +        ast::StructKind::Record(record) => {
 +            let (field,) = record.fields().collect_tuple()?;
 +            let name = field.name()?.to_string();
 +            let ty = field.ty()?;
 +            let pattern_suffix = format!(" {{ {name} }}");
 +            (pattern_suffix, ty, name)
 +        }
 +        ast::StructKind::Tuple(tuple) => {
 +            let (field,) = tuple.fields().collect_tuple()?;
 +            let ty = field.ty()?;
 +            ("(v)".to_owned(), ty, "v".to_owned())
 +        }
 +        ast::StructKind::Unit => return None,
 +    };
 +
-             let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{v} "));
++    let fn_name = format!("{fn_name_prefix}_{}", &to_lower_snake_case(&variant_name.text()));
 +
 +    // Return early if we've found an existing new fn
 +    let impl_def = find_struct_impl(ctx, &parent_enum, &[fn_name.clone()])?;
 +
 +    let target = variant.syntax().text_range();
 +    acc.add_group(
 +        &GroupLabel("Generate an `is_`,`as_`, or `try_into_` for this enum variant".to_owned()),
 +        AssistId(assist_id, AssistKind::Generate),
 +        assist_description,
 +        target,
 +        |builder| {
-                 "    {vis}fn {fn_name}({self_param}) -> {return_prefix}{field_type}{return_suffix} {{
++            let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{} ", v));
++
++            let field_type_syntax = field_type.syntax();
++
++            let must_use = if ctx.config.assist_emit_must_use {
++                "#[must_use]\n    "
++            } else {
++                ""
++            };
++
 +            let method = format!(
-     }}");
++                "    {must_use}{vis}fn {fn_name}({self_param}) -> {return_prefix}{field_type_syntax}{return_suffix} {{
 +        if let Self::{variant_name}{pattern_suffix} = self {{
 +            {happy_case}({bound_name})
 +        }} else {{
 +            {sad_case}
 +        }}
++    }}"
++            );
 +
 +            add_method_to_adt(builder, &parent_enum, impl_def, &method);
 +        },
 +    )
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use crate::tests::{check_assist, check_assist_not_applicable};
 +
 +    use super::*;
 +
 +    #[test]
 +    fn test_generate_enum_try_into_tuple_variant() {
 +        check_assist(
 +            generate_enum_try_into_method,
 +            r#"
 +enum Value {
 +    Number(i32),
 +    Text(String)$0,
 +}"#,
 +            r#"enum Value {
 +    Number(i32),
 +    Text(String),
 +}
 +
 +impl Value {
 +    fn try_into_text(self) -> Result<String, Self> {
 +        if let Self::Text(v) = self {
 +            Ok(v)
 +        } else {
 +            Err(self)
 +        }
 +    }
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_generate_enum_try_into_already_implemented() {
 +        check_assist_not_applicable(
 +            generate_enum_try_into_method,
 +            r#"enum Value {
 +    Number(i32),
 +    Text(String)$0,
 +}
 +
 +impl Value {
 +    fn try_into_text(self) -> Result<String, Self> {
 +        if let Self::Text(v) = self {
 +            Ok(v)
 +        } else {
 +            Err(self)
 +        }
 +    }
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_generate_enum_try_into_unit_variant() {
 +        check_assist_not_applicable(
 +            generate_enum_try_into_method,
 +            r#"enum Value {
 +    Number(i32),
 +    Text(String),
 +    Unit$0,
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_generate_enum_try_into_record_with_multiple_fields() {
 +        check_assist_not_applicable(
 +            generate_enum_try_into_method,
 +            r#"enum Value {
 +    Number(i32),
 +    Text(String),
 +    Both { first: i32, second: String }$0,
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_generate_enum_try_into_tuple_with_multiple_fields() {
 +        check_assist_not_applicable(
 +            generate_enum_try_into_method,
 +            r#"enum Value {
 +    Number(i32),
 +    Text(String, String)$0,
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_generate_enum_try_into_record_variant() {
 +        check_assist(
 +            generate_enum_try_into_method,
 +            r#"enum Value {
 +    Number(i32),
 +    Text { text: String }$0,
 +}"#,
 +            r#"enum Value {
 +    Number(i32),
 +    Text { text: String },
 +}
 +
 +impl Value {
 +    fn try_into_text(self) -> Result<String, Self> {
 +        if let Self::Text { text } = self {
 +            Ok(text)
 +        } else {
 +            Err(self)
 +        }
 +    }
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_generate_enum_as_tuple_variant() {
 +        check_assist(
 +            generate_enum_as_method,
 +            r#"
 +enum Value {
 +    Number(i32),
 +    Text(String)$0,
 +}"#,
 +            r#"enum Value {
 +    Number(i32),
 +    Text(String),
 +}
 +
 +impl Value {
 +    fn as_text(&self) -> Option<&String> {
 +        if let Self::Text(v) = self {
 +            Some(v)
 +        } else {
 +            None
 +        }
 +    }
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_generate_enum_as_record_variant() {
 +        check_assist(
 +            generate_enum_as_method,
 +            r#"enum Value {
 +    Number(i32),
 +    Text { text: String }$0,
 +}"#,
 +            r#"enum Value {
 +    Number(i32),
 +    Text { text: String },
 +}
 +
 +impl Value {
 +    fn as_text(&self) -> Option<&String> {
 +        if let Self::Text { text } = self {
 +            Some(text)
 +        } else {
 +            None
 +        }
 +    }
 +}"#,
 +        );
 +    }
 +}
index 9f51cdaf8b1eb0d7eccc6ae154ec038f5118504c,0000000000000000000000000000000000000000..0c546ce5d41c6520bc9df8402d8d77753f6933b6
mode 100644,000000..100644
--- /dev/null
@@@ -1,1259 -1,0 +1,1321 @@@
-             (function, format!("Inline `{}`", path))
++use std::collections::BTreeSet;
++
 +use ast::make;
 +use either::Either;
 +use hir::{db::HirDatabase, PathResolution, Semantics, TypeInfo};
 +use ide_db::{
 +    base_db::{FileId, FileRange},
 +    defs::Definition,
 +    imports::insert_use::remove_path_if_in_use_stmt,
 +    path_transform::PathTransform,
 +    search::{FileReference, SearchScope},
 +    source_change::SourceChangeBuilder,
 +    syntax_helpers::{insert_whitespace_into_node::insert_ws_into, node_ext::expr_as_name_ref},
 +    RootDatabase,
 +};
 +use itertools::{izip, Itertools};
 +use syntax::{
 +    ast::{self, edit_in_place::Indent, HasArgList, PathExpr},
 +    ted, AstNode, NodeOrToken, SyntaxKind,
 +};
 +
 +use crate::{
 +    assist_context::{AssistContext, Assists},
 +    AssistId, AssistKind,
 +};
 +
 +// Assist: inline_into_callers
 +//
 +// Inline a function or method body into all of its callers where possible, creating a `let` statement per parameter
 +// unless the parameter can be inlined. The parameter will be inlined either if it the supplied argument is a simple local
 +// or if the parameter is only accessed inside the function body once.
 +// If all calls can be inlined the function will be removed.
 +//
 +// ```
 +// fn print(_: &str) {}
 +// fn foo$0(word: &str) {
 +//     if !word.is_empty() {
 +//         print(word);
 +//     }
 +// }
 +// fn bar() {
 +//     foo("안녕하세요");
 +//     foo("여러분");
 +// }
 +// ```
 +// ->
 +// ```
 +// fn print(_: &str) {}
 +//
 +// fn bar() {
 +//     {
 +//         let word = "안녕하세요";
 +//         if !word.is_empty() {
 +//             print(word);
 +//         }
 +//     };
 +//     {
 +//         let word = "여러분";
 +//         if !word.is_empty() {
 +//             print(word);
 +//         }
 +//     };
 +// }
 +// ```
 +pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    let def_file = ctx.file_id();
 +    let name = ctx.find_node_at_offset::<ast::Name>()?;
 +    let ast_func = name.syntax().parent().and_then(ast::Fn::cast)?;
 +    let func_body = ast_func.body()?;
 +    let param_list = ast_func.param_list()?;
 +
 +    let function = ctx.sema.to_def(&ast_func)?;
 +
 +    let params = get_fn_params(ctx.sema.db, function, &param_list)?;
 +
 +    let usages = Definition::Function(function).usages(&ctx.sema);
 +    if !usages.at_least_one() {
 +        return None;
 +    }
 +
 +    let is_recursive_fn = usages
 +        .clone()
 +        .in_scope(SearchScope::file_range(FileRange {
 +            file_id: def_file,
 +            range: func_body.syntax().text_range(),
 +        }))
 +        .at_least_one();
 +    if is_recursive_fn {
 +        cov_mark::hit!(inline_into_callers_recursive);
 +        return None;
 +    }
 +
 +    acc.add(
 +        AssistId("inline_into_callers", AssistKind::RefactorInline),
 +        "Inline into all callers",
 +        name.syntax().text_range(),
 +        |builder| {
 +            let mut usages = usages.all();
 +            let current_file_usage = usages.references.remove(&def_file);
 +
 +            let mut remove_def = true;
 +            let mut inline_refs_for_file = |file_id, refs: Vec<FileReference>| {
 +                builder.edit_file(file_id);
 +                let count = refs.len();
 +                // The collects are required as we are otherwise iterating while mutating 🙅‍♀️🙅‍♂️
 +                let (name_refs, name_refs_use) = split_refs_and_uses(builder, refs, Some);
 +                let call_infos: Vec<_> = name_refs
 +                    .into_iter()
 +                    .filter_map(CallInfo::from_name_ref)
 +                    .map(|call_info| {
 +                        let mut_node = builder.make_syntax_mut(call_info.node.syntax().clone());
 +                        (call_info, mut_node)
 +                    })
 +                    .collect();
 +                let replaced = call_infos
 +                    .into_iter()
 +                    .map(|(call_info, mut_node)| {
 +                        let replacement =
 +                            inline(&ctx.sema, def_file, function, &func_body, &params, &call_info);
 +                        ted::replace(mut_node, replacement.syntax());
 +                    })
 +                    .count();
 +                if replaced + name_refs_use.len() == count {
 +                    // we replaced all usages in this file, so we can remove the imports
 +                    name_refs_use.iter().for_each(remove_path_if_in_use_stmt);
 +                } else {
 +                    remove_def = false;
 +                }
 +            };
 +            for (file_id, refs) in usages.into_iter() {
 +                inline_refs_for_file(file_id, refs);
 +            }
 +            match current_file_usage {
 +                Some(refs) => inline_refs_for_file(def_file, refs),
 +                None => builder.edit_file(def_file),
 +            }
 +            if remove_def {
 +                builder.delete(ast_func.syntax().text_range());
 +            }
 +        },
 +    )
 +}
 +
 +pub(super) fn split_refs_and_uses<T: ast::AstNode>(
 +    builder: &mut SourceChangeBuilder,
 +    iter: impl IntoIterator<Item = FileReference>,
 +    mut map_ref: impl FnMut(ast::NameRef) -> Option<T>,
 +) -> (Vec<T>, Vec<ast::Path>) {
 +    iter.into_iter()
 +        .filter_map(|file_ref| match file_ref.name {
 +            ast::NameLike::NameRef(name_ref) => Some(name_ref),
 +            _ => None,
 +        })
 +        .filter_map(|name_ref| match name_ref.syntax().ancestors().find_map(ast::UseTree::cast) {
 +            Some(use_tree) => builder.make_mut(use_tree).path().map(Either::Right),
 +            None => map_ref(name_ref).map(Either::Left),
 +        })
 +        .partition_map(|either| either)
 +}
 +
 +// Assist: inline_call
 +//
 +// Inlines a function or method body creating a `let` statement per parameter unless the parameter
 +// can be inlined. The parameter will be inlined either if it the supplied argument is a simple local
 +// or if the parameter is only accessed inside the function body once.
 +//
 +// ```
 +// # //- minicore: option
 +// fn foo(name: Option<&str>) {
 +//     let name = name.unwrap$0();
 +// }
 +// ```
 +// ->
 +// ```
 +// fn foo(name: Option<&str>) {
 +//     let name = match name {
 +//             Some(val) => val,
 +//             None => panic!("called `Option::unwrap()` on a `None` value"),
 +//         };
 +// }
 +// ```
 +pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    let name_ref: ast::NameRef = ctx.find_node_at_offset()?;
 +    let call_info = CallInfo::from_name_ref(name_ref.clone())?;
 +    let (function, label) = match &call_info.node {
 +        ast::CallableExpr::Call(call) => {
 +            let path = match call.expr()? {
 +                ast::Expr::PathExpr(path) => path.path(),
 +                _ => None,
 +            }?;
 +            let function = match ctx.sema.resolve_path(&path)? {
 +                PathResolution::Def(hir::ModuleDef::Function(f)) => f,
 +                _ => return None,
 +            };
-             (ctx.sema.resolve_method_call(call)?, format!("Inline `{}`", name_ref))
++            (function, format!("Inline `{path}`"))
 +        }
 +        ast::CallableExpr::MethodCall(call) => {
-         // izip confuses RA due to our lack of hygiene info currently losing us type info causing incorrect errors
-         let usages: &[ast::PathExpr] = &*usages;
-         let expr: &ast::Expr = expr;
++            (ctx.sema.resolve_method_call(call)?, format!("Inline `{name_ref}`"))
 +        }
 +    };
 +
 +    let fn_source = ctx.sema.source(function)?;
 +    let fn_body = fn_source.value.body()?;
 +    let param_list = fn_source.value.param_list()?;
 +
 +    let FileRange { file_id, range } = fn_source.syntax().original_file_range(ctx.sema.db);
 +    if file_id == ctx.file_id() && range.contains(ctx.offset()) {
 +        cov_mark::hit!(inline_call_recursive);
 +        return None;
 +    }
 +    let params = get_fn_params(ctx.sema.db, function, &param_list)?;
 +
 +    if call_info.arguments.len() != params.len() {
 +        // Can't inline the function because they've passed the wrong number of
 +        // arguments to this function
 +        cov_mark::hit!(inline_call_incorrect_number_of_arguments);
 +        return None;
 +    }
 +
 +    let syntax = call_info.node.syntax().clone();
 +    acc.add(
 +        AssistId("inline_call", AssistKind::RefactorInline),
 +        label,
 +        syntax.text_range(),
 +        |builder| {
 +            let replacement = inline(&ctx.sema, file_id, function, &fn_body, &params, &call_info);
 +
 +            builder.replace_ast(
 +                match call_info.node {
 +                    ast::CallableExpr::Call(it) => ast::Expr::CallExpr(it),
 +                    ast::CallableExpr::MethodCall(it) => ast::Expr::MethodCallExpr(it),
 +                },
 +                replacement,
 +            );
 +        },
 +    )
 +}
 +
 +struct CallInfo {
 +    node: ast::CallableExpr,
 +    arguments: Vec<ast::Expr>,
 +    generic_arg_list: Option<ast::GenericArgList>,
 +}
 +
 +impl CallInfo {
 +    fn from_name_ref(name_ref: ast::NameRef) -> Option<CallInfo> {
 +        let parent = name_ref.syntax().parent()?;
 +        if let Some(call) = ast::MethodCallExpr::cast(parent.clone()) {
 +            let receiver = call.receiver()?;
 +            let mut arguments = vec![receiver];
 +            arguments.extend(call.arg_list()?.args());
 +            Some(CallInfo {
 +                generic_arg_list: call.generic_arg_list(),
 +                node: ast::CallableExpr::MethodCall(call),
 +                arguments,
 +            })
 +        } else if let Some(segment) = ast::PathSegment::cast(parent) {
 +            let path = segment.syntax().parent().and_then(ast::Path::cast)?;
 +            let path = path.syntax().parent().and_then(ast::PathExpr::cast)?;
 +            let call = path.syntax().parent().and_then(ast::CallExpr::cast)?;
 +
 +            Some(CallInfo {
 +                arguments: call.arg_list()?.args().collect(),
 +                node: ast::CallableExpr::Call(call),
 +                generic_arg_list: segment.generic_arg_list(),
 +            })
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +fn get_fn_params(
 +    db: &dyn HirDatabase,
 +    function: hir::Function,
 +    param_list: &ast::ParamList,
 +) -> Option<Vec<(ast::Pat, Option<ast::Type>, hir::Param)>> {
 +    let mut assoc_fn_params = function.assoc_fn_params(db).into_iter();
 +
 +    let mut params = Vec::new();
 +    if let Some(self_param) = param_list.self_param() {
 +        // FIXME this should depend on the receiver as well as the self_param
 +        params.push((
 +            make::ident_pat(
 +                self_param.amp_token().is_some(),
 +                self_param.mut_token().is_some(),
 +                make::name("this"),
 +            )
 +            .into(),
 +            None,
 +            assoc_fn_params.next()?,
 +        ));
 +    }
 +    for param in param_list.params() {
 +        params.push((param.pat()?, param.ty(), assoc_fn_params.next()?));
 +    }
 +
 +    Some(params)
 +}
 +
 +fn inline(
 +    sema: &Semantics<'_, RootDatabase>,
 +    function_def_file_id: FileId,
 +    function: hir::Function,
 +    fn_body: &ast::BlockExpr,
 +    params: &[(ast::Pat, Option<ast::Type>, hir::Param)],
 +    CallInfo { node, arguments, generic_arg_list }: &CallInfo,
 +) -> ast::Expr {
 +    let body = if sema.hir_file_for(fn_body.syntax()).is_macro() {
 +        cov_mark::hit!(inline_call_defined_in_macro);
 +        if let Some(body) = ast::BlockExpr::cast(insert_ws_into(fn_body.syntax().clone())) {
 +            body
 +        } else {
 +            fn_body.clone_for_update()
 +        }
 +    } else {
 +        fn_body.clone_for_update()
 +    };
 +    if let Some(imp) = body.syntax().ancestors().find_map(ast::Impl::cast) {
 +        if !node.syntax().ancestors().any(|anc| &anc == imp.syntax()) {
 +            if let Some(t) = imp.self_ty() {
 +                body.syntax()
 +                    .descendants_with_tokens()
 +                    .filter_map(NodeOrToken::into_token)
 +                    .filter(|tok| tok.kind() == SyntaxKind::SELF_TYPE_KW)
 +                    .for_each(|tok| ted::replace(tok, t.syntax()));
 +            }
 +        }
 +    }
 +    let usages_for_locals = |local| {
 +        Definition::Local(local)
 +            .usages(sema)
 +            .all()
 +            .references
 +            .remove(&function_def_file_id)
 +            .unwrap_or_default()
 +            .into_iter()
 +    };
 +    let param_use_nodes: Vec<Vec<_>> = params
 +        .iter()
 +        .map(|(pat, _, param)| {
 +            if !matches!(pat, ast::Pat::IdentPat(pat) if pat.is_simple_ident()) {
 +                return Vec::new();
 +            }
 +            // FIXME: we need to fetch all locals declared in the parameter here
 +            // not only the local if it is a simple binding
 +            match param.as_local(sema.db) {
 +                Some(l) => usages_for_locals(l)
 +                    .map(|FileReference { name, range, .. }| match name {
 +                        ast::NameLike::NameRef(_) => body
 +                            .syntax()
 +                            .covering_element(range)
 +                            .ancestors()
 +                            .nth(3)
 +                            .and_then(ast::PathExpr::cast),
 +                        _ => None,
 +                    })
 +                    .collect::<Option<Vec<_>>>()
 +                    .unwrap_or_default(),
 +                None => Vec::new(),
 +            }
 +        })
 +        .collect();
 +
 +    if function.self_param(sema.db).is_some() {
 +        let this = || make::name_ref("this").syntax().clone_for_update();
 +        if let Some(self_local) = params[0].2.as_local(sema.db) {
 +            usages_for_locals(self_local)
 +                .flat_map(|FileReference { name, range, .. }| match name {
 +                    ast::NameLike::NameRef(_) => Some(body.syntax().covering_element(range)),
 +                    _ => None,
 +                })
 +                .for_each(|it| {
 +                    ted::replace(it, &this());
 +                })
 +        }
 +    }
++
++    let mut func_let_vars: BTreeSet<String> = BTreeSet::new();
++
++    // grab all of the local variable declarations in the function
++    for stmt in fn_body.statements() {
++        if let Some(let_stmt) = ast::LetStmt::cast(stmt.syntax().to_owned()) {
++            for has_token in let_stmt.syntax().children_with_tokens() {
++                if let Some(node) = has_token.as_node() {
++                    if let Some(ident_pat) = ast::IdentPat::cast(node.to_owned()) {
++                        func_let_vars.insert(ident_pat.syntax().text().to_string());
++                    }
++                }
++            }
++        }
++    }
++
 +    // Inline parameter expressions or generate `let` statements depending on whether inlining works or not.
 +    for ((pat, param_ty, _), usages, expr) in izip!(params, param_use_nodes, arguments).rev() {
++        // izip confuses RA due to our lack of hygiene info currently losing us type info causing incorrect errors
++        let usages: &[ast::PathExpr] = &*usages;
++        let expr: &ast::Expr = expr;
++
++        let insert_let_stmt = || {
++            let ty = sema.type_of_expr(expr).filter(TypeInfo::has_adjustment).and(param_ty.clone());
++            if let Some(stmt_list) = body.stmt_list() {
++                stmt_list.push_front(
++                    make::let_stmt(pat.clone(), ty, Some(expr.clone())).clone_for_update().into(),
++                )
++            }
++        };
++
++        // check if there is a local var in the function that conflicts with parameter
++        // if it does then emit a let statement and continue
++        if func_let_vars.contains(&expr.syntax().text().to_string()) {
++            insert_let_stmt();
++            continue;
++        }
++
 +        let inline_direct = |usage, replacement: &ast::Expr| {
 +            if let Some(field) = path_expr_as_record_field(usage) {
 +                cov_mark::hit!(inline_call_inline_direct_field);
 +                field.replace_expr(replacement.clone_for_update());
 +            } else {
 +                ted::replace(usage.syntax(), &replacement.syntax().clone_for_update());
 +            }
 +        };
-                 let ty =
-                     sema.type_of_expr(expr).filter(TypeInfo::has_adjustment).and(param_ty.clone());
-                 if let Some(stmt_list) = body.stmt_list() {
-                     stmt_list.push_front(
-                         make::let_stmt(pat.clone(), ty, Some(expr.clone()))
-                             .clone_for_update()
-                             .into(),
-                     )
-                 }
++
 +        match usages {
 +            // inline single use closure arguments
 +            [usage]
 +                if matches!(expr, ast::Expr::ClosureExpr(_))
 +                    && usage.syntax().parent().and_then(ast::Expr::cast).is_some() =>
 +            {
 +                cov_mark::hit!(inline_call_inline_closure);
 +                let expr = make::expr_paren(expr.clone());
 +                inline_direct(usage, &expr);
 +            }
 +            // inline single use literals
 +            [usage] if matches!(expr, ast::Expr::Literal(_)) => {
 +                cov_mark::hit!(inline_call_inline_literal);
 +                inline_direct(usage, expr);
 +            }
 +            // inline direct local arguments
 +            [_, ..] if expr_as_name_ref(expr).is_some() => {
 +                cov_mark::hit!(inline_call_inline_locals);
 +                usages.iter().for_each(|usage| inline_direct(usage, expr));
 +            }
 +            // can't inline, emit a let statement
 +            _ => {
++                insert_let_stmt();
 +            }
 +        }
 +    }
++
 +    if let Some(generic_arg_list) = generic_arg_list.clone() {
 +        if let Some((target, source)) = &sema.scope(node.syntax()).zip(sema.scope(fn_body.syntax()))
 +        {
 +            PathTransform::function_call(target, source, function, generic_arg_list)
 +                .apply(body.syntax());
 +        }
 +    }
 +
 +    let original_indentation = match node {
 +        ast::CallableExpr::Call(it) => it.indent_level(),
 +        ast::CallableExpr::MethodCall(it) => it.indent_level(),
 +    };
 +    body.reindent_to(original_indentation);
 +
 +    match body.tail_expr() {
 +        Some(expr) if body.statements().next().is_none() => expr,
 +        _ => match node
 +            .syntax()
 +            .parent()
 +            .and_then(ast::BinExpr::cast)
 +            .and_then(|bin_expr| bin_expr.lhs())
 +        {
 +            Some(lhs) if lhs.syntax() == node.syntax() => {
 +                make::expr_paren(ast::Expr::BlockExpr(body)).clone_for_update()
 +            }
 +            _ => ast::Expr::BlockExpr(body),
 +        },
 +    }
 +}
 +
 +fn path_expr_as_record_field(usage: &PathExpr) -> Option<ast::RecordExprField> {
 +    let path = usage.path()?;
 +    let name_ref = path.as_single_name_ref()?;
 +    ast::RecordExprField::for_name_ref(&name_ref)
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use crate::tests::{check_assist, check_assist_not_applicable};
 +
 +    use super::*;
 +
 +    #[test]
 +    fn no_args_or_return_value_gets_inlined_without_block() {
 +        check_assist(
 +            inline_call,
 +            r#"
 +fn foo() { println!("Hello, World!"); }
 +fn main() {
 +    fo$0o();
 +}
 +"#,
 +            r#"
 +fn foo() { println!("Hello, World!"); }
 +fn main() {
 +    { println!("Hello, World!"); };
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn not_applicable_when_incorrect_number_of_parameters_are_provided() {
 +        cov_mark::check!(inline_call_incorrect_number_of_arguments);
 +        check_assist_not_applicable(
 +            inline_call,
 +            r#"
 +fn add(a: u32, b: u32) -> u32 { a + b }
 +fn main() { let x = add$0(42); }
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn args_with_side_effects() {
 +        check_assist(
 +            inline_call,
 +            r#"
 +fn foo(name: String) {
 +    println!("Hello, {}!", name);
 +}
 +fn main() {
 +    foo$0(String::from("Michael"));
 +}
 +"#,
 +            r#"
 +fn foo(name: String) {
 +    println!("Hello, {}!", name);
 +}
 +fn main() {
 +    {
 +        let name = String::from("Michael");
 +        println!("Hello, {}!", name);
 +    };
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn function_with_multiple_statements() {
 +        check_assist(
 +            inline_call,
 +            r#"
 +fn foo(a: u32, b: u32) -> u32 {
 +    let x = a + b;
 +    let y = x - b;
 +    x * y
 +}
 +
 +fn main() {
 +    let x = foo$0(1, 2);
 +}
 +"#,
 +            r#"
 +fn foo(a: u32, b: u32) -> u32 {
 +    let x = a + b;
 +    let y = x - b;
 +    x * y
 +}
 +
 +fn main() {
 +    let x = {
 +        let b = 2;
 +        let x = 1 + b;
 +        let y = x - b;
 +        x * y
 +    };
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn function_with_self_param() {
 +        check_assist(
 +            inline_call,
 +            r#"
 +struct Foo(u32);
 +
 +impl Foo {
 +    fn add(self, a: u32) -> Self {
 +        Foo(self.0 + a)
 +    }
 +}
 +
 +fn main() {
 +    let x = Foo::add$0(Foo(3), 2);
 +}
 +"#,
 +            r#"
 +struct Foo(u32);
 +
 +impl Foo {
 +    fn add(self, a: u32) -> Self {
 +        Foo(self.0 + a)
 +    }
 +}
 +
 +fn main() {
 +    let x = {
 +        let this = Foo(3);
 +        Foo(this.0 + 2)
 +    };
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn method_by_val() {
 +        check_assist(
 +            inline_call,
 +            r#"
 +struct Foo(u32);
 +
 +impl Foo {
 +    fn add(self, a: u32) -> Self {
 +        Foo(self.0 + a)
 +    }
 +}
 +
 +fn main() {
 +    let x = Foo(3).add$0(2);
 +}
 +"#,
 +            r#"
 +struct Foo(u32);
 +
 +impl Foo {
 +    fn add(self, a: u32) -> Self {
 +        Foo(self.0 + a)
 +    }
 +}
 +
 +fn main() {
 +    let x = {
 +        let this = Foo(3);
 +        Foo(this.0 + 2)
 +    };
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn method_by_ref() {
 +        check_assist(
 +            inline_call,
 +            r#"
 +struct Foo(u32);
 +
 +impl Foo {
 +    fn add(&self, a: u32) -> Self {
 +        Foo(self.0 + a)
 +    }
 +}
 +
 +fn main() {
 +    let x = Foo(3).add$0(2);
 +}
 +"#,
 +            r#"
 +struct Foo(u32);
 +
 +impl Foo {
 +    fn add(&self, a: u32) -> Self {
 +        Foo(self.0 + a)
 +    }
 +}
 +
 +fn main() {
 +    let x = {
 +        let ref this = Foo(3);
 +        Foo(this.0 + 2)
 +    };
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn method_by_ref_mut() {
 +        check_assist(
 +            inline_call,
 +            r#"
 +struct Foo(u32);
 +
 +impl Foo {
 +    fn clear(&mut self) {
 +        self.0 = 0;
 +    }
 +}
 +
 +fn main() {
 +    let mut foo = Foo(3);
 +    foo.clear$0();
 +}
 +"#,
 +            r#"
 +struct Foo(u32);
 +
 +impl Foo {
 +    fn clear(&mut self) {
 +        self.0 = 0;
 +    }
 +}
 +
 +fn main() {
 +    let mut foo = Foo(3);
 +    {
 +        let ref mut this = foo;
 +        this.0 = 0;
 +    };
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn function_multi_use_expr_in_param() {
 +        check_assist(
 +            inline_call,
 +            r#"
 +fn square(x: u32) -> u32 {
 +    x * x
 +}
 +fn main() {
 +    let x = 51;
 +    let y = square$0(10 + x);
 +}
 +"#,
 +            r#"
 +fn square(x: u32) -> u32 {
 +    x * x
 +}
 +fn main() {
 +    let x = 51;
 +    let y = {
 +        let x = 10 + x;
 +        x * x
 +    };
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn function_use_local_in_param() {
 +        cov_mark::check!(inline_call_inline_locals);
 +        check_assist(
 +            inline_call,
 +            r#"
 +fn square(x: u32) -> u32 {
 +    x * x
 +}
 +fn main() {
 +    let local = 51;
 +    let y = square$0(local);
 +}
 +"#,
 +            r#"
 +fn square(x: u32) -> u32 {
 +    x * x
 +}
 +fn main() {
 +    let local = 51;
 +    let y = local * local;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn method_in_impl() {
 +        check_assist(
 +            inline_call,
 +            r#"
 +struct Foo;
 +impl Foo {
 +    fn foo(&self) {
 +        self;
 +        self;
 +    }
 +    fn bar(&self) {
 +        self.foo$0();
 +    }
 +}
 +"#,
 +            r#"
 +struct Foo;
 +impl Foo {
 +    fn foo(&self) {
 +        self;
 +        self;
 +    }
 +    fn bar(&self) {
 +        {
 +            let ref this = self;
 +            this;
 +            this;
 +        };
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn wraps_closure_in_paren() {
 +        cov_mark::check!(inline_call_inline_closure);
 +        check_assist(
 +            inline_call,
 +            r#"
 +fn foo(x: fn()) {
 +    x();
 +}
 +
 +fn main() {
 +    foo$0(|| {})
 +}
 +"#,
 +            r#"
 +fn foo(x: fn()) {
 +    x();
 +}
 +
 +fn main() {
 +    {
 +        (|| {})();
 +    }
 +}
 +"#,
 +        );
 +        check_assist(
 +            inline_call,
 +            r#"
 +fn foo(x: fn()) {
 +    x();
 +}
 +
 +fn main() {
 +    foo$0(main)
 +}
 +"#,
 +            r#"
 +fn foo(x: fn()) {
 +    x();
 +}
 +
 +fn main() {
 +    {
 +        main();
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn inline_single_literal_expr() {
 +        cov_mark::check!(inline_call_inline_literal);
 +        check_assist(
 +            inline_call,
 +            r#"
 +fn foo(x: u32) -> u32{
 +    x
 +}
 +
 +fn main() {
 +    foo$0(222);
 +}
 +"#,
 +            r#"
 +fn foo(x: u32) -> u32{
 +    x
 +}
 +
 +fn main() {
 +    222;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn inline_emits_type_for_coercion() {
 +        check_assist(
 +            inline_call,
 +            r#"
 +fn foo(x: *const u32) -> u32 {
 +    x as u32
 +}
 +
 +fn main() {
 +    foo$0(&222);
 +}
 +"#,
 +            r#"
 +fn foo(x: *const u32) -> u32 {
 +    x as u32
 +}
 +
 +fn main() {
 +    {
 +        let x: *const u32 = &222;
 +        x as u32
 +    };
 +}
 +"#,
 +        );
 +    }
 +
 +    // FIXME: const generics aren't being substituted, this is blocked on better support for them
 +    #[test]
 +    fn inline_substitutes_generics() {
 +        check_assist(
 +            inline_call,
 +            r#"
 +fn foo<T, const N: usize>() {
 +    bar::<T, N>()
 +}
 +
 +fn bar<U, const M: usize>() {}
 +
 +fn main() {
 +    foo$0::<usize, {0}>();
 +}
 +"#,
 +            r#"
 +fn foo<T, const N: usize>() {
 +    bar::<T, N>()
 +}
 +
 +fn bar<U, const M: usize>() {}
 +
 +fn main() {
 +    bar::<usize, N>();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn inline_callers() {
 +        check_assist(
 +            inline_into_callers,
 +            r#"
 +fn do_the_math$0(b: u32) -> u32 {
 +    let foo = 10;
 +    foo * b + foo
 +}
 +fn foo() {
 +    do_the_math(0);
 +    let bar = 10;
 +    do_the_math(bar);
 +}
 +"#,
 +            r#"
 +
 +fn foo() {
 +    {
 +        let foo = 10;
 +        foo * 0 + foo
 +    };
 +    let bar = 10;
 +    {
 +        let foo = 10;
 +        foo * bar + foo
 +    };
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn inline_callers_across_files() {
 +        check_assist(
 +            inline_into_callers,
 +            r#"
 +//- /lib.rs
 +mod foo;
 +fn do_the_math$0(b: u32) -> u32 {
 +    let foo = 10;
 +    foo * b + foo
 +}
 +//- /foo.rs
 +use super::do_the_math;
 +fn foo() {
 +    do_the_math(0);
 +    let bar = 10;
 +    do_the_math(bar);
 +}
 +"#,
 +            r#"
 +//- /lib.rs
 +mod foo;
 +
 +//- /foo.rs
 +fn foo() {
 +    {
 +        let foo = 10;
 +        foo * 0 + foo
 +    };
 +    let bar = 10;
 +    {
 +        let foo = 10;
 +        foo * bar + foo
 +    };
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn inline_callers_across_files_with_def_file() {
 +        check_assist(
 +            inline_into_callers,
 +            r#"
 +//- /lib.rs
 +mod foo;
 +fn do_the_math$0(b: u32) -> u32 {
 +    let foo = 10;
 +    foo * b + foo
 +}
 +fn bar(a: u32, b: u32) -> u32 {
 +    do_the_math(0);
 +}
 +//- /foo.rs
 +use super::do_the_math;
 +fn foo() {
 +    do_the_math(0);
 +}
 +"#,
 +            r#"
 +//- /lib.rs
 +mod foo;
 +
 +fn bar(a: u32, b: u32) -> u32 {
 +    {
 +        let foo = 10;
 +        foo * 0 + foo
 +    };
 +}
 +//- /foo.rs
 +fn foo() {
 +    {
 +        let foo = 10;
 +        foo * 0 + foo
 +    };
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn inline_callers_recursive() {
 +        cov_mark::check!(inline_into_callers_recursive);
 +        check_assist_not_applicable(
 +            inline_into_callers,
 +            r#"
 +fn foo$0() {
 +    foo();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn inline_call_recursive() {
 +        cov_mark::check!(inline_call_recursive);
 +        check_assist_not_applicable(
 +            inline_call,
 +            r#"
 +fn foo() {
 +    foo$0();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn inline_call_field_shorthand() {
 +        cov_mark::check!(inline_call_inline_direct_field);
 +        check_assist(
 +            inline_call,
 +            r#"
 +struct Foo {
 +    field: u32,
 +    field1: u32,
 +    field2: u32,
 +    field3: u32,
 +}
 +fn foo(field: u32, field1: u32, val2: u32, val3: u32) -> Foo {
 +    Foo {
 +        field,
 +        field1,
 +        field2: val2,
 +        field3: val3,
 +    }
 +}
 +fn main() {
 +    let bar = 0;
 +    let baz = 0;
 +    foo$0(bar, 0, baz, 0);
 +}
 +"#,
 +            r#"
 +struct Foo {
 +    field: u32,
 +    field1: u32,
 +    field2: u32,
 +    field3: u32,
 +}
 +fn foo(field: u32, field1: u32, val2: u32, val3: u32) -> Foo {
 +    Foo {
 +        field,
 +        field1,
 +        field2: val2,
 +        field3: val3,
 +    }
 +}
 +fn main() {
 +    let bar = 0;
 +    let baz = 0;
 +    Foo {
 +            field: bar,
 +            field1: 0,
 +            field2: baz,
 +            field3: 0,
 +        };
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn inline_callers_wrapped_in_parentheses() {
 +        check_assist(
 +            inline_into_callers,
 +            r#"
 +fn foo$0() -> u32 {
 +    let x = 0;
 +    x
 +}
 +fn bar() -> u32 {
 +    foo() + foo()
 +}
 +"#,
 +            r#"
 +
 +fn bar() -> u32 {
 +    ({
 +        let x = 0;
 +        x
 +    }) + {
 +        let x = 0;
 +        x
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn inline_call_wrapped_in_parentheses() {
 +        check_assist(
 +            inline_call,
 +            r#"
 +fn foo() -> u32 {
 +    let x = 0;
 +    x
 +}
 +fn bar() -> u32 {
 +    foo$0() + foo()
 +}
 +"#,
 +            r#"
 +fn foo() -> u32 {
 +    let x = 0;
 +    x
 +}
 +fn bar() -> u32 {
 +    ({
 +        let x = 0;
 +        x
 +    }) + foo()
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn inline_call_defined_in_macro() {
 +        cov_mark::check!(inline_call_defined_in_macro);
 +        check_assist(
 +            inline_call,
 +            r#"
 +macro_rules! define_foo {
 +    () => { fn foo() -> u32 {
 +        let x = 0;
 +        x
 +    } };
 +}
 +define_foo!();
 +fn bar() -> u32 {
 +    foo$0()
 +}
 +"#,
 +            r#"
 +macro_rules! define_foo {
 +    () => { fn foo() -> u32 {
 +        let x = 0;
 +        x
 +    } };
 +}
 +define_foo!();
 +fn bar() -> u32 {
 +    {
 +      let x = 0;
 +      x
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn inline_call_with_self_type() {
 +        check_assist(
 +            inline_call,
 +            r#"
 +struct A(u32);
 +impl A {
 +    fn f() -> Self { Self(114514) }
 +}
 +fn main() {
 +    A::f$0();
 +}
 +"#,
 +            r#"
 +struct A(u32);
 +impl A {
 +    fn f() -> Self { Self(114514) }
 +}
 +fn main() {
 +    A(114514);
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn inline_call_with_self_type_but_within_same_impl() {
 +        check_assist(
 +            inline_call,
 +            r#"
 +struct A(u32);
 +impl A {
 +    fn f() -> Self { Self(1919810) }
 +    fn main() {
 +        Self::f$0();
 +    }
 +}
 +"#,
 +            r#"
 +struct A(u32);
 +impl A {
 +    fn f() -> Self { Self(1919810) }
 +    fn main() {
 +        Self(1919810);
 +    }
 +}
 +"#,
 +        )
 +    }
++
++    #[test]
++    fn local_variable_shadowing_callers_argument() {
++        check_assist(
++            inline_call,
++            r#"
++fn foo(bar: u32, baz: u32) -> u32 {
++    let a = 1;
++    bar * baz * a * 6
++}
++fn main() {
++    let a = 7;
++    let b = 1;
++    let res = foo$0(a, b);
++}
++"#,
++            r#"
++fn foo(bar: u32, baz: u32) -> u32 {
++    let a = 1;
++    bar * baz * a * 6
++}
++fn main() {
++    let a = 7;
++    let b = 1;
++    let res = {
++        let bar = a;
++        let a = 1;
++        bar * b * a * 6
++    };
++}
++"#,
++        );
++    }
 +}
index 7259d67819416e502dcceb64452b5f3b2bedc6a6,0000000000000000000000000000000000000000..ce44100e34bebee523197b6bf66035b810459917
mode 100644,000000..100644
--- /dev/null
@@@ -1,954 -1,0 +1,954 @@@
-     let init_in_paren = format!("({})", &init_str);
 +use either::Either;
 +use hir::{PathResolution, Semantics};
 +use ide_db::{
 +    base_db::FileId,
 +    defs::Definition,
 +    search::{FileReference, UsageSearchResult},
 +    RootDatabase,
 +};
 +use syntax::{
 +    ast::{self, AstNode, AstToken, HasName},
 +    SyntaxElement, TextRange,
 +};
 +
 +use crate::{
 +    assist_context::{AssistContext, Assists},
 +    AssistId, AssistKind,
 +};
 +
 +// Assist: inline_local_variable
 +//
 +// Inlines a local variable.
 +//
 +// ```
 +// fn main() {
 +//     let x$0 = 1 + 2;
 +//     x * 4;
 +// }
 +// ```
 +// ->
 +// ```
 +// fn main() {
 +//     (1 + 2) * 4;
 +// }
 +// ```
 +pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    let file_id = ctx.file_id();
 +    let range = ctx.selection_trimmed();
 +    let InlineData { let_stmt, delete_let, references, target } =
 +        if let Some(path_expr) = ctx.find_node_at_offset::<ast::PathExpr>() {
 +            inline_usage(&ctx.sema, path_expr, range, file_id)
 +        } else if let Some(let_stmt) = ctx.find_node_at_offset::<ast::LetStmt>() {
 +            inline_let(&ctx.sema, let_stmt, range, file_id)
 +        } else {
 +            None
 +        }?;
 +    let initializer_expr = let_stmt.initializer()?;
 +
 +    let delete_range = delete_let.then(|| {
 +        if let Some(whitespace) = let_stmt
 +            .syntax()
 +            .next_sibling_or_token()
 +            .and_then(SyntaxElement::into_token)
 +            .and_then(ast::Whitespace::cast)
 +        {
 +            TextRange::new(
 +                let_stmt.syntax().text_range().start(),
 +                whitespace.syntax().text_range().end(),
 +            )
 +        } else {
 +            let_stmt.syntax().text_range()
 +        }
 +    });
 +
 +    let wrap_in_parens = references
 +        .into_iter()
 +        .filter_map(|FileReference { range, name, .. }| match name {
 +            ast::NameLike::NameRef(name) => Some((range, name)),
 +            _ => None,
 +        })
 +        .map(|(range, name_ref)| {
 +            if range != name_ref.syntax().text_range() {
 +                // Do not rename inside macros
 +                // FIXME: This feels like a bad heuristic for macros
 +                return None;
 +            }
 +            let usage_node =
 +                name_ref.syntax().ancestors().find(|it| ast::PathExpr::can_cast(it.kind()));
 +            let usage_parent_option =
 +                usage_node.and_then(|it| it.parent()).and_then(ast::Expr::cast);
 +            let usage_parent = match usage_parent_option {
 +                Some(u) => u,
 +                None => return Some((range, name_ref, false)),
 +            };
 +            let initializer = matches!(
 +                initializer_expr,
 +                ast::Expr::CallExpr(_)
 +                    | ast::Expr::IndexExpr(_)
 +                    | ast::Expr::MethodCallExpr(_)
 +                    | ast::Expr::FieldExpr(_)
 +                    | ast::Expr::TryExpr(_)
 +                    | ast::Expr::Literal(_)
 +                    | ast::Expr::TupleExpr(_)
 +                    | ast::Expr::ArrayExpr(_)
 +                    | ast::Expr::ParenExpr(_)
 +                    | ast::Expr::PathExpr(_)
 +                    | ast::Expr::BlockExpr(_),
 +            );
 +            let parent = matches!(
 +                usage_parent,
 +                ast::Expr::CallExpr(_)
 +                    | ast::Expr::TupleExpr(_)
 +                    | ast::Expr::ArrayExpr(_)
 +                    | ast::Expr::ParenExpr(_)
 +                    | ast::Expr::ForExpr(_)
 +                    | ast::Expr::WhileExpr(_)
 +                    | ast::Expr::BreakExpr(_)
 +                    | ast::Expr::ReturnExpr(_)
 +                    | ast::Expr::MatchExpr(_)
 +                    | ast::Expr::BlockExpr(_)
 +            );
 +            Some((range, name_ref, !(initializer || parent)))
 +        })
 +        .collect::<Option<Vec<_>>>()?;
 +
 +    let init_str = initializer_expr.syntax().text().to_string();
-                     builder.insert(range.end(), format!(": {}", replacement));
++    let init_in_paren = format!("({init_str})");
 +
 +    let target = match target {
 +        ast::NameOrNameRef::Name(it) => it.syntax().text_range(),
 +        ast::NameOrNameRef::NameRef(it) => it.syntax().text_range(),
 +    };
 +
 +    acc.add(
 +        AssistId("inline_local_variable", AssistKind::RefactorInline),
 +        "Inline variable",
 +        target,
 +        move |builder| {
 +            if let Some(range) = delete_range {
 +                builder.delete(range);
 +            }
 +            for (range, name, should_wrap) in wrap_in_parens {
 +                let replacement = if should_wrap { &init_in_paren } else { &init_str };
 +                if ast::RecordExprField::for_field_name(&name).is_some() {
 +                    cov_mark::hit!(inline_field_shorthand);
++                    builder.insert(range.end(), format!(": {replacement}"));
 +                } else {
 +                    builder.replace(range, replacement.clone())
 +                }
 +            }
 +        },
 +    )
 +}
 +
 +struct InlineData {
 +    let_stmt: ast::LetStmt,
 +    delete_let: bool,
 +    target: ast::NameOrNameRef,
 +    references: Vec<FileReference>,
 +}
 +
 +fn inline_let(
 +    sema: &Semantics<'_, RootDatabase>,
 +    let_stmt: ast::LetStmt,
 +    range: TextRange,
 +    file_id: FileId,
 +) -> Option<InlineData> {
 +    let bind_pat = match let_stmt.pat()? {
 +        ast::Pat::IdentPat(pat) => pat,
 +        _ => return None,
 +    };
 +    if bind_pat.mut_token().is_some() {
 +        cov_mark::hit!(test_not_inline_mut_variable);
 +        return None;
 +    }
 +    if !bind_pat.syntax().text_range().contains_range(range) {
 +        cov_mark::hit!(not_applicable_outside_of_bind_pat);
 +        return None;
 +    }
 +
 +    let local = sema.to_def(&bind_pat)?;
 +    let UsageSearchResult { mut references } = Definition::Local(local).usages(sema).all();
 +    match references.remove(&file_id) {
 +        Some(references) => Some(InlineData {
 +            let_stmt,
 +            delete_let: true,
 +            target: ast::NameOrNameRef::Name(bind_pat.name()?),
 +            references,
 +        }),
 +        None => {
 +            cov_mark::hit!(test_not_applicable_if_variable_unused);
 +            None
 +        }
 +    }
 +}
 +
 +fn inline_usage(
 +    sema: &Semantics<'_, RootDatabase>,
 +    path_expr: ast::PathExpr,
 +    range: TextRange,
 +    file_id: FileId,
 +) -> Option<InlineData> {
 +    let path = path_expr.path()?;
 +    let name = path.as_single_name_ref()?;
 +    if !name.syntax().text_range().contains_range(range) {
 +        cov_mark::hit!(test_not_inline_selection_too_broad);
 +        return None;
 +    }
 +
 +    let local = match sema.resolve_path(&path)? {
 +        PathResolution::Local(local) => local,
 +        _ => return None,
 +    };
 +    if local.is_mut(sema.db) {
 +        cov_mark::hit!(test_not_inline_mut_variable_use);
 +        return None;
 +    }
 +
 +    // FIXME: Handle multiple local definitions
 +    let bind_pat = match local.source(sema.db).value {
 +        Either::Left(ident) => ident,
 +        _ => return None,
 +    };
 +
 +    let let_stmt = ast::LetStmt::cast(bind_pat.syntax().parent()?)?;
 +
 +    let UsageSearchResult { mut references } = Definition::Local(local).usages(sema).all();
 +    let mut references = references.remove(&file_id)?;
 +    let delete_let = references.len() == 1;
 +    references.retain(|fref| fref.name.as_name_ref() == Some(&name));
 +
 +    Some(InlineData { let_stmt, delete_let, target: ast::NameOrNameRef::NameRef(name), references })
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use crate::tests::{check_assist, check_assist_not_applicable};
 +
 +    use super::*;
 +
 +    #[test]
 +    fn test_inline_let_bind_literal_expr() {
 +        check_assist(
 +            inline_local_variable,
 +            r"
 +fn bar(a: usize) {}
 +fn foo() {
 +    let a$0 = 1;
 +    a + 1;
 +    if a > 10 {
 +    }
 +
 +    while a > 10 {
 +
 +    }
 +    let b = a * 10;
 +    bar(a);
 +}",
 +            r"
 +fn bar(a: usize) {}
 +fn foo() {
 +    1 + 1;
 +    if 1 > 10 {
 +    }
 +
 +    while 1 > 10 {
 +
 +    }
 +    let b = 1 * 10;
 +    bar(1);
 +}",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_inline_let_bind_bin_expr() {
 +        check_assist(
 +            inline_local_variable,
 +            r"
 +fn bar(a: usize) {}
 +fn foo() {
 +    let a$0 = 1 + 1;
 +    a + 1;
 +    if a > 10 {
 +    }
 +
 +    while a > 10 {
 +
 +    }
 +    let b = a * 10;
 +    bar(a);
 +}",
 +            r"
 +fn bar(a: usize) {}
 +fn foo() {
 +    (1 + 1) + 1;
 +    if (1 + 1) > 10 {
 +    }
 +
 +    while (1 + 1) > 10 {
 +
 +    }
 +    let b = (1 + 1) * 10;
 +    bar(1 + 1);
 +}",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_inline_let_bind_function_call_expr() {
 +        check_assist(
 +            inline_local_variable,
 +            r"
 +fn bar(a: usize) {}
 +fn foo() {
 +    let a$0 = bar(1);
 +    a + 1;
 +    if a > 10 {
 +    }
 +
 +    while a > 10 {
 +
 +    }
 +    let b = a * 10;
 +    bar(a);
 +}",
 +            r"
 +fn bar(a: usize) {}
 +fn foo() {
 +    bar(1) + 1;
 +    if bar(1) > 10 {
 +    }
 +
 +    while bar(1) > 10 {
 +
 +    }
 +    let b = bar(1) * 10;
 +    bar(bar(1));
 +}",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_inline_let_bind_cast_expr() {
 +        check_assist(
 +            inline_local_variable,
 +            r"
 +fn bar(a: usize): usize { a }
 +fn foo() {
 +    let a$0 = bar(1) as u64;
 +    a + 1;
 +    if a > 10 {
 +    }
 +
 +    while a > 10 {
 +
 +    }
 +    let b = a * 10;
 +    bar(a);
 +}",
 +            r"
 +fn bar(a: usize): usize { a }
 +fn foo() {
 +    (bar(1) as u64) + 1;
 +    if (bar(1) as u64) > 10 {
 +    }
 +
 +    while (bar(1) as u64) > 10 {
 +
 +    }
 +    let b = (bar(1) as u64) * 10;
 +    bar(bar(1) as u64);
 +}",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_inline_let_bind_block_expr() {
 +        check_assist(
 +            inline_local_variable,
 +            r"
 +fn foo() {
 +    let a$0 = { 10 + 1 };
 +    a + 1;
 +    if a > 10 {
 +    }
 +
 +    while a > 10 {
 +
 +    }
 +    let b = a * 10;
 +    bar(a);
 +}",
 +            r"
 +fn foo() {
 +    { 10 + 1 } + 1;
 +    if { 10 + 1 } > 10 {
 +    }
 +
 +    while { 10 + 1 } > 10 {
 +
 +    }
 +    let b = { 10 + 1 } * 10;
 +    bar({ 10 + 1 });
 +}",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_inline_let_bind_paren_expr() {
 +        check_assist(
 +            inline_local_variable,
 +            r"
 +fn foo() {
 +    let a$0 = ( 10 + 1 );
 +    a + 1;
 +    if a > 10 {
 +    }
 +
 +    while a > 10 {
 +
 +    }
 +    let b = a * 10;
 +    bar(a);
 +}",
 +            r"
 +fn foo() {
 +    ( 10 + 1 ) + 1;
 +    if ( 10 + 1 ) > 10 {
 +    }
 +
 +    while ( 10 + 1 ) > 10 {
 +
 +    }
 +    let b = ( 10 + 1 ) * 10;
 +    bar(( 10 + 1 ));
 +}",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_not_inline_mut_variable() {
 +        cov_mark::check!(test_not_inline_mut_variable);
 +        check_assist_not_applicable(
 +            inline_local_variable,
 +            r"
 +fn foo() {
 +    let mut a$0 = 1 + 1;
 +    a + 1;
 +}",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_not_inline_mut_variable_use() {
 +        cov_mark::check!(test_not_inline_mut_variable_use);
 +        check_assist_not_applicable(
 +            inline_local_variable,
 +            r"
 +fn foo() {
 +    let mut a = 1 + 1;
 +    a$0 + 1;
 +}",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_call_expr() {
 +        check_assist(
 +            inline_local_variable,
 +            r"
 +fn foo() {
 +    let a$0 = bar(10 + 1);
 +    let b = a * 10;
 +    let c = a as usize;
 +}",
 +            r"
 +fn foo() {
 +    let b = bar(10 + 1) * 10;
 +    let c = bar(10 + 1) as usize;
 +}",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_index_expr() {
 +        check_assist(
 +            inline_local_variable,
 +            r"
 +fn foo() {
 +    let x = vec![1, 2, 3];
 +    let a$0 = x[0];
 +    let b = a * 10;
 +    let c = a as usize;
 +}",
 +            r"
 +fn foo() {
 +    let x = vec![1, 2, 3];
 +    let b = x[0] * 10;
 +    let c = x[0] as usize;
 +}",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_method_call_expr() {
 +        check_assist(
 +            inline_local_variable,
 +            r"
 +fn foo() {
 +    let bar = vec![1];
 +    let a$0 = bar.len();
 +    let b = a * 10;
 +    let c = a as usize;
 +}",
 +            r"
 +fn foo() {
 +    let bar = vec![1];
 +    let b = bar.len() * 10;
 +    let c = bar.len() as usize;
 +}",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_field_expr() {
 +        check_assist(
 +            inline_local_variable,
 +            r"
 +struct Bar {
 +    foo: usize
 +}
 +
 +fn foo() {
 +    let bar = Bar { foo: 1 };
 +    let a$0 = bar.foo;
 +    let b = a * 10;
 +    let c = a as usize;
 +}",
 +            r"
 +struct Bar {
 +    foo: usize
 +}
 +
 +fn foo() {
 +    let bar = Bar { foo: 1 };
 +    let b = bar.foo * 10;
 +    let c = bar.foo as usize;
 +}",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_try_expr() {
 +        check_assist(
 +            inline_local_variable,
 +            r"
 +fn foo() -> Option<usize> {
 +    let bar = Some(1);
 +    let a$0 = bar?;
 +    let b = a * 10;
 +    let c = a as usize;
 +    None
 +}",
 +            r"
 +fn foo() -> Option<usize> {
 +    let bar = Some(1);
 +    let b = bar? * 10;
 +    let c = bar? as usize;
 +    None
 +}",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_ref_expr() {
 +        check_assist(
 +            inline_local_variable,
 +            r"
 +fn foo() {
 +    let bar = 10;
 +    let a$0 = &bar;
 +    let b = a * 10;
 +}",
 +            r"
 +fn foo() {
 +    let bar = 10;
 +    let b = (&bar) * 10;
 +}",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_tuple_expr() {
 +        check_assist(
 +            inline_local_variable,
 +            r"
 +fn foo() {
 +    let a$0 = (10, 20);
 +    let b = a[0];
 +}",
 +            r"
 +fn foo() {
 +    let b = (10, 20)[0];
 +}",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_array_expr() {
 +        check_assist(
 +            inline_local_variable,
 +            r"
 +fn foo() {
 +    let a$0 = [1, 2, 3];
 +    let b = a.len();
 +}",
 +            r"
 +fn foo() {
 +    let b = [1, 2, 3].len();
 +}",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_paren() {
 +        check_assist(
 +            inline_local_variable,
 +            r"
 +fn foo() {
 +    let a$0 = (10 + 20);
 +    let b = a * 10;
 +    let c = a as usize;
 +}",
 +            r"
 +fn foo() {
 +    let b = (10 + 20) * 10;
 +    let c = (10 + 20) as usize;
 +}",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_path_expr() {
 +        check_assist(
 +            inline_local_variable,
 +            r"
 +fn foo() {
 +    let d = 10;
 +    let a$0 = d;
 +    let b = a * 10;
 +    let c = a as usize;
 +}",
 +            r"
 +fn foo() {
 +    let d = 10;
 +    let b = d * 10;
 +    let c = d as usize;
 +}",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_block_expr() {
 +        check_assist(
 +            inline_local_variable,
 +            r"
 +fn foo() {
 +    let a$0 = { 10 };
 +    let b = a * 10;
 +    let c = a as usize;
 +}",
 +            r"
 +fn foo() {
 +    let b = { 10 } * 10;
 +    let c = { 10 } as usize;
 +}",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_used_in_different_expr1() {
 +        check_assist(
 +            inline_local_variable,
 +            r"
 +fn foo() {
 +    let a$0 = 10 + 20;
 +    let b = a * 10;
 +    let c = (a, 20);
 +    let d = [a, 10];
 +    let e = (a);
 +}",
 +            r"
 +fn foo() {
 +    let b = (10 + 20) * 10;
 +    let c = (10 + 20, 20);
 +    let d = [10 + 20, 10];
 +    let e = (10 + 20);
 +}",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_used_in_for_expr() {
 +        check_assist(
 +            inline_local_variable,
 +            r"
 +fn foo() {
 +    let a$0 = vec![10, 20];
 +    for i in a {}
 +}",
 +            r"
 +fn foo() {
 +    for i in vec![10, 20] {}
 +}",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_used_in_while_expr() {
 +        check_assist(
 +            inline_local_variable,
 +            r"
 +fn foo() {
 +    let a$0 = 1 > 0;
 +    while a {}
 +}",
 +            r"
 +fn foo() {
 +    while 1 > 0 {}
 +}",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_used_in_break_expr() {
 +        check_assist(
 +            inline_local_variable,
 +            r"
 +fn foo() {
 +    let a$0 = 1 + 1;
 +    loop {
 +        break a;
 +    }
 +}",
 +            r"
 +fn foo() {
 +    loop {
 +        break 1 + 1;
 +    }
 +}",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_used_in_return_expr() {
 +        check_assist(
 +            inline_local_variable,
 +            r"
 +fn foo() {
 +    let a$0 = 1 > 0;
 +    return a;
 +}",
 +            r"
 +fn foo() {
 +    return 1 > 0;
 +}",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_used_in_match_expr() {
 +        check_assist(
 +            inline_local_variable,
 +            r"
 +fn foo() {
 +    let a$0 = 1 > 0;
 +    match a {}
 +}",
 +            r"
 +fn foo() {
 +    match 1 > 0 {}
 +}",
 +        );
 +    }
 +
 +    #[test]
 +    fn inline_field_shorthand() {
 +        cov_mark::check!(inline_field_shorthand);
 +        check_assist(
 +            inline_local_variable,
 +            r"
 +struct S { foo: i32}
 +fn main() {
 +    let $0foo = 92;
 +    S { foo }
 +}
 +",
 +            r"
 +struct S { foo: i32}
 +fn main() {
 +    S { foo: 92 }
 +}
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_not_applicable_if_variable_unused() {
 +        cov_mark::check!(test_not_applicable_if_variable_unused);
 +        check_assist_not_applicable(
 +            inline_local_variable,
 +            r"
 +fn foo() {
 +    let $0a = 0;
 +}
 +            ",
 +        )
 +    }
 +
 +    #[test]
 +    fn not_applicable_outside_of_bind_pat() {
 +        cov_mark::check!(not_applicable_outside_of_bind_pat);
 +        check_assist_not_applicable(
 +            inline_local_variable,
 +            r"
 +fn main() {
 +    let x = $01 + 2;
 +    x * 4;
 +}
 +",
 +        )
 +    }
 +
 +    #[test]
 +    fn works_on_local_usage() {
 +        check_assist(
 +            inline_local_variable,
 +            r#"
 +fn f() {
 +    let xyz = 0;
 +    xyz$0;
 +}
 +"#,
 +            r#"
 +fn f() {
 +    0;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn does_not_remove_let_when_multiple_usages() {
 +        check_assist(
 +            inline_local_variable,
 +            r#"
 +fn f() {
 +    let xyz = 0;
 +    xyz$0;
 +    xyz;
 +}
 +"#,
 +            r#"
 +fn f() {
 +    let xyz = 0;
 +    0;
 +    xyz;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn not_applicable_with_non_ident_pattern() {
 +        check_assist_not_applicable(
 +            inline_local_variable,
 +            r#"
 +fn main() {
 +    let (x, y) = (0, 1);
 +    x$0;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn not_applicable_on_local_usage_in_macro() {
 +        check_assist_not_applicable(
 +            inline_local_variable,
 +            r#"
 +macro_rules! m {
 +    ($i:ident) => { $i }
 +}
 +fn f() {
 +    let xyz = 0;
 +    m!(xyz$0); // replacing it would break the macro
 +}
 +"#,
 +        );
 +        check_assist_not_applicable(
 +            inline_local_variable,
 +            r#"
 +macro_rules! m {
 +    ($i:ident) => { $i }
 +}
 +fn f() {
 +    let xyz$0 = 0;
 +    m!(xyz); // replacing it would break the macro
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_not_inline_selection_too_broad() {
 +        cov_mark::check!(test_not_inline_selection_too_broad);
 +        check_assist_not_applicable(
 +            inline_local_variable,
 +            r#"
 +fn f() {
 +    let foo = 0;
 +    let bar = 0;
 +    $0foo + bar$0;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_inline_ref_in_let() {
 +        check_assist(
 +            inline_local_variable,
 +            r#"
 +fn f() {
 +    let x = {
 +        let y = 0;
 +        y$0
 +    };
 +}
 +"#,
 +            r#"
 +fn f() {
 +    let x = {
 +        0
 +    };
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_inline_let_unit_struct() {
 +        check_assist_not_applicable(
 +            inline_local_variable,
 +            r#"
 +struct S;
 +fn f() {
 +    let S$0 = S;
 +    S;
 +}
 +"#,
 +        );
 +    }
 +}
index 2fc754e3e50d1b81f1376fe2167aed17282218b4,0000000000000000000000000000000000000000..a54dc4f96de00e0f198b8c86b9d26a6d8b8bd484
mode 100644,000000..100644
--- /dev/null
@@@ -1,338 -1,0 +1,338 @@@
-             ('a'..='z').map(|it| format!("'{}", it)).find(|it| !used_lifetime_params.contains(it))
 +use ide_db::FxHashSet;
 +use syntax::{
 +    ast::{self, edit_in_place::GenericParamsOwnerEdit, make, HasGenericParams},
 +    ted::{self, Position},
 +    AstNode, TextRange,
 +};
 +
 +use crate::{assist_context::SourceChangeBuilder, AssistContext, AssistId, AssistKind, Assists};
 +
 +static ASSIST_NAME: &str = "introduce_named_lifetime";
 +static ASSIST_LABEL: &str = "Introduce named lifetime";
 +
 +// Assist: introduce_named_lifetime
 +//
 +// Change an anonymous lifetime to a named lifetime.
 +//
 +// ```
 +// impl Cursor<'_$0> {
 +//     fn node(self) -> &SyntaxNode {
 +//         match self {
 +//             Cursor::Replace(node) | Cursor::Before(node) => node,
 +//         }
 +//     }
 +// }
 +// ```
 +// ->
 +// ```
 +// impl<'a> Cursor<'a> {
 +//     fn node(self) -> &SyntaxNode {
 +//         match self {
 +//             Cursor::Replace(node) | Cursor::Before(node) => node,
 +//         }
 +//     }
 +// }
 +// ```
 +pub(crate) fn introduce_named_lifetime(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    // FIXME: How can we handle renaming any one of multiple anonymous lifetimes?
 +    // FIXME: should also add support for the case fun(f: &Foo) -> &$0Foo
 +    let lifetime =
 +        ctx.find_node_at_offset::<ast::Lifetime>().filter(|lifetime| lifetime.text() == "'_")?;
 +    let lifetime_loc = lifetime.lifetime_ident_token()?.text_range();
 +
 +    if let Some(fn_def) = lifetime.syntax().ancestors().find_map(ast::Fn::cast) {
 +        generate_fn_def_assist(acc, fn_def, lifetime_loc, lifetime)
 +    } else if let Some(impl_def) = lifetime.syntax().ancestors().find_map(ast::Impl::cast) {
 +        generate_impl_def_assist(acc, impl_def, lifetime_loc, lifetime)
 +    } else {
 +        None
 +    }
 +}
 +
 +/// Generate the assist for the fn def case
 +fn generate_fn_def_assist(
 +    acc: &mut Assists,
 +    fn_def: ast::Fn,
 +    lifetime_loc: TextRange,
 +    lifetime: ast::Lifetime,
 +) -> Option<()> {
 +    let param_list: ast::ParamList = fn_def.param_list()?;
 +    let new_lifetime_param = generate_unique_lifetime_param_name(fn_def.generic_param_list())?;
 +    let self_param =
 +        // use the self if it's a reference and has no explicit lifetime
 +        param_list.self_param().filter(|p| p.lifetime().is_none() && p.amp_token().is_some());
 +    // compute the location which implicitly has the same lifetime as the anonymous lifetime
 +    let loc_needing_lifetime = if let Some(self_param) = self_param {
 +        // if we have a self reference, use that
 +        Some(NeedsLifetime::SelfParam(self_param))
 +    } else {
 +        // otherwise, if there's a single reference parameter without a named liftime, use that
 +        let fn_params_without_lifetime: Vec<_> = param_list
 +            .params()
 +            .filter_map(|param| match param.ty() {
 +                Some(ast::Type::RefType(ascribed_type)) if ascribed_type.lifetime().is_none() => {
 +                    Some(NeedsLifetime::RefType(ascribed_type))
 +                }
 +                _ => None,
 +            })
 +            .collect();
 +        match fn_params_without_lifetime.len() {
 +            1 => Some(fn_params_without_lifetime.into_iter().next()?),
 +            0 => None,
 +            // multiple unnnamed is invalid. assist is not applicable
 +            _ => return None,
 +        }
 +    };
 +    acc.add(AssistId(ASSIST_NAME, AssistKind::Refactor), ASSIST_LABEL, lifetime_loc, |builder| {
 +        let fn_def = builder.make_mut(fn_def);
 +        let lifetime = builder.make_mut(lifetime);
 +        let loc_needing_lifetime =
 +            loc_needing_lifetime.and_then(|it| it.make_mut(builder).to_position());
 +
 +        fn_def.get_or_create_generic_param_list().add_generic_param(
 +            make::lifetime_param(new_lifetime_param.clone()).clone_for_update().into(),
 +        );
 +        ted::replace(lifetime.syntax(), new_lifetime_param.clone_for_update().syntax());
 +        if let Some(position) = loc_needing_lifetime {
 +            ted::insert(position, new_lifetime_param.clone_for_update().syntax());
 +        }
 +    })
 +}
 +
 +/// Generate the assist for the impl def case
 +fn generate_impl_def_assist(
 +    acc: &mut Assists,
 +    impl_def: ast::Impl,
 +    lifetime_loc: TextRange,
 +    lifetime: ast::Lifetime,
 +) -> Option<()> {
 +    let new_lifetime_param = generate_unique_lifetime_param_name(impl_def.generic_param_list())?;
 +    acc.add(AssistId(ASSIST_NAME, AssistKind::Refactor), ASSIST_LABEL, lifetime_loc, |builder| {
 +        let impl_def = builder.make_mut(impl_def);
 +        let lifetime = builder.make_mut(lifetime);
 +
 +        impl_def.get_or_create_generic_param_list().add_generic_param(
 +            make::lifetime_param(new_lifetime_param.clone()).clone_for_update().into(),
 +        );
 +        ted::replace(lifetime.syntax(), new_lifetime_param.clone_for_update().syntax());
 +    })
 +}
 +
 +/// Given a type parameter list, generate a unique lifetime parameter name
 +/// which is not in the list
 +fn generate_unique_lifetime_param_name(
 +    existing_type_param_list: Option<ast::GenericParamList>,
 +) -> Option<ast::Lifetime> {
 +    match existing_type_param_list {
 +        Some(type_params) => {
 +            let used_lifetime_params: FxHashSet<_> =
 +                type_params.lifetime_params().map(|p| p.syntax().text().to_string()).collect();
++            ('a'..='z').map(|it| format!("'{it}")).find(|it| !used_lifetime_params.contains(it))
 +        }
 +        None => Some("'a".to_string()),
 +    }
 +    .map(|it| make::lifetime(&it))
 +}
 +
 +enum NeedsLifetime {
 +    SelfParam(ast::SelfParam),
 +    RefType(ast::RefType),
 +}
 +
 +impl NeedsLifetime {
 +    fn make_mut(self, builder: &mut SourceChangeBuilder) -> Self {
 +        match self {
 +            Self::SelfParam(it) => Self::SelfParam(builder.make_mut(it)),
 +            Self::RefType(it) => Self::RefType(builder.make_mut(it)),
 +        }
 +    }
 +
 +    fn to_position(self) -> Option<Position> {
 +        match self {
 +            Self::SelfParam(it) => Some(Position::after(it.amp_token()?)),
 +            Self::RefType(it) => Some(Position::after(it.amp_token()?)),
 +        }
 +    }
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use super::*;
 +    use crate::tests::{check_assist, check_assist_not_applicable};
 +
 +    #[test]
 +    fn test_example_case() {
 +        check_assist(
 +            introduce_named_lifetime,
 +            r#"impl Cursor<'_$0> {
 +                fn node(self) -> &SyntaxNode {
 +                    match self {
 +                        Cursor::Replace(node) | Cursor::Before(node) => node,
 +                    }
 +                }
 +            }"#,
 +            r#"impl<'a> Cursor<'a> {
 +                fn node(self) -> &SyntaxNode {
 +                    match self {
 +                        Cursor::Replace(node) | Cursor::Before(node) => node,
 +                    }
 +                }
 +            }"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_example_case_simplified() {
 +        check_assist(
 +            introduce_named_lifetime,
 +            r#"impl Cursor<'_$0> {"#,
 +            r#"impl<'a> Cursor<'a> {"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_example_case_cursor_after_tick() {
 +        check_assist(
 +            introduce_named_lifetime,
 +            r#"impl Cursor<'$0_> {"#,
 +            r#"impl<'a> Cursor<'a> {"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_impl_with_other_type_param() {
 +        check_assist(
 +            introduce_named_lifetime,
 +            "impl<I> fmt::Display for SepByBuilder<'_$0, I>
 +        where
 +            I: Iterator,
 +            I::Item: fmt::Display,
 +        {",
 +            "impl<I, 'a> fmt::Display for SepByBuilder<'a, I>
 +        where
 +            I: Iterator,
 +            I::Item: fmt::Display,
 +        {",
 +        )
 +    }
 +
 +    #[test]
 +    fn test_example_case_cursor_before_tick() {
 +        check_assist(
 +            introduce_named_lifetime,
 +            r#"impl Cursor<$0'_> {"#,
 +            r#"impl<'a> Cursor<'a> {"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_not_applicable_cursor_position() {
 +        check_assist_not_applicable(introduce_named_lifetime, r#"impl Cursor<'_>$0 {"#);
 +        check_assist_not_applicable(introduce_named_lifetime, r#"impl Cursor$0<'_> {"#);
 +    }
 +
 +    #[test]
 +    fn test_not_applicable_lifetime_already_name() {
 +        check_assist_not_applicable(introduce_named_lifetime, r#"impl Cursor<'a$0> {"#);
 +        check_assist_not_applicable(introduce_named_lifetime, r#"fn my_fun<'a>() -> X<'a$0>"#);
 +    }
 +
 +    #[test]
 +    fn test_with_type_parameter() {
 +        check_assist(
 +            introduce_named_lifetime,
 +            r#"impl<T> Cursor<T, '_$0>"#,
 +            r#"impl<T, 'a> Cursor<T, 'a>"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_with_existing_lifetime_name_conflict() {
 +        check_assist(
 +            introduce_named_lifetime,
 +            r#"impl<'a, 'b> Cursor<'a, 'b, '_$0>"#,
 +            r#"impl<'a, 'b, 'c> Cursor<'a, 'b, 'c>"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_function_return_value_anon_lifetime_param() {
 +        check_assist(
 +            introduce_named_lifetime,
 +            r#"fn my_fun() -> X<'_$0>"#,
 +            r#"fn my_fun<'a>() -> X<'a>"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_function_return_value_anon_reference_lifetime() {
 +        check_assist(
 +            introduce_named_lifetime,
 +            r#"fn my_fun() -> &'_$0 X"#,
 +            r#"fn my_fun<'a>() -> &'a X"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_function_param_anon_lifetime() {
 +        check_assist(
 +            introduce_named_lifetime,
 +            r#"fn my_fun(x: X<'_$0>)"#,
 +            r#"fn my_fun<'a>(x: X<'a>)"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_function_add_lifetime_to_params() {
 +        check_assist(
 +            introduce_named_lifetime,
 +            r#"fn my_fun(f: &Foo) -> X<'_$0>"#,
 +            r#"fn my_fun<'a>(f: &'a Foo) -> X<'a>"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_function_add_lifetime_to_params_in_presence_of_other_lifetime() {
 +        check_assist(
 +            introduce_named_lifetime,
 +            r#"fn my_fun<'other>(f: &Foo, b: &'other Bar) -> X<'_$0>"#,
 +            r#"fn my_fun<'other, 'a>(f: &'a Foo, b: &'other Bar) -> X<'a>"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_function_not_applicable_without_self_and_multiple_unnamed_param_lifetimes() {
 +        // this is not permitted under lifetime elision rules
 +        check_assist_not_applicable(
 +            introduce_named_lifetime,
 +            r#"fn my_fun(f: &Foo, b: &Bar) -> X<'_$0>"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_function_add_lifetime_to_self_ref_param() {
 +        check_assist(
 +            introduce_named_lifetime,
 +            r#"fn my_fun<'other>(&self, f: &Foo, b: &'other Bar) -> X<'_$0>"#,
 +            r#"fn my_fun<'other, 'a>(&'a self, f: &Foo, b: &'other Bar) -> X<'a>"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_function_add_lifetime_to_param_with_non_ref_self() {
 +        check_assist(
 +            introduce_named_lifetime,
 +            r#"fn my_fun<'other>(self, f: &Foo, b: &'other Bar) -> X<'_$0>"#,
 +            r#"fn my_fun<'other, 'a>(self, f: &'a Foo, b: &'other Bar) -> X<'a>"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_function_add_lifetime_to_self_ref_mut() {
 +        check_assist(
 +            introduce_named_lifetime,
 +            r#"fn foo(&mut self) -> &'_$0 ()"#,
 +            r#"fn foo<'a>(&'a mut self) -> &'a ()"#,
 +        );
 +    }
 +}
index c24015b1c5175a410d45dab0974e913b78346de1,0000000000000000000000000000000000000000..641c90885bf530d09a509a10e496879f5f61598a
mode 100644,000000..100644
--- /dev/null
@@@ -1,822 -1,0 +1,822 @@@
-             let arm = format!("{} => {},", pats, current_expr.syntax().text());
 +use hir::TypeInfo;
 +use std::{collections::HashMap, iter::successors};
 +use syntax::{
 +    algo::neighbor,
 +    ast::{self, AstNode, HasName},
 +    Direction,
 +};
 +
 +use crate::{AssistContext, AssistId, AssistKind, Assists, TextRange};
 +
 +// Assist: merge_match_arms
 +//
 +// Merges the current match arm with the following if their bodies are identical.
 +//
 +// ```
 +// enum Action { Move { distance: u32 }, Stop }
 +//
 +// fn handle(action: Action) {
 +//     match action {
 +//         $0Action::Move(..) => foo(),
 +//         Action::Stop => foo(),
 +//     }
 +// }
 +// ```
 +// ->
 +// ```
 +// enum Action { Move { distance: u32 }, Stop }
 +//
 +// fn handle(action: Action) {
 +//     match action {
 +//         Action::Move(..) | Action::Stop => foo(),
 +//     }
 +// }
 +// ```
 +pub(crate) fn merge_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    let current_arm = ctx.find_node_at_offset::<ast::MatchArm>()?;
 +    // Don't try to handle arms with guards for now - can add support for this later
 +    if current_arm.guard().is_some() {
 +        return None;
 +    }
 +    let current_expr = current_arm.expr()?;
 +    let current_text_range = current_arm.syntax().text_range();
 +    let current_arm_types = get_arm_types(ctx, &current_arm);
 +
 +    // We check if the following match arms match this one. We could, but don't,
 +    // compare to the previous match arm as well.
 +    let arms_to_merge = successors(Some(current_arm), |it| neighbor(it, Direction::Next))
 +        .take_while(|arm| match arm.expr() {
 +            Some(expr) if arm.guard().is_none() => {
 +                let same_text = expr.syntax().text() == current_expr.syntax().text();
 +                if !same_text {
 +                    return false;
 +                }
 +
 +                are_same_types(&current_arm_types, arm, ctx)
 +            }
 +            _ => false,
 +        })
 +        .collect::<Vec<_>>();
 +
 +    if arms_to_merge.len() <= 1 {
 +        return None;
 +    }
 +
 +    acc.add(
 +        AssistId("merge_match_arms", AssistKind::RefactorRewrite),
 +        "Merge match arms",
 +        current_text_range,
 +        |edit| {
 +            let pats = if arms_to_merge.iter().any(contains_placeholder) {
 +                "_".into()
 +            } else {
 +                arms_to_merge
 +                    .iter()
 +                    .filter_map(ast::MatchArm::pat)
 +                    .map(|x| x.syntax().to_string())
 +                    .collect::<Vec<String>>()
 +                    .join(" | ")
 +            };
 +
++            let arm = format!("{pats} => {current_expr},");
 +
 +            if let [first, .., last] = &*arms_to_merge {
 +                let start = first.syntax().text_range().start();
 +                let end = last.syntax().text_range().end();
 +
 +                edit.replace(TextRange::new(start, end), arm);
 +            }
 +        },
 +    )
 +}
 +
 +fn contains_placeholder(a: &ast::MatchArm) -> bool {
 +    matches!(a.pat(), Some(ast::Pat::WildcardPat(..)))
 +}
 +
 +fn are_same_types(
 +    current_arm_types: &HashMap<String, Option<TypeInfo>>,
 +    arm: &ast::MatchArm,
 +    ctx: &AssistContext<'_>,
 +) -> bool {
 +    let arm_types = get_arm_types(ctx, arm);
 +    for (other_arm_type_name, other_arm_type) in arm_types {
 +        match (current_arm_types.get(&other_arm_type_name), other_arm_type) {
 +            (Some(Some(current_arm_type)), Some(other_arm_type))
 +                if other_arm_type.original == current_arm_type.original => {}
 +            _ => return false,
 +        }
 +    }
 +
 +    true
 +}
 +
 +fn get_arm_types(
 +    context: &AssistContext<'_>,
 +    arm: &ast::MatchArm,
 +) -> HashMap<String, Option<TypeInfo>> {
 +    let mut mapping: HashMap<String, Option<TypeInfo>> = HashMap::new();
 +
 +    fn recurse(
 +        map: &mut HashMap<String, Option<TypeInfo>>,
 +        ctx: &AssistContext<'_>,
 +        pat: &Option<ast::Pat>,
 +    ) {
 +        if let Some(local_pat) = pat {
 +            match pat {
 +                Some(ast::Pat::TupleStructPat(tuple)) => {
 +                    for field in tuple.fields() {
 +                        recurse(map, ctx, &Some(field));
 +                    }
 +                }
 +                Some(ast::Pat::TuplePat(tuple)) => {
 +                    for field in tuple.fields() {
 +                        recurse(map, ctx, &Some(field));
 +                    }
 +                }
 +                Some(ast::Pat::RecordPat(record)) => {
 +                    if let Some(field_list) = record.record_pat_field_list() {
 +                        for field in field_list.fields() {
 +                            recurse(map, ctx, &field.pat());
 +                        }
 +                    }
 +                }
 +                Some(ast::Pat::ParenPat(parentheses)) => {
 +                    recurse(map, ctx, &parentheses.pat());
 +                }
 +                Some(ast::Pat::SlicePat(slice)) => {
 +                    for slice_pat in slice.pats() {
 +                        recurse(map, ctx, &Some(slice_pat));
 +                    }
 +                }
 +                Some(ast::Pat::IdentPat(ident_pat)) => {
 +                    if let Some(name) = ident_pat.name() {
 +                        let pat_type = ctx.sema.type_of_pat(local_pat);
 +                        map.insert(name.text().to_string(), pat_type);
 +                    }
 +                }
 +                _ => (),
 +            }
 +        }
 +    }
 +
 +    recurse(&mut mapping, context, &arm.pat());
 +    mapping
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use crate::tests::{check_assist, check_assist_not_applicable};
 +
 +    use super::*;
 +
 +    #[test]
 +    fn merge_match_arms_single_patterns() {
 +        check_assist(
 +            merge_match_arms,
 +            r#"
 +#[derive(Debug)]
 +enum X { A, B, C }
 +
 +fn main() {
 +    let x = X::A;
 +    let y = match x {
 +        X::A => { 1i32$0 }
 +        X::B => { 1i32 }
 +        X::C => { 2i32 }
 +    }
 +}
 +"#,
 +            r#"
 +#[derive(Debug)]
 +enum X { A, B, C }
 +
 +fn main() {
 +    let x = X::A;
 +    let y = match x {
 +        X::A | X::B => { 1i32 },
 +        X::C => { 2i32 }
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn merge_match_arms_multiple_patterns() {
 +        check_assist(
 +            merge_match_arms,
 +            r#"
 +#[derive(Debug)]
 +enum X { A, B, C, D, E }
 +
 +fn main() {
 +    let x = X::A;
 +    let y = match x {
 +        X::A | X::B => {$0 1i32 },
 +        X::C | X::D => { 1i32 },
 +        X::E => { 2i32 },
 +    }
 +}
 +"#,
 +            r#"
 +#[derive(Debug)]
 +enum X { A, B, C, D, E }
 +
 +fn main() {
 +    let x = X::A;
 +    let y = match x {
 +        X::A | X::B | X::C | X::D => { 1i32 },
 +        X::E => { 2i32 },
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn merge_match_arms_placeholder_pattern() {
 +        check_assist(
 +            merge_match_arms,
 +            r#"
 +#[derive(Debug)]
 +enum X { A, B, C, D, E }
 +
 +fn main() {
 +    let x = X::A;
 +    let y = match x {
 +        X::A => { 1i32 },
 +        X::B => { 2i$032 },
 +        _ => { 2i32 }
 +    }
 +}
 +"#,
 +            r#"
 +#[derive(Debug)]
 +enum X { A, B, C, D, E }
 +
 +fn main() {
 +    let x = X::A;
 +    let y = match x {
 +        X::A => { 1i32 },
 +        _ => { 2i32 },
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn merges_all_subsequent_arms() {
 +        check_assist(
 +            merge_match_arms,
 +            r#"
 +enum X { A, B, C, D, E }
 +
 +fn main() {
 +    match X::A {
 +        X::A$0 => 92,
 +        X::B => 92,
 +        X::C => 92,
 +        X::D => 62,
 +        _ => panic!(),
 +    }
 +}
 +"#,
 +            r#"
 +enum X { A, B, C, D, E }
 +
 +fn main() {
 +    match X::A {
 +        X::A | X::B | X::C => 92,
 +        X::D => 62,
 +        _ => panic!(),
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn merge_match_arms_rejects_guards() {
 +        check_assist_not_applicable(
 +            merge_match_arms,
 +            r#"
 +#[derive(Debug)]
 +enum X {
 +    A(i32),
 +    B,
 +    C
 +}
 +
 +fn main() {
 +    let x = X::A;
 +    let y = match x {
 +        X::A(a) if a > 5 => { $01i32 },
 +        X::B => { 1i32 },
 +        X::C => { 2i32 }
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn merge_match_arms_different_type() {
 +        check_assist_not_applicable(
 +            merge_match_arms,
 +            r#"
 +//- minicore: result
 +fn func() {
 +    match Result::<f64, f32>::Ok(0f64) {
 +        Ok(x) => $0x.classify(),
 +        Err(x) => x.classify()
 +    };
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn merge_match_arms_different_type_multiple_fields() {
 +        check_assist_not_applicable(
 +            merge_match_arms,
 +            r#"
 +//- minicore: result
 +fn func() {
 +    match Result::<(f64, f64), (f32, f32)>::Ok((0f64, 0f64)) {
 +        Ok(x) => $0x.1.classify(),
 +        Err(x) => x.1.classify()
 +    };
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn merge_match_arms_same_type_multiple_fields() {
 +        check_assist(
 +            merge_match_arms,
 +            r#"
 +//- minicore: result
 +fn func() {
 +    match Result::<(f64, f64), (f64, f64)>::Ok((0f64, 0f64)) {
 +        Ok(x) => $0x.1.classify(),
 +        Err(x) => x.1.classify()
 +    };
 +}
 +"#,
 +            r#"
 +fn func() {
 +    match Result::<(f64, f64), (f64, f64)>::Ok((0f64, 0f64)) {
 +        Ok(x) | Err(x) => x.1.classify(),
 +    };
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn merge_match_arms_same_type_subsequent_arm_with_different_type_in_other() {
 +        check_assist(
 +            merge_match_arms,
 +            r#"
 +enum MyEnum {
 +    OptionA(f32),
 +    OptionB(f32),
 +    OptionC(f64)
 +}
 +
 +fn func(e: MyEnum) {
 +    match e {
 +        MyEnum::OptionA(x) => $0x.classify(),
 +        MyEnum::OptionB(x) => x.classify(),
 +        MyEnum::OptionC(x) => x.classify(),
 +    };
 +}
 +"#,
 +            r#"
 +enum MyEnum {
 +    OptionA(f32),
 +    OptionB(f32),
 +    OptionC(f64)
 +}
 +
 +fn func(e: MyEnum) {
 +    match e {
 +        MyEnum::OptionA(x) | MyEnum::OptionB(x) => x.classify(),
 +        MyEnum::OptionC(x) => x.classify(),
 +    };
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn merge_match_arms_same_type_skip_arm_with_different_type_in_between() {
 +        check_assist_not_applicable(
 +            merge_match_arms,
 +            r#"
 +enum MyEnum {
 +    OptionA(f32),
 +    OptionB(f64),
 +    OptionC(f32)
 +}
 +
 +fn func(e: MyEnum) {
 +    match e {
 +        MyEnum::OptionA(x) => $0x.classify(),
 +        MyEnum::OptionB(x) => x.classify(),
 +        MyEnum::OptionC(x) => x.classify(),
 +    };
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn merge_match_arms_same_type_different_number_of_fields() {
 +        check_assist_not_applicable(
 +            merge_match_arms,
 +            r#"
 +//- minicore: result
 +fn func() {
 +    match Result::<(f64, f64, f64), (f64, f64)>::Ok((0f64, 0f64, 0f64)) {
 +        Ok(x) => $0x.1.classify(),
 +        Err(x) => x.1.classify()
 +    };
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn merge_match_same_destructuring_different_types() {
 +        check_assist_not_applicable(
 +            merge_match_arms,
 +            r#"
 +struct Point {
 +    x: i32,
 +    y: i32,
 +}
 +
 +fn func() {
 +    let p = Point { x: 0, y: 7 };
 +
 +    match p {
 +        Point { x, y: 0 } => $0"",
 +        Point { x: 0, y } => "",
 +        Point { x, y } => "",
 +    };
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn merge_match_arms_range() {
 +        check_assist(
 +            merge_match_arms,
 +            r#"
 +fn func() {
 +    let x = 'c';
 +
 +    match x {
 +        'a'..='j' => $0"",
 +        'c'..='z' => "",
 +        _ => "other",
 +    };
 +}
 +"#,
 +            r#"
 +fn func() {
 +    let x = 'c';
 +
 +    match x {
 +        'a'..='j' | 'c'..='z' => "",
 +        _ => "other",
 +    };
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn merge_match_arms_enum_without_field() {
 +        check_assist_not_applicable(
 +            merge_match_arms,
 +            r#"
 +enum MyEnum {
 +    NoField,
 +    AField(u8)
 +}
 +
 +fn func(x: MyEnum) {
 +    match x {
 +        MyEnum::NoField => $0"",
 +        MyEnum::AField(x) => ""
 +    };
 +}
 +        "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn merge_match_arms_enum_destructuring_different_types() {
 +        check_assist_not_applicable(
 +            merge_match_arms,
 +            r#"
 +enum MyEnum {
 +    Move { x: i32, y: i32 },
 +    Write(String),
 +}
 +
 +fn func(x: MyEnum) {
 +    match x {
 +        MyEnum::Move { x, y } => $0"",
 +        MyEnum::Write(text) => "",
 +    };
 +}
 +        "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn merge_match_arms_enum_destructuring_same_types() {
 +        check_assist(
 +            merge_match_arms,
 +            r#"
 +enum MyEnum {
 +    Move { x: i32, y: i32 },
 +    Crawl { x: i32, y: i32 }
 +}
 +
 +fn func(x: MyEnum) {
 +    match x {
 +        MyEnum::Move { x, y } => $0"",
 +        MyEnum::Crawl { x, y } => "",
 +    };
 +}
 +        "#,
 +            r#"
 +enum MyEnum {
 +    Move { x: i32, y: i32 },
 +    Crawl { x: i32, y: i32 }
 +}
 +
 +fn func(x: MyEnum) {
 +    match x {
 +        MyEnum::Move { x, y } | MyEnum::Crawl { x, y } => "",
 +    };
 +}
 +        "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn merge_match_arms_enum_destructuring_same_types_different_name() {
 +        check_assist_not_applicable(
 +            merge_match_arms,
 +            r#"
 +enum MyEnum {
 +    Move { x: i32, y: i32 },
 +    Crawl { a: i32, b: i32 }
 +}
 +
 +fn func(x: MyEnum) {
 +    match x {
 +        MyEnum::Move { x, y } => $0"",
 +        MyEnum::Crawl { a, b } => "",
 +    };
 +}
 +        "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn merge_match_arms_enum_nested_pattern_different_names() {
 +        check_assist_not_applicable(
 +            merge_match_arms,
 +            r#"
 +enum Color {
 +    Rgb(i32, i32, i32),
 +    Hsv(i32, i32, i32),
 +}
 +
 +enum Message {
 +    Quit,
 +    Move { x: i32, y: i32 },
 +    Write(String),
 +    ChangeColor(Color),
 +}
 +
 +fn main(msg: Message) {
 +    match msg {
 +        Message::ChangeColor(Color::Rgb(r, g, b)) => $0"",
 +        Message::ChangeColor(Color::Hsv(h, s, v)) => "",
 +        _ => "other"
 +    };
 +}
 +        "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn merge_match_arms_enum_nested_pattern_same_names() {
 +        check_assist(
 +            merge_match_arms,
 +            r#"
 +enum Color {
 +    Rgb(i32, i32, i32),
 +    Hsv(i32, i32, i32),
 +}
 +
 +enum Message {
 +    Quit,
 +    Move { x: i32, y: i32 },
 +    Write(String),
 +    ChangeColor(Color),
 +}
 +
 +fn main(msg: Message) {
 +    match msg {
 +        Message::ChangeColor(Color::Rgb(a, b, c)) => $0"",
 +        Message::ChangeColor(Color::Hsv(a, b, c)) => "",
 +        _ => "other"
 +    };
 +}
 +        "#,
 +            r#"
 +enum Color {
 +    Rgb(i32, i32, i32),
 +    Hsv(i32, i32, i32),
 +}
 +
 +enum Message {
 +    Quit,
 +    Move { x: i32, y: i32 },
 +    Write(String),
 +    ChangeColor(Color),
 +}
 +
 +fn main(msg: Message) {
 +    match msg {
 +        Message::ChangeColor(Color::Rgb(a, b, c)) | Message::ChangeColor(Color::Hsv(a, b, c)) => "",
 +        _ => "other"
 +    };
 +}
 +        "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn merge_match_arms_enum_destructuring_with_ignore() {
 +        check_assist(
 +            merge_match_arms,
 +            r#"
 +enum MyEnum {
 +    Move { x: i32, a: i32 },
 +    Crawl { x: i32, b: i32 }
 +}
 +
 +fn func(x: MyEnum) {
 +    match x {
 +        MyEnum::Move { x, .. } => $0"",
 +        MyEnum::Crawl { x, .. } => "",
 +    };
 +}
 +        "#,
 +            r#"
 +enum MyEnum {
 +    Move { x: i32, a: i32 },
 +    Crawl { x: i32, b: i32 }
 +}
 +
 +fn func(x: MyEnum) {
 +    match x {
 +        MyEnum::Move { x, .. } | MyEnum::Crawl { x, .. } => "",
 +    };
 +}
 +        "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn merge_match_arms_nested_with_conflicting_identifier() {
 +        check_assist_not_applicable(
 +            merge_match_arms,
 +            r#"
 +enum Color {
 +    Rgb(i32, i32, i32),
 +    Hsv(i32, i32, i32),
 +}
 +
 +enum Message {
 +    Move { x: i32, y: i32 },
 +    ChangeColor(u8, Color),
 +}
 +
 +fn main(msg: Message) {
 +    match msg {
 +        Message::ChangeColor(x, Color::Rgb(y, b, c)) => $0"",
 +        Message::ChangeColor(y, Color::Hsv(x, b, c)) => "",
 +        _ => "other"
 +    };
 +}
 +        "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn merge_match_arms_tuple() {
 +        check_assist_not_applicable(
 +            merge_match_arms,
 +            r#"
 +fn func() {
 +    match (0, "boo") {
 +        (x, y) => $0"",
 +        (y, x) => "",
 +    };
 +}
 +        "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn merge_match_arms_parentheses() {
 +        check_assist_not_applicable(
 +            merge_match_arms,
 +            r#"
 +fn func(x: i32) {
 +    let variable = 2;
 +    match x {
 +        1 => $0"",
 +        ((((variable)))) => "",
 +        _ => "other"
 +    };
 +}
 +        "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn merge_match_arms_refpat() {
 +        check_assist_not_applicable(
 +            merge_match_arms,
 +            r#"
 +fn func() {
 +    let name = Some(String::from(""));
 +    let n = String::from("");
 +    match name {
 +        Some(ref n) => $0"",
 +        Some(n) => "",
 +        _ => "other",
 +    };
 +}
 +        "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn merge_match_arms_slice() {
 +        check_assist_not_applicable(
 +            merge_match_arms,
 +            r#"
 +fn func(binary: &[u8]) {
 +    let space = b' ';
 +    match binary {
 +        [0x7f, b'E', b'L', b'F', ..] => $0"",
 +        [space] => "",
 +        _ => "other",
 +    };
 +}
 +        "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn merge_match_arms_slice_identical() {
 +        check_assist(
 +            merge_match_arms,
 +            r#"
 +fn func(binary: &[u8]) {
 +    let space = b' ';
 +    match binary {
 +        [space, 5u8] => $0"",
 +        [space] => "",
 +        _ => "other",
 +    };
 +}
 +        "#,
 +            r#"
 +fn func(binary: &[u8]) {
 +    let space = b' ';
 +    match binary {
 +        [space, 5u8] | [space] => "",
 +        _ => "other",
 +    };
 +}
 +        "#,
 +        )
 +    }
 +}
index a6c85a2b18b34b535c9e98e3ea01fbfc4b4f2dc6,0000000000000000000000000000000000000000..1728c03cd03e2e36f553c5dc6af983edbcfab83e
mode 100644,000000..100644
--- /dev/null
@@@ -1,130 -1,0 +1,130 @@@
-     let path = format!("../{}.rs", module_name);
 +use ide_db::{
 +    assists::{AssistId, AssistKind},
 +    base_db::AnchoredPathBuf,
 +};
 +use syntax::{ast, AstNode};
 +
 +use crate::{
 +    assist_context::{AssistContext, Assists},
 +    utils::trimmed_text_range,
 +};
 +
 +// Assist: move_from_mod_rs
 +//
 +// Moves xxx/mod.rs to xxx.rs.
 +//
 +// ```
 +// //- /main.rs
 +// mod a;
 +// //- /a/mod.rs
 +// $0fn t() {}$0
 +// ```
 +// ->
 +// ```
 +// fn t() {}
 +// ```
 +pub(crate) fn move_from_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    let source_file = ctx.find_node_at_offset::<ast::SourceFile>()?;
 +    let module = ctx.sema.to_module_def(ctx.file_id())?;
 +    // Enable this assist if the user select all "meaningful" content in the source file
 +    let trimmed_selected_range = trimmed_text_range(&source_file, ctx.selection_trimmed());
 +    let trimmed_file_range = trimmed_text_range(&source_file, source_file.syntax().text_range());
 +    if !module.is_mod_rs(ctx.db()) {
 +        cov_mark::hit!(not_mod_rs);
 +        return None;
 +    }
 +    if trimmed_selected_range != trimmed_file_range {
 +        cov_mark::hit!(not_all_selected);
 +        return None;
 +    }
 +
 +    let target = source_file.syntax().text_range();
 +    let module_name = module.name(ctx.db())?.to_string();
-         format!("Convert {}/mod.rs to {}.rs", module_name, module_name),
++    let path = format!("../{module_name}.rs");
 +    let dst = AnchoredPathBuf { anchor: ctx.file_id(), path };
 +    acc.add(
 +        AssistId("move_from_mod_rs", AssistKind::Refactor),
++        format!("Convert {module_name}/mod.rs to {module_name}.rs"),
 +        target,
 +        |builder| {
 +            builder.move_file(ctx.file_id(), dst);
 +        },
 +    )
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use crate::tests::{check_assist, check_assist_not_applicable};
 +
 +    use super::*;
 +
 +    #[test]
 +    fn trivial() {
 +        check_assist(
 +            move_from_mod_rs,
 +            r#"
 +//- /main.rs
 +mod a;
 +//- /a/mod.rs
 +$0fn t() {}
 +$0"#,
 +            r#"
 +//- /a.rs
 +fn t() {}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn must_select_all_file() {
 +        cov_mark::check!(not_all_selected);
 +        check_assist_not_applicable(
 +            move_from_mod_rs,
 +            r#"
 +//- /main.rs
 +mod a;
 +//- /a/mod.rs
 +fn t() {}$0
 +"#,
 +        );
 +        cov_mark::check!(not_all_selected);
 +        check_assist_not_applicable(
 +            move_from_mod_rs,
 +            r#"
 +//- /main.rs
 +mod a;
 +//- /a/mod.rs
 +$0fn$0 t() {}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn cannot_move_not_mod_rs() {
 +        cov_mark::check!(not_mod_rs);
 +        check_assist_not_applicable(
 +            move_from_mod_rs,
 +            r#"//- /main.rs
 +mod a;
 +//- /a.rs
 +$0fn t() {}$0
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn cannot_downgrade_main_and_lib_rs() {
 +        check_assist_not_applicable(
 +            move_from_mod_rs,
 +            r#"//- /main.rs
 +$0fn t() {}$0
 +"#,
 +        );
 +        check_assist_not_applicable(
 +            move_from_mod_rs,
 +            r#"//- /lib.rs
 +$0fn t() {}$0
 +"#,
 +        );
 +    }
 +}
index b8f1b36deb93cbbbed959f05c8fe1dc047ab5926,0000000000000000000000000000000000000000..ec3281619cc3ff7bb21d889d63dfc0d268582632
mode 100644,000000..100644
--- /dev/null
@@@ -1,997 -1,0 +1,997 @@@
-             let spaces = "    ".repeat(indent_level.0 as _);
 +use syntax::{
 +    ast::{edit::AstNodeEdit, make, AstNode, BlockExpr, ElseBranch, Expr, IfExpr, MatchArm, Pat},
 +    SyntaxKind::WHITESPACE,
 +};
 +
 +use crate::{AssistContext, AssistId, AssistKind, Assists};
 +
 +// Assist: move_guard_to_arm_body
 +//
 +// Moves match guard into match arm body.
 +//
 +// ```
 +// enum Action { Move { distance: u32 }, Stop }
 +//
 +// fn handle(action: Action) {
 +//     match action {
 +//         Action::Move { distance } $0if distance > 10 => foo(),
 +//         _ => (),
 +//     }
 +// }
 +// ```
 +// ->
 +// ```
 +// enum Action { Move { distance: u32 }, Stop }
 +//
 +// fn handle(action: Action) {
 +//     match action {
 +//         Action::Move { distance } => if distance > 10 {
 +//             foo()
 +//         },
 +//         _ => (),
 +//     }
 +// }
 +// ```
 +pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    let match_arm = ctx.find_node_at_offset::<MatchArm>()?;
 +    let guard = match_arm.guard()?;
 +    if ctx.offset() > guard.syntax().text_range().end() {
 +        cov_mark::hit!(move_guard_unapplicable_in_arm_body);
 +        return None;
 +    }
 +    let space_before_guard = guard.syntax().prev_sibling_or_token();
 +
 +    let guard_condition = guard.condition()?;
 +    let arm_expr = match_arm.expr()?;
 +    let if_expr =
 +        make::expr_if(guard_condition, make::block_expr(None, Some(arm_expr.clone())), None)
 +            .indent(arm_expr.indent_level());
 +
 +    let target = guard.syntax().text_range();
 +    acc.add(
 +        AssistId("move_guard_to_arm_body", AssistKind::RefactorRewrite),
 +        "Move guard to arm body",
 +        target,
 +        |edit| {
 +            match space_before_guard {
 +                Some(element) if element.kind() == WHITESPACE => {
 +                    edit.delete(element.text_range());
 +                }
 +                _ => (),
 +            };
 +
 +            edit.delete(guard.syntax().text_range());
 +            edit.replace_ast(arm_expr, if_expr);
 +        },
 +    )
 +}
 +
 +// Assist: move_arm_cond_to_match_guard
 +//
 +// Moves if expression from match arm body into a guard.
 +//
 +// ```
 +// enum Action { Move { distance: u32 }, Stop }
 +//
 +// fn handle(action: Action) {
 +//     match action {
 +//         Action::Move { distance } => $0if distance > 10 { foo() },
 +//         _ => (),
 +//     }
 +// }
 +// ```
 +// ->
 +// ```
 +// enum Action { Move { distance: u32 }, Stop }
 +//
 +// fn handle(action: Action) {
 +//     match action {
 +//         Action::Move { distance } if distance > 10 => foo(),
 +//         _ => (),
 +//     }
 +// }
 +// ```
 +pub(crate) fn move_arm_cond_to_match_guard(
 +    acc: &mut Assists,
 +    ctx: &AssistContext<'_>,
 +) -> Option<()> {
 +    let match_arm: MatchArm = ctx.find_node_at_offset::<MatchArm>()?;
 +    let match_pat = match_arm.pat()?;
 +    let arm_body = match_arm.expr()?;
 +
 +    let mut replace_node = None;
 +    let if_expr: IfExpr = IfExpr::cast(arm_body.syntax().clone()).or_else(|| {
 +        let block_expr = BlockExpr::cast(arm_body.syntax().clone())?;
 +        if let Expr::IfExpr(e) = block_expr.tail_expr()? {
 +            replace_node = Some(block_expr.syntax().clone());
 +            Some(e)
 +        } else {
 +            None
 +        }
 +    })?;
 +    if ctx.offset() > if_expr.then_branch()?.syntax().text_range().start() {
 +        return None;
 +    }
 +
 +    let replace_node = replace_node.unwrap_or_else(|| if_expr.syntax().clone());
 +    let needs_dedent = replace_node != *if_expr.syntax();
 +    let (conds_blocks, tail) = parse_if_chain(if_expr)?;
 +
 +    acc.add(
 +        AssistId("move_arm_cond_to_match_guard", AssistKind::RefactorRewrite),
 +        "Move condition to match guard",
 +        replace_node.text_range(),
 +        |edit| {
 +            edit.delete(match_arm.syntax().text_range());
 +            // Dedent if if_expr is in a BlockExpr
 +            let dedent = if needs_dedent {
 +                cov_mark::hit!(move_guard_ifelse_in_block);
 +                1
 +            } else {
 +                cov_mark::hit!(move_guard_ifelse_else_block);
 +                0
 +            };
 +            let then_arm_end = match_arm.syntax().text_range().end();
 +            let indent_level = match_arm.indent_level();
-                     edit.insert(then_arm_end, format!("\n{}", spaces));
++            let spaces = indent_level;
 +
 +            let mut first = true;
 +            for (cond, block) in conds_blocks {
 +                if !first {
-                 let guard = format!("{} if {} => ", match_pat, cond.syntax().text());
++                    edit.insert(then_arm_end, format!("\n{spaces}"));
 +                } else {
 +                    first = false;
 +                }
-                 let guard = format!("\n{}{} => ", spaces, match_pat);
++                let guard = format!("{match_pat} if {cond} => ");
 +                edit.insert(then_arm_end, guard);
 +                let only_expr = block.statements().next().is_none();
 +                match &block.tail_expr() {
 +                    Some(then_expr) if only_expr => {
 +                        edit.insert(then_arm_end, then_expr.syntax().text());
 +                        edit.insert(then_arm_end, ",");
 +                    }
 +                    _ => {
 +                        let to_insert = block.dedent(dedent.into()).syntax().text();
 +                        edit.insert(then_arm_end, to_insert)
 +                    }
 +                }
 +            }
 +            if let Some(e) = tail {
 +                cov_mark::hit!(move_guard_ifelse_else_tail);
-                     _ => edit.insert(then_arm_end, format!("\n{}{} => {{}}", spaces, match_pat)),
++                let guard = format!("\n{spaces}{match_pat} => ");
 +                edit.insert(then_arm_end, guard);
 +                let only_expr = e.statements().next().is_none();
 +                match &e.tail_expr() {
 +                    Some(expr) if only_expr => {
 +                        cov_mark::hit!(move_guard_ifelse_expr_only);
 +                        edit.insert(then_arm_end, expr.syntax().text());
 +                        edit.insert(then_arm_end, ",");
 +                    }
 +                    _ => {
 +                        let to_insert = e.dedent(dedent.into()).syntax().text();
 +                        edit.insert(then_arm_end, to_insert)
 +                    }
 +                }
 +            } else {
 +                // There's no else branch. Add a pattern without guard, unless the following match
 +                // arm is `_ => ...`
 +                cov_mark::hit!(move_guard_ifelse_notail);
 +                match match_arm.syntax().next_sibling().and_then(MatchArm::cast) {
 +                    Some(next_arm)
 +                        if matches!(next_arm.pat(), Some(Pat::WildcardPat(_)))
 +                            && next_arm.guard().is_none() =>
 +                    {
 +                        cov_mark::hit!(move_guard_ifelse_has_wildcard);
 +                    }
++                    _ => edit.insert(then_arm_end, format!("\n{spaces}{match_pat} => {{}}")),
 +                }
 +            }
 +        },
 +    )
 +}
 +
 +// Parses an if-else-if chain to get the conditions and the then branches until we encounter an else
 +// branch or the end.
 +fn parse_if_chain(if_expr: IfExpr) -> Option<(Vec<(Expr, BlockExpr)>, Option<BlockExpr>)> {
 +    let mut conds_blocks = Vec::new();
 +    let mut curr_if = if_expr;
 +    let tail = loop {
 +        let cond = curr_if.condition()?;
 +        conds_blocks.push((cond, curr_if.then_branch()?));
 +        match curr_if.else_branch() {
 +            Some(ElseBranch::IfExpr(e)) => {
 +                curr_if = e;
 +            }
 +            Some(ElseBranch::Block(b)) => {
 +                break Some(b);
 +            }
 +            None => break None,
 +        }
 +    };
 +    Some((conds_blocks, tail))
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use super::*;
 +
 +    use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
 +
 +    #[test]
 +    fn move_guard_to_arm_body_range() {
 +        cov_mark::check!(move_guard_unapplicable_in_arm_body);
 +        check_assist_not_applicable(
 +            move_guard_to_arm_body,
 +            r#"
 +fn main() {
 +    match 92 {
 +        x if x > 10 => $0false,
 +        _ => true
 +    }
 +}
 +"#,
 +        );
 +    }
 +    #[test]
 +    fn move_guard_to_arm_body_target() {
 +        check_assist_target(
 +            move_guard_to_arm_body,
 +            r#"
 +fn main() {
 +    match 92 {
 +        x $0if x > 10 => false,
 +        _ => true
 +    }
 +}
 +"#,
 +            r#"if x > 10"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn move_guard_to_arm_body_works() {
 +        check_assist(
 +            move_guard_to_arm_body,
 +            r#"
 +fn main() {
 +    match 92 {
 +        x $0if x > 10 => false,
 +        _ => true
 +    }
 +}
 +"#,
 +            r#"
 +fn main() {
 +    match 92 {
 +        x => if x > 10 {
 +            false
 +        },
 +        _ => true
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn move_let_guard_to_arm_body_works() {
 +        check_assist(
 +            move_guard_to_arm_body,
 +            r#"
 +fn main() {
 +    match 92 {
 +        x $0if (let 1 = x) => false,
 +        _ => true
 +    }
 +}
 +"#,
 +            r#"
 +fn main() {
 +    match 92 {
 +        x => if (let 1 = x) {
 +            false
 +        },
 +        _ => true
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn move_guard_to_arm_body_works_complex_match() {
 +        check_assist(
 +            move_guard_to_arm_body,
 +            r#"
 +fn main() {
 +    match 92 {
 +        $0x @ 4 | x @ 5    if x > 5 => true,
 +        _ => false
 +    }
 +}
 +"#,
 +            r#"
 +fn main() {
 +    match 92 {
 +        x @ 4 | x @ 5 => if x > 5 {
 +            true
 +        },
 +        _ => false
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn move_arm_cond_to_match_guard_works() {
 +        check_assist(
 +            move_arm_cond_to_match_guard,
 +            r#"
 +fn main() {
 +    match 92 {
 +        x => if x > 10$0 { false },
 +        _ => true
 +    }
 +}
 +"#,
 +            r#"
 +fn main() {
 +    match 92 {
 +        x if x > 10 => false,
 +        _ => true
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn move_arm_cond_in_block_to_match_guard_works() {
 +        cov_mark::check!(move_guard_ifelse_has_wildcard);
 +        check_assist(
 +            move_arm_cond_to_match_guard,
 +            r#"
 +fn main() {
 +    match 92 {
 +        x => {
 +            $0if x > 10 {
 +                false
 +            }
 +        },
 +        _ => true
 +    }
 +}
 +"#,
 +            r#"
 +fn main() {
 +    match 92 {
 +        x if x > 10 => false,
 +        _ => true
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn move_arm_cond_in_block_to_match_guard_no_wildcard_works() {
 +        cov_mark::check_count!(move_guard_ifelse_has_wildcard, 0);
 +        check_assist(
 +            move_arm_cond_to_match_guard,
 +            r#"
 +fn main() {
 +    match 92 {
 +        x => {
 +            $0if x > 10 {
 +                false
 +            }
 +        }
 +    }
 +}
 +"#,
 +            r#"
 +fn main() {
 +    match 92 {
 +        x if x > 10 => false,
 +        x => {}
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn move_arm_cond_in_block_to_match_guard_wildcard_guard_works() {
 +        cov_mark::check_count!(move_guard_ifelse_has_wildcard, 0);
 +        check_assist(
 +            move_arm_cond_to_match_guard,
 +            r#"
 +fn main() {
 +    match 92 {
 +        x => {
 +            $0if x > 10 {
 +                false
 +            }
 +        }
 +        _ if x > 10 => true,
 +    }
 +}
 +"#,
 +            r#"
 +fn main() {
 +    match 92 {
 +        x if x > 10 => false,
 +        x => {}
 +        _ if x > 10 => true,
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn move_arm_cond_in_block_to_match_guard_add_comma_works() {
 +        check_assist(
 +            move_arm_cond_to_match_guard,
 +            r#"
 +fn main() {
 +    match 92 {
 +        x => {
 +            $0if x > 10 {
 +                false
 +            }
 +        }
 +        _ => true
 +    }
 +}
 +"#,
 +            r#"
 +fn main() {
 +    match 92 {
 +        x if x > 10 => false,
 +        _ => true
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn move_arm_cond_to_match_guard_if_let_works() {
 +        check_assist(
 +            move_arm_cond_to_match_guard,
 +            r#"
 +fn main() {
 +    match 92 {
 +        x => if let 62 = x $0&& true { false },
 +        _ => true
 +    }
 +}
 +"#,
 +            r#"
 +fn main() {
 +    match 92 {
 +        x if let 62 = x && true => false,
 +        _ => true
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn move_arm_cond_to_match_guard_if_empty_body_works() {
 +        check_assist(
 +            move_arm_cond_to_match_guard,
 +            r#"
 +fn main() {
 +    match 92 {
 +        x => if x $0> 10 {  },
 +        _ => true
 +    }
 +}
 +"#,
 +            r#"
 +fn main() {
 +    match 92 {
 +        x if x > 10 => {  }
 +        _ => true
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn move_arm_cond_to_match_guard_if_multiline_body_works() {
 +        check_assist(
 +            move_arm_cond_to_match_guard,
 +            r#"
 +fn main() {
 +    match 92 {
 +        x => if$0 x > 10 {
 +            92;
 +            false
 +        },
 +        _ => true
 +    }
 +}
 +"#,
 +            r#"
 +fn main() {
 +    match 92 {
 +        x if x > 10 => {
 +            92;
 +            false
 +        }
 +        _ => true
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn move_arm_cond_in_block_to_match_guard_if_multiline_body_works() {
 +        check_assist(
 +            move_arm_cond_to_match_guard,
 +            r#"
 +fn main() {
 +    match 92 {
 +        x => {
 +            if x > $010 {
 +                92;
 +                false
 +            }
 +        }
 +        _ => true
 +    }
 +}
 +"#,
 +            r#"
 +fn main() {
 +    match 92 {
 +        x if x > 10 => {
 +            92;
 +            false
 +        }
 +        _ => true
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn move_arm_cond_to_match_guard_with_else_works() {
 +        check_assist(
 +            move_arm_cond_to_match_guard,
 +            r#"
 +fn main() {
 +    match 92 {
 +        x => if x > $010 {
 +            false
 +        } else {
 +            true
 +        }
 +        _ => true,
 +    }
 +}
 +"#,
 +            r#"
 +fn main() {
 +    match 92 {
 +        x if x > 10 => false,
 +        x => true,
 +        _ => true,
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn move_arm_cond_to_match_guard_with_else_block_works() {
 +        cov_mark::check!(move_guard_ifelse_expr_only);
 +        check_assist(
 +            move_arm_cond_to_match_guard,
 +            r#"
 +fn main() {
 +    match 92 {
 +        x => {
 +            if x $0> 10 {
 +                false
 +            } else {
 +                true
 +            }
 +        }
 +        _ => true
 +    }
 +}
 +"#,
 +            r#"
 +fn main() {
 +    match 92 {
 +        x if x > 10 => false,
 +        x => true,
 +        _ => true
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn move_arm_cond_to_match_guard_else_if_empty_body_works() {
 +        check_assist(
 +            move_arm_cond_to_match_guard,
 +            r#"
 +fn main() {
 +    match 92 {
 +        x => if x > $010 {  } else { },
 +        _ => true
 +    }
 +}
 +"#,
 +            r#"
 +fn main() {
 +    match 92 {
 +        x if x > 10 => {  }
 +        x => { }
 +        _ => true
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn move_arm_cond_to_match_guard_with_else_multiline_works() {
 +        check_assist(
 +            move_arm_cond_to_match_guard,
 +            r#"
 +fn main() {
 +    match 92 {
 +        x => if$0 x > 10 {
 +            92;
 +            false
 +        } else {
 +            true
 +        }
 +        _ => true
 +    }
 +}
 +"#,
 +            r#"
 +fn main() {
 +    match 92 {
 +        x if x > 10 => {
 +            92;
 +            false
 +        }
 +        x => true,
 +        _ => true
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn move_arm_cond_to_match_guard_with_else_multiline_else_works() {
 +        cov_mark::check!(move_guard_ifelse_else_block);
 +        check_assist(
 +            move_arm_cond_to_match_guard,
 +            r#"
 +fn main() {
 +    match 92 {
 +        x => if x $0> 10 {
 +            false
 +        } else {
 +            42;
 +            true
 +        }
 +        _ => true
 +    }
 +}
 +"#,
 +            r#"
 +fn main() {
 +    match 92 {
 +        x if x > 10 => false,
 +        x => {
 +            42;
 +            true
 +        }
 +        _ => true
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn move_arm_cond_to_match_guard_with_else_multiline_else_block_works() {
 +        cov_mark::check!(move_guard_ifelse_in_block);
 +        check_assist(
 +            move_arm_cond_to_match_guard,
 +            r#"
 +fn main() {
 +    match 92 {
 +        x => {
 +            if x > $010 {
 +                false
 +            } else {
 +                42;
 +                true
 +            }
 +        }
 +        _ => true
 +    }
 +}
 +"#,
 +            r#"
 +fn main() {
 +    match 92 {
 +        x if x > 10 => false,
 +        x => {
 +            42;
 +            true
 +        }
 +        _ => true
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn move_arm_cond_to_match_guard_with_else_last_arm_works() {
 +        check_assist(
 +            move_arm_cond_to_match_guard,
 +            r#"
 +fn main() {
 +    match 92 {
 +        3 => true,
 +        x => {
 +            if x > $010 {
 +                false
 +            } else {
 +                92;
 +                true
 +            }
 +        }
 +    }
 +}
 +"#,
 +            r#"
 +fn main() {
 +    match 92 {
 +        3 => true,
 +        x if x > 10 => false,
 +        x => {
 +            92;
 +            true
 +        }
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn move_arm_cond_to_match_guard_with_else_comma_works() {
 +        check_assist(
 +            move_arm_cond_to_match_guard,
 +            r#"
 +fn main() {
 +    match 92 {
 +        3 => true,
 +        x => if x > $010 {
 +            false
 +        } else {
 +            92;
 +            true
 +        },
 +    }
 +}
 +"#,
 +            r#"
 +fn main() {
 +    match 92 {
 +        3 => true,
 +        x if x > 10 => false,
 +        x => {
 +            92;
 +            true
 +        }
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn move_arm_cond_to_match_guard_elseif() {
 +        check_assist(
 +            move_arm_cond_to_match_guard,
 +            r#"
 +fn main() {
 +    match 92 {
 +        3 => true,
 +        x => if x $0> 10 {
 +            false
 +        } else if x > 5 {
 +            true
 +        } else if x > 4 {
 +            false
 +        } else {
 +            true
 +        },
 +    }
 +}
 +"#,
 +            r#"
 +fn main() {
 +    match 92 {
 +        3 => true,
 +        x if x > 10 => false,
 +        x if x > 5 => true,
 +        x if x > 4 => false,
 +        x => true,
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn move_arm_cond_to_match_guard_elseif_in_block() {
 +        cov_mark::check!(move_guard_ifelse_in_block);
 +        check_assist(
 +            move_arm_cond_to_match_guard,
 +            r#"
 +fn main() {
 +    match 92 {
 +        3 => true,
 +        x => {
 +            if x > $010 {
 +                false
 +            } else if x > 5 {
 +                true
 +            } else if x > 4 {
 +                false
 +            } else {
 +                true
 +            }
 +        }
 +    }
 +}
 +"#,
 +            r#"
 +fn main() {
 +    match 92 {
 +        3 => true,
 +        x if x > 10 => false,
 +        x if x > 5 => true,
 +        x if x > 4 => false,
 +        x => true,
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn move_arm_cond_to_match_guard_elseif_chain() {
 +        cov_mark::check!(move_guard_ifelse_else_tail);
 +        check_assist(
 +            move_arm_cond_to_match_guard,
 +            r#"
 +fn main() {
 +    match 92 {
 +        3 => 0,
 +        x => if x $0> 10 {
 +            1
 +        } else if x > 5 {
 +            2
 +        } else if x > 3 {
 +            42;
 +            3
 +        } else {
 +            4
 +        },
 +    }
 +}
 +"#,
 +            r#"
 +fn main() {
 +    match 92 {
 +        3 => 0,
 +        x if x > 10 => 1,
 +        x if x > 5 => 2,
 +        x if x > 3 => {
 +            42;
 +            3
 +        }
 +        x => 4,
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn move_arm_cond_to_match_guard_elseif_iflet() {
 +        check_assist(
 +            move_arm_cond_to_match_guard,
 +            r#"
 +fn main() {
 +    match 92 {
 +        3 => 0,
 +        x => if x $0> 10 {
 +            1
 +        } else if x > 5 {
 +            2
 +        } else if let 4 = 4 {
 +            42;
 +            3
 +        } else {
 +            4
 +        },
 +    }
 +}"#,
 +            r#"
 +fn main() {
 +    match 92 {
 +        3 => 0,
 +        x if x > 10 => 1,
 +        x if x > 5 => 2,
 +        x if let 4 = 4 => {
 +            42;
 +            3
 +        }
 +        x => 4,
 +    }
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn move_arm_cond_to_match_guard_elseif_notail() {
 +        cov_mark::check!(move_guard_ifelse_notail);
 +        check_assist(
 +            move_arm_cond_to_match_guard,
 +            r#"
 +fn main() {
 +    match 92 {
 +        3 => 0,
 +        x => if x > $010 {
 +            1
 +        } else if x > 5 {
 +            2
 +        } else if x > 4 {
 +            42;
 +            3
 +        },
 +    }
 +}
 +"#,
 +            r#"
 +fn main() {
 +    match 92 {
 +        3 => 0,
 +        x if x > 10 => 1,
 +        x if x > 5 => 2,
 +        x if x > 4 => {
 +            42;
 +            3
 +        }
 +        x => {}
 +    }
 +}
 +"#,
 +        )
 +    }
 +}
index 7468318a594afbea619df9e475aebdb414030484,0000000000000000000000000000000000000000..a7c605325ea6938cfe3f102bf500add03efdf5da
mode 100644,000000..100644
--- /dev/null
@@@ -1,337 -1,0 +1,337 @@@
-                         format_to!(buf, "{}/", name)
 +use std::iter;
 +
 +use ast::edit::IndentLevel;
 +use ide_db::base_db::AnchoredPathBuf;
 +use itertools::Itertools;
 +use stdx::format_to;
 +use syntax::{
 +    ast::{self, edit::AstNodeEdit, HasName},
 +    AstNode, SmolStr, TextRange,
 +};
 +
 +use crate::{AssistContext, AssistId, AssistKind, Assists};
 +
 +// Assist: move_module_to_file
 +//
 +// Moves inline module's contents to a separate file.
 +//
 +// ```
 +// mod $0foo {
 +//     fn t() {}
 +// }
 +// ```
 +// ->
 +// ```
 +// mod foo;
 +// ```
 +pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    let module_ast = ctx.find_node_at_offset::<ast::Module>()?;
 +    let module_items = module_ast.item_list()?;
 +
 +    let l_curly_offset = module_items.syntax().text_range().start();
 +    if l_curly_offset <= ctx.offset() {
 +        cov_mark::hit!(available_before_curly);
 +        return None;
 +    }
 +    let target = TextRange::new(module_ast.syntax().text_range().start(), l_curly_offset);
 +
 +    let module_name = module_ast.name()?;
 +
 +    // get to the outermost module syntax so we can grab the module of file we are in
 +    let outermost_mod_decl =
 +        iter::successors(Some(module_ast.clone()), |module| module.parent()).last()?;
 +    let module_def = ctx.sema.to_def(&outermost_mod_decl)?;
 +    let parent_module = module_def.parent(ctx.db())?;
 +
 +    acc.add(
 +        AssistId("move_module_to_file", AssistKind::RefactorExtract),
 +        "Extract module to file",
 +        target,
 +        |builder| {
 +            let path = {
 +                let mut buf = String::from("./");
 +                match parent_module.name(ctx.db()) {
 +                    Some(name) if !parent_module.is_mod_rs(ctx.db()) => {
-             let buf = format!("mod {};", module_name);
++                        format_to!(buf, "{name}/")
 +                    }
 +                    _ => (),
 +                }
 +                let segments = iter::successors(Some(module_ast.clone()), |module| module.parent())
 +                    .filter_map(|it| it.name())
 +                    .map(|name| SmolStr::from(name.text().trim_start_matches("r#")))
 +                    .collect::<Vec<_>>();
 +
 +                format_to!(buf, "{}", segments.into_iter().rev().format("/"));
 +
 +                // We need to special case mod named `r#mod` and place the file in a
 +                // subdirectory as "mod.rs" would be of its parent module otherwise.
 +                if module_name.text() == "r#mod" {
 +                    format_to!(buf, "/mod.rs");
 +                } else {
 +                    format_to!(buf, ".rs");
 +                }
 +                buf
 +            };
 +            let contents = {
 +                let items = module_items.dedent(IndentLevel(1)).to_string();
 +                let mut items =
 +                    items.trim_start_matches('{').trim_end_matches('}').trim().to_string();
 +                if !items.is_empty() {
 +                    items.push('\n');
 +                }
 +                items
 +            };
 +
++            let buf = format!("mod {module_name};");
 +
 +            let replacement_start = match module_ast.mod_token() {
 +                Some(mod_token) => mod_token.text_range(),
 +                None => module_ast.syntax().text_range(),
 +            }
 +            .start();
 +
 +            builder.replace(
 +                TextRange::new(replacement_start, module_ast.syntax().text_range().end()),
 +                buf,
 +            );
 +
 +            let dst = AnchoredPathBuf { anchor: ctx.file_id(), path };
 +            builder.create_file(dst, contents);
 +        },
 +    )
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use crate::tests::{check_assist, check_assist_not_applicable};
 +
 +    use super::*;
 +
 +    #[test]
 +    fn extract_from_root() {
 +        check_assist(
 +            move_module_to_file,
 +            r#"
 +mod $0tests {
 +    #[test] fn t() {}
 +}
 +"#,
 +            r#"
 +//- /main.rs
 +mod tests;
 +//- /tests.rs
 +#[test] fn t() {}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn extract_from_submodule() {
 +        check_assist(
 +            move_module_to_file,
 +            r#"
 +//- /main.rs
 +mod submod;
 +//- /submod.rs
 +$0mod inner {
 +    fn f() {}
 +}
 +fn g() {}
 +"#,
 +            r#"
 +//- /submod.rs
 +mod inner;
 +fn g() {}
 +//- /submod/inner.rs
 +fn f() {}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn extract_from_mod_rs() {
 +        check_assist(
 +            move_module_to_file,
 +            r#"
 +//- /main.rs
 +mod submodule;
 +//- /submodule/mod.rs
 +mod inner$0 {
 +    fn f() {}
 +}
 +fn g() {}
 +"#,
 +            r#"
 +//- /submodule/mod.rs
 +mod inner;
 +fn g() {}
 +//- /submodule/inner.rs
 +fn f() {}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn extract_public() {
 +        check_assist(
 +            move_module_to_file,
 +            r#"
 +pub mod $0tests {
 +    #[test] fn t() {}
 +}
 +"#,
 +            r#"
 +//- /main.rs
 +pub mod tests;
 +//- /tests.rs
 +#[test] fn t() {}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn extract_public_crate() {
 +        check_assist(
 +            move_module_to_file,
 +            r#"
 +pub(crate) mod $0tests {
 +    #[test] fn t() {}
 +}
 +"#,
 +            r#"
 +//- /main.rs
 +pub(crate) mod tests;
 +//- /tests.rs
 +#[test] fn t() {}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn available_before_curly() {
 +        cov_mark::check!(available_before_curly);
 +        check_assist_not_applicable(move_module_to_file, r#"mod m { $0 }"#);
 +    }
 +
 +    #[test]
 +    fn keep_outer_comments_and_attributes() {
 +        check_assist(
 +            move_module_to_file,
 +            r#"
 +/// doc comment
 +#[attribute]
 +mod $0tests {
 +    #[test] fn t() {}
 +}
 +"#,
 +            r#"
 +//- /main.rs
 +/// doc comment
 +#[attribute]
 +mod tests;
 +//- /tests.rs
 +#[test] fn t() {}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn extract_nested() {
 +        check_assist(
 +            move_module_to_file,
 +            r#"
 +//- /lib.rs
 +mod foo;
 +//- /foo.rs
 +mod bar {
 +    mod baz {
 +        mod qux$0 {}
 +    }
 +}
 +"#,
 +            r#"
 +//- /foo.rs
 +mod bar {
 +    mod baz {
 +        mod qux;
 +    }
 +}
 +//- /foo/bar/baz/qux.rs
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn extract_mod_with_raw_ident() {
 +        check_assist(
 +            move_module_to_file,
 +            r#"
 +//- /main.rs
 +mod $0r#static {}
 +"#,
 +            r#"
 +//- /main.rs
 +mod r#static;
 +//- /static.rs
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn extract_r_mod() {
 +        check_assist(
 +            move_module_to_file,
 +            r#"
 +//- /main.rs
 +mod $0r#mod {}
 +"#,
 +            r#"
 +//- /main.rs
 +mod r#mod;
 +//- /mod/mod.rs
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn extract_r_mod_from_mod_rs() {
 +        check_assist(
 +            move_module_to_file,
 +            r#"
 +//- /main.rs
 +mod foo;
 +//- /foo/mod.rs
 +mod $0r#mod {}
 +"#,
 +            r#"
 +//- /foo/mod.rs
 +mod r#mod;
 +//- /foo/mod/mod.rs
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn extract_nested_r_mod() {
 +        check_assist(
 +            move_module_to_file,
 +            r#"
 +//- /main.rs
 +mod r#mod {
 +    mod foo {
 +        mod $0r#mod {}
 +    }
 +}
 +"#,
 +            r#"
 +//- /main.rs
 +mod r#mod {
 +    mod foo {
 +        mod r#mod;
 +    }
 +}
 +//- /mod/foo/mod/mod.rs
 +"#,
 +        )
 +    }
 +}
index a909ce8b26791af34208f0aa32b20017978e697b,0000000000000000000000000000000000000000..076d25411a8180e7627fbf93084b786144270443
mode 100644,000000..100644
--- /dev/null
@@@ -1,151 -1,0 +1,151 @@@
-     let path = format!("./{}/mod.rs", module_name);
 +use ide_db::{
 +    assists::{AssistId, AssistKind},
 +    base_db::AnchoredPathBuf,
 +};
 +use syntax::{ast, AstNode};
 +
 +use crate::{
 +    assist_context::{AssistContext, Assists},
 +    utils::trimmed_text_range,
 +};
 +
 +// Assist: move_to_mod_rs
 +//
 +// Moves xxx.rs to xxx/mod.rs.
 +//
 +// ```
 +// //- /main.rs
 +// mod a;
 +// //- /a.rs
 +// $0fn t() {}$0
 +// ```
 +// ->
 +// ```
 +// fn t() {}
 +// ```
 +pub(crate) fn move_to_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    let source_file = ctx.find_node_at_offset::<ast::SourceFile>()?;
 +    let module = ctx.sema.to_module_def(ctx.file_id())?;
 +    // Enable this assist if the user select all "meaningful" content in the source file
 +    let trimmed_selected_range = trimmed_text_range(&source_file, ctx.selection_trimmed());
 +    let trimmed_file_range = trimmed_text_range(&source_file, source_file.syntax().text_range());
 +    if module.is_mod_rs(ctx.db()) {
 +        cov_mark::hit!(already_mod_rs);
 +        return None;
 +    }
 +    if trimmed_selected_range != trimmed_file_range {
 +        cov_mark::hit!(not_all_selected);
 +        return None;
 +    }
 +
 +    let target = source_file.syntax().text_range();
 +    let module_name = module.name(ctx.db())?.to_string();
-         format!("Convert {}.rs to {}/mod.rs", module_name, module_name),
++    let path = format!("./{module_name}/mod.rs");
 +    let dst = AnchoredPathBuf { anchor: ctx.file_id(), path };
 +    acc.add(
 +        AssistId("move_to_mod_rs", AssistKind::Refactor),
++        format!("Convert {module_name}.rs to {module_name}/mod.rs"),
 +        target,
 +        |builder| {
 +            builder.move_file(ctx.file_id(), dst);
 +        },
 +    )
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use crate::tests::{check_assist, check_assist_not_applicable};
 +
 +    use super::*;
 +
 +    #[test]
 +    fn trivial() {
 +        check_assist(
 +            move_to_mod_rs,
 +            r#"
 +//- /main.rs
 +mod a;
 +//- /a.rs
 +$0fn t() {}
 +$0"#,
 +            r#"
 +//- /a/mod.rs
 +fn t() {}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn must_select_all_file() {
 +        cov_mark::check!(not_all_selected);
 +        check_assist_not_applicable(
 +            move_to_mod_rs,
 +            r#"
 +//- /main.rs
 +mod a;
 +//- /a.rs
 +fn t() {}$0
 +"#,
 +        );
 +        cov_mark::check!(not_all_selected);
 +        check_assist_not_applicable(
 +            move_to_mod_rs,
 +            r#"
 +//- /main.rs
 +mod a;
 +//- /a.rs
 +$0fn$0 t() {}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn cannot_promote_mod_rs() {
 +        cov_mark::check!(already_mod_rs);
 +        check_assist_not_applicable(
 +            move_to_mod_rs,
 +            r#"//- /main.rs
 +mod a;
 +//- /a/mod.rs
 +$0fn t() {}$0
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn cannot_promote_main_and_lib_rs() {
 +        check_assist_not_applicable(
 +            move_to_mod_rs,
 +            r#"//- /main.rs
 +$0fn t() {}$0
 +"#,
 +        );
 +        check_assist_not_applicable(
 +            move_to_mod_rs,
 +            r#"//- /lib.rs
 +$0fn t() {}$0
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn works_in_mod() {
 +        // note: /a/b.rs remains untouched
 +        check_assist(
 +            move_to_mod_rs,
 +            r#"//- /main.rs
 +mod a;
 +//- /a.rs
 +$0mod b;
 +fn t() {}$0
 +//- /a/b.rs
 +fn t1() {}
 +"#,
 +            r#"
 +//- /a/mod.rs
 +mod b;
 +fn t() {}
 +"#,
 +        );
 +    }
 +}
index 424db7437a743b4bce4fa639dc500048718f87d7,0000000000000000000000000000000000000000..7e3fef516bfd8f2f399aba6e2c97e4fee3d6b792
mode 100644,000000..100644
--- /dev/null
@@@ -1,183 -1,0 +1,183 @@@
-     let label = format!("Convert {} to {}", literal, converted);
 +use syntax::{ast, ast::Radix, AstToken};
 +
 +use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
 +
 +const MIN_NUMBER_OF_DIGITS_TO_FORMAT: usize = 5;
 +
 +// Assist: reformat_number_literal
 +//
 +// Adds or removes separators from integer literal.
 +//
 +// ```
 +// const _: i32 = 1012345$0;
 +// ```
 +// ->
 +// ```
 +// const _: i32 = 1_012_345;
 +// ```
 +pub(crate) fn reformat_number_literal(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    let literal = ctx.find_node_at_offset::<ast::Literal>()?;
 +    let literal = match literal.kind() {
 +        ast::LiteralKind::IntNumber(it) => it,
 +        _ => return None,
 +    };
 +
 +    let text = literal.text();
 +    if text.contains('_') {
 +        return remove_separators(acc, literal);
 +    }
 +
 +    let (prefix, value, suffix) = literal.split_into_parts();
 +    if value.len() < MIN_NUMBER_OF_DIGITS_TO_FORMAT {
 +        return None;
 +    }
 +
 +    let radix = literal.radix();
 +    let mut converted = prefix.to_string();
 +    converted.push_str(&add_group_separators(value, group_size(radix)));
 +    converted.push_str(suffix);
 +
 +    let group_id = GroupLabel("Reformat number literal".into());
++    let label = format!("Convert {literal} to {converted}");
 +    let range = literal.syntax().text_range();
 +    acc.add_group(
 +        &group_id,
 +        AssistId("reformat_number_literal", AssistKind::RefactorInline),
 +        label,
 +        range,
 +        |builder| builder.replace(range, converted),
 +    )
 +}
 +
 +fn remove_separators(acc: &mut Assists, literal: ast::IntNumber) -> Option<()> {
 +    let group_id = GroupLabel("Reformat number literal".into());
 +    let range = literal.syntax().text_range();
 +    acc.add_group(
 +        &group_id,
 +        AssistId("reformat_number_literal", AssistKind::RefactorInline),
 +        "Remove digit separators",
 +        range,
 +        |builder| builder.replace(range, literal.text().replace('_', "")),
 +    )
 +}
 +
 +const fn group_size(r: Radix) -> usize {
 +    match r {
 +        Radix::Binary => 4,
 +        Radix::Octal => 3,
 +        Radix::Decimal => 3,
 +        Radix::Hexadecimal => 4,
 +    }
 +}
 +
 +fn add_group_separators(s: &str, group_size: usize) -> String {
 +    let mut chars = Vec::new();
 +    for (i, ch) in s.chars().filter(|&ch| ch != '_').rev().enumerate() {
 +        if i > 0 && i % group_size == 0 {
 +            chars.push('_');
 +        }
 +        chars.push(ch);
 +    }
 +
 +    chars.into_iter().rev().collect()
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use crate::tests::{check_assist_by_label, check_assist_not_applicable, check_assist_target};
 +
 +    use super::*;
 +
 +    #[test]
 +    fn group_separators() {
 +        let cases = vec![
 +            ("", 4, ""),
 +            ("1", 4, "1"),
 +            ("12", 4, "12"),
 +            ("123", 4, "123"),
 +            ("1234", 4, "1234"),
 +            ("12345", 4, "1_2345"),
 +            ("123456", 4, "12_3456"),
 +            ("1234567", 4, "123_4567"),
 +            ("12345678", 4, "1234_5678"),
 +            ("123456789", 4, "1_2345_6789"),
 +            ("1234567890", 4, "12_3456_7890"),
 +            ("1_2_3_4_5_6_7_8_9_0_", 4, "12_3456_7890"),
 +            ("1234567890", 3, "1_234_567_890"),
 +            ("1234567890", 2, "12_34_56_78_90"),
 +            ("1234567890", 1, "1_2_3_4_5_6_7_8_9_0"),
 +        ];
 +
 +        for case in cases {
 +            let (input, group_size, expected) = case;
 +            assert_eq!(add_group_separators(input, group_size), expected)
 +        }
 +    }
 +
 +    #[test]
 +    fn good_targets() {
 +        let cases = vec![
 +            ("const _: i32 = 0b11111$0", "0b11111"),
 +            ("const _: i32 = 0o77777$0;", "0o77777"),
 +            ("const _: i32 = 10000$0;", "10000"),
 +            ("const _: i32 = 0xFFFFF$0;", "0xFFFFF"),
 +            ("const _: i32 = 10000i32$0;", "10000i32"),
 +            ("const _: i32 = 0b_10_0i32$0;", "0b_10_0i32"),
 +        ];
 +
 +        for case in cases {
 +            check_assist_target(reformat_number_literal, case.0, case.1);
 +        }
 +    }
 +
 +    #[test]
 +    fn bad_targets() {
 +        let cases = vec![
 +            "const _: i32 = 0b111$0",
 +            "const _: i32 = 0b1111$0",
 +            "const _: i32 = 0o77$0;",
 +            "const _: i32 = 0o777$0;",
 +            "const _: i32 = 10$0;",
 +            "const _: i32 = 999$0;",
 +            "const _: i32 = 0xFF$0;",
 +            "const _: i32 = 0xFFFF$0;",
 +        ];
 +
 +        for case in cases {
 +            check_assist_not_applicable(reformat_number_literal, case);
 +        }
 +    }
 +
 +    #[test]
 +    fn labels() {
 +        let cases = vec![
 +            ("const _: i32 = 10000$0", "const _: i32 = 10_000", "Convert 10000 to 10_000"),
 +            (
 +                "const _: i32 = 0xFF0000$0;",
 +                "const _: i32 = 0xFF_0000;",
 +                "Convert 0xFF0000 to 0xFF_0000",
 +            ),
 +            (
 +                "const _: i32 = 0b11111111$0;",
 +                "const _: i32 = 0b1111_1111;",
 +                "Convert 0b11111111 to 0b1111_1111",
 +            ),
 +            (
 +                "const _: i32 = 0o377211$0;",
 +                "const _: i32 = 0o377_211;",
 +                "Convert 0o377211 to 0o377_211",
 +            ),
 +            (
 +                "const _: i32 = 10000i32$0;",
 +                "const _: i32 = 10_000i32;",
 +                "Convert 10000i32 to 10_000i32",
 +            ),
 +            ("const _: i32 = 1_0_0_0_i32$0;", "const _: i32 = 1000i32;", "Remove digit separators"),
 +        ];
 +
 +        for case in cases {
 +            let (before, after, label) = case;
 +            check_assist_by_label(reformat_number_literal, before, after, label);
 +        }
 +    }
 +}
index e57d1d065d6229361ca68604e8ace6c8b9331c1d,0000000000000000000000000000000000000000..1ea87429c50928bc3dfc5cffe738c46c3297b4a9
mode 100644,000000..100644
--- /dev/null
@@@ -1,551 -1,0 +1,551 @@@
-         format!("Qualify `{}` method call", ident.text()),
 +use hir::{db::HirDatabase, AsAssocItem, AssocItem, AssocItemContainer, ItemInNs, ModuleDef};
 +use ide_db::assists::{AssistId, AssistKind};
 +use syntax::{ast, AstNode};
 +
 +use crate::{
 +    assist_context::{AssistContext, Assists},
 +    handlers::qualify_path::QualifyCandidate,
 +};
 +
 +// Assist: qualify_method_call
 +//
 +// Replaces the method call with a qualified function call.
 +//
 +// ```
 +// struct Foo;
 +// impl Foo {
 +//     fn foo(&self) {}
 +// }
 +// fn main() {
 +//     let foo = Foo;
 +//     foo.fo$0o();
 +// }
 +// ```
 +// ->
 +// ```
 +// struct Foo;
 +// impl Foo {
 +//     fn foo(&self) {}
 +// }
 +// fn main() {
 +//     let foo = Foo;
 +//     Foo::foo(&foo);
 +// }
 +// ```
 +pub(crate) fn qualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    let name: ast::NameRef = ctx.find_node_at_offset()?;
 +    let call = name.syntax().parent().and_then(ast::MethodCallExpr::cast)?;
 +
 +    let ident = name.ident_token()?;
 +
 +    let range = call.syntax().text_range();
 +    let resolved_call = ctx.sema.resolve_method_call(&call)?;
 +
 +    let current_module = ctx.sema.scope(call.syntax())?.module();
 +    let target_module_def = ModuleDef::from(resolved_call);
 +    let item_in_ns = ItemInNs::from(target_module_def);
 +    let receiver_path = current_module.find_use_path(
 +        ctx.sema.db,
 +        item_for_path_search(ctx.sema.db, item_in_ns)?,
 +        ctx.config.prefer_no_std,
 +    )?;
 +
 +    let qualify_candidate = QualifyCandidate::ImplMethod(ctx.sema.db, call, resolved_call);
 +
 +    acc.add(
 +        AssistId("qualify_method_call", AssistKind::RefactorInline),
++        format!("Qualify `{ident}` method call"),
 +        range,
 +        |builder| {
 +            qualify_candidate.qualify(
 +                |replace_with: String| builder.replace(range, replace_with),
 +                &receiver_path,
 +                item_in_ns,
 +            )
 +        },
 +    );
 +    Some(())
 +}
 +
 +fn item_for_path_search(db: &dyn HirDatabase, item: ItemInNs) -> Option<ItemInNs> {
 +    Some(match item {
 +        ItemInNs::Types(_) | ItemInNs::Values(_) => match item_as_assoc(db, item) {
 +            Some(assoc_item) => match assoc_item.container(db) {
 +                AssocItemContainer::Trait(trait_) => ItemInNs::from(ModuleDef::from(trait_)),
 +                AssocItemContainer::Impl(impl_) => match impl_.trait_(db) {
 +                    None => ItemInNs::from(ModuleDef::from(impl_.self_ty(db).as_adt()?)),
 +                    Some(trait_) => ItemInNs::from(ModuleDef::from(trait_)),
 +                },
 +            },
 +            None => item,
 +        },
 +        ItemInNs::Macros(_) => item,
 +    })
 +}
 +
 +fn item_as_assoc(db: &dyn HirDatabase, item: ItemInNs) -> Option<AssocItem> {
 +    item.as_module_def().and_then(|module_def| module_def.as_assoc_item(db))
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use super::*;
 +    use crate::tests::{check_assist, check_assist_not_applicable};
 +
 +    #[test]
 +    fn struct_method() {
 +        check_assist(
 +            qualify_method_call,
 +            r#"
 +struct Foo;
 +impl Foo {
 +    fn foo(&self) {}
 +}
 +
 +fn main() {
 +    let foo = Foo {};
 +    foo.fo$0o()
 +}
 +"#,
 +            r#"
 +struct Foo;
 +impl Foo {
 +    fn foo(&self) {}
 +}
 +
 +fn main() {
 +    let foo = Foo {};
 +    Foo::foo(&foo)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn struct_method_multi_params() {
 +        check_assist(
 +            qualify_method_call,
 +            r#"
 +struct Foo;
 +impl Foo {
 +    fn foo(&self, p1: i32, p2: u32) {}
 +}
 +
 +fn main() {
 +    let foo = Foo {};
 +    foo.fo$0o(9, 9u)
 +}
 +"#,
 +            r#"
 +struct Foo;
 +impl Foo {
 +    fn foo(&self, p1: i32, p2: u32) {}
 +}
 +
 +fn main() {
 +    let foo = Foo {};
 +    Foo::foo(&foo, 9, 9u)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn struct_method_consume() {
 +        check_assist(
 +            qualify_method_call,
 +            r#"
 +struct Foo;
 +impl Foo {
 +    fn foo(self, p1: i32, p2: u32) {}
 +}
 +
 +fn main() {
 +    let foo = Foo {};
 +    foo.fo$0o(9, 9u)
 +}
 +"#,
 +            r#"
 +struct Foo;
 +impl Foo {
 +    fn foo(self, p1: i32, p2: u32) {}
 +}
 +
 +fn main() {
 +    let foo = Foo {};
 +    Foo::foo(foo, 9, 9u)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn struct_method_exclusive() {
 +        check_assist(
 +            qualify_method_call,
 +            r#"
 +struct Foo;
 +impl Foo {
 +    fn foo(&mut self, p1: i32, p2: u32) {}
 +}
 +
 +fn main() {
 +    let foo = Foo {};
 +    foo.fo$0o(9, 9u)
 +}
 +"#,
 +            r#"
 +struct Foo;
 +impl Foo {
 +    fn foo(&mut self, p1: i32, p2: u32) {}
 +}
 +
 +fn main() {
 +    let foo = Foo {};
 +    Foo::foo(&mut foo, 9, 9u)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn struct_method_cross_crate() {
 +        check_assist(
 +            qualify_method_call,
 +            r#"
 +//- /main.rs crate:main deps:dep
 +fn main() {
 +    let foo = dep::test_mod::Foo {};
 +    foo.fo$0o(9, 9u)
 +}
 +//- /dep.rs crate:dep
 +pub mod test_mod {
 +    pub struct Foo;
 +    impl Foo {
 +        pub fn foo(&mut self, p1: i32, p2: u32) {}
 +    }
 +}
 +"#,
 +            r#"
 +fn main() {
 +    let foo = dep::test_mod::Foo {};
 +    dep::test_mod::Foo::foo(&mut foo, 9, 9u)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn struct_method_generic() {
 +        check_assist(
 +            qualify_method_call,
 +            r#"
 +struct Foo;
 +impl Foo {
 +    fn foo<T>(&self) {}
 +}
 +
 +fn main() {
 +    let foo = Foo {};
 +    foo.fo$0o::<()>()
 +}
 +"#,
 +            r#"
 +struct Foo;
 +impl Foo {
 +    fn foo<T>(&self) {}
 +}
 +
 +fn main() {
 +    let foo = Foo {};
 +    Foo::foo::<()>(&foo)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn trait_method() {
 +        check_assist(
 +            qualify_method_call,
 +            r#"
 +mod test_mod {
 +    pub trait TestTrait {
 +        fn test_method(&self);
 +    }
 +    pub struct TestStruct {}
 +    impl TestTrait for TestStruct {
 +        fn test_method(&self) {}
 +    }
 +}
 +
 +use test_mod::*;
 +
 +fn main() {
 +    let test_struct = test_mod::TestStruct {};
 +    test_struct.test_meth$0od()
 +}
 +"#,
 +            r#"
 +mod test_mod {
 +    pub trait TestTrait {
 +        fn test_method(&self);
 +    }
 +    pub struct TestStruct {}
 +    impl TestTrait for TestStruct {
 +        fn test_method(&self) {}
 +    }
 +}
 +
 +use test_mod::*;
 +
 +fn main() {
 +    let test_struct = test_mod::TestStruct {};
 +    TestTrait::test_method(&test_struct)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn trait_method_multi_params() {
 +        check_assist(
 +            qualify_method_call,
 +            r#"
 +mod test_mod {
 +    pub trait TestTrait {
 +        fn test_method(&self, p1: i32, p2: u32);
 +    }
 +    pub struct TestStruct {}
 +    impl TestTrait for TestStruct {
 +        fn test_method(&self, p1: i32, p2: u32) {}
 +    }
 +}
 +
 +use test_mod::*;
 +
 +fn main() {
 +    let test_struct = test_mod::TestStruct {};
 +    test_struct.test_meth$0od(12, 32u)
 +}
 +"#,
 +            r#"
 +mod test_mod {
 +    pub trait TestTrait {
 +        fn test_method(&self, p1: i32, p2: u32);
 +    }
 +    pub struct TestStruct {}
 +    impl TestTrait for TestStruct {
 +        fn test_method(&self, p1: i32, p2: u32) {}
 +    }
 +}
 +
 +use test_mod::*;
 +
 +fn main() {
 +    let test_struct = test_mod::TestStruct {};
 +    TestTrait::test_method(&test_struct, 12, 32u)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn trait_method_consume() {
 +        check_assist(
 +            qualify_method_call,
 +            r#"
 +mod test_mod {
 +    pub trait TestTrait {
 +        fn test_method(self, p1: i32, p2: u32);
 +    }
 +    pub struct TestStruct {}
 +    impl TestTrait for TestStruct {
 +        fn test_method(self, p1: i32, p2: u32) {}
 +    }
 +}
 +
 +use test_mod::*;
 +
 +fn main() {
 +    let test_struct = test_mod::TestStruct {};
 +    test_struct.test_meth$0od(12, 32u)
 +}
 +"#,
 +            r#"
 +mod test_mod {
 +    pub trait TestTrait {
 +        fn test_method(self, p1: i32, p2: u32);
 +    }
 +    pub struct TestStruct {}
 +    impl TestTrait for TestStruct {
 +        fn test_method(self, p1: i32, p2: u32) {}
 +    }
 +}
 +
 +use test_mod::*;
 +
 +fn main() {
 +    let test_struct = test_mod::TestStruct {};
 +    TestTrait::test_method(test_struct, 12, 32u)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn trait_method_exclusive() {
 +        check_assist(
 +            qualify_method_call,
 +            r#"
 +mod test_mod {
 +    pub trait TestTrait {
 +        fn test_method(&mut self, p1: i32, p2: u32);
 +    }
 +    pub struct TestStruct {}
 +    impl TestTrait for TestStruct {
 +        fn test_method(&mut self, p1: i32, p2: u32);
 +    }
 +}
 +
 +use test_mod::*;
 +
 +fn main() {
 +    let test_struct = test_mod::TestStruct {};
 +    test_struct.test_meth$0od(12, 32u)
 +}
 +"#,
 +            r#"
 +mod test_mod {
 +    pub trait TestTrait {
 +        fn test_method(&mut self, p1: i32, p2: u32);
 +    }
 +    pub struct TestStruct {}
 +    impl TestTrait for TestStruct {
 +        fn test_method(&mut self, p1: i32, p2: u32);
 +    }
 +}
 +
 +use test_mod::*;
 +
 +fn main() {
 +    let test_struct = test_mod::TestStruct {};
 +    TestTrait::test_method(&mut test_struct, 12, 32u)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn trait_method_cross_crate() {
 +        check_assist(
 +            qualify_method_call,
 +            r#"
 +//- /main.rs crate:main deps:dep
 +fn main() {
 +    let foo = dep::test_mod::Foo {};
 +    foo.fo$0o(9, 9u)
 +}
 +//- /dep.rs crate:dep
 +pub mod test_mod {
 +    pub struct Foo;
 +    impl Foo {
 +        pub fn foo(&mut self, p1: i32, p2: u32) {}
 +    }
 +}
 +"#,
 +            r#"
 +fn main() {
 +    let foo = dep::test_mod::Foo {};
 +    dep::test_mod::Foo::foo(&mut foo, 9, 9u)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn trait_method_generic() {
 +        check_assist(
 +            qualify_method_call,
 +            r#"
 +mod test_mod {
 +    pub trait TestTrait {
 +        fn test_method<T>(&self);
 +    }
 +    pub struct TestStruct {}
 +    impl TestTrait for TestStruct {
 +        fn test_method<T>(&self) {}
 +    }
 +}
 +
 +use test_mod::*;
 +
 +fn main() {
 +    let test_struct = TestStruct {};
 +    test_struct.test_meth$0od::<()>()
 +}
 +"#,
 +            r#"
 +mod test_mod {
 +    pub trait TestTrait {
 +        fn test_method<T>(&self);
 +    }
 +    pub struct TestStruct {}
 +    impl TestTrait for TestStruct {
 +        fn test_method<T>(&self) {}
 +    }
 +}
 +
 +use test_mod::*;
 +
 +fn main() {
 +    let test_struct = TestStruct {};
 +    TestTrait::test_method::<()>(&test_struct)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn struct_method_over_stuct_instance() {
 +        check_assist_not_applicable(
 +            qualify_method_call,
 +            r#"
 +struct Foo;
 +impl Foo {
 +    fn foo(&self) {}
 +}
 +
 +fn main() {
 +    let foo = Foo {};
 +    f$0oo.foo()
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn trait_method_over_stuct_instance() {
 +        check_assist_not_applicable(
 +            qualify_method_call,
 +            r#"
 +mod test_mod {
 +    pub trait TestTrait {
 +        fn test_method(&self);
 +    }
 +    pub struct TestStruct {}
 +    impl TestTrait for TestStruct {
 +        fn test_method(&self) {}
 +    }
 +}
 +
 +use test_mod::*;
 +
 +fn main() {
 +    let test_struct = test_mod::TestStruct {};
 +    tes$0t_struct.test_method()
 +}
 +"#,
 +        );
 +    }
 +}
index 4b2af550bc5e4902c15bdf4a158ba6ee0bef6326,0000000000000000000000000000000000000000..e759e1561cbd0a5ba956f42bacabd232de330d58
mode 100644,000000..100644
--- /dev/null
@@@ -1,1298 -1,0 +1,1295 @@@
-                 replacer(format!("{}{}::{}", import, generics, segment));
 +use std::iter;
 +
 +use hir::AsAssocItem;
 +use ide_db::RootDatabase;
 +use ide_db::{
 +    helpers::mod_path_to_ast,
 +    imports::import_assets::{ImportCandidate, LocatedImport},
 +};
 +use syntax::{
 +    ast,
 +    ast::{make, HasArgList},
 +    AstNode, NodeOrToken,
 +};
 +
 +use crate::{
 +    assist_context::{AssistContext, Assists},
 +    handlers::auto_import::find_importable_node,
 +    AssistId, AssistKind, GroupLabel,
 +};
 +
 +// Assist: qualify_path
 +//
 +// If the name is unresolved, provides all possible qualified paths for it.
 +//
 +// ```
 +// fn main() {
 +//     let map = HashMap$0::new();
 +// }
 +// # pub mod std { pub mod collections { pub struct HashMap { } } }
 +// ```
 +// ->
 +// ```
 +// fn main() {
 +//     let map = std::collections::HashMap::new();
 +// }
 +// # pub mod std { pub mod collections { pub struct HashMap { } } }
 +// ```
 +pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    let (import_assets, syntax_under_caret) = find_importable_node(ctx)?;
 +    let mut proposed_imports =
 +        import_assets.search_for_relative_paths(&ctx.sema, ctx.config.prefer_no_std);
 +    if proposed_imports.is_empty() {
 +        return None;
 +    }
 +
 +    let range = match &syntax_under_caret {
 +        NodeOrToken::Node(node) => ctx.sema.original_range(node).range,
 +        NodeOrToken::Token(token) => token.text_range(),
 +    };
 +    let candidate = import_assets.import_candidate();
 +    let qualify_candidate = match syntax_under_caret {
 +        NodeOrToken::Node(syntax_under_caret) => match candidate {
 +            ImportCandidate::Path(candidate) if candidate.qualifier.is_some() => {
 +                cov_mark::hit!(qualify_path_qualifier_start);
 +                let path = ast::Path::cast(syntax_under_caret)?;
 +                let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?);
 +                QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list())
 +            }
 +            ImportCandidate::Path(_) => {
 +                cov_mark::hit!(qualify_path_unqualified_name);
 +                let path = ast::Path::cast(syntax_under_caret)?;
 +                let generics = path.segment()?.generic_arg_list();
 +                QualifyCandidate::UnqualifiedName(generics)
 +            }
 +            ImportCandidate::TraitAssocItem(_) => {
 +                cov_mark::hit!(qualify_path_trait_assoc_item);
 +                let path = ast::Path::cast(syntax_under_caret)?;
 +                let (qualifier, segment) = (path.qualifier()?, path.segment()?);
 +                QualifyCandidate::TraitAssocItem(qualifier, segment)
 +            }
 +            ImportCandidate::TraitMethod(_) => {
 +                cov_mark::hit!(qualify_path_trait_method);
 +                let mcall_expr = ast::MethodCallExpr::cast(syntax_under_caret)?;
 +                QualifyCandidate::TraitMethod(ctx.sema.db, mcall_expr)
 +            }
 +        },
 +        // derive attribute path
 +        NodeOrToken::Token(_) => QualifyCandidate::UnqualifiedName(None),
 +    };
 +
 +    // we aren't interested in different namespaces
 +    proposed_imports.dedup_by(|a, b| a.import_path == b.import_path);
 +
 +    let group_label = group_label(candidate);
 +    for import in proposed_imports {
 +        acc.add_group(
 +            &group_label,
 +            AssistId("qualify_path", AssistKind::QuickFix),
 +            label(candidate, &import),
 +            range,
 +            |builder| {
 +                qualify_candidate.qualify(
 +                    |replace_with: String| builder.replace(range, replace_with),
 +                    &import.import_path,
 +                    import.item_to_import,
 +                )
 +            },
 +        );
 +    }
 +    Some(())
 +}
 +pub(crate) enum QualifyCandidate<'db> {
 +    QualifierStart(ast::PathSegment, Option<ast::GenericArgList>),
 +    UnqualifiedName(Option<ast::GenericArgList>),
 +    TraitAssocItem(ast::Path, ast::PathSegment),
 +    TraitMethod(&'db RootDatabase, ast::MethodCallExpr),
 +    ImplMethod(&'db RootDatabase, ast::MethodCallExpr, hir::Function),
 +}
 +
 +impl QualifyCandidate<'_> {
 +    pub(crate) fn qualify(
 +        &self,
 +        mut replacer: impl FnMut(String),
 +        import: &hir::ModPath,
 +        item: hir::ItemInNs,
 +    ) {
 +        let import = mod_path_to_ast(import);
 +        match self {
 +            QualifyCandidate::QualifierStart(segment, generics) => {
 +                let generics = generics.as_ref().map_or_else(String::new, ToString::to_string);
-                 replacer(format!("{}{}", import, generics));
++                replacer(format!("{import}{generics}::{segment}"));
 +            }
 +            QualifyCandidate::UnqualifiedName(generics) => {
 +                let generics = generics.as_ref().map_or_else(String::new, ToString::to_string);
-                 replacer(format!("<{} as {}>::{}", qualifier, import, segment));
++                replacer(format!("{import}{generics}"));
 +            }
 +            QualifyCandidate::TraitAssocItem(qualifier, segment) => {
-             replacer(format!(
-                 "{}::{}{}{}",
-                 import,
-                 method_name,
-                 generics,
-                 match arg_list {
-                     Some(args) => make::arg_list(iter::once(receiver).chain(args)),
-                     None => make::arg_list(iter::once(receiver)),
-                 }
-             ));
++                replacer(format!("<{qualifier} as {import}>::{segment}"));
 +            }
 +            QualifyCandidate::TraitMethod(db, mcall_expr) => {
 +                Self::qualify_trait_method(db, mcall_expr, replacer, import, item);
 +            }
 +            QualifyCandidate::ImplMethod(db, mcall_expr, hir_fn) => {
 +                Self::qualify_fn_call(db, mcall_expr, replacer, import, hir_fn);
 +            }
 +        }
 +    }
 +
 +    fn qualify_fn_call(
 +        db: &RootDatabase,
 +        mcall_expr: &ast::MethodCallExpr,
 +        mut replacer: impl FnMut(String),
 +        import: ast::Path,
 +        hir_fn: &hir::Function,
 +    ) -> Option<()> {
 +        let receiver = mcall_expr.receiver()?;
 +        let method_name = mcall_expr.name_ref()?;
 +        let generics =
 +            mcall_expr.generic_arg_list().as_ref().map_or_else(String::new, ToString::to_string);
 +        let arg_list = mcall_expr.arg_list().map(|arg_list| arg_list.args());
 +
 +        if let Some(self_access) = hir_fn.self_param(db).map(|sp| sp.access(db)) {
 +            let receiver = match self_access {
 +                hir::Access::Shared => make::expr_ref(receiver, false),
 +                hir::Access::Exclusive => make::expr_ref(receiver, true),
 +                hir::Access::Owned => receiver,
 +            };
-     GroupLabel(format!("Qualify {}", name))
++            let arg_list = match arg_list {
++                Some(args) => make::arg_list(iter::once(receiver).chain(args)),
++                None => make::arg_list(iter::once(receiver)),
++            };
++            replacer(format!("{import}::{method_name}{generics}{arg_list}"));
 +        }
 +        Some(())
 +    }
 +
 +    fn qualify_trait_method(
 +        db: &RootDatabase,
 +        mcall_expr: &ast::MethodCallExpr,
 +        replacer: impl FnMut(String),
 +        import: ast::Path,
 +        item: hir::ItemInNs,
 +    ) -> Option<()> {
 +        let trait_method_name = mcall_expr.name_ref()?;
 +        let trait_ = item_as_trait(db, item)?;
 +        let method = find_trait_method(db, trait_, &trait_method_name)?;
 +        Self::qualify_fn_call(db, mcall_expr, replacer, import, &method)
 +    }
 +}
 +
 +fn find_trait_method(
 +    db: &RootDatabase,
 +    trait_: hir::Trait,
 +    trait_method_name: &ast::NameRef,
 +) -> Option<hir::Function> {
 +    if let Some(hir::AssocItem::Function(method)) =
 +        trait_.items(db).into_iter().find(|item: &hir::AssocItem| {
 +            item.name(db)
 +                .map(|name| name.to_string() == trait_method_name.to_string())
 +                .unwrap_or(false)
 +        })
 +    {
 +        Some(method)
 +    } else {
 +        None
 +    }
 +}
 +
 +fn item_as_trait(db: &RootDatabase, item: hir::ItemInNs) -> Option<hir::Trait> {
 +    let item_module_def = item.as_module_def()?;
 +
 +    match item_module_def {
 +        hir::ModuleDef::Trait(trait_) => Some(trait_),
 +        _ => item_module_def.as_assoc_item(db)?.containing_trait(db),
 +    }
 +}
 +
 +fn group_label(candidate: &ImportCandidate) -> GroupLabel {
 +    let name = match candidate {
 +        ImportCandidate::Path(it) => &it.name,
 +        ImportCandidate::TraitAssocItem(it) | ImportCandidate::TraitMethod(it) => {
 +            &it.assoc_item_name
 +        }
 +    }
 +    .text();
-             format!("Qualify as `{}`", import.import_path)
++    GroupLabel(format!("Qualify {name}"))
 +}
 +
 +fn label(candidate: &ImportCandidate, import: &LocatedImport) -> String {
++    let import_path = &import.import_path;
++
 +    match candidate {
 +        ImportCandidate::Path(candidate) if candidate.qualifier.is_none() => {
-         _ => format!("Qualify with `{}`", import.import_path),
++            format!("Qualify as `{import_path}`")
 +        }
++        _ => format!("Qualify with `{import_path}`"),
 +    }
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
 +
 +    use super::*;
 +
 +    #[test]
 +    fn applicable_when_found_an_import_partial() {
 +        cov_mark::check!(qualify_path_unqualified_name);
 +        check_assist(
 +            qualify_path,
 +            r#"
 +mod std {
 +    pub mod fmt {
 +        pub struct Formatter;
 +    }
 +}
 +
 +use std::fmt;
 +
 +$0Formatter
 +"#,
 +            r#"
 +mod std {
 +    pub mod fmt {
 +        pub struct Formatter;
 +    }
 +}
 +
 +use std::fmt;
 +
 +fmt::Formatter
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn applicable_when_found_an_import() {
 +        check_assist(
 +            qualify_path,
 +            r#"
 +$0PubStruct
 +
 +pub mod PubMod {
 +    pub struct PubStruct;
 +}
 +"#,
 +            r#"
 +PubMod::PubStruct
 +
 +pub mod PubMod {
 +    pub struct PubStruct;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn applicable_in_macros() {
 +        check_assist(
 +            qualify_path,
 +            r#"
 +macro_rules! foo {
 +    ($i:ident) => { fn foo(a: $i) {} }
 +}
 +foo!(Pub$0Struct);
 +
 +pub mod PubMod {
 +    pub struct PubStruct;
 +}
 +"#,
 +            r#"
 +macro_rules! foo {
 +    ($i:ident) => { fn foo(a: $i) {} }
 +}
 +foo!(PubMod::PubStruct);
 +
 +pub mod PubMod {
 +    pub struct PubStruct;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn applicable_when_found_multiple_imports() {
 +        check_assist(
 +            qualify_path,
 +            r#"
 +PubSt$0ruct
 +
 +pub mod PubMod1 {
 +    pub struct PubStruct;
 +}
 +pub mod PubMod2 {
 +    pub struct PubStruct;
 +}
 +pub mod PubMod3 {
 +    pub struct PubStruct;
 +}
 +"#,
 +            r#"
 +PubMod3::PubStruct
 +
 +pub mod PubMod1 {
 +    pub struct PubStruct;
 +}
 +pub mod PubMod2 {
 +    pub struct PubStruct;
 +}
 +pub mod PubMod3 {
 +    pub struct PubStruct;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn not_applicable_for_already_imported_types() {
 +        check_assist_not_applicable(
 +            qualify_path,
 +            r#"
 +use PubMod::PubStruct;
 +
 +PubStruct$0
 +
 +pub mod PubMod {
 +    pub struct PubStruct;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn not_applicable_for_types_with_private_paths() {
 +        check_assist_not_applicable(
 +            qualify_path,
 +            r#"
 +PrivateStruct$0
 +
 +pub mod PubMod {
 +    struct PrivateStruct;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn not_applicable_when_no_imports_found() {
 +        check_assist_not_applicable(qualify_path, r#"PubStruct$0"#);
 +    }
 +
 +    #[test]
 +    fn qualify_function() {
 +        check_assist(
 +            qualify_path,
 +            r#"
 +test_function$0
 +
 +pub mod PubMod {
 +    pub fn test_function() {};
 +}
 +"#,
 +            r#"
 +PubMod::test_function
 +
 +pub mod PubMod {
 +    pub fn test_function() {};
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn qualify_macro() {
 +        check_assist(
 +            qualify_path,
 +            r#"
 +//- /lib.rs crate:crate_with_macro
 +#[macro_export]
 +macro_rules! foo {
 +    () => ()
 +}
 +
 +//- /main.rs crate:main deps:crate_with_macro
 +fn main() {
 +    foo$0
 +}
 +"#,
 +            r#"
 +fn main() {
 +    crate_with_macro::foo
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn qualify_path_target() {
 +        check_assist_target(
 +            qualify_path,
 +            r#"
 +struct AssistInfo {
 +    group_label: Option<$0GroupLabel>,
 +}
 +
 +mod m { pub struct GroupLabel; }
 +"#,
 +            "GroupLabel",
 +        )
 +    }
 +
 +    #[test]
 +    fn not_applicable_when_path_start_is_imported() {
 +        check_assist_not_applicable(
 +            qualify_path,
 +            r#"
 +pub mod mod1 {
 +    pub mod mod2 {
 +        pub mod mod3 {
 +            pub struct TestStruct;
 +        }
 +    }
 +}
 +
 +use mod1::mod2;
 +fn main() {
 +    mod2::mod3::TestStruct$0
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn not_applicable_for_imported_function() {
 +        check_assist_not_applicable(
 +            qualify_path,
 +            r#"
 +pub mod test_mod {
 +    pub fn test_function() {}
 +}
 +
 +use test_mod::test_function;
 +fn main() {
 +    test_function$0
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn associated_struct_function() {
 +        check_assist(
 +            qualify_path,
 +            r#"
 +mod test_mod {
 +    pub struct TestStruct {}
 +    impl TestStruct {
 +        pub fn test_function() {}
 +    }
 +}
 +
 +fn main() {
 +    TestStruct::test_function$0
 +}
 +"#,
 +            r#"
 +mod test_mod {
 +    pub struct TestStruct {}
 +    impl TestStruct {
 +        pub fn test_function() {}
 +    }
 +}
 +
 +fn main() {
 +    test_mod::TestStruct::test_function
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn associated_struct_const() {
 +        cov_mark::check!(qualify_path_qualifier_start);
 +        check_assist(
 +            qualify_path,
 +            r#"
 +mod test_mod {
 +    pub struct TestStruct {}
 +    impl TestStruct {
 +        const TEST_CONST: u8 = 42;
 +    }
 +}
 +
 +fn main() {
 +    TestStruct::TEST_CONST$0
 +}
 +"#,
 +            r#"
 +mod test_mod {
 +    pub struct TestStruct {}
 +    impl TestStruct {
 +        const TEST_CONST: u8 = 42;
 +    }
 +}
 +
 +fn main() {
 +    test_mod::TestStruct::TEST_CONST
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn associated_struct_const_unqualified() {
 +        // FIXME: non-trait assoc items completion is unsupported yet, see FIXME in the import_assets.rs for more details
 +        check_assist_not_applicable(
 +            qualify_path,
 +            r#"
 +mod test_mod {
 +    pub struct TestStruct {}
 +    impl TestStruct {
 +        const TEST_CONST: u8 = 42;
 +    }
 +}
 +
 +fn main() {
 +    TEST_CONST$0
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn associated_trait_function() {
 +        check_assist(
 +            qualify_path,
 +            r#"
 +mod test_mod {
 +    pub trait TestTrait {
 +        fn test_function();
 +    }
 +    pub struct TestStruct {}
 +    impl TestTrait for TestStruct {
 +        fn test_function() {}
 +    }
 +}
 +
 +fn main() {
 +    test_mod::TestStruct::test_function$0
 +}
 +"#,
 +            r#"
 +mod test_mod {
 +    pub trait TestTrait {
 +        fn test_function();
 +    }
 +    pub struct TestStruct {}
 +    impl TestTrait for TestStruct {
 +        fn test_function() {}
 +    }
 +}
 +
 +fn main() {
 +    <test_mod::TestStruct as test_mod::TestTrait>::test_function
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn not_applicable_for_imported_trait_for_function() {
 +        check_assist_not_applicable(
 +            qualify_path,
 +            r#"
 +mod test_mod {
 +    pub trait TestTrait {
 +        fn test_function();
 +    }
 +    pub trait TestTrait2 {
 +        fn test_function();
 +    }
 +    pub enum TestEnum {
 +        One,
 +        Two,
 +    }
 +    impl TestTrait2 for TestEnum {
 +        fn test_function() {}
 +    }
 +    impl TestTrait for TestEnum {
 +        fn test_function() {}
 +    }
 +}
 +
 +use test_mod::TestTrait2;
 +fn main() {
 +    test_mod::TestEnum::test_function$0;
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn associated_trait_const() {
 +        cov_mark::check!(qualify_path_trait_assoc_item);
 +        check_assist(
 +            qualify_path,
 +            r#"
 +mod test_mod {
 +    pub trait TestTrait {
 +        const TEST_CONST: u8;
 +    }
 +    pub struct TestStruct {}
 +    impl TestTrait for TestStruct {
 +        const TEST_CONST: u8 = 42;
 +    }
 +}
 +
 +fn main() {
 +    test_mod::TestStruct::TEST_CONST$0
 +}
 +"#,
 +            r#"
 +mod test_mod {
 +    pub trait TestTrait {
 +        const TEST_CONST: u8;
 +    }
 +    pub struct TestStruct {}
 +    impl TestTrait for TestStruct {
 +        const TEST_CONST: u8 = 42;
 +    }
 +}
 +
 +fn main() {
 +    <test_mod::TestStruct as test_mod::TestTrait>::TEST_CONST
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn not_applicable_for_imported_trait_for_const() {
 +        check_assist_not_applicable(
 +            qualify_path,
 +            r#"
 +mod test_mod {
 +    pub trait TestTrait {
 +        const TEST_CONST: u8;
 +    }
 +    pub trait TestTrait2 {
 +        const TEST_CONST: f64;
 +    }
 +    pub enum TestEnum {
 +        One,
 +        Two,
 +    }
 +    impl TestTrait2 for TestEnum {
 +        const TEST_CONST: f64 = 42.0;
 +    }
 +    impl TestTrait for TestEnum {
 +        const TEST_CONST: u8 = 42;
 +    }
 +}
 +
 +use test_mod::TestTrait2;
 +fn main() {
 +    test_mod::TestEnum::TEST_CONST$0;
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn trait_method() {
 +        cov_mark::check!(qualify_path_trait_method);
 +        check_assist(
 +            qualify_path,
 +            r#"
 +mod test_mod {
 +    pub trait TestTrait {
 +        fn test_method(&self);
 +    }
 +    pub struct TestStruct {}
 +    impl TestTrait for TestStruct {
 +        fn test_method(&self) {}
 +    }
 +}
 +
 +fn main() {
 +    let test_struct = test_mod::TestStruct {};
 +    test_struct.test_meth$0od()
 +}
 +"#,
 +            r#"
 +mod test_mod {
 +    pub trait TestTrait {
 +        fn test_method(&self);
 +    }
 +    pub struct TestStruct {}
 +    impl TestTrait for TestStruct {
 +        fn test_method(&self) {}
 +    }
 +}
 +
 +fn main() {
 +    let test_struct = test_mod::TestStruct {};
 +    test_mod::TestTrait::test_method(&test_struct)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn trait_method_multi_params() {
 +        check_assist(
 +            qualify_path,
 +            r#"
 +mod test_mod {
 +    pub trait TestTrait {
 +        fn test_method(&self, test: i32);
 +    }
 +    pub struct TestStruct {}
 +    impl TestTrait for TestStruct {
 +        fn test_method(&self, test: i32) {}
 +    }
 +}
 +
 +fn main() {
 +    let test_struct = test_mod::TestStruct {};
 +    test_struct.test_meth$0od(42)
 +}
 +"#,
 +            r#"
 +mod test_mod {
 +    pub trait TestTrait {
 +        fn test_method(&self, test: i32);
 +    }
 +    pub struct TestStruct {}
 +    impl TestTrait for TestStruct {
 +        fn test_method(&self, test: i32) {}
 +    }
 +}
 +
 +fn main() {
 +    let test_struct = test_mod::TestStruct {};
 +    test_mod::TestTrait::test_method(&test_struct, 42)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn trait_method_consume() {
 +        check_assist(
 +            qualify_path,
 +            r#"
 +mod test_mod {
 +    pub trait TestTrait {
 +        fn test_method(self);
 +    }
 +    pub struct TestStruct {}
 +    impl TestTrait for TestStruct {
 +        fn test_method(self) {}
 +    }
 +}
 +
 +fn main() {
 +    let test_struct = test_mod::TestStruct {};
 +    test_struct.test_meth$0od()
 +}
 +"#,
 +            r#"
 +mod test_mod {
 +    pub trait TestTrait {
 +        fn test_method(self);
 +    }
 +    pub struct TestStruct {}
 +    impl TestTrait for TestStruct {
 +        fn test_method(self) {}
 +    }
 +}
 +
 +fn main() {
 +    let test_struct = test_mod::TestStruct {};
 +    test_mod::TestTrait::test_method(test_struct)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn trait_method_cross_crate() {
 +        check_assist(
 +            qualify_path,
 +            r#"
 +//- /main.rs crate:main deps:dep
 +fn main() {
 +    let test_struct = dep::test_mod::TestStruct {};
 +    test_struct.test_meth$0od()
 +}
 +//- /dep.rs crate:dep
 +pub mod test_mod {
 +    pub trait TestTrait {
 +        fn test_method(&self);
 +    }
 +    pub struct TestStruct {}
 +    impl TestTrait for TestStruct {
 +        fn test_method(&self) {}
 +    }
 +}
 +"#,
 +            r#"
 +fn main() {
 +    let test_struct = dep::test_mod::TestStruct {};
 +    dep::test_mod::TestTrait::test_method(&test_struct)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn assoc_fn_cross_crate() {
 +        check_assist(
 +            qualify_path,
 +            r#"
 +//- /main.rs crate:main deps:dep
 +fn main() {
 +    dep::test_mod::TestStruct::test_func$0tion
 +}
 +//- /dep.rs crate:dep
 +pub mod test_mod {
 +    pub trait TestTrait {
 +        fn test_function();
 +    }
 +    pub struct TestStruct {}
 +    impl TestTrait for TestStruct {
 +        fn test_function() {}
 +    }
 +}
 +"#,
 +            r#"
 +fn main() {
 +    <dep::test_mod::TestStruct as dep::test_mod::TestTrait>::test_function
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn assoc_const_cross_crate() {
 +        check_assist(
 +            qualify_path,
 +            r#"
 +//- /main.rs crate:main deps:dep
 +fn main() {
 +    dep::test_mod::TestStruct::CONST$0
 +}
 +//- /dep.rs crate:dep
 +pub mod test_mod {
 +    pub trait TestTrait {
 +        const CONST: bool;
 +    }
 +    pub struct TestStruct {}
 +    impl TestTrait for TestStruct {
 +        const CONST: bool = true;
 +    }
 +}
 +"#,
 +            r#"
 +fn main() {
 +    <dep::test_mod::TestStruct as dep::test_mod::TestTrait>::CONST
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn assoc_fn_as_method_cross_crate() {
 +        check_assist_not_applicable(
 +            qualify_path,
 +            r#"
 +//- /main.rs crate:main deps:dep
 +fn main() {
 +    let test_struct = dep::test_mod::TestStruct {};
 +    test_struct.test_func$0tion()
 +}
 +//- /dep.rs crate:dep
 +pub mod test_mod {
 +    pub trait TestTrait {
 +        fn test_function();
 +    }
 +    pub struct TestStruct {}
 +    impl TestTrait for TestStruct {
 +        fn test_function() {}
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn private_trait_cross_crate() {
 +        check_assist_not_applicable(
 +            qualify_path,
 +            r#"
 +//- /main.rs crate:main deps:dep
 +fn main() {
 +    let test_struct = dep::test_mod::TestStruct {};
 +    test_struct.test_meth$0od()
 +}
 +//- /dep.rs crate:dep
 +pub mod test_mod {
 +    trait TestTrait {
 +        fn test_method(&self);
 +    }
 +    pub struct TestStruct {}
 +    impl TestTrait for TestStruct {
 +        fn test_method(&self) {}
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn not_applicable_for_imported_trait_for_method() {
 +        check_assist_not_applicable(
 +            qualify_path,
 +            r#"
 +mod test_mod {
 +    pub trait TestTrait {
 +        fn test_method(&self);
 +    }
 +    pub trait TestTrait2 {
 +        fn test_method(&self);
 +    }
 +    pub enum TestEnum {
 +        One,
 +        Two,
 +    }
 +    impl TestTrait2 for TestEnum {
 +        fn test_method(&self) {}
 +    }
 +    impl TestTrait for TestEnum {
 +        fn test_method(&self) {}
 +    }
 +}
 +
 +use test_mod::TestTrait2;
 +fn main() {
 +    let one = test_mod::TestEnum::One;
 +    one.test$0_method();
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn dep_import() {
 +        check_assist(
 +            qualify_path,
 +            r"
 +//- /lib.rs crate:dep
 +pub struct Struct;
 +
 +//- /main.rs crate:main deps:dep
 +fn main() {
 +    Struct$0
 +}
 +",
 +            r"
 +fn main() {
 +    dep::Struct
 +}
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn whole_segment() {
 +        // Tests that only imports whose last segment matches the identifier get suggested.
 +        check_assist(
 +            qualify_path,
 +            r"
 +//- /lib.rs crate:dep
 +pub mod fmt {
 +    pub trait Display {}
 +}
 +
 +pub fn panic_fmt() {}
 +
 +//- /main.rs crate:main deps:dep
 +struct S;
 +
 +impl f$0mt::Display for S {}
 +",
 +            r"
 +struct S;
 +
 +impl dep::fmt::Display for S {}
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn macro_generated() {
 +        // Tests that macro-generated items are suggested from external crates.
 +        check_assist(
 +            qualify_path,
 +            r"
 +//- /lib.rs crate:dep
 +macro_rules! mac {
 +    () => {
 +        pub struct Cheese;
 +    };
 +}
 +
 +mac!();
 +
 +//- /main.rs crate:main deps:dep
 +fn main() {
 +    Cheese$0;
 +}
 +",
 +            r"
 +fn main() {
 +    dep::Cheese;
 +}
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn casing() {
 +        // Tests that differently cased names don't interfere and we only suggest the matching one.
 +        check_assist(
 +            qualify_path,
 +            r"
 +//- /lib.rs crate:dep
 +pub struct FMT;
 +pub struct fmt;
 +
 +//- /main.rs crate:main deps:dep
 +fn main() {
 +    FMT$0;
 +}
 +",
 +            r"
 +fn main() {
 +    dep::FMT;
 +}
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn keep_generic_annotations() {
 +        check_assist(
 +            qualify_path,
 +            r"
 +//- /lib.rs crate:dep
 +pub mod generic { pub struct Thing<'a, T>(&'a T); }
 +
 +//- /main.rs crate:main deps:dep
 +fn foo() -> Thin$0g<'static, ()> {}
 +
 +fn main() {}
 +",
 +            r"
 +fn foo() -> dep::generic::Thing<'static, ()> {}
 +
 +fn main() {}
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn keep_generic_annotations_leading_colon() {
 +        check_assist(
 +            qualify_path,
 +            r#"
 +//- /lib.rs crate:dep
 +pub mod generic { pub struct Thing<'a, T>(&'a T); }
 +
 +//- /main.rs crate:main deps:dep
 +fn foo() -> Thin$0g::<'static, ()> {}
 +
 +fn main() {}
 +"#,
 +            r"
 +fn foo() -> dep::generic::Thing::<'static, ()> {}
 +
 +fn main() {}
 +",
 +        );
 +    }
 +
 +    #[test]
 +    fn associated_struct_const_generic() {
 +        check_assist(
 +            qualify_path,
 +            r#"
 +mod test_mod {
 +    pub struct TestStruct<T> {}
 +    impl<T> TestStruct<T> {
 +        const TEST_CONST: u8 = 42;
 +    }
 +}
 +
 +fn main() {
 +    TestStruct::<()>::TEST_CONST$0
 +}
 +"#,
 +            r#"
 +mod test_mod {
 +    pub struct TestStruct<T> {}
 +    impl<T> TestStruct<T> {
 +        const TEST_CONST: u8 = 42;
 +    }
 +}
 +
 +fn main() {
 +    test_mod::TestStruct::<()>::TEST_CONST
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn associated_trait_const_generic() {
 +        check_assist(
 +            qualify_path,
 +            r#"
 +mod test_mod {
 +    pub trait TestTrait {
 +        const TEST_CONST: u8;
 +    }
 +    pub struct TestStruct<T> {}
 +    impl<T> TestTrait for TestStruct<T> {
 +        const TEST_CONST: u8 = 42;
 +    }
 +}
 +
 +fn main() {
 +    test_mod::TestStruct::<()>::TEST_CONST$0
 +}
 +"#,
 +            r#"
 +mod test_mod {
 +    pub trait TestTrait {
 +        const TEST_CONST: u8;
 +    }
 +    pub struct TestStruct<T> {}
 +    impl<T> TestTrait for TestStruct<T> {
 +        const TEST_CONST: u8 = 42;
 +    }
 +}
 +
 +fn main() {
 +    <test_mod::TestStruct::<()> as test_mod::TestTrait>::TEST_CONST
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn trait_method_generic() {
 +        check_assist(
 +            qualify_path,
 +            r#"
 +mod test_mod {
 +    pub trait TestTrait {
 +        fn test_method<T>(&self);
 +    }
 +    pub struct TestStruct {}
 +    impl TestTrait for TestStruct {
 +        fn test_method<T>(&self) {}
 +    }
 +}
 +
 +fn main() {
 +    let test_struct = test_mod::TestStruct {};
 +    test_struct.test_meth$0od::<()>()
 +}
 +"#,
 +            r#"
 +mod test_mod {
 +    pub trait TestTrait {
 +        fn test_method<T>(&self);
 +    }
 +    pub struct TestStruct {}
 +    impl TestTrait for TestStruct {
 +        fn test_method<T>(&self) {}
 +    }
 +}
 +
 +fn main() {
 +    let test_struct = test_mod::TestStruct {};
 +    test_mod::TestTrait::test_method::<()>(&test_struct)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn works_in_derives() {
 +        check_assist(
 +            qualify_path,
 +            r#"
 +//- minicore:derive
 +mod foo {
 +    #[rustc_builtin_macro]
 +    pub macro Copy {}
 +}
 +#[derive(Copy$0)]
 +struct Foo;
 +"#,
 +            r#"
 +mod foo {
 +    #[rustc_builtin_macro]
 +    pub macro Copy {}
 +}
 +#[derive(foo::Copy)]
 +struct Foo;
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn works_in_use_start() {
 +        check_assist(
 +            qualify_path,
 +            r#"
 +mod bar {
 +    pub mod foo {
 +        pub struct Foo;
 +    }
 +}
 +use foo$0::Foo;
 +"#,
 +            r#"
 +mod bar {
 +    pub mod foo {
 +        pub struct Foo;
 +    }
 +}
 +use bar::foo::Foo;
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn not_applicable_in_non_start_use() {
 +        check_assist_not_applicable(
 +            qualify_path,
 +            r"
 +mod bar {
 +    pub mod foo {
 +        pub struct Foo;
 +    }
 +}
 +use foo::Foo$0;
 +",
 +        );
 +    }
 +}
index dbe8cb7bf031faadb71ed8ebae13819624980f55,0000000000000000000000000000000000000000..c9bc25b27a5ed8c0d1c3543d1d2be932cb50c139
mode 100644,000000..100644
--- /dev/null
@@@ -1,509 -1,0 +1,506 @@@
-                 edit.insert(token.syntax().text_range().start(), format!("r{}", hashes));
 +use std::borrow::Cow;
 +
 +use syntax::{ast, ast::IsString, AstToken, TextRange, TextSize};
 +
 +use crate::{AssistContext, AssistId, AssistKind, Assists};
 +
 +// Assist: make_raw_string
 +//
 +// Adds `r#` to a plain string literal.
 +//
 +// ```
 +// fn main() {
 +//     "Hello,$0 World!";
 +// }
 +// ```
 +// ->
 +// ```
 +// fn main() {
 +//     r#"Hello, World!"#;
 +// }
 +// ```
 +pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    let token = ctx.find_token_at_offset::<ast::String>()?;
 +    if token.is_raw() {
 +        return None;
 +    }
 +    let value = token.value()?;
 +    let target = token.syntax().text_range();
 +    acc.add(
 +        AssistId("make_raw_string", AssistKind::RefactorRewrite),
 +        "Rewrite as raw string",
 +        target,
 +        |edit| {
 +            let hashes = "#".repeat(required_hashes(&value).max(1));
 +            if matches!(value, Cow::Borrowed(_)) {
 +                // Avoid replacing the whole string to better position the cursor.
-                 edit.replace(
-                     token.syntax().text_range(),
-                     format!("r{}\"{}\"{}", hashes, value, hashes),
-                 );
++                edit.insert(token.syntax().text_range().start(), format!("r{hashes}"));
 +                edit.insert(token.syntax().text_range().end(), hashes);
 +            } else {
-             edit.replace(token.syntax().text_range(), format!("\"{}\"", escaped));
++                edit.replace(token.syntax().text_range(), format!("r{hashes}\"{value}\"{hashes}"));
 +            }
 +        },
 +    )
 +}
 +
 +// Assist: make_usual_string
 +//
 +// Turns a raw string into a plain string.
 +//
 +// ```
 +// fn main() {
 +//     r#"Hello,$0 "World!""#;
 +// }
 +// ```
 +// ->
 +// ```
 +// fn main() {
 +//     "Hello, \"World!\"";
 +// }
 +// ```
 +pub(crate) fn make_usual_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    let token = ctx.find_token_at_offset::<ast::String>()?;
 +    if !token.is_raw() {
 +        return None;
 +    }
 +    let value = token.value()?;
 +    let target = token.syntax().text_range();
 +    acc.add(
 +        AssistId("make_usual_string", AssistKind::RefactorRewrite),
 +        "Rewrite as regular string",
 +        target,
 +        |edit| {
 +            // parse inside string to escape `"`
 +            let escaped = value.escape_default().to_string();
 +            if let Some(offsets) = token.quote_offsets() {
 +                if token.text()[offsets.contents - token.syntax().text_range().start()] == escaped {
 +                    edit.replace(offsets.quotes.0, "\"");
 +                    edit.replace(offsets.quotes.1, "\"");
 +                    return;
 +                }
 +            }
 +
++            edit.replace(token.syntax().text_range(), format!("\"{escaped}\""));
 +        },
 +    )
 +}
 +
 +// Assist: add_hash
 +//
 +// Adds a hash to a raw string literal.
 +//
 +// ```
 +// fn main() {
 +//     r#"Hello,$0 World!"#;
 +// }
 +// ```
 +// ->
 +// ```
 +// fn main() {
 +//     r##"Hello, World!"##;
 +// }
 +// ```
 +pub(crate) fn add_hash(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    let token = ctx.find_token_at_offset::<ast::String>()?;
 +    if !token.is_raw() {
 +        return None;
 +    }
 +    let text_range = token.syntax().text_range();
 +    let target = text_range;
 +    acc.add(AssistId("add_hash", AssistKind::Refactor), "Add #", target, |edit| {
 +        edit.insert(text_range.start() + TextSize::of('r'), "#");
 +        edit.insert(text_range.end(), "#");
 +    })
 +}
 +
 +// Assist: remove_hash
 +//
 +// Removes a hash from a raw string literal.
 +//
 +// ```
 +// fn main() {
 +//     r#"Hello,$0 World!"#;
 +// }
 +// ```
 +// ->
 +// ```
 +// fn main() {
 +//     r"Hello, World!";
 +// }
 +// ```
 +pub(crate) fn remove_hash(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    let token = ctx.find_token_at_offset::<ast::String>()?;
 +    if !token.is_raw() {
 +        return None;
 +    }
 +
 +    let text = token.text();
 +    if !text.starts_with("r#") && text.ends_with('#') {
 +        return None;
 +    }
 +
 +    let existing_hashes = text.chars().skip(1).take_while(|&it| it == '#').count();
 +
 +    let text_range = token.syntax().text_range();
 +    let internal_text = &text[token.text_range_between_quotes()? - text_range.start()];
 +
 +    if existing_hashes == required_hashes(internal_text) {
 +        cov_mark::hit!(cant_remove_required_hash);
 +        return None;
 +    }
 +
 +    acc.add(AssistId("remove_hash", AssistKind::RefactorRewrite), "Remove #", text_range, |edit| {
 +        edit.delete(TextRange::at(text_range.start() + TextSize::of('r'), TextSize::of('#')));
 +        edit.delete(TextRange::new(text_range.end() - TextSize::of('#'), text_range.end()));
 +    })
 +}
 +
 +fn required_hashes(s: &str) -> usize {
 +    let mut res = 0usize;
 +    for idx in s.match_indices('"').map(|(i, _)| i) {
 +        let (_, sub) = s.split_at(idx + 1);
 +        let n_hashes = sub.chars().take_while(|c| *c == '#').count();
 +        res = res.max(n_hashes + 1)
 +    }
 +    res
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
 +
 +    use super::*;
 +
 +    #[test]
 +    fn test_required_hashes() {
 +        assert_eq!(0, required_hashes("abc"));
 +        assert_eq!(0, required_hashes("###"));
 +        assert_eq!(1, required_hashes("\""));
 +        assert_eq!(2, required_hashes("\"#abc"));
 +        assert_eq!(0, required_hashes("#abc"));
 +        assert_eq!(3, required_hashes("#ab\"##c"));
 +        assert_eq!(5, required_hashes("#ab\"##\"####c"));
 +    }
 +
 +    #[test]
 +    fn make_raw_string_target() {
 +        check_assist_target(
 +            make_raw_string,
 +            r#"
 +            fn f() {
 +                let s = $0"random\nstring";
 +            }
 +            "#,
 +            r#""random\nstring""#,
 +        );
 +    }
 +
 +    #[test]
 +    fn make_raw_string_works() {
 +        check_assist(
 +            make_raw_string,
 +            r#"
 +fn f() {
 +    let s = $0"random\nstring";
 +}
 +"#,
 +            r##"
 +fn f() {
 +    let s = r#"random
 +string"#;
 +}
 +"##,
 +        )
 +    }
 +
 +    #[test]
 +    fn make_raw_string_works_inside_macros() {
 +        check_assist(
 +            make_raw_string,
 +            r#"
 +            fn f() {
 +                format!($0"x = {}", 92)
 +            }
 +            "#,
 +            r##"
 +            fn f() {
 +                format!(r#"x = {}"#, 92)
 +            }
 +            "##,
 +        )
 +    }
 +
 +    #[test]
 +    fn make_raw_string_hashes_inside_works() {
 +        check_assist(
 +            make_raw_string,
 +            r###"
 +fn f() {
 +    let s = $0"#random##\nstring";
 +}
 +"###,
 +            r####"
 +fn f() {
 +    let s = r#"#random##
 +string"#;
 +}
 +"####,
 +        )
 +    }
 +
 +    #[test]
 +    fn make_raw_string_closing_hashes_inside_works() {
 +        check_assist(
 +            make_raw_string,
 +            r###"
 +fn f() {
 +    let s = $0"#random\"##\nstring";
 +}
 +"###,
 +            r####"
 +fn f() {
 +    let s = r###"#random"##
 +string"###;
 +}
 +"####,
 +        )
 +    }
 +
 +    #[test]
 +    fn make_raw_string_nothing_to_unescape_works() {
 +        check_assist(
 +            make_raw_string,
 +            r#"
 +            fn f() {
 +                let s = $0"random string";
 +            }
 +            "#,
 +            r##"
 +            fn f() {
 +                let s = r#"random string"#;
 +            }
 +            "##,
 +        )
 +    }
 +
 +    #[test]
 +    fn make_raw_string_not_works_on_partial_string() {
 +        check_assist_not_applicable(
 +            make_raw_string,
 +            r#"
 +            fn f() {
 +                let s = "foo$0
 +            }
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn make_usual_string_not_works_on_partial_string() {
 +        check_assist_not_applicable(
 +            make_usual_string,
 +            r#"
 +            fn main() {
 +                let s = r#"bar$0
 +            }
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn add_hash_target() {
 +        check_assist_target(
 +            add_hash,
 +            r#"
 +            fn f() {
 +                let s = $0r"random string";
 +            }
 +            "#,
 +            r#"r"random string""#,
 +        );
 +    }
 +
 +    #[test]
 +    fn add_hash_works() {
 +        check_assist(
 +            add_hash,
 +            r#"
 +            fn f() {
 +                let s = $0r"random string";
 +            }
 +            "#,
 +            r##"
 +            fn f() {
 +                let s = r#"random string"#;
 +            }
 +            "##,
 +        )
 +    }
 +
 +    #[test]
 +    fn add_more_hash_works() {
 +        check_assist(
 +            add_hash,
 +            r##"
 +            fn f() {
 +                let s = $0r#"random"string"#;
 +            }
 +            "##,
 +            r###"
 +            fn f() {
 +                let s = r##"random"string"##;
 +            }
 +            "###,
 +        )
 +    }
 +
 +    #[test]
 +    fn add_hash_not_works() {
 +        check_assist_not_applicable(
 +            add_hash,
 +            r#"
 +            fn f() {
 +                let s = $0"random string";
 +            }
 +            "#,
 +        );
 +    }
 +
 +    #[test]
 +    fn remove_hash_target() {
 +        check_assist_target(
 +            remove_hash,
 +            r##"
 +            fn f() {
 +                let s = $0r#"random string"#;
 +            }
 +            "##,
 +            r##"r#"random string"#"##,
 +        );
 +    }
 +
 +    #[test]
 +    fn remove_hash_works() {
 +        check_assist(
 +            remove_hash,
 +            r##"fn f() { let s = $0r#"random string"#; }"##,
 +            r#"fn f() { let s = r"random string"; }"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn cant_remove_required_hash() {
 +        cov_mark::check!(cant_remove_required_hash);
 +        check_assist_not_applicable(
 +            remove_hash,
 +            r##"
 +            fn f() {
 +                let s = $0r#"random"str"ing"#;
 +            }
 +            "##,
 +        )
 +    }
 +
 +    #[test]
 +    fn remove_more_hash_works() {
 +        check_assist(
 +            remove_hash,
 +            r###"
 +            fn f() {
 +                let s = $0r##"random string"##;
 +            }
 +            "###,
 +            r##"
 +            fn f() {
 +                let s = r#"random string"#;
 +            }
 +            "##,
 +        )
 +    }
 +
 +    #[test]
 +    fn remove_hash_doesnt_work() {
 +        check_assist_not_applicable(remove_hash, r#"fn f() { let s = $0"random string"; }"#);
 +    }
 +
 +    #[test]
 +    fn remove_hash_no_hash_doesnt_work() {
 +        check_assist_not_applicable(remove_hash, r#"fn f() { let s = $0r"random string"; }"#);
 +    }
 +
 +    #[test]
 +    fn make_usual_string_target() {
 +        check_assist_target(
 +            make_usual_string,
 +            r##"
 +            fn f() {
 +                let s = $0r#"random string"#;
 +            }
 +            "##,
 +            r##"r#"random string"#"##,
 +        );
 +    }
 +
 +    #[test]
 +    fn make_usual_string_works() {
 +        check_assist(
 +            make_usual_string,
 +            r##"
 +            fn f() {
 +                let s = $0r#"random string"#;
 +            }
 +            "##,
 +            r#"
 +            fn f() {
 +                let s = "random string";
 +            }
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn make_usual_string_with_quote_works() {
 +        check_assist(
 +            make_usual_string,
 +            r##"
 +            fn f() {
 +                let s = $0r#"random"str"ing"#;
 +            }
 +            "##,
 +            r#"
 +            fn f() {
 +                let s = "random\"str\"ing";
 +            }
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn make_usual_string_more_hash_works() {
 +        check_assist(
 +            make_usual_string,
 +            r###"
 +            fn f() {
 +                let s = $0r##"random string"##;
 +            }
 +            "###,
 +            r##"
 +            fn f() {
 +                let s = "random string";
 +            }
 +            "##,
 +        )
 +    }
 +
 +    #[test]
 +    fn make_usual_string_not_works() {
 +        check_assist_not_applicable(
 +            make_usual_string,
 +            r#"
 +            fn f() {
 +                let s = $0"random string";
 +            }
 +            "#,
 +        );
 +    }
 +}
index afaa7c933c73951093dea8d12d0c12557c03cc76,0000000000000000000000000000000000000000..3d9cbff177ba9c68bfed291a2c2ca58eea3c1051
mode 100644,000000..100644
--- /dev/null
@@@ -1,241 -1,0 +1,241 @@@
-                 if wrap { format!("({})", expr) } else { expr.to_string() },
 +use itertools::Itertools;
 +use syntax::{
 +    ast::{self, AstNode, AstToken},
 +    match_ast, NodeOrToken, SyntaxElement, TextSize, T,
 +};
 +
 +use crate::{AssistContext, AssistId, AssistKind, Assists};
 +
 +// Assist: remove_dbg
 +//
 +// Removes `dbg!()` macro call.
 +//
 +// ```
 +// fn main() {
 +//     $0dbg!(92);
 +// }
 +// ```
 +// ->
 +// ```
 +// fn main() {
 +//     92;
 +// }
 +// ```
 +pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    let macro_call = ctx.find_node_at_offset::<ast::MacroCall>()?;
 +    let tt = macro_call.token_tree()?;
 +    let r_delim = NodeOrToken::Token(tt.right_delimiter_token()?);
 +    if macro_call.path()?.segment()?.name_ref()?.text() != "dbg"
 +        || macro_call.excl_token().is_none()
 +    {
 +        return None;
 +    }
 +
 +    let mac_input = tt.syntax().children_with_tokens().skip(1).take_while(|it| *it != r_delim);
 +    let input_expressions = mac_input.group_by(|tok| tok.kind() == T![,]);
 +    let input_expressions = input_expressions
 +        .into_iter()
 +        .filter_map(|(is_sep, group)| (!is_sep).then(|| group))
 +        .map(|mut tokens| syntax::hacks::parse_expr_from_str(&tokens.join("")))
 +        .collect::<Option<Vec<ast::Expr>>>()?;
 +
 +    let macro_expr = ast::MacroExpr::cast(macro_call.syntax().parent()?)?;
 +    let parent = macro_expr.syntax().parent()?;
 +    let (range, text) = match &*input_expressions {
 +        // dbg!()
 +        [] => {
 +            match_ast! {
 +                match parent {
 +                    ast::StmtList(__) => {
 +                        let range = macro_expr.syntax().text_range();
 +                        let range = match whitespace_start(macro_expr.syntax().prev_sibling_or_token()) {
 +                            Some(start) => range.cover_offset(start),
 +                            None => range,
 +                        };
 +                        (range, String::new())
 +                    },
 +                    ast::ExprStmt(it) => {
 +                        let range = it.syntax().text_range();
 +                        let range = match whitespace_start(it.syntax().prev_sibling_or_token()) {
 +                            Some(start) => range.cover_offset(start),
 +                            None => range,
 +                        };
 +                        (range, String::new())
 +                    },
 +                    _ => (macro_call.syntax().text_range(), "()".to_owned())
 +                }
 +            }
 +        }
 +        // dbg!(expr0)
 +        [expr] => {
 +            let wrap = match ast::Expr::cast(parent) {
 +                Some(parent) => match (expr, parent) {
 +                    (ast::Expr::CastExpr(_), ast::Expr::CastExpr(_)) => false,
 +                    (
 +                        ast::Expr::BoxExpr(_) | ast::Expr::PrefixExpr(_) | ast::Expr::RefExpr(_),
 +                        ast::Expr::AwaitExpr(_)
 +                        | ast::Expr::CallExpr(_)
 +                        | ast::Expr::CastExpr(_)
 +                        | ast::Expr::FieldExpr(_)
 +                        | ast::Expr::IndexExpr(_)
 +                        | ast::Expr::MethodCallExpr(_)
 +                        | ast::Expr::RangeExpr(_)
 +                        | ast::Expr::TryExpr(_),
 +                    ) => true,
 +                    (
 +                        ast::Expr::BinExpr(_) | ast::Expr::CastExpr(_) | ast::Expr::RangeExpr(_),
 +                        ast::Expr::AwaitExpr(_)
 +                        | ast::Expr::BinExpr(_)
 +                        | ast::Expr::CallExpr(_)
 +                        | ast::Expr::CastExpr(_)
 +                        | ast::Expr::FieldExpr(_)
 +                        | ast::Expr::IndexExpr(_)
 +                        | ast::Expr::MethodCallExpr(_)
 +                        | ast::Expr::PrefixExpr(_)
 +                        | ast::Expr::RangeExpr(_)
 +                        | ast::Expr::RefExpr(_)
 +                        | ast::Expr::TryExpr(_),
 +                    ) => true,
 +                    _ => false,
 +                },
 +                None => false,
 +            };
 +            (
 +                macro_call.syntax().text_range(),
-             &format!("fn main() {{\n{}\n}}", ra_fixture_before),
-             &format!("fn main() {{\n{}\n}}", ra_fixture_after),
++                if wrap { format!("({expr})") } else { expr.to_string() },
 +            )
 +        }
 +        // dbg!(expr0, expr1, ...)
 +        exprs => (macro_call.syntax().text_range(), format!("({})", exprs.iter().format(", "))),
 +    };
 +
 +    acc.add(AssistId("remove_dbg", AssistKind::Refactor), "Remove dbg!()", range, |builder| {
 +        builder.replace(range, text);
 +    })
 +}
 +
 +fn whitespace_start(it: Option<SyntaxElement>) -> Option<TextSize> {
 +    Some(it?.into_token().and_then(ast::Whitespace::cast)?.syntax().text_range().start())
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use crate::tests::{check_assist, check_assist_not_applicable};
 +
 +    use super::*;
 +
 +    fn check(ra_fixture_before: &str, ra_fixture_after: &str) {
 +        check_assist(
 +            remove_dbg,
++            &format!("fn main() {{\n{ra_fixture_before}\n}}"),
++            &format!("fn main() {{\n{ra_fixture_after}\n}}"),
 +        );
 +    }
 +
 +    #[test]
 +    fn test_remove_dbg() {
 +        check("$0dbg!(1 + 1)", "1 + 1");
 +        check("dbg!$0(1 + 1)", "1 + 1");
 +        check("dbg!(1 $0+ 1)", "1 + 1");
 +        check("dbg![$01 + 1]", "1 + 1");
 +        check("dbg!{$01 + 1}", "1 + 1");
 +    }
 +
 +    #[test]
 +    fn test_remove_dbg_not_applicable() {
 +        check_assist_not_applicable(remove_dbg, "fn main() {$0vec![1, 2, 3]}");
 +        check_assist_not_applicable(remove_dbg, "fn main() {$0dbg(5, 6, 7)}");
 +        check_assist_not_applicable(remove_dbg, "fn main() {$0dbg!(5, 6, 7}");
 +    }
 +
 +    #[test]
 +    fn test_remove_dbg_keep_semicolon_in_let() {
 +        // https://github.com/rust-lang/rust-analyzer/issues/5129#issuecomment-651399779
 +        check(
 +            r#"let res = $0dbg!(1 * 20); // needless comment"#,
 +            r#"let res = 1 * 20; // needless comment"#,
 +        );
 +        check(r#"let res = $0dbg!(); // needless comment"#, r#"let res = (); // needless comment"#);
 +        check(
 +            r#"let res = $0dbg!(1, 2); // needless comment"#,
 +            r#"let res = (1, 2); // needless comment"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_remove_dbg_cast_cast() {
 +        check(r#"let res = $0dbg!(x as u32) as u32;"#, r#"let res = x as u32 as u32;"#);
 +    }
 +
 +    #[test]
 +    fn test_remove_dbg_prefix() {
 +        check(r#"let res = $0dbg!(&result).foo();"#, r#"let res = (&result).foo();"#);
 +        check(r#"let res = &$0dbg!(&result);"#, r#"let res = &&result;"#);
 +        check(r#"let res = $0dbg!(!result) && true;"#, r#"let res = !result && true;"#);
 +    }
 +
 +    #[test]
 +    fn test_remove_dbg_post_expr() {
 +        check(r#"let res = $0dbg!(fut.await).foo();"#, r#"let res = fut.await.foo();"#);
 +        check(r#"let res = $0dbg!(result?).foo();"#, r#"let res = result?.foo();"#);
 +        check(r#"let res = $0dbg!(foo as u32).foo();"#, r#"let res = (foo as u32).foo();"#);
 +        check(r#"let res = $0dbg!(array[3]).foo();"#, r#"let res = array[3].foo();"#);
 +        check(r#"let res = $0dbg!(tuple.3).foo();"#, r#"let res = tuple.3.foo();"#);
 +    }
 +
 +    #[test]
 +    fn test_remove_dbg_range_expr() {
 +        check(r#"let res = $0dbg!(foo..bar).foo();"#, r#"let res = (foo..bar).foo();"#);
 +        check(r#"let res = $0dbg!(foo..=bar).foo();"#, r#"let res = (foo..=bar).foo();"#);
 +    }
 +
 +    #[test]
 +    fn test_remove_empty_dbg() {
 +        check_assist(remove_dbg, r#"fn foo() { $0dbg!(); }"#, r#"fn foo() { }"#);
 +        check_assist(
 +            remove_dbg,
 +            r#"
 +fn foo() {
 +    $0dbg!();
 +}
 +"#,
 +            r#"
 +fn foo() {
 +}
 +"#,
 +        );
 +        check_assist(
 +            remove_dbg,
 +            r#"
 +fn foo() {
 +    let test = $0dbg!();
 +}"#,
 +            r#"
 +fn foo() {
 +    let test = ();
 +}"#,
 +        );
 +        check_assist(
 +            remove_dbg,
 +            r#"
 +fn foo() {
 +    let t = {
 +        println!("Hello, world");
 +        $0dbg!()
 +    };
 +}"#,
 +            r#"
 +fn foo() {
 +    let t = {
 +        println!("Hello, world");
 +    };
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_remove_multi_dbg() {
 +        check(r#"$0dbg!(0, 1)"#, r#"(0, 1)"#);
 +        check(r#"$0dbg!(0, (1, 2))"#, r#"(0, (1, 2))"#);
 +    }
 +}
index 9fd5e1886d206ef3dad043c6e29f2a4eb88b1219,0000000000000000000000000000000000000000..f9ba289ee175fe76d554aeb28811b774b5fc34a1
mode 100644,000000..100644
--- /dev/null
@@@ -1,1250 -1,0 +1,1247 @@@
-     let label = format!("Convert to manual `impl {} for {}`", replace_trait_path, annotated_name);
 +use hir::{InFile, ModuleDef};
 +use ide_db::{
 +    helpers::mod_path_to_ast, imports::import_assets::NameToImport, items_locator,
 +    syntax_helpers::insert_whitespace_into_node::insert_ws_into,
 +};
 +use itertools::Itertools;
 +use syntax::{
 +    ast::{self, AstNode, HasName},
 +    SyntaxKind::WHITESPACE,
 +};
 +
 +use crate::{
 +    assist_context::{AssistContext, Assists, SourceChangeBuilder},
 +    utils::{
 +        add_trait_assoc_items_to_impl, filter_assoc_items, gen_trait_fn_body,
 +        generate_trait_impl_text, render_snippet, Cursor, DefaultMethods,
 +    },
 +    AssistId, AssistKind,
 +};
 +
 +// Assist: replace_derive_with_manual_impl
 +//
 +// Converts a `derive` impl into a manual one.
 +//
 +// ```
 +// # //- minicore: derive
 +// # trait Debug { fn fmt(&self, f: &mut Formatter) -> Result<()>; }
 +// #[derive(Deb$0ug, Display)]
 +// struct S;
 +// ```
 +// ->
 +// ```
 +// # trait Debug { fn fmt(&self, f: &mut Formatter) -> Result<()>; }
 +// #[derive(Display)]
 +// struct S;
 +//
 +// impl Debug for S {
 +//     $0fn fmt(&self, f: &mut Formatter) -> Result<()> {
 +//         f.debug_struct("S").finish()
 +//     }
 +// }
 +// ```
 +pub(crate) fn replace_derive_with_manual_impl(
 +    acc: &mut Assists,
 +    ctx: &AssistContext<'_>,
 +) -> Option<()> {
 +    let attr = ctx.find_node_at_offset_with_descend::<ast::Attr>()?;
 +    let path = attr.path()?;
 +    let hir_file = ctx.sema.hir_file_for(attr.syntax());
 +    if !hir_file.is_derive_attr_pseudo_expansion(ctx.db()) {
 +        return None;
 +    }
 +
 +    let InFile { file_id, value } = hir_file.call_node(ctx.db())?;
 +    if file_id.is_macro() {
 +        // FIXME: make this work in macro files
 +        return None;
 +    }
 +    // collect the derive paths from the #[derive] expansion
 +    let current_derives = ctx
 +        .sema
 +        .parse_or_expand(hir_file)?
 +        .descendants()
 +        .filter_map(ast::Attr::cast)
 +        .filter_map(|attr| attr.path())
 +        .collect::<Vec<_>>();
 +
 +    let adt = value.parent().and_then(ast::Adt::cast)?;
 +    let attr = ast::Attr::cast(value)?;
 +    let args = attr.token_tree()?;
 +
 +    let current_module = ctx.sema.scope(adt.syntax())?.module();
 +    let current_crate = current_module.krate();
 +
 +    let found_traits = items_locator::items_with_name(
 +        &ctx.sema,
 +        current_crate,
 +        NameToImport::exact_case_sensitive(path.segments().last()?.to_string()),
 +        items_locator::AssocItemSearch::Exclude,
 +        Some(items_locator::DEFAULT_QUERY_SEARCH_LIMIT.inner()),
 +    )
 +    .filter_map(|item| match item.as_module_def()? {
 +        ModuleDef::Trait(trait_) => Some(trait_),
 +        _ => None,
 +    })
 +    .flat_map(|trait_| {
 +        current_module
 +            .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_), ctx.config.prefer_no_std)
 +            .as_ref()
 +            .map(mod_path_to_ast)
 +            .zip(Some(trait_))
 +    });
 +
 +    let mut no_traits_found = true;
 +    for (replace_trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) {
 +        add_assist(
 +            acc,
 +            ctx,
 +            &attr,
 +            &current_derives,
 +            &args,
 +            &path,
 +            &replace_trait_path,
 +            Some(trait_),
 +            &adt,
 +        )?;
 +    }
 +    if no_traits_found {
 +        add_assist(acc, ctx, &attr, &current_derives, &args, &path, &path, None, &adt)?;
 +    }
 +    Some(())
 +}
 +
 +fn add_assist(
 +    acc: &mut Assists,
 +    ctx: &AssistContext<'_>,
 +    attr: &ast::Attr,
 +    old_derives: &[ast::Path],
 +    old_tree: &ast::TokenTree,
 +    old_trait_path: &ast::Path,
 +    replace_trait_path: &ast::Path,
 +    trait_: Option<hir::Trait>,
 +    adt: &ast::Adt,
 +) -> Option<()> {
 +    let target = attr.syntax().text_range();
 +    let annotated_name = adt.name()?;
-                     builder.insert_snippet(
-                         cap,
-                         insert_pos,
-                         format!("\n\n{}", render_snippet(cap, impl_def.syntax(), cursor)),
-                     )
++    let label = format!("Convert to manual `impl {replace_trait_path} for {annotated_name}`");
 +
 +    acc.add(
 +        AssistId("replace_derive_with_manual_impl", AssistKind::Refactor),
 +        label,
 +        target,
 +        |builder| {
 +            let insert_pos = adt.syntax().text_range().end();
 +            let impl_def_with_items =
 +                impl_def_from_trait(&ctx.sema, adt, &annotated_name, trait_, replace_trait_path);
 +            update_attribute(builder, old_derives, old_tree, old_trait_path, attr);
 +            let trait_path = replace_trait_path.to_string();
 +            match (ctx.config.snippet_cap, impl_def_with_items) {
 +                (None, _) => {
 +                    builder.insert(insert_pos, generate_trait_impl_text(adt, &trait_path, ""))
 +                }
 +                (Some(cap), None) => builder.insert_snippet(
 +                    cap,
 +                    insert_pos,
 +                    generate_trait_impl_text(adt, &trait_path, "    $0"),
 +                ),
 +                (Some(cap), Some((impl_def, first_assoc_item))) => {
 +                    let mut cursor = Cursor::Before(first_assoc_item.syntax());
 +                    let placeholder;
 +                    if let ast::AssocItem::Fn(ref func) = first_assoc_item {
 +                        if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast)
 +                        {
 +                            if m.syntax().text() == "todo!()" {
 +                                placeholder = m;
 +                                cursor = Cursor::Replace(placeholder.syntax());
 +                            }
 +                        }
 +                    }
 +
++                    let rendered = render_snippet(cap, impl_def.syntax(), cursor);
++                    builder.insert_snippet(cap, insert_pos, format!("\n\n{rendered}"))
 +                }
 +            };
 +        },
 +    )
 +}
 +
 +fn impl_def_from_trait(
 +    sema: &hir::Semantics<'_, ide_db::RootDatabase>,
 +    adt: &ast::Adt,
 +    annotated_name: &ast::Name,
 +    trait_: Option<hir::Trait>,
 +    trait_path: &ast::Path,
 +) -> Option<(ast::Impl, ast::AssocItem)> {
 +    let trait_ = trait_?;
 +    let target_scope = sema.scope(annotated_name.syntax())?;
 +    let trait_items = filter_assoc_items(sema, &trait_.items(sema.db), DefaultMethods::No);
 +    if trait_items.is_empty() {
 +        return None;
 +    }
 +    let impl_def = {
 +        use syntax::ast::Impl;
 +        let text = generate_trait_impl_text(adt, trait_path.to_string().as_str(), "");
 +        let parse = syntax::SourceFile::parse(&text);
 +        let node = match parse.tree().syntax().descendants().find_map(Impl::cast) {
 +            Some(it) => it,
 +            None => {
 +                panic!(
 +                    "Failed to make ast node `{}` from text {}",
 +                    std::any::type_name::<Impl>(),
 +                    text
 +                )
 +            }
 +        };
 +        let node = node.clone_subtree();
 +        assert_eq!(node.syntax().text_range().start(), 0.into());
 +        node
 +    };
 +
 +    let trait_items = trait_items
 +        .into_iter()
 +        .map(|it| {
 +            if sema.hir_file_for(it.syntax()).is_macro() {
 +                if let Some(it) = ast::AssocItem::cast(insert_ws_into(it.syntax().clone())) {
 +                    return it;
 +                }
 +            }
 +            it.clone_for_update()
 +        })
 +        .collect();
 +    let (impl_def, first_assoc_item) =
 +        add_trait_assoc_items_to_impl(sema, trait_items, trait_, impl_def, target_scope);
 +
 +    // Generate a default `impl` function body for the derived trait.
 +    if let ast::AssocItem::Fn(ref func) = first_assoc_item {
 +        let _ = gen_trait_fn_body(func, trait_path, adt);
 +    };
 +
 +    Some((impl_def, first_assoc_item))
 +}
 +
 +fn update_attribute(
 +    builder: &mut SourceChangeBuilder,
 +    old_derives: &[ast::Path],
 +    old_tree: &ast::TokenTree,
 +    old_trait_path: &ast::Path,
 +    attr: &ast::Attr,
 +) {
 +    let new_derives = old_derives
 +        .iter()
 +        .filter(|t| t.to_string() != old_trait_path.to_string())
 +        .collect::<Vec<_>>();
 +    let has_more_derives = !new_derives.is_empty();
 +
 +    if has_more_derives {
 +        let new_derives = format!("({})", new_derives.iter().format(", "));
 +        builder.replace(old_tree.syntax().text_range(), new_derives);
 +    } else {
 +        let attr_range = attr.syntax().text_range();
 +        builder.delete(attr_range);
 +
 +        if let Some(line_break_range) = attr
 +            .syntax()
 +            .next_sibling_or_token()
 +            .filter(|t| t.kind() == WHITESPACE)
 +            .map(|t| t.text_range())
 +        {
 +            builder.delete(line_break_range);
 +        }
 +    }
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use crate::tests::{check_assist, check_assist_not_applicable};
 +
 +    use super::*;
 +
 +    #[test]
 +    fn add_custom_impl_debug_record_struct() {
 +        check_assist(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: fmt, derive
 +#[derive(Debu$0g)]
 +struct Foo {
 +    bar: String,
 +}
 +"#,
 +            r#"
 +struct Foo {
 +    bar: String,
 +}
 +
 +impl core::fmt::Debug for Foo {
 +    $0fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
 +        f.debug_struct("Foo").field("bar", &self.bar).finish()
 +    }
 +}
 +"#,
 +        )
 +    }
 +    #[test]
 +    fn add_custom_impl_debug_tuple_struct() {
 +        check_assist(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: fmt, derive
 +#[derive(Debu$0g)]
 +struct Foo(String, usize);
 +"#,
 +            r#"struct Foo(String, usize);
 +
 +impl core::fmt::Debug for Foo {
 +    $0fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
 +        f.debug_tuple("Foo").field(&self.0).field(&self.1).finish()
 +    }
 +}
 +"#,
 +        )
 +    }
 +    #[test]
 +    fn add_custom_impl_debug_empty_struct() {
 +        check_assist(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: fmt, derive
 +#[derive(Debu$0g)]
 +struct Foo;
 +"#,
 +            r#"
 +struct Foo;
 +
 +impl core::fmt::Debug for Foo {
 +    $0fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
 +        f.debug_struct("Foo").finish()
 +    }
 +}
 +"#,
 +        )
 +    }
 +    #[test]
 +    fn add_custom_impl_debug_enum() {
 +        check_assist(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: fmt, derive
 +#[derive(Debu$0g)]
 +enum Foo {
 +    Bar,
 +    Baz,
 +}
 +"#,
 +            r#"
 +enum Foo {
 +    Bar,
 +    Baz,
 +}
 +
 +impl core::fmt::Debug for Foo {
 +    $0fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
 +        match self {
 +            Self::Bar => write!(f, "Bar"),
 +            Self::Baz => write!(f, "Baz"),
 +        }
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn add_custom_impl_debug_tuple_enum() {
 +        check_assist(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: fmt, derive
 +#[derive(Debu$0g)]
 +enum Foo {
 +    Bar(usize, usize),
 +    Baz,
 +}
 +"#,
 +            r#"
 +enum Foo {
 +    Bar(usize, usize),
 +    Baz,
 +}
 +
 +impl core::fmt::Debug for Foo {
 +    $0fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
 +        match self {
 +            Self::Bar(arg0, arg1) => f.debug_tuple("Bar").field(arg0).field(arg1).finish(),
 +            Self::Baz => write!(f, "Baz"),
 +        }
 +    }
 +}
 +"#,
 +        )
 +    }
 +    #[test]
 +    fn add_custom_impl_debug_record_enum() {
 +        check_assist(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: fmt, derive
 +#[derive(Debu$0g)]
 +enum Foo {
 +    Bar {
 +        baz: usize,
 +        qux: usize,
 +    },
 +    Baz,
 +}
 +"#,
 +            r#"
 +enum Foo {
 +    Bar {
 +        baz: usize,
 +        qux: usize,
 +    },
 +    Baz,
 +}
 +
 +impl core::fmt::Debug for Foo {
 +    $0fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
 +        match self {
 +            Self::Bar { baz, qux } => f.debug_struct("Bar").field("baz", baz).field("qux", qux).finish(),
 +            Self::Baz => write!(f, "Baz"),
 +        }
 +    }
 +}
 +"#,
 +        )
 +    }
 +    #[test]
 +    fn add_custom_impl_default_record_struct() {
 +        check_assist(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: default, derive
 +#[derive(Defau$0lt)]
 +struct Foo {
 +    foo: usize,
 +}
 +"#,
 +            r#"
 +struct Foo {
 +    foo: usize,
 +}
 +
 +impl Default for Foo {
 +    $0fn default() -> Self {
 +        Self { foo: Default::default() }
 +    }
 +}
 +"#,
 +        )
 +    }
 +    #[test]
 +    fn add_custom_impl_default_tuple_struct() {
 +        check_assist(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: default, derive
 +#[derive(Defau$0lt)]
 +struct Foo(usize);
 +"#,
 +            r#"
 +struct Foo(usize);
 +
 +impl Default for Foo {
 +    $0fn default() -> Self {
 +        Self(Default::default())
 +    }
 +}
 +"#,
 +        )
 +    }
 +    #[test]
 +    fn add_custom_impl_default_empty_struct() {
 +        check_assist(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: default, derive
 +#[derive(Defau$0lt)]
 +struct Foo;
 +"#,
 +            r#"
 +struct Foo;
 +
 +impl Default for Foo {
 +    $0fn default() -> Self {
 +        Self {  }
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn add_custom_impl_hash_record_struct() {
 +        check_assist(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: hash, derive
 +#[derive(Has$0h)]
 +struct Foo {
 +    bin: usize,
 +    bar: usize,
 +}
 +"#,
 +            r#"
 +struct Foo {
 +    bin: usize,
 +    bar: usize,
 +}
 +
 +impl core::hash::Hash for Foo {
 +    $0fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
 +        self.bin.hash(state);
 +        self.bar.hash(state);
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn add_custom_impl_hash_tuple_struct() {
 +        check_assist(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: hash, derive
 +#[derive(Has$0h)]
 +struct Foo(usize, usize);
 +"#,
 +            r#"
 +struct Foo(usize, usize);
 +
 +impl core::hash::Hash for Foo {
 +    $0fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
 +        self.0.hash(state);
 +        self.1.hash(state);
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn add_custom_impl_hash_enum() {
 +        check_assist(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: hash, derive
 +#[derive(Has$0h)]
 +enum Foo {
 +    Bar,
 +    Baz,
 +}
 +"#,
 +            r#"
 +enum Foo {
 +    Bar,
 +    Baz,
 +}
 +
 +impl core::hash::Hash for Foo {
 +    $0fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
 +        core::mem::discriminant(self).hash(state);
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn add_custom_impl_clone_record_struct() {
 +        check_assist(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: clone, derive
 +#[derive(Clo$0ne)]
 +struct Foo {
 +    bin: usize,
 +    bar: usize,
 +}
 +"#,
 +            r#"
 +struct Foo {
 +    bin: usize,
 +    bar: usize,
 +}
 +
 +impl Clone for Foo {
 +    $0fn clone(&self) -> Self {
 +        Self { bin: self.bin.clone(), bar: self.bar.clone() }
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn add_custom_impl_clone_tuple_struct() {
 +        check_assist(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: clone, derive
 +#[derive(Clo$0ne)]
 +struct Foo(usize, usize);
 +"#,
 +            r#"
 +struct Foo(usize, usize);
 +
 +impl Clone for Foo {
 +    $0fn clone(&self) -> Self {
 +        Self(self.0.clone(), self.1.clone())
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn add_custom_impl_clone_empty_struct() {
 +        check_assist(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: clone, derive
 +#[derive(Clo$0ne)]
 +struct Foo;
 +"#,
 +            r#"
 +struct Foo;
 +
 +impl Clone for Foo {
 +    $0fn clone(&self) -> Self {
 +        Self {  }
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn add_custom_impl_clone_enum() {
 +        check_assist(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: clone, derive
 +#[derive(Clo$0ne)]
 +enum Foo {
 +    Bar,
 +    Baz,
 +}
 +"#,
 +            r#"
 +enum Foo {
 +    Bar,
 +    Baz,
 +}
 +
 +impl Clone for Foo {
 +    $0fn clone(&self) -> Self {
 +        match self {
 +            Self::Bar => Self::Bar,
 +            Self::Baz => Self::Baz,
 +        }
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn add_custom_impl_clone_tuple_enum() {
 +        check_assist(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: clone, derive
 +#[derive(Clo$0ne)]
 +enum Foo {
 +    Bar(String),
 +    Baz,
 +}
 +"#,
 +            r#"
 +enum Foo {
 +    Bar(String),
 +    Baz,
 +}
 +
 +impl Clone for Foo {
 +    $0fn clone(&self) -> Self {
 +        match self {
 +            Self::Bar(arg0) => Self::Bar(arg0.clone()),
 +            Self::Baz => Self::Baz,
 +        }
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn add_custom_impl_clone_record_enum() {
 +        check_assist(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: clone, derive
 +#[derive(Clo$0ne)]
 +enum Foo {
 +    Bar {
 +        bin: String,
 +    },
 +    Baz,
 +}
 +"#,
 +            r#"
 +enum Foo {
 +    Bar {
 +        bin: String,
 +    },
 +    Baz,
 +}
 +
 +impl Clone for Foo {
 +    $0fn clone(&self) -> Self {
 +        match self {
 +            Self::Bar { bin } => Self::Bar { bin: bin.clone() },
 +            Self::Baz => Self::Baz,
 +        }
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn add_custom_impl_partial_ord_record_struct() {
 +        check_assist(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: ord, derive
 +#[derive(Partial$0Ord)]
 +struct Foo {
 +    bin: usize,
 +}
 +"#,
 +            r#"
 +struct Foo {
 +    bin: usize,
 +}
 +
 +impl PartialOrd for Foo {
 +    $0fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
 +        self.bin.partial_cmp(&other.bin)
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn add_custom_impl_partial_ord_record_struct_multi_field() {
 +        check_assist(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: ord, derive
 +#[derive(Partial$0Ord)]
 +struct Foo {
 +    bin: usize,
 +    bar: usize,
 +    baz: usize,
 +}
 +"#,
 +            r#"
 +struct Foo {
 +    bin: usize,
 +    bar: usize,
 +    baz: usize,
 +}
 +
 +impl PartialOrd for Foo {
 +    $0fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
 +        match self.bin.partial_cmp(&other.bin) {
 +            Some(core::cmp::Ordering::Equal) => {}
 +            ord => return ord,
 +        }
 +        match self.bar.partial_cmp(&other.bar) {
 +            Some(core::cmp::Ordering::Equal) => {}
 +            ord => return ord,
 +        }
 +        self.baz.partial_cmp(&other.baz)
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn add_custom_impl_partial_ord_tuple_struct() {
 +        check_assist(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: ord, derive
 +#[derive(Partial$0Ord)]
 +struct Foo(usize, usize, usize);
 +"#,
 +            r#"
 +struct Foo(usize, usize, usize);
 +
 +impl PartialOrd for Foo {
 +    $0fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
 +        match self.0.partial_cmp(&other.0) {
 +            Some(core::cmp::Ordering::Equal) => {}
 +            ord => return ord,
 +        }
 +        match self.1.partial_cmp(&other.1) {
 +            Some(core::cmp::Ordering::Equal) => {}
 +            ord => return ord,
 +        }
 +        self.2.partial_cmp(&other.2)
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn add_custom_impl_partial_eq_record_struct() {
 +        check_assist(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: eq, derive
 +#[derive(Partial$0Eq)]
 +struct Foo {
 +    bin: usize,
 +    bar: usize,
 +}
 +"#,
 +            r#"
 +struct Foo {
 +    bin: usize,
 +    bar: usize,
 +}
 +
 +impl PartialEq for Foo {
 +    $0fn eq(&self, other: &Self) -> bool {
 +        self.bin == other.bin && self.bar == other.bar
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn add_custom_impl_partial_eq_tuple_struct() {
 +        check_assist(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: eq, derive
 +#[derive(Partial$0Eq)]
 +struct Foo(usize, usize);
 +"#,
 +            r#"
 +struct Foo(usize, usize);
 +
 +impl PartialEq for Foo {
 +    $0fn eq(&self, other: &Self) -> bool {
 +        self.0 == other.0 && self.1 == other.1
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn add_custom_impl_partial_eq_empty_struct() {
 +        check_assist(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: eq, derive
 +#[derive(Partial$0Eq)]
 +struct Foo;
 +"#,
 +            r#"
 +struct Foo;
 +
 +impl PartialEq for Foo {
 +    $0fn eq(&self, other: &Self) -> bool {
 +        true
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn add_custom_impl_partial_eq_enum() {
 +        check_assist(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: eq, derive
 +#[derive(Partial$0Eq)]
 +enum Foo {
 +    Bar,
 +    Baz,
 +}
 +"#,
 +            r#"
 +enum Foo {
 +    Bar,
 +    Baz,
 +}
 +
 +impl PartialEq for Foo {
 +    $0fn eq(&self, other: &Self) -> bool {
 +        core::mem::discriminant(self) == core::mem::discriminant(other)
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn add_custom_impl_partial_eq_tuple_enum() {
 +        check_assist(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: eq, derive
 +#[derive(Partial$0Eq)]
 +enum Foo {
 +    Bar(String),
 +    Baz,
 +}
 +"#,
 +            r#"
 +enum Foo {
 +    Bar(String),
 +    Baz,
 +}
 +
 +impl PartialEq for Foo {
 +    $0fn eq(&self, other: &Self) -> bool {
 +        match (self, other) {
 +            (Self::Bar(l0), Self::Bar(r0)) => l0 == r0,
 +            _ => core::mem::discriminant(self) == core::mem::discriminant(other),
 +        }
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn add_custom_impl_partial_eq_record_enum() {
 +        check_assist(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: eq, derive
 +#[derive(Partial$0Eq)]
 +enum Foo {
 +    Bar {
 +        bin: String,
 +    },
 +    Baz {
 +        qux: String,
 +        fez: String,
 +    },
 +    Qux {},
 +    Bin,
 +}
 +"#,
 +            r#"
 +enum Foo {
 +    Bar {
 +        bin: String,
 +    },
 +    Baz {
 +        qux: String,
 +        fez: String,
 +    },
 +    Qux {},
 +    Bin,
 +}
 +
 +impl PartialEq for Foo {
 +    $0fn eq(&self, other: &Self) -> bool {
 +        match (self, other) {
 +            (Self::Bar { bin: l_bin }, Self::Bar { bin: r_bin }) => l_bin == r_bin,
 +            (Self::Baz { qux: l_qux, fez: l_fez }, Self::Baz { qux: r_qux, fez: r_fez }) => l_qux == r_qux && l_fez == r_fez,
 +            _ => core::mem::discriminant(self) == core::mem::discriminant(other),
 +        }
 +    }
 +}
 +"#,
 +        )
 +    }
 +    #[test]
 +    fn add_custom_impl_all() {
 +        check_assist(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: derive
 +mod foo {
 +    pub trait Bar {
 +        type Qux;
 +        const Baz: usize = 42;
 +        const Fez: usize;
 +        fn foo();
 +        fn bar() {}
 +    }
 +}
 +
 +#[derive($0Bar)]
 +struct Foo {
 +    bar: String,
 +}
 +"#,
 +            r#"
 +mod foo {
 +    pub trait Bar {
 +        type Qux;
 +        const Baz: usize = 42;
 +        const Fez: usize;
 +        fn foo();
 +        fn bar() {}
 +    }
 +}
 +
 +struct Foo {
 +    bar: String,
 +}
 +
 +impl foo::Bar for Foo {
 +    $0type Qux;
 +
 +    const Baz: usize = 42;
 +
 +    const Fez: usize;
 +
 +    fn foo() {
 +        todo!()
 +    }
 +}
 +"#,
 +        )
 +    }
 +    #[test]
 +    fn add_custom_impl_for_unique_input_unknown() {
 +        check_assist(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: derive
 +#[derive(Debu$0g)]
 +struct Foo {
 +    bar: String,
 +}
 +            "#,
 +            r#"
 +struct Foo {
 +    bar: String,
 +}
 +
 +impl Debug for Foo {
 +    $0
 +}
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn add_custom_impl_for_with_visibility_modifier() {
 +        check_assist(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: derive
 +#[derive(Debug$0)]
 +pub struct Foo {
 +    bar: String,
 +}
 +            "#,
 +            r#"
 +pub struct Foo {
 +    bar: String,
 +}
 +
 +impl Debug for Foo {
 +    $0
 +}
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn add_custom_impl_when_multiple_inputs() {
 +        check_assist(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: derive
 +#[derive(Display, Debug$0, Serialize)]
 +struct Foo {}
 +            "#,
 +            r#"
 +#[derive(Display, Serialize)]
 +struct Foo {}
 +
 +impl Debug for Foo {
 +    $0
 +}
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn add_custom_impl_default_generic_record_struct() {
 +        check_assist(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: default, derive
 +#[derive(Defau$0lt)]
 +struct Foo<T, U> {
 +    foo: T,
 +    bar: U,
 +}
 +"#,
 +            r#"
 +struct Foo<T, U> {
 +    foo: T,
 +    bar: U,
 +}
 +
 +impl<T, U> Default for Foo<T, U> {
 +    $0fn default() -> Self {
 +        Self { foo: Default::default(), bar: Default::default() }
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn add_custom_impl_clone_generic_tuple_struct_with_bounds() {
 +        check_assist(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: clone, derive
 +#[derive(Clo$0ne)]
 +struct Foo<T: Clone>(T, usize);
 +"#,
 +            r#"
 +struct Foo<T: Clone>(T, usize);
 +
 +impl<T: Clone> Clone for Foo<T> {
 +    $0fn clone(&self) -> Self {
 +        Self(self.0.clone(), self.1.clone())
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn test_ignore_derive_macro_without_input() {
 +        check_assist_not_applicable(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: derive
 +#[derive($0)]
 +struct Foo {}
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn test_ignore_if_cursor_on_param() {
 +        check_assist_not_applicable(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: derive, fmt
 +#[derive$0(Debug)]
 +struct Foo {}
 +            "#,
 +        );
 +
 +        check_assist_not_applicable(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: derive, fmt
 +#[derive(Debug)$0]
 +struct Foo {}
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn test_ignore_if_not_derive() {
 +        check_assist_not_applicable(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: derive
 +#[allow(non_camel_$0case_types)]
 +struct Foo {}
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn works_at_start_of_file() {
 +        check_assist_not_applicable(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: derive, fmt
 +$0#[derive(Debug)]
 +struct S;
 +            "#,
 +        );
 +    }
 +
 +    #[test]
 +    fn add_custom_impl_keep_path() {
 +        check_assist(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: clone, derive
 +#[derive(std::fmt::Debug, Clo$0ne)]
 +pub struct Foo;
 +"#,
 +            r#"
 +#[derive(std::fmt::Debug)]
 +pub struct Foo;
 +
 +impl Clone for Foo {
 +    $0fn clone(&self) -> Self {
 +        Self {  }
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn add_custom_impl_replace_path() {
 +        check_assist(
 +            replace_derive_with_manual_impl,
 +            r#"
 +//- minicore: fmt, derive
 +#[derive(core::fmt::Deb$0ug, Clone)]
 +pub struct Foo;
 +"#,
 +            r#"
 +#[derive(Clone)]
 +pub struct Foo;
 +
 +impl core::fmt::Debug for Foo {
 +    $0fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
 +        f.debug_struct("Foo").finish()
 +    }
 +}
 +"#,
 +        )
 +    }
 +}
index 7d91be621013653aca193b1f23fc8a0469eaf376,0000000000000000000000000000000000000000..77382056c18339eb8d29335c5aa50d2cc8d58106
mode 100644,000000..100644
--- /dev/null
@@@ -1,364 -1,0 +1,364 @@@
-         format!("Replace {} with {}", name.text(), replace),
 +use ide_db::{
 +    assists::{AssistId, AssistKind},
 +    famous_defs::FamousDefs,
 +};
 +use syntax::{
 +    ast::{self, make, Expr, HasArgList},
 +    AstNode,
 +};
 +
 +use crate::{AssistContext, Assists};
 +
 +// Assist: replace_or_with_or_else
 +//
 +// Replace `unwrap_or` with `unwrap_or_else` and `ok_or` with `ok_or_else`.
 +//
 +// ```
 +// # //- minicore:option
 +// fn foo() {
 +//     let a = Some(1);
 +//     a.unwra$0p_or(2);
 +// }
 +// ```
 +// ->
 +// ```
 +// fn foo() {
 +//     let a = Some(1);
 +//     a.unwrap_or_else(|| 2);
 +// }
 +// ```
 +pub(crate) fn replace_or_with_or_else(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    let call: ast::MethodCallExpr = ctx.find_node_at_offset()?;
 +
 +    let kind = is_option_or_result(call.receiver()?, ctx)?;
 +
 +    let (name, arg_list) = (call.name_ref()?, call.arg_list()?);
 +
 +    let mut map_or = false;
 +
 +    let replace = match &*name.text() {
 +        "unwrap_or" => "unwrap_or_else".to_string(),
 +        "or" => "or_else".to_string(),
 +        "ok_or" if kind == Kind::Option => "ok_or_else".to_string(),
 +        "map_or" => {
 +            map_or = true;
 +            "map_or_else".to_string()
 +        }
 +        _ => return None,
 +    };
 +
 +    let arg = match arg_list.args().collect::<Vec<_>>().as_slice() {
 +        [] => make::arg_list(Vec::new()),
 +        [first] => {
 +            let param = into_closure(first);
 +            make::arg_list(vec![param])
 +        }
 +        [first, second] if map_or => {
 +            let param = into_closure(first);
 +            make::arg_list(vec![param, second.clone()])
 +        }
 +        _ => return None,
 +    };
 +
 +    acc.add(
 +        AssistId("replace_or_with_or_else", AssistKind::RefactorRewrite),
-         format!("Replace {} with {}", name.text(), replace),
++        format!("Replace {name} with {replace}"),
 +        call.syntax().text_range(),
 +        |builder| {
 +            builder.replace(name.syntax().text_range(), replace);
 +            builder.replace_ast(arg_list, arg)
 +        },
 +    )
 +}
 +
 +fn into_closure(param: &Expr) -> Expr {
 +    (|| {
 +        if let ast::Expr::CallExpr(call) = param {
 +            if call.arg_list()?.args().count() == 0 {
 +                Some(call.expr()?.clone())
 +            } else {
 +                None
 +            }
 +        } else {
 +            None
 +        }
 +    })()
 +    .unwrap_or_else(|| make::expr_closure(None, param.clone()))
 +}
 +
 +// Assist: replace_or_else_with_or
 +//
 +// Replace `unwrap_or_else` with `unwrap_or` and `ok_or_else` with `ok_or`.
 +//
 +// ```
 +// # //- minicore:option
 +// fn foo() {
 +//     let a = Some(1);
 +//     a.unwra$0p_or_else(|| 2);
 +// }
 +// ```
 +// ->
 +// ```
 +// fn foo() {
 +//     let a = Some(1);
 +//     a.unwrap_or(2);
 +// }
 +// ```
 +pub(crate) fn replace_or_else_with_or(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    let call: ast::MethodCallExpr = ctx.find_node_at_offset()?;
 +
 +    let kind = is_option_or_result(call.receiver()?, ctx)?;
 +
 +    let (name, arg_list) = (call.name_ref()?, call.arg_list()?);
 +
 +    let mut map_or = false;
 +    let replace = match &*name.text() {
 +        "unwrap_or_else" => "unwrap_or".to_string(),
 +        "or_else" => "or".to_string(),
 +        "ok_or_else" if kind == Kind::Option => "ok_or".to_string(),
 +        "map_or_else" => {
 +            map_or = true;
 +            "map_or".to_string()
 +        }
 +        _ => return None,
 +    };
 +
 +    let arg = match arg_list.args().collect::<Vec<_>>().as_slice() {
 +        [] => make::arg_list(Vec::new()),
 +        [first] => {
 +            let param = into_call(first);
 +            make::arg_list(vec![param])
 +        }
 +        [first, second] if map_or => {
 +            let param = into_call(first);
 +            make::arg_list(vec![param, second.clone()])
 +        }
 +        _ => return None,
 +    };
 +
 +    acc.add(
 +        AssistId("replace_or_else_with_or", AssistKind::RefactorRewrite),
++        format!("Replace {name} with {replace}"),
 +        call.syntax().text_range(),
 +        |builder| {
 +            builder.replace(name.syntax().text_range(), replace);
 +            builder.replace_ast(arg_list, arg)
 +        },
 +    )
 +}
 +
 +fn into_call(param: &Expr) -> Expr {
 +    (|| {
 +        if let ast::Expr::ClosureExpr(closure) = param {
 +            if closure.param_list()?.params().count() == 0 {
 +                Some(closure.body()?.clone())
 +            } else {
 +                None
 +            }
 +        } else {
 +            None
 +        }
 +    })()
 +    .unwrap_or_else(|| make::expr_call(param.clone(), make::arg_list(Vec::new())))
 +}
 +
 +#[derive(PartialEq, Eq)]
 +enum Kind {
 +    Option,
 +    Result,
 +}
 +
 +fn is_option_or_result(receiver: Expr, ctx: &AssistContext<'_>) -> Option<Kind> {
 +    let ty = ctx.sema.type_of_expr(&receiver)?.adjusted().as_adt()?.as_enum()?;
 +    let option_enum =
 +        FamousDefs(&ctx.sema, ctx.sema.scope(receiver.syntax())?.krate()).core_option_Option();
 +
 +    if let Some(option_enum) = option_enum {
 +        if ty == option_enum {
 +            return Some(Kind::Option);
 +        }
 +    }
 +
 +    let result_enum =
 +        FamousDefs(&ctx.sema, ctx.sema.scope(receiver.syntax())?.krate()).core_result_Result();
 +
 +    if let Some(result_enum) = result_enum {
 +        if ty == result_enum {
 +            return Some(Kind::Result);
 +        }
 +    }
 +
 +    None
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use crate::tests::{check_assist, check_assist_not_applicable};
 +
 +    use super::*;
 +
 +    #[test]
 +    fn replace_or_with_or_else_simple() {
 +        check_assist(
 +            replace_or_with_or_else,
 +            r#"
 +//- minicore: option
 +fn foo() {
 +    let foo = Some(1);
 +    return foo.unwrap_$0or(2);
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    let foo = Some(1);
 +    return foo.unwrap_or_else(|| 2);
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn replace_or_with_or_else_call() {
 +        check_assist(
 +            replace_or_with_or_else,
 +            r#"
 +//- minicore: option
 +fn foo() {
 +    let foo = Some(1);
 +    return foo.unwrap_$0or(x());
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    let foo = Some(1);
 +    return foo.unwrap_or_else(x);
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn replace_or_with_or_else_block() {
 +        check_assist(
 +            replace_or_with_or_else,
 +            r#"
 +//- minicore: option
 +fn foo() {
 +    let foo = Some(1);
 +    return foo.unwrap_$0or({
 +        let mut x = bar();
 +        for i in 0..10 {
 +            x += i;
 +        }
 +        x
 +    });
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    let foo = Some(1);
 +    return foo.unwrap_or_else(|| {
 +        let mut x = bar();
 +        for i in 0..10 {
 +            x += i;
 +        }
 +        x
 +    });
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn replace_or_else_with_or_simple() {
 +        check_assist(
 +            replace_or_else_with_or,
 +            r#"
 +//- minicore: option
 +fn foo() {
 +    let foo = Some(1);
 +    return foo.unwrap_$0or_else(|| 2);
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    let foo = Some(1);
 +    return foo.unwrap_or(2);
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn replace_or_else_with_or_call() {
 +        check_assist(
 +            replace_or_else_with_or,
 +            r#"
 +//- minicore: option
 +fn foo() {
 +    let foo = Some(1);
 +    return foo.unwrap_$0or_else(x);
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    let foo = Some(1);
 +    return foo.unwrap_or(x());
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn replace_or_else_with_or_result() {
 +        check_assist(
 +            replace_or_else_with_or,
 +            r#"
 +//- minicore: result
 +fn foo() {
 +    let foo = Ok(1);
 +    return foo.unwrap_$0or_else(x);
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    let foo = Ok(1);
 +    return foo.unwrap_or(x());
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn replace_or_else_with_or_map() {
 +        check_assist(
 +            replace_or_else_with_or,
 +            r#"
 +//- minicore: result
 +fn foo() {
 +    let foo = Ok("foo");
 +    return foo.map$0_or_else(|| 42, |v| v.len());
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    let foo = Ok("foo");
 +    return foo.map_or(42, |v| v.len());
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn replace_or_else_with_or_not_applicable() {
 +        check_assist_not_applicable(
 +            replace_or_else_with_or,
 +            r#"
 +fn foo() {
 +    let foo = Ok(1);
 +    return foo.unwrap_$0or_else(x);
 +}
 +"#,
 +        )
 +    }
 +}
index 521447c26dfbed01d20ac10592fd127a286f16ec,0000000000000000000000000000000000000000..c177adc7a10d73a78cb034e8388f4e0200d79852
mode 100644,000000..100644
--- /dev/null
@@@ -1,365 -1,0 +1,365 @@@
-                 builder.insert(ident_range.end(), format!(": {}", returned_type));
 +use hir::HirDisplay;
 +use syntax::{
 +    ast::{Expr, GenericArg, GenericArgList},
 +    ast::{LetStmt, Type::InferType},
 +    AstNode, TextRange,
 +};
 +
 +use crate::{
 +    assist_context::{AssistContext, Assists},
 +    AssistId, AssistKind,
 +};
 +
 +// Assist: replace_turbofish_with_explicit_type
 +//
 +// Converts `::<_>` to an explicit type assignment.
 +//
 +// ```
 +// fn make<T>() -> T { ) }
 +// fn main() {
 +//     let a = make$0::<i32>();
 +// }
 +// ```
 +// ->
 +// ```
 +// fn make<T>() -> T { ) }
 +// fn main() {
 +//     let a: i32 = make();
 +// }
 +// ```
 +pub(crate) fn replace_turbofish_with_explicit_type(
 +    acc: &mut Assists,
 +    ctx: &AssistContext<'_>,
 +) -> Option<()> {
 +    let let_stmt = ctx.find_node_at_offset::<LetStmt>()?;
 +
 +    let initializer = let_stmt.initializer()?;
 +
 +    let generic_args = generic_arg_list(&initializer)?;
 +
 +    // Find range of ::<_>
 +    let colon2 = generic_args.coloncolon_token()?;
 +    let r_angle = generic_args.r_angle_token()?;
 +    let turbofish_range = TextRange::new(colon2.text_range().start(), r_angle.text_range().end());
 +
 +    let turbofish_args: Vec<GenericArg> = generic_args.generic_args().into_iter().collect();
 +
 +    // Find type of ::<_>
 +    if turbofish_args.len() != 1 {
 +        cov_mark::hit!(not_applicable_if_not_single_arg);
 +        return None;
 +    }
 +
 +    // An improvement would be to check that this is correctly part of the return value of the
 +    // function call, or sub in the actual return type.
 +    let returned_type = match ctx.sema.type_of_expr(&initializer) {
 +        Some(returned_type) if !returned_type.original.contains_unknown() => {
 +            let module = ctx.sema.scope(let_stmt.syntax())?.module();
 +            returned_type.original.display_source_code(ctx.db(), module.into()).ok()?
 +        }
 +        _ => {
 +            cov_mark::hit!(fallback_to_turbofish_type_if_type_info_not_available);
 +            turbofish_args[0].to_string()
 +        }
 +    };
 +
 +    let initializer_start = initializer.syntax().text_range().start();
 +    if ctx.offset() > turbofish_range.end() || ctx.offset() < initializer_start {
 +        cov_mark::hit!(not_applicable_outside_turbofish);
 +        return None;
 +    }
 +
 +    if let None = let_stmt.colon_token() {
 +        // If there's no colon in a let statement, then there is no explicit type.
 +        // let x = fn::<...>();
 +        let ident_range = let_stmt.pat()?.syntax().text_range();
 +
 +        return acc.add(
 +            AssistId("replace_turbofish_with_explicit_type", AssistKind::RefactorRewrite),
 +            "Replace turbofish with explicit type",
 +            TextRange::new(initializer_start, turbofish_range.end()),
 +            |builder| {
++                builder.insert(ident_range.end(), format!(": {returned_type}"));
 +                builder.delete(turbofish_range);
 +            },
 +        );
 +    } else if let Some(InferType(t)) = let_stmt.ty() {
 +        // If there's a type inference underscore, we can offer to replace it with the type in
 +        // the turbofish.
 +        // let x: _ = fn::<...>();
 +        let underscore_range = t.syntax().text_range();
 +
 +        return acc.add(
 +            AssistId("replace_turbofish_with_explicit_type", AssistKind::RefactorRewrite),
 +            "Replace `_` with turbofish type",
 +            turbofish_range,
 +            |builder| {
 +                builder.replace(underscore_range, returned_type);
 +                builder.delete(turbofish_range);
 +            },
 +        );
 +    }
 +
 +    None
 +}
 +
 +fn generic_arg_list(expr: &Expr) -> Option<GenericArgList> {
 +    match expr {
 +        Expr::MethodCallExpr(expr) => expr.generic_arg_list(),
 +        Expr::CallExpr(expr) => {
 +            if let Expr::PathExpr(pe) = expr.expr()? {
 +                pe.path()?.segment()?.generic_arg_list()
 +            } else {
 +                cov_mark::hit!(not_applicable_if_non_path_function_call);
 +                return None;
 +            }
 +        }
 +        Expr::AwaitExpr(expr) => generic_arg_list(&expr.expr()?),
 +        Expr::TryExpr(expr) => generic_arg_list(&expr.expr()?),
 +        _ => {
 +            cov_mark::hit!(not_applicable_if_non_function_call_initializer);
 +            None
 +        }
 +    }
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use super::*;
 +
 +    use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
 +
 +    #[test]
 +    fn replaces_turbofish_for_vec_string() {
 +        cov_mark::check!(fallback_to_turbofish_type_if_type_info_not_available);
 +        check_assist(
 +            replace_turbofish_with_explicit_type,
 +            r#"
 +fn make<T>() -> T {}
 +fn main() {
 +    let a = make$0::<Vec<String>>();
 +}
 +"#,
 +            r#"
 +fn make<T>() -> T {}
 +fn main() {
 +    let a: Vec<String> = make();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn replaces_method_calls() {
 +        // foo.make() is a method call which uses a different expr in the let initializer
 +        cov_mark::check!(fallback_to_turbofish_type_if_type_info_not_available);
 +        check_assist(
 +            replace_turbofish_with_explicit_type,
 +            r#"
 +fn make<T>() -> T {}
 +fn main() {
 +    let a = foo.make$0::<Vec<String>>();
 +}
 +"#,
 +            r#"
 +fn make<T>() -> T {}
 +fn main() {
 +    let a: Vec<String> = foo.make();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn replace_turbofish_target() {
 +        check_assist_target(
 +            replace_turbofish_with_explicit_type,
 +            r#"
 +fn make<T>() -> T {}
 +fn main() {
 +    let a = $0make::<Vec<String>>();
 +}
 +"#,
 +            r#"make::<Vec<String>>"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn not_applicable_outside_turbofish() {
 +        cov_mark::check!(not_applicable_outside_turbofish);
 +        check_assist_not_applicable(
 +            replace_turbofish_with_explicit_type,
 +            r#"
 +fn make<T>() -> T {}
 +fn main() {
 +    let $0a = make::<Vec<String>>();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn replace_inferred_type_placeholder() {
 +        check_assist(
 +            replace_turbofish_with_explicit_type,
 +            r#"
 +fn make<T>() -> T {}
 +fn main() {
 +    let a: _ = make$0::<Vec<String>>();
 +}
 +"#,
 +            r#"
 +fn make<T>() -> T {}
 +fn main() {
 +    let a: Vec<String> = make();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn not_applicable_constant_initializer() {
 +        cov_mark::check!(not_applicable_if_non_function_call_initializer);
 +        check_assist_not_applicable(
 +            replace_turbofish_with_explicit_type,
 +            r#"
 +fn make<T>() -> T {}
 +fn main() {
 +    let a = "foo"$0;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn not_applicable_non_path_function_call() {
 +        cov_mark::check!(not_applicable_if_non_path_function_call);
 +        check_assist_not_applicable(
 +            replace_turbofish_with_explicit_type,
 +            r#"
 +fn make<T>() -> T {}
 +fn main() {
 +    $0let a = (|| {})();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn non_applicable_multiple_generic_args() {
 +        cov_mark::check!(not_applicable_if_not_single_arg);
 +        check_assist_not_applicable(
 +            replace_turbofish_with_explicit_type,
 +            r#"
 +fn make<T>() -> T {}
 +fn main() {
 +    let a = make$0::<Vec<String>, i32>();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn replaces_turbofish_for_known_type() {
 +        check_assist(
 +            replace_turbofish_with_explicit_type,
 +            r#"
 +fn make<T>() -> T {}
 +fn main() {
 +    let a = make$0::<i32>();
 +}
 +"#,
 +            r#"
 +fn make<T>() -> T {}
 +fn main() {
 +    let a: i32 = make();
 +}
 +"#,
 +        );
 +        check_assist(
 +            replace_turbofish_with_explicit_type,
 +            r#"
 +//- minicore: option
 +fn make<T>() -> T {}
 +fn main() {
 +    let a = make$0::<Option<bool>>();
 +}
 +"#,
 +            r#"
 +fn make<T>() -> T {}
 +fn main() {
 +    let a: Option<bool> = make();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn replaces_turbofish_not_same_type() {
 +        check_assist(
 +            replace_turbofish_with_explicit_type,
 +            r#"
 +//- minicore: option
 +fn make<T>() -> Option<T> {}
 +fn main() {
 +    let a = make$0::<u128>();
 +}
 +"#,
 +            r#"
 +fn make<T>() -> Option<T> {}
 +fn main() {
 +    let a: Option<u128> = make();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn replaces_turbofish_for_type_with_defaulted_generic_param() {
 +        check_assist(
 +            replace_turbofish_with_explicit_type,
 +            r#"
 +struct HasDefault<T, U = i32>(T, U);
 +fn make<T>() -> HasDefault<T> {}
 +fn main() {
 +    let a = make$0::<bool>();
 +}
 +"#,
 +            r#"
 +struct HasDefault<T, U = i32>(T, U);
 +fn make<T>() -> HasDefault<T> {}
 +fn main() {
 +    let a: HasDefault<bool> = make();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn replaces_turbofish_try_await() {
 +        check_assist(
 +            replace_turbofish_with_explicit_type,
 +            r#"
 +//- minicore: option, future
 +struct Fut<T>(T);
 +impl<T> core::future::Future for Fut<T> {
 +    type Output = Option<T>;
 +}
 +fn make<T>() -> Fut<T> {}
 +fn main() {
 +    let a = make$0::<bool>().await?;
 +}
 +"#,
 +            r#"
 +struct Fut<T>(T);
 +impl<T> core::future::Future for Fut<T> {
 +    type Output = Option<T>;
 +}
 +fn make<T>() -> Fut<T> {}
 +fn main() {
 +    let a: bool = make().await?;
 +}
 +"#,
 +        );
 +    }
 +}
index d5cd2d551349d7c225be5e262ce399e2354deafb,0000000000000000000000000000000000000000..04398832253307be610ef1da72c05d5b33fa7d20
mode 100644,000000..100644
--- /dev/null
@@@ -1,257 -1,0 +1,277 @@@
 +use ide_db::{
 +    assists::{AssistId, AssistKind},
 +    base_db::FileId,
 +    defs::Definition,
 +    search::FileReference,
 +    syntax_helpers::node_ext::full_path_of_name_ref,
 +};
 +use syntax::{
 +    ast::{self, NameLike, NameRef},
 +    AstNode, SyntaxKind, TextRange,
 +};
 +
 +use crate::{AssistContext, Assists};
 +
 +// Assist: unnecessary_async
 +//
 +// Removes the `async` mark from functions which have no `.await` in their body.
 +// Looks for calls to the functions and removes the `.await` on the call site.
 +//
 +// ```
 +// pub async f$0n foo() {}
 +// pub async fn bar() { foo().await }
 +// ```
 +// ->
 +// ```
 +// pub fn foo() {}
 +// pub async fn bar() { foo() }
 +// ```
 +pub(crate) fn unnecessary_async(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    let function: ast::Fn = ctx.find_node_at_offset()?;
 +
 +    // Do nothing if the cursor is not on the prototype. This is so that the check does not pollute
 +    // when the user asks us for assists when in the middle of the function body.
 +    // We consider the prototype to be anything that is before the body of the function.
 +    let cursor_position = ctx.offset();
 +    if cursor_position >= function.body()?.syntax().text_range().start() {
 +        return None;
 +    }
 +    // Do nothing if the function isn't async.
 +    if let None = function.async_token() {
 +        return None;
 +    }
 +    // Do nothing if the function has an `await` expression in its body.
 +    if function.body()?.syntax().descendants().find_map(ast::AwaitExpr::cast).is_some() {
 +        return None;
 +    }
++    // Do nothing if the method is a member of trait.
++    if let Some(impl_) = function.syntax().ancestors().nth(2).and_then(ast::Impl::cast) {
++        if let Some(_) = impl_.trait_() {
++            return None;
++        }
++    }
 +
 +    // Remove the `async` keyword plus whitespace after it, if any.
 +    let async_range = {
 +        let async_token = function.async_token()?;
 +        let next_token = async_token.next_token()?;
 +        if matches!(next_token.kind(), SyntaxKind::WHITESPACE) {
 +            TextRange::new(async_token.text_range().start(), next_token.text_range().end())
 +        } else {
 +            async_token.text_range()
 +        }
 +    };
 +
 +    // Otherwise, we may remove the `async` keyword.
 +    acc.add(
 +        AssistId("unnecessary_async", AssistKind::QuickFix),
 +        "Remove unnecessary async",
 +        async_range,
 +        |edit| {
 +            // Remove async on the function definition.
 +            edit.replace(async_range, "");
 +
 +            // Remove all `.await`s from calls to the function we remove `async` from.
 +            if let Some(fn_def) = ctx.sema.to_def(&function) {
 +                for await_expr in find_all_references(ctx, &Definition::Function(fn_def))
 +                    // Keep only references that correspond NameRefs.
 +                    .filter_map(|(_, reference)| match reference.name {
 +                        NameLike::NameRef(nameref) => Some(nameref),
 +                        _ => None,
 +                    })
 +                    // Keep only references that correspond to await expressions
 +                    .filter_map(|nameref| find_await_expression(ctx, &nameref))
 +                {
 +                    if let Some(await_token) = &await_expr.await_token() {
 +                        edit.replace(await_token.text_range(), "");
 +                    }
 +                    if let Some(dot_token) = &await_expr.dot_token() {
 +                        edit.replace(dot_token.text_range(), "");
 +                    }
 +                }
 +            }
 +        },
 +    )
 +}
 +
 +fn find_all_references(
 +    ctx: &AssistContext<'_>,
 +    def: &Definition,
 +) -> impl Iterator<Item = (FileId, FileReference)> {
 +    def.usages(&ctx.sema).all().into_iter().flat_map(|(file_id, references)| {
 +        references.into_iter().map(move |reference| (file_id, reference))
 +    })
 +}
 +
 +/// Finds the await expression for the given `NameRef`.
 +/// If no await expression is found, returns None.
 +fn find_await_expression(ctx: &AssistContext<'_>, nameref: &NameRef) -> Option<ast::AwaitExpr> {
 +    // From the nameref, walk up the tree to the await expression.
 +    let await_expr = if let Some(path) = full_path_of_name_ref(&nameref) {
 +        // Function calls.
 +        path.syntax()
 +            .parent()
 +            .and_then(ast::PathExpr::cast)?
 +            .syntax()
 +            .parent()
 +            .and_then(ast::CallExpr::cast)?
 +            .syntax()
 +            .parent()
 +            .and_then(ast::AwaitExpr::cast)
 +    } else {
 +        // Method calls.
 +        nameref
 +            .syntax()
 +            .parent()
 +            .and_then(ast::MethodCallExpr::cast)?
 +            .syntax()
 +            .parent()
 +            .and_then(ast::AwaitExpr::cast)
 +    };
 +
 +    ctx.sema.original_ast_node(await_expr?)
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use super::*;
 +
 +    use crate::tests::{check_assist, check_assist_not_applicable};
 +
 +    #[test]
 +    fn applies_on_empty_function() {
 +        check_assist(unnecessary_async, "pub async f$0n f() {}", "pub fn f() {}")
 +    }
 +
 +    #[test]
 +    fn applies_and_removes_whitespace() {
 +        check_assist(unnecessary_async, "pub async       f$0n f() {}", "pub fn f() {}")
 +    }
 +
 +    #[test]
 +    fn does_not_apply_on_non_async_function() {
 +        check_assist_not_applicable(unnecessary_async, "pub f$0n f() {}")
 +    }
 +
 +    #[test]
 +    fn applies_on_function_with_a_non_await_expr() {
 +        check_assist(unnecessary_async, "pub async f$0n f() { f2() }", "pub fn f() { f2() }")
 +    }
 +
 +    #[test]
 +    fn does_not_apply_on_function_with_an_await_expr() {
 +        check_assist_not_applicable(unnecessary_async, "pub async f$0n f() { f2().await }")
 +    }
 +
 +    #[test]
 +    fn applies_and_removes_await_on_reference() {
 +        check_assist(
 +            unnecessary_async,
 +            r#"
 +pub async fn f4() { }
 +pub async f$0n f2() { }
 +pub async fn f() { f2().await }
 +pub async fn f3() { f2().await }"#,
 +            r#"
 +pub async fn f4() { }
 +pub fn f2() { }
 +pub async fn f() { f2() }
 +pub async fn f3() { f2() }"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn applies_and_removes_await_from_within_module() {
 +        check_assist(
 +            unnecessary_async,
 +            r#"
 +pub async fn f4() { }
 +mod a { pub async f$0n f2() { } }
 +pub async fn f() { a::f2().await }
 +pub async fn f3() { a::f2().await }"#,
 +            r#"
 +pub async fn f4() { }
 +mod a { pub fn f2() { } }
 +pub async fn f() { a::f2() }
 +pub async fn f3() { a::f2() }"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn applies_and_removes_await_on_inner_await() {
 +        check_assist(
 +            unnecessary_async,
 +            // Ensure that it is the first await on the 3rd line that is removed
 +            r#"
 +pub async fn f() { f2().await }
 +pub async f$0n f2() -> i32 { 1 }
 +pub async fn f3() { f4(f2().await).await }
 +pub async fn f4(i: i32) { }"#,
 +            r#"
 +pub async fn f() { f2() }
 +pub fn f2() -> i32 { 1 }
 +pub async fn f3() { f4(f2()).await }
 +pub async fn f4(i: i32) { }"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn applies_and_removes_await_on_outer_await() {
 +        check_assist(
 +            unnecessary_async,
 +            // Ensure that it is the second await on the 3rd line that is removed
 +            r#"
 +pub async fn f() { f2().await }
 +pub async f$0n f2(i: i32) { }
 +pub async fn f3() { f2(f4().await).await }
 +pub async fn f4() -> i32 { 1 }"#,
 +            r#"
 +pub async fn f() { f2() }
 +pub fn f2(i: i32) { }
 +pub async fn f3() { f2(f4().await) }
 +pub async fn f4() -> i32 { 1 }"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn applies_on_method_call() {
 +        check_assist(
 +            unnecessary_async,
 +            r#"
 +pub struct S { }
 +impl S { pub async f$0n f2(&self) { } }
 +pub async fn f(s: &S) { s.f2().await }"#,
 +            r#"
 +pub struct S { }
 +impl S { pub fn f2(&self) { } }
 +pub async fn f(s: &S) { s.f2() }"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn does_not_apply_on_function_with_a_nested_await_expr() {
 +        check_assist_not_applicable(
 +            unnecessary_async,
 +            "async f$0n f() { if true { loop { f2().await } } }",
 +        )
 +    }
 +
 +    #[test]
 +    fn does_not_apply_when_not_on_prototype() {
 +        check_assist_not_applicable(unnecessary_async, "pub async fn f() { $0f2() }")
 +    }
++
++    #[test]
++    fn does_not_apply_on_async_trait_method() {
++        check_assist_not_applicable(
++            unnecessary_async,
++            r#"
++trait Trait {
++    async fn foo();
++}
++impl Trait for () {
++    $0async fn foo() {}
++}"#,
++        );
++    }
 +}
index 25c58d086e977d71f3711fb2c09d65fdd26df653,0000000000000000000000000000000000000000..d09614c51127ed8232c86e8c02b3cc4f57ddd858
mode 100644,000000..100644
--- /dev/null
@@@ -1,159 -1,0 +1,159 @@@
-                     zipped_decls.push_str(&format!("{}let {pat}: {ty} = {expr};\n", indents))
 +use syntax::{
 +    ast::{self, edit::AstNodeEdit},
 +    AstNode, T,
 +};
 +
 +use crate::{AssistContext, AssistId, AssistKind, Assists};
 +
 +// Assist: unwrap_tuple
 +//
 +// Unwrap the tuple to different variables.
 +//
 +// ```
 +// # //- minicore: result
 +// fn main() {
 +//     $0let (foo, bar) = ("Foo", "Bar");
 +// }
 +// ```
 +// ->
 +// ```
 +// fn main() {
 +//     let foo = "Foo";
 +//     let bar = "Bar";
 +// }
 +// ```
 +pub(crate) fn unwrap_tuple(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    let let_kw = ctx.find_token_syntax_at_offset(T![let])?;
 +    let let_stmt = let_kw.parent().and_then(ast::LetStmt::cast)?;
 +    let indent_level = let_stmt.indent_level().0 as usize;
 +    let pat = let_stmt.pat()?;
 +    let ty = let_stmt.ty();
 +    let init = let_stmt.initializer()?;
 +
 +    // This only applies for tuple patterns, types, and initializers.
 +    let tuple_pat = match pat {
 +        ast::Pat::TuplePat(pat) => pat,
 +        _ => return None,
 +    };
 +    let tuple_ty = ty.and_then(|it| match it {
 +        ast::Type::TupleType(ty) => Some(ty),
 +        _ => None,
 +    });
 +    let tuple_init = match init {
 +        ast::Expr::TupleExpr(expr) => expr,
 +        _ => return None,
 +    };
 +
 +    if tuple_pat.fields().count() != tuple_init.fields().count() {
 +        return None;
 +    }
 +    if let Some(tys) = &tuple_ty {
 +        if tuple_pat.fields().count() != tys.fields().count() {
 +            return None;
 +        }
 +    }
 +
 +    let parent = let_kw.parent()?;
 +
 +    acc.add(
 +        AssistId("unwrap_tuple", AssistKind::RefactorRewrite),
 +        "Unwrap tuple",
 +        let_kw.text_range(),
 +        |edit| {
 +            let indents = "    ".repeat(indent_level);
 +
 +            // If there is an ascribed type, insert that type for each declaration,
 +            // otherwise, omit that type.
 +            if let Some(tys) = tuple_ty {
 +                let mut zipped_decls = String::new();
 +                for (pat, ty, expr) in
 +                    itertools::izip!(tuple_pat.fields(), tys.fields(), tuple_init.fields())
 +                {
-                     zipped_decls.push_str(&format!("{}let {pat} = {expr};\n", indents));
++                    zipped_decls.push_str(&format!("{indents}let {pat}: {ty} = {expr};\n"))
 +                }
 +                edit.replace(parent.text_range(), zipped_decls.trim());
 +            } else {
 +                let mut zipped_decls = String::new();
 +                for (pat, expr) in itertools::izip!(tuple_pat.fields(), tuple_init.fields()) {
++                    zipped_decls.push_str(&format!("{indents}let {pat} = {expr};\n"));
 +                }
 +                edit.replace(parent.text_range(), zipped_decls.trim());
 +            }
 +        },
 +    )
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use crate::tests::check_assist;
 +
 +    use super::*;
 +
 +    #[test]
 +    fn unwrap_tuples() {
 +        check_assist(
 +            unwrap_tuple,
 +            r#"
 +fn main() {
 +    $0let (foo, bar) = ("Foo", "Bar");
 +}
 +"#,
 +            r#"
 +fn main() {
 +    let foo = "Foo";
 +    let bar = "Bar";
 +}
 +"#,
 +        );
 +
 +        check_assist(
 +            unwrap_tuple,
 +            r#"
 +fn main() {
 +    $0let (foo, bar, baz) = ("Foo", "Bar", "Baz");
 +}
 +"#,
 +            r#"
 +fn main() {
 +    let foo = "Foo";
 +    let bar = "Bar";
 +    let baz = "Baz";
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn unwrap_tuple_with_types() {
 +        check_assist(
 +            unwrap_tuple,
 +            r#"
 +fn main() {
 +    $0let (foo, bar): (u8, i32) = (5, 10);
 +}
 +"#,
 +            r#"
 +fn main() {
 +    let foo: u8 = 5;
 +    let bar: i32 = 10;
 +}
 +"#,
 +        );
 +
 +        check_assist(
 +            unwrap_tuple,
 +            r#"
 +fn main() {
 +    $0let (foo, bar, baz): (u8, i32, f64) = (5, 10, 17.5);
 +}
 +"#,
 +            r#"
 +fn main() {
 +    let foo: u8 = 5;
 +    let bar: i32 = 10;
 +    let baz: f64 = 17.5;
 +}
 +"#,
 +        );
 +    }
 +}
index 83446387db1cf65332b559105fb75a7a8c7326e3,0000000000000000000000000000000000000000..b6c489eb62eef2c734e9fdf726f5fcd455ef22c0
mode 100644,000000..100644
--- /dev/null
@@@ -1,980 -1,0 +1,980 @@@
-                     let snippet = format!("Result<{}, ${{0:_}}>", type_ref);
 +use std::iter;
 +
 +use ide_db::{
 +    famous_defs::FamousDefs,
 +    syntax_helpers::node_ext::{for_each_tail_expr, walk_expr},
 +};
 +use syntax::{
 +    ast::{self, make, Expr},
 +    match_ast, AstNode,
 +};
 +
 +use crate::{AssistContext, AssistId, AssistKind, Assists};
 +
 +// Assist: wrap_return_type_in_result
 +//
 +// Wrap the function's return type into Result.
 +//
 +// ```
 +// # //- minicore: result
 +// fn foo() -> i32$0 { 42i32 }
 +// ```
 +// ->
 +// ```
 +// fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }
 +// ```
 +pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
 +    let ret_type = ctx.find_node_at_offset::<ast::RetType>()?;
 +    let parent = ret_type.syntax().parent()?;
 +    let body = match_ast! {
 +        match parent {
 +            ast::Fn(func) => func.body()?,
 +            ast::ClosureExpr(closure) => match closure.body()? {
 +                Expr::BlockExpr(block) => block,
 +                // closures require a block when a return type is specified
 +                _ => return None,
 +            },
 +            _ => return None,
 +        }
 +    };
 +
 +    let type_ref = &ret_type.ty()?;
 +    let ty = ctx.sema.resolve_type(type_ref)?.as_adt();
 +    let result_enum =
 +        FamousDefs(&ctx.sema, ctx.sema.scope(type_ref.syntax())?.krate()).core_result_Result()?;
 +
 +    if matches!(ty, Some(hir::Adt::Enum(ret_type)) if ret_type == result_enum) {
 +        cov_mark::hit!(wrap_return_type_in_result_simple_return_type_already_result);
 +        return None;
 +    }
 +
 +    acc.add(
 +        AssistId("wrap_return_type_in_result", AssistKind::RefactorRewrite),
 +        "Wrap return type in Result",
 +        type_ref.syntax().text_range(),
 +        |builder| {
 +            let body = ast::Expr::BlockExpr(body);
 +
 +            let mut exprs_to_wrap = Vec::new();
 +            let tail_cb = &mut |e: &_| tail_cb_impl(&mut exprs_to_wrap, e);
 +            walk_expr(&body, &mut |expr| {
 +                if let Expr::ReturnExpr(ret_expr) = expr {
 +                    if let Some(ret_expr_arg) = &ret_expr.expr() {
 +                        for_each_tail_expr(ret_expr_arg, tail_cb);
 +                    }
 +                }
 +            });
 +            for_each_tail_expr(&body, tail_cb);
 +
 +            for ret_expr_arg in exprs_to_wrap {
 +                let ok_wrapped = make::expr_call(
 +                    make::expr_path(make::ext::ident_path("Ok")),
 +                    make::arg_list(iter::once(ret_expr_arg.clone())),
 +                );
 +                builder.replace_ast(ret_expr_arg, ok_wrapped);
 +            }
 +
 +            match ctx.config.snippet_cap {
 +                Some(cap) => {
-                     .replace(type_ref.syntax().text_range(), format!("Result<{}, _>", type_ref)),
++                    let snippet = format!("Result<{type_ref}, ${{0:_}}>");
 +                    builder.replace_snippet(cap, type_ref.syntax().text_range(), snippet)
 +                }
 +                None => builder
++                    .replace(type_ref.syntax().text_range(), format!("Result<{type_ref}, _>")),
 +            }
 +        },
 +    )
 +}
 +
 +fn tail_cb_impl(acc: &mut Vec<ast::Expr>, e: &ast::Expr) {
 +    match e {
 +        Expr::BreakExpr(break_expr) => {
 +            if let Some(break_expr_arg) = break_expr.expr() {
 +                for_each_tail_expr(&break_expr_arg, &mut |e| tail_cb_impl(acc, e))
 +            }
 +        }
 +        Expr::ReturnExpr(ret_expr) => {
 +            if let Some(ret_expr_arg) = &ret_expr.expr() {
 +                for_each_tail_expr(ret_expr_arg, &mut |e| tail_cb_impl(acc, e));
 +            }
 +        }
 +        e => acc.push(e.clone()),
 +    }
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use crate::tests::{check_assist, check_assist_not_applicable};
 +
 +    use super::*;
 +
 +    #[test]
 +    fn wrap_return_type_in_result_simple() {
 +        check_assist(
 +            wrap_return_type_in_result,
 +            r#"
 +//- minicore: result
 +fn foo() -> i3$02 {
 +    let test = "test";
 +    return 42i32;
 +}
 +"#,
 +            r#"
 +fn foo() -> Result<i32, ${0:_}> {
 +    let test = "test";
 +    return Ok(42i32);
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn wrap_return_type_break_split_tail() {
 +        check_assist(
 +            wrap_return_type_in_result,
 +            r#"
 +//- minicore: result
 +fn foo() -> i3$02 {
 +    loop {
 +        break if true {
 +            1
 +        } else {
 +            0
 +        };
 +    }
 +}
 +"#,
 +            r#"
 +fn foo() -> Result<i32, ${0:_}> {
 +    loop {
 +        break if true {
 +            Ok(1)
 +        } else {
 +            Ok(0)
 +        };
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn wrap_return_type_in_result_simple_closure() {
 +        check_assist(
 +            wrap_return_type_in_result,
 +            r#"
 +//- minicore: result
 +fn foo() {
 +    || -> i32$0 {
 +        let test = "test";
 +        return 42i32;
 +    };
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    || -> Result<i32, ${0:_}> {
 +        let test = "test";
 +        return Ok(42i32);
 +    };
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn wrap_return_type_in_result_simple_return_type_bad_cursor() {
 +        check_assist_not_applicable(
 +            wrap_return_type_in_result,
 +            r#"
 +//- minicore: result
 +fn foo() -> i32 {
 +    let test = "test";$0
 +    return 42i32;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn wrap_return_type_in_result_simple_return_type_bad_cursor_closure() {
 +        check_assist_not_applicable(
 +            wrap_return_type_in_result,
 +            r#"
 +//- minicore: result
 +fn foo() {
 +    || -> i32 {
 +        let test = "test";$0
 +        return 42i32;
 +    };
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn wrap_return_type_in_result_closure_non_block() {
 +        check_assist_not_applicable(
 +            wrap_return_type_in_result,
 +            r#"
 +//- minicore: result
 +fn foo() { || -> i$032 3; }
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn wrap_return_type_in_result_simple_return_type_already_result_std() {
 +        check_assist_not_applicable(
 +            wrap_return_type_in_result,
 +            r#"
 +//- minicore: result
 +fn foo() -> core::result::Result<i32$0, String> {
 +    let test = "test";
 +    return 42i32;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn wrap_return_type_in_result_simple_return_type_already_result() {
 +        cov_mark::check!(wrap_return_type_in_result_simple_return_type_already_result);
 +        check_assist_not_applicable(
 +            wrap_return_type_in_result,
 +            r#"
 +//- minicore: result
 +fn foo() -> Result<i32$0, String> {
 +    let test = "test";
 +    return 42i32;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn wrap_return_type_in_result_simple_return_type_already_result_closure() {
 +        check_assist_not_applicable(
 +            wrap_return_type_in_result,
 +            r#"
 +//- minicore: result
 +fn foo() {
 +    || -> Result<i32$0, String> {
 +        let test = "test";
 +        return 42i32;
 +    };
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn wrap_return_type_in_result_simple_with_cursor() {
 +        check_assist(
 +            wrap_return_type_in_result,
 +            r#"
 +//- minicore: result
 +fn foo() -> $0i32 {
 +    let test = "test";
 +    return 42i32;
 +}
 +"#,
 +            r#"
 +fn foo() -> Result<i32, ${0:_}> {
 +    let test = "test";
 +    return Ok(42i32);
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn wrap_return_type_in_result_simple_with_tail() {
 +        check_assist(
 +            wrap_return_type_in_result,
 +            r#"
 +//- minicore: result
 +fn foo() ->$0 i32 {
 +    let test = "test";
 +    42i32
 +}
 +"#,
 +            r#"
 +fn foo() -> Result<i32, ${0:_}> {
 +    let test = "test";
 +    Ok(42i32)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn wrap_return_type_in_result_simple_with_tail_closure() {
 +        check_assist(
 +            wrap_return_type_in_result,
 +            r#"
 +//- minicore: result
 +fn foo() {
 +    || ->$0 i32 {
 +        let test = "test";
 +        42i32
 +    };
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    || -> Result<i32, ${0:_}> {
 +        let test = "test";
 +        Ok(42i32)
 +    };
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn wrap_return_type_in_result_simple_with_tail_only() {
 +        check_assist(
 +            wrap_return_type_in_result,
 +            r#"
 +//- minicore: result
 +fn foo() -> i32$0 { 42i32 }
 +"#,
 +            r#"
 +fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn wrap_return_type_in_result_simple_with_tail_block_like() {
 +        check_assist(
 +            wrap_return_type_in_result,
 +            r#"
 +//- minicore: result
 +fn foo() -> i32$0 {
 +    if true {
 +        42i32
 +    } else {
 +        24i32
 +    }
 +}
 +"#,
 +            r#"
 +fn foo() -> Result<i32, ${0:_}> {
 +    if true {
 +        Ok(42i32)
 +    } else {
 +        Ok(24i32)
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn wrap_return_type_in_result_simple_without_block_closure() {
 +        check_assist(
 +            wrap_return_type_in_result,
 +            r#"
 +//- minicore: result
 +fn foo() {
 +    || -> i32$0 {
 +        if true {
 +            42i32
 +        } else {
 +            24i32
 +        }
 +    };
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    || -> Result<i32, ${0:_}> {
 +        if true {
 +            Ok(42i32)
 +        } else {
 +            Ok(24i32)
 +        }
 +    };
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn wrap_return_type_in_result_simple_with_nested_if() {
 +        check_assist(
 +            wrap_return_type_in_result,
 +            r#"
 +//- minicore: result
 +fn foo() -> i32$0 {
 +    if true {
 +        if false {
 +            1
 +        } else {
 +            2
 +        }
 +    } else {
 +        24i32
 +    }
 +}
 +"#,
 +            r#"
 +fn foo() -> Result<i32, ${0:_}> {
 +    if true {
 +        if false {
 +            Ok(1)
 +        } else {
 +            Ok(2)
 +        }
 +    } else {
 +        Ok(24i32)
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn wrap_return_type_in_result_simple_with_await() {
 +        check_assist(
 +            wrap_return_type_in_result,
 +            r#"
 +//- minicore: result
 +async fn foo() -> i$032 {
 +    if true {
 +        if false {
 +            1.await
 +        } else {
 +            2.await
 +        }
 +    } else {
 +        24i32.await
 +    }
 +}
 +"#,
 +            r#"
 +async fn foo() -> Result<i32, ${0:_}> {
 +    if true {
 +        if false {
 +            Ok(1.await)
 +        } else {
 +            Ok(2.await)
 +        }
 +    } else {
 +        Ok(24i32.await)
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn wrap_return_type_in_result_simple_with_array() {
 +        check_assist(
 +            wrap_return_type_in_result,
 +            r#"
 +//- minicore: result
 +fn foo() -> [i32;$0 3] { [1, 2, 3] }
 +"#,
 +            r#"
 +fn foo() -> Result<[i32; 3], ${0:_}> { Ok([1, 2, 3]) }
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn wrap_return_type_in_result_simple_with_cast() {
 +        check_assist(
 +            wrap_return_type_in_result,
 +            r#"
 +//- minicore: result
 +fn foo() -$0> i32 {
 +    if true {
 +        if false {
 +            1 as i32
 +        } else {
 +            2 as i32
 +        }
 +    } else {
 +        24 as i32
 +    }
 +}
 +"#,
 +            r#"
 +fn foo() -> Result<i32, ${0:_}> {
 +    if true {
 +        if false {
 +            Ok(1 as i32)
 +        } else {
 +            Ok(2 as i32)
 +        }
 +    } else {
 +        Ok(24 as i32)
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn wrap_return_type_in_result_simple_with_tail_block_like_match() {
 +        check_assist(
 +            wrap_return_type_in_result,
 +            r#"
 +//- minicore: result
 +fn foo() -> i32$0 {
 +    let my_var = 5;
 +    match my_var {
 +        5 => 42i32,
 +        _ => 24i32,
 +    }
 +}
 +"#,
 +            r#"
 +fn foo() -> Result<i32, ${0:_}> {
 +    let my_var = 5;
 +    match my_var {
 +        5 => Ok(42i32),
 +        _ => Ok(24i32),
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn wrap_return_type_in_result_simple_with_loop_with_tail() {
 +        check_assist(
 +            wrap_return_type_in_result,
 +            r#"
 +//- minicore: result
 +fn foo() -> i32$0 {
 +    let my_var = 5;
 +    loop {
 +        println!("test");
 +        5
 +    }
 +    my_var
 +}
 +"#,
 +            r#"
 +fn foo() -> Result<i32, ${0:_}> {
 +    let my_var = 5;
 +    loop {
 +        println!("test");
 +        5
 +    }
 +    Ok(my_var)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn wrap_return_type_in_result_simple_with_loop_in_let_stmt() {
 +        check_assist(
 +            wrap_return_type_in_result,
 +            r#"
 +//- minicore: result
 +fn foo() -> i32$0 {
 +    let my_var = let x = loop {
 +        break 1;
 +    };
 +    my_var
 +}
 +"#,
 +            r#"
 +fn foo() -> Result<i32, ${0:_}> {
 +    let my_var = let x = loop {
 +        break 1;
 +    };
 +    Ok(my_var)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn wrap_return_type_in_result_simple_with_tail_block_like_match_return_expr() {
 +        check_assist(
 +            wrap_return_type_in_result,
 +            r#"
 +//- minicore: result
 +fn foo() -> i32$0 {
 +    let my_var = 5;
 +    let res = match my_var {
 +        5 => 42i32,
 +        _ => return 24i32,
 +    };
 +    res
 +}
 +"#,
 +            r#"
 +fn foo() -> Result<i32, ${0:_}> {
 +    let my_var = 5;
 +    let res = match my_var {
 +        5 => 42i32,
 +        _ => return Ok(24i32),
 +    };
 +    Ok(res)
 +}
 +"#,
 +        );
 +
 +        check_assist(
 +            wrap_return_type_in_result,
 +            r#"
 +//- minicore: result
 +fn foo() -> i32$0 {
 +    let my_var = 5;
 +    let res = if my_var == 5 {
 +        42i32
 +    } else {
 +        return 24i32;
 +    };
 +    res
 +}
 +"#,
 +            r#"
 +fn foo() -> Result<i32, ${0:_}> {
 +    let my_var = 5;
 +    let res = if my_var == 5 {
 +        42i32
 +    } else {
 +        return Ok(24i32);
 +    };
 +    Ok(res)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn wrap_return_type_in_result_simple_with_tail_block_like_match_deeper() {
 +        check_assist(
 +            wrap_return_type_in_result,
 +            r#"
 +//- minicore: result
 +fn foo() -> i32$0 {
 +    let my_var = 5;
 +    match my_var {
 +        5 => {
 +            if true {
 +                42i32
 +            } else {
 +                25i32
 +            }
 +        },
 +        _ => {
 +            let test = "test";
 +            if test == "test" {
 +                return bar();
 +            }
 +            53i32
 +        },
 +    }
 +}
 +"#,
 +            r#"
 +fn foo() -> Result<i32, ${0:_}> {
 +    let my_var = 5;
 +    match my_var {
 +        5 => {
 +            if true {
 +                Ok(42i32)
 +            } else {
 +                Ok(25i32)
 +            }
 +        },
 +        _ => {
 +            let test = "test";
 +            if test == "test" {
 +                return Ok(bar());
 +            }
 +            Ok(53i32)
 +        },
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn wrap_return_type_in_result_simple_with_tail_block_like_early_return() {
 +        check_assist(
 +            wrap_return_type_in_result,
 +            r#"
 +//- minicore: result
 +fn foo() -> i$032 {
 +    let test = "test";
 +    if test == "test" {
 +        return 24i32;
 +    }
 +    53i32
 +}
 +"#,
 +            r#"
 +fn foo() -> Result<i32, ${0:_}> {
 +    let test = "test";
 +    if test == "test" {
 +        return Ok(24i32);
 +    }
 +    Ok(53i32)
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn wrap_return_type_in_result_simple_with_closure() {
 +        check_assist(
 +            wrap_return_type_in_result,
 +            r#"
 +//- minicore: result
 +fn foo(the_field: u32) ->$0 u32 {
 +    let true_closure = || { return true; };
 +    if the_field < 5 {
 +        let mut i = 0;
 +        if true_closure() {
 +            return 99;
 +        } else {
 +            return 0;
 +        }
 +    }
 +    the_field
 +}
 +"#,
 +            r#"
 +fn foo(the_field: u32) -> Result<u32, ${0:_}> {
 +    let true_closure = || { return true; };
 +    if the_field < 5 {
 +        let mut i = 0;
 +        if true_closure() {
 +            return Ok(99);
 +        } else {
 +            return Ok(0);
 +        }
 +    }
 +    Ok(the_field)
 +}
 +"#,
 +        );
 +
 +        check_assist(
 +            wrap_return_type_in_result,
 +            r#"
 +//- minicore: result
 +fn foo(the_field: u32) -> u32$0 {
 +    let true_closure = || {
 +        return true;
 +    };
 +    if the_field < 5 {
 +        let mut i = 0;
 +
 +
 +        if true_closure() {
 +            return 99;
 +        } else {
 +            return 0;
 +        }
 +    }
 +    let t = None;
 +
 +    t.unwrap_or_else(|| the_field)
 +}
 +"#,
 +            r#"
 +fn foo(the_field: u32) -> Result<u32, ${0:_}> {
 +    let true_closure = || {
 +        return true;
 +    };
 +    if the_field < 5 {
 +        let mut i = 0;
 +
 +
 +        if true_closure() {
 +            return Ok(99);
 +        } else {
 +            return Ok(0);
 +        }
 +    }
 +    let t = None;
 +
 +    Ok(t.unwrap_or_else(|| the_field))
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn wrap_return_type_in_result_simple_with_weird_forms() {
 +        check_assist(
 +            wrap_return_type_in_result,
 +            r#"
 +//- minicore: result
 +fn foo() -> i32$0 {
 +    let test = "test";
 +    if test == "test" {
 +        return 24i32;
 +    }
 +    let mut i = 0;
 +    loop {
 +        if i == 1 {
 +            break 55;
 +        }
 +        i += 1;
 +    }
 +}
 +"#,
 +            r#"
 +fn foo() -> Result<i32, ${0:_}> {
 +    let test = "test";
 +    if test == "test" {
 +        return Ok(24i32);
 +    }
 +    let mut i = 0;
 +    loop {
 +        if i == 1 {
 +            break Ok(55);
 +        }
 +        i += 1;
 +    }
 +}
 +"#,
 +        );
 +
 +        check_assist(
 +            wrap_return_type_in_result,
 +            r#"
 +//- minicore: result
 +fn foo(the_field: u32) -> u32$0 {
 +    if the_field < 5 {
 +        let mut i = 0;
 +        loop {
 +            if i > 5 {
 +                return 55u32;
 +            }
 +            i += 3;
 +        }
 +        match i {
 +            5 => return 99,
 +            _ => return 0,
 +        };
 +    }
 +    the_field
 +}
 +"#,
 +            r#"
 +fn foo(the_field: u32) -> Result<u32, ${0:_}> {
 +    if the_field < 5 {
 +        let mut i = 0;
 +        loop {
 +            if i > 5 {
 +                return Ok(55u32);
 +            }
 +            i += 3;
 +        }
 +        match i {
 +            5 => return Ok(99),
 +            _ => return Ok(0),
 +        };
 +    }
 +    Ok(the_field)
 +}
 +"#,
 +        );
 +
 +        check_assist(
 +            wrap_return_type_in_result,
 +            r#"
 +//- minicore: result
 +fn foo(the_field: u32) -> u3$02 {
 +    if the_field < 5 {
 +        let mut i = 0;
 +        match i {
 +            5 => return 99,
 +            _ => return 0,
 +        }
 +    }
 +    the_field
 +}
 +"#,
 +            r#"
 +fn foo(the_field: u32) -> Result<u32, ${0:_}> {
 +    if the_field < 5 {
 +        let mut i = 0;
 +        match i {
 +            5 => return Ok(99),
 +            _ => return Ok(0),
 +        }
 +    }
 +    Ok(the_field)
 +}
 +"#,
 +        );
 +
 +        check_assist(
 +            wrap_return_type_in_result,
 +            r#"
 +//- minicore: result
 +fn foo(the_field: u32) -> u32$0 {
 +    if the_field < 5 {
 +        let mut i = 0;
 +        if i == 5 {
 +            return 99
 +        } else {
 +            return 0
 +        }
 +    }
 +    the_field
 +}
 +"#,
 +            r#"
 +fn foo(the_field: u32) -> Result<u32, ${0:_}> {
 +    if the_field < 5 {
 +        let mut i = 0;
 +        if i == 5 {
 +            return Ok(99)
 +        } else {
 +            return Ok(0)
 +        }
 +    }
 +    Ok(the_field)
 +}
 +"#,
 +        );
 +
 +        check_assist(
 +            wrap_return_type_in_result,
 +            r#"
 +//- minicore: result
 +fn foo(the_field: u32) -> $0u32 {
 +    if the_field < 5 {
 +        let mut i = 0;
 +        if i == 5 {
 +            return 99;
 +        } else {
 +            return 0;
 +        }
 +    }
 +    the_field
 +}
 +"#,
 +            r#"
 +fn foo(the_field: u32) -> Result<u32, ${0:_}> {
 +    if the_field < 5 {
 +        let mut i = 0;
 +        if i == 5 {
 +            return Ok(99);
 +        } else {
 +            return Ok(0);
 +        }
 +    }
 +    Ok(the_field)
 +}
 +"#,
 +        );
 +    }
 +}
index a07318cefad273dbdb4410b12a670f06f86107f9,0000000000000000000000000000000000000000..387cc631428250e9fa82ee9afe72a9a8608849f0
mode 100644,000000..100644
--- /dev/null
@@@ -1,323 -1,0 +1,325 @@@
 +//! `assists` crate provides a bunch of code assists, also known as code actions
 +//! (in LSP) or intentions (in IntelliJ).
 +//!
 +//! An assist is a micro-refactoring, which is automatically activated in
 +//! certain context. For example, if the cursor is over `,`, a "swap `,`" assist
 +//! becomes available.
 +//!
 +//! ## Assists Guidelines
 +//!
 +//! Assists are the main mechanism to deliver advanced IDE features to the user,
 +//! so we should pay extra attention to the UX.
 +//!
 +//! The power of assists comes from their context-awareness. The main problem
 +//! with IDE features is that there are a lot of them, and it's hard to teach
 +//! the user what's available. Assists solve this problem nicely: 💡 signifies
 +//! that *something* is possible, and clicking on it reveals a *short* list of
 +//! actions. Contrast it with Emacs `M-x`, which just spits an infinite list of
 +//! all the features.
 +//!
 +//! Here are some considerations when creating a new assist:
 +//!
 +//! * It's good to preserve semantics, and it's good to keep the code compiling,
 +//!   but it isn't necessary. Example: "flip binary operation" might change
 +//!   semantics.
 +//! * Assist shouldn't necessary make the code "better". A lot of assist come in
 +//!   pairs: "if let <-> match".
 +//! * Assists should have as narrow scope as possible. Each new assists greatly
 +//!   improves UX for cases where the user actually invokes it, but it makes UX
 +//!   worse for every case where the user clicks 💡 to invoke some *other*
 +//!   assist. So, a rarely useful assist which is always applicable can be a net
 +//!   negative.
 +//! * Rarely useful actions are tricky. Sometimes there are features which are
 +//!   clearly useful to some users, but are just noise most of the time. We
 +//!   don't have a good solution here, our current approach is to make this
 +//!   functionality available only if assist is applicable to the whole
 +//!   selection. Example: `sort_items` sorts items alphabetically. Naively, it
 +//!   should be available more or less everywhere, which isn't useful. So
 +//!   instead we only show it if the user *selects* the items they want to sort.
 +//! * Consider grouping related assists together (see [`Assists::add_group`]).
 +//! * Make assists robust. If the assist depends on results of type-inference too
 +//!   much, it might only fire in fully-correct code. This makes assist less
 +//!   useful and (worse) less predictable. The user should have a clear
 +//!   intuition when each particular assist is available.
 +//! * Make small assists, which compose. Example: rather than auto-importing
 +//!   enums in `add_missing_match_arms`, we use fully-qualified names. There's a
 +//!   separate assist to shorten a fully-qualified name.
 +//! * Distinguish between assists and fixits for diagnostics. Internally, fixits
 +//!   and assists are equivalent. They have the same "show a list + invoke a
 +//!   single element" workflow, and both use [`Assist`] data structure. The main
 +//!   difference is in the UX: while 💡 looks only at the cursor position,
 +//!   diagnostics squigglies and fixits are calculated for the whole file and
 +//!   are presented to the user eagerly. So, diagnostics should be fixable
 +//!   errors, while assists can be just suggestions for an alternative way to do
 +//!   something. If something *could* be a diagnostic, it should be a
 +//!   diagnostic. Conversely, it might be valuable to turn a diagnostic with a
 +//!   lot of false errors into an assist.
 +//!
 +//! See also this post:
 +//! <https://rust-analyzer.github.io/blog/2020/09/28/how-to-make-a-light-bulb.html>
 +
 +#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)]
 +
 +#[allow(unused)]
 +macro_rules! eprintln {
 +    ($($tt:tt)*) => { stdx::eprintln!($($tt)*) };
 +}
 +
 +mod assist_config;
 +mod assist_context;
 +#[cfg(test)]
 +mod tests;
 +pub mod utils;
 +
 +use hir::Semantics;
 +use ide_db::{base_db::FileRange, RootDatabase};
 +use syntax::TextRange;
 +
 +pub(crate) use crate::assist_context::{AssistContext, Assists};
 +
 +pub use assist_config::AssistConfig;
 +pub use ide_db::assists::{
 +    Assist, AssistId, AssistKind, AssistResolveStrategy, GroupLabel, SingleResolve,
 +};
 +
 +/// Return all the assists applicable at the given position.
 +///
 +// NOTE: We don't have a `Feature: ` section for assists, they are special-cased
 +// in the manual.
 +pub fn assists(
 +    db: &RootDatabase,
 +    config: &AssistConfig,
 +    resolve: AssistResolveStrategy,
 +    range: FileRange,
 +) -> Vec<Assist> {
 +    let sema = Semantics::new(db);
 +    let ctx = AssistContext::new(sema, config, range);
 +    let mut acc = Assists::new(&ctx, resolve);
 +    handlers::all().iter().for_each(|handler| {
 +        handler(&mut acc, &ctx);
 +    });
 +    acc.finish()
 +}
 +
 +mod handlers {
 +    use crate::{AssistContext, Assists};
 +
 +    pub(crate) type Handler = fn(&mut Assists, &AssistContext<'_>) -> Option<()>;
 +
 +    mod add_explicit_type;
 +    mod add_label_to_loop;
 +    mod add_lifetime_to_type;
 +    mod add_missing_impl_members;
 +    mod add_turbo_fish;
 +    mod apply_demorgan;
 +    mod auto_import;
 +    mod change_visibility;
 +    mod convert_bool_then;
 +    mod convert_comment_block;
 +    mod convert_integer_literal;
 +    mod convert_into_to_from;
 +    mod convert_iter_for_each_to_for;
 +    mod convert_let_else_to_match;
++    mod convert_match_to_let_else;
 +    mod convert_tuple_struct_to_named_struct;
 +    mod convert_named_struct_to_tuple_struct;
 +    mod convert_to_guarded_return;
 +    mod convert_two_arm_bool_match_to_matches_macro;
 +    mod convert_while_to_loop;
 +    mod destructure_tuple_binding;
 +    mod expand_glob_import;
 +    mod extract_function;
 +    mod extract_module;
 +    mod extract_struct_from_enum_variant;
 +    mod extract_type_alias;
 +    mod extract_variable;
 +    mod add_missing_match_arms;
 +    mod fix_visibility;
 +    mod flip_binexpr;
 +    mod flip_comma;
 +    mod flip_trait_bound;
 +    mod move_format_string_arg;
 +    mod generate_constant;
 +    mod generate_default_from_enum_variant;
 +    mod generate_default_from_new;
 +    mod generate_deref;
 +    mod generate_derive;
 +    mod generate_documentation_template;
 +    mod generate_enum_is_method;
 +    mod generate_enum_projection_method;
 +    mod generate_enum_variant;
 +    mod generate_from_impl_for_enum;
 +    mod generate_function;
 +    mod generate_getter;
 +    mod generate_impl;
 +    mod generate_is_empty_from_len;
 +    mod generate_new;
 +    mod generate_setter;
 +    mod generate_delegate_methods;
 +    mod add_return_type;
 +    mod inline_call;
 +    mod inline_local_variable;
 +    mod inline_type_alias;
 +    mod introduce_named_lifetime;
 +    mod invert_if;
 +    mod merge_imports;
 +    mod merge_match_arms;
 +    mod move_bounds;
 +    mod move_guard;
 +    mod move_module_to_file;
 +    mod move_to_mod_rs;
 +    mod move_from_mod_rs;
 +    mod number_representation;
 +    mod promote_local_to_const;
 +    mod pull_assignment_up;
 +    mod qualify_path;
 +    mod qualify_method_call;
 +    mod raw_string;
 +    mod remove_dbg;
 +    mod remove_mut;
 +    mod remove_unused_param;
 +    mod reorder_fields;
 +    mod reorder_impl_items;
 +    mod replace_try_expr_with_match;
 +    mod replace_derive_with_manual_impl;
 +    mod replace_if_let_with_match;
 +    mod replace_or_with_or_else;
 +    mod introduce_named_generic;
 +    mod replace_let_with_if_let;
 +    mod replace_qualified_name_with_use;
 +    mod replace_string_with_char;
 +    mod replace_turbofish_with_explicit_type;
 +    mod split_import;
 +    mod unmerge_match_arm;
 +    mod unwrap_tuple;
 +    mod sort_items;
 +    mod toggle_ignore;
 +    mod unmerge_use;
 +    mod unnecessary_async;
 +    mod unwrap_block;
 +    mod unwrap_result_return_type;
 +    mod wrap_return_type_in_result;
 +
 +    pub(crate) fn all() -> &'static [Handler] {
 +        &[
 +            // These are alphabetic for the foolish consistency
 +            add_explicit_type::add_explicit_type,
 +            add_label_to_loop::add_label_to_loop,
 +            add_missing_match_arms::add_missing_match_arms,
 +            add_lifetime_to_type::add_lifetime_to_type,
 +            add_return_type::add_return_type,
 +            add_turbo_fish::add_turbo_fish,
 +            apply_demorgan::apply_demorgan,
 +            auto_import::auto_import,
 +            change_visibility::change_visibility,
 +            convert_bool_then::convert_bool_then_to_if,
 +            convert_bool_then::convert_if_to_bool_then,
 +            convert_comment_block::convert_comment_block,
 +            convert_integer_literal::convert_integer_literal,
 +            convert_into_to_from::convert_into_to_from,
 +            convert_iter_for_each_to_for::convert_iter_for_each_to_for,
 +            convert_iter_for_each_to_for::convert_for_loop_with_for_each,
 +            convert_let_else_to_match::convert_let_else_to_match,
 +            convert_named_struct_to_tuple_struct::convert_named_struct_to_tuple_struct,
++            convert_match_to_let_else::convert_match_to_let_else,
 +            convert_to_guarded_return::convert_to_guarded_return,
 +            convert_tuple_struct_to_named_struct::convert_tuple_struct_to_named_struct,
 +            convert_two_arm_bool_match_to_matches_macro::convert_two_arm_bool_match_to_matches_macro,
 +            convert_while_to_loop::convert_while_to_loop,
 +            destructure_tuple_binding::destructure_tuple_binding,
 +            expand_glob_import::expand_glob_import,
 +            extract_struct_from_enum_variant::extract_struct_from_enum_variant,
 +            extract_type_alias::extract_type_alias,
 +            fix_visibility::fix_visibility,
 +            flip_binexpr::flip_binexpr,
 +            flip_comma::flip_comma,
 +            flip_trait_bound::flip_trait_bound,
 +            generate_constant::generate_constant,
 +            generate_default_from_enum_variant::generate_default_from_enum_variant,
 +            generate_default_from_new::generate_default_from_new,
 +            generate_derive::generate_derive,
 +            generate_documentation_template::generate_documentation_template,
 +            generate_documentation_template::generate_doc_example,
 +            generate_enum_is_method::generate_enum_is_method,
 +            generate_enum_projection_method::generate_enum_as_method,
 +            generate_enum_projection_method::generate_enum_try_into_method,
 +            generate_enum_variant::generate_enum_variant,
 +            generate_from_impl_for_enum::generate_from_impl_for_enum,
 +            generate_function::generate_function,
 +            generate_impl::generate_impl,
 +            generate_is_empty_from_len::generate_is_empty_from_len,
 +            generate_new::generate_new,
 +            inline_call::inline_call,
 +            inline_call::inline_into_callers,
 +            inline_local_variable::inline_local_variable,
 +            inline_type_alias::inline_type_alias,
 +            inline_type_alias::inline_type_alias_uses,
 +            introduce_named_generic::introduce_named_generic,
 +            introduce_named_lifetime::introduce_named_lifetime,
 +            invert_if::invert_if,
 +            merge_imports::merge_imports,
 +            merge_match_arms::merge_match_arms,
 +            move_bounds::move_bounds_to_where_clause,
 +            move_format_string_arg::move_format_string_arg,
 +            move_guard::move_arm_cond_to_match_guard,
 +            move_guard::move_guard_to_arm_body,
 +            move_module_to_file::move_module_to_file,
 +            move_to_mod_rs::move_to_mod_rs,
 +            move_from_mod_rs::move_from_mod_rs,
 +            number_representation::reformat_number_literal,
 +            pull_assignment_up::pull_assignment_up,
 +            promote_local_to_const::promote_local_to_const,
 +            qualify_path::qualify_path,
 +            qualify_method_call::qualify_method_call,
 +            raw_string::add_hash,
 +            raw_string::make_usual_string,
 +            raw_string::remove_hash,
 +            remove_dbg::remove_dbg,
 +            remove_mut::remove_mut,
 +            remove_unused_param::remove_unused_param,
 +            reorder_fields::reorder_fields,
 +            reorder_impl_items::reorder_impl_items,
 +            replace_try_expr_with_match::replace_try_expr_with_match,
 +            replace_derive_with_manual_impl::replace_derive_with_manual_impl,
 +            replace_if_let_with_match::replace_if_let_with_match,
 +            replace_if_let_with_match::replace_match_with_if_let,
 +            replace_let_with_if_let::replace_let_with_if_let,
 +            replace_or_with_or_else::replace_or_else_with_or,
 +            replace_or_with_or_else::replace_or_with_or_else,
 +            replace_turbofish_with_explicit_type::replace_turbofish_with_explicit_type,
 +            replace_qualified_name_with_use::replace_qualified_name_with_use,
 +            sort_items::sort_items,
 +            split_import::split_import,
 +            toggle_ignore::toggle_ignore,
 +            unmerge_match_arm::unmerge_match_arm,
 +            unmerge_use::unmerge_use,
 +            unnecessary_async::unnecessary_async,
 +            unwrap_block::unwrap_block,
 +            unwrap_result_return_type::unwrap_result_return_type,
 +            unwrap_tuple::unwrap_tuple,
 +            wrap_return_type_in_result::wrap_return_type_in_result,
 +            // These are manually sorted for better priorities. By default,
 +            // priority is determined by the size of the target range (smaller
 +            // target wins). If the ranges are equal, position in this list is
 +            // used as a tie-breaker.
 +            add_missing_impl_members::add_missing_impl_members,
 +            add_missing_impl_members::add_missing_default_members,
 +            //
 +            replace_string_with_char::replace_string_with_char,
 +            replace_string_with_char::replace_char_with_string,
 +            raw_string::make_raw_string,
 +            //
 +            extract_variable::extract_variable,
 +            extract_function::extract_function,
 +            extract_module::extract_module,
 +            //
 +            generate_getter::generate_getter,
 +            generate_getter::generate_getter_mut,
 +            generate_setter::generate_setter,
 +            generate_delegate_methods::generate_delegate_methods,
 +            generate_deref::generate_deref,
 +            // Are you sure you want to add new assist here, and not to the
 +            // sorted list above?
 +        ]
 +    }
 +}
index f7f2417d0745d7a4088ed75a11f7241875bb5663,0000000000000000000000000000000000000000..92ced27c78aedf801b3c5eee80a066aea043be72
mode 100644,000000..100644
--- /dev/null
@@@ -1,564 -1,0 +1,565 @@@
 +mod generated;
 +#[cfg(not(feature = "in-rust-tree"))]
 +mod sourcegen;
 +
 +use expect_test::expect;
 +use hir::{db::DefDatabase, Semantics};
 +use ide_db::{
 +    base_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt},
 +    imports::insert_use::{ImportGranularity, InsertUseConfig},
 +    source_change::FileSystemEdit,
 +    RootDatabase, SnippetCap,
 +};
 +use stdx::{format_to, trim_indent};
 +use syntax::TextRange;
 +use test_utils::{assert_eq_text, extract_offset};
 +
 +use crate::{
 +    assists, handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind,
 +    AssistResolveStrategy, Assists, SingleResolve,
 +};
 +
 +pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig {
 +    snippet_cap: SnippetCap::new(true),
 +    allowed: None,
 +    insert_use: InsertUseConfig {
 +        granularity: ImportGranularity::Crate,
 +        prefix_kind: hir::PrefixKind::Plain,
 +        enforce_granularity: true,
 +        group: true,
 +        skip_glob_imports: true,
 +    },
 +    prefer_no_std: false,
++    assist_emit_must_use: false,
 +};
 +
 +pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) {
 +    RootDatabase::with_single_file(text)
 +}
 +
 +#[track_caller]
 +pub(crate) fn check_assist(assist: Handler, ra_fixture_before: &str, ra_fixture_after: &str) {
 +    let ra_fixture_after = trim_indent(ra_fixture_after);
 +    check(assist, ra_fixture_before, ExpectedResult::After(&ra_fixture_after), None);
 +}
 +
 +// There is no way to choose what assist within a group you want to test against,
 +// so this is here to allow you choose.
 +pub(crate) fn check_assist_by_label(
 +    assist: Handler,
 +    ra_fixture_before: &str,
 +    ra_fixture_after: &str,
 +    label: &str,
 +) {
 +    let ra_fixture_after = trim_indent(ra_fixture_after);
 +    check(assist, ra_fixture_before, ExpectedResult::After(&ra_fixture_after), Some(label));
 +}
 +
 +// FIXME: instead of having a separate function here, maybe use
 +// `extract_ranges` and mark the target as `<target> </target>` in the
 +// fixture?
 +#[track_caller]
 +pub(crate) fn check_assist_target(assist: Handler, ra_fixture: &str, target: &str) {
 +    check(assist, ra_fixture, ExpectedResult::Target(target), None);
 +}
 +
 +#[track_caller]
 +pub(crate) fn check_assist_not_applicable(assist: Handler, ra_fixture: &str) {
 +    check(assist, ra_fixture, ExpectedResult::NotApplicable, None);
 +}
 +
 +/// Check assist in unresolved state. Useful to check assists for lazy computation.
 +#[track_caller]
 +pub(crate) fn check_assist_unresolved(assist: Handler, ra_fixture: &str) {
 +    check(assist, ra_fixture, ExpectedResult::Unresolved, None);
 +}
 +
 +#[track_caller]
 +fn check_doc_test(assist_id: &str, before: &str, after: &str) {
 +    let after = trim_indent(after);
 +    let (db, file_id, selection) = RootDatabase::with_range_or_offset(before);
 +    let before = db.file_text(file_id).to_string();
 +    let frange = FileRange { file_id, range: selection.into() };
 +
 +    let assist = assists(&db, &TEST_CONFIG, AssistResolveStrategy::All, frange)
 +        .into_iter()
 +        .find(|assist| assist.id.0 == assist_id)
 +        .unwrap_or_else(|| {
 +            panic!(
 +                "\n\nAssist is not applicable: {}\nAvailable assists: {}",
 +                assist_id,
 +                assists(&db, &TEST_CONFIG, AssistResolveStrategy::None, frange)
 +                    .into_iter()
 +                    .map(|assist| assist.id.0)
 +                    .collect::<Vec<_>>()
 +                    .join(", ")
 +            )
 +        });
 +
 +    let actual = {
 +        let source_change = assist
 +            .source_change
 +            .filter(|it| !it.source_file_edits.is_empty() || !it.file_system_edits.is_empty())
 +            .expect("Assist did not contain any source changes");
 +        let mut actual = before;
 +        if let Some(source_file_edit) = source_change.get_source_edit(file_id) {
 +            source_file_edit.apply(&mut actual);
 +        }
 +        actual
 +    };
 +    assert_eq_text!(&after, &actual);
 +}
 +
 +enum ExpectedResult<'a> {
 +    NotApplicable,
 +    Unresolved,
 +    After(&'a str),
 +    Target(&'a str),
 +}
 +
 +#[track_caller]
 +fn check(handler: Handler, before: &str, expected: ExpectedResult<'_>, assist_label: Option<&str>) {
 +    let (mut db, file_with_caret_id, range_or_offset) = RootDatabase::with_range_or_offset(before);
 +    db.set_enable_proc_attr_macros(true);
 +    let text_without_caret = db.file_text(file_with_caret_id).to_string();
 +
 +    let frange = FileRange { file_id: file_with_caret_id, range: range_or_offset.into() };
 +
 +    let sema = Semantics::new(&db);
 +    let config = TEST_CONFIG;
 +    let ctx = AssistContext::new(sema, &config, frange);
 +    let resolve = match expected {
 +        ExpectedResult::Unresolved => AssistResolveStrategy::None,
 +        _ => AssistResolveStrategy::All,
 +    };
 +    let mut acc = Assists::new(&ctx, resolve);
 +    handler(&mut acc, &ctx);
 +    let mut res = acc.finish();
 +
 +    let assist = match assist_label {
 +        Some(label) => res.into_iter().find(|resolved| resolved.label == label),
 +        None => res.pop(),
 +    };
 +
 +    match (assist, expected) {
 +        (Some(assist), ExpectedResult::After(after)) => {
 +            let source_change = assist
 +                .source_change
 +                .filter(|it| !it.source_file_edits.is_empty() || !it.file_system_edits.is_empty())
 +                .expect("Assist did not contain any source changes");
 +            let skip_header = source_change.source_file_edits.len() == 1
 +                && source_change.file_system_edits.len() == 0;
 +
 +            let mut buf = String::new();
 +            for (file_id, edit) in source_change.source_file_edits {
 +                let mut text = db.file_text(file_id).as_ref().to_owned();
 +                edit.apply(&mut text);
 +                if !skip_header {
 +                    let sr = db.file_source_root(file_id);
 +                    let sr = db.source_root(sr);
 +                    let path = sr.path_for_file(&file_id).unwrap();
 +                    format_to!(buf, "//- {}\n", path)
 +                }
 +                buf.push_str(&text);
 +            }
 +
 +            for file_system_edit in source_change.file_system_edits {
 +                let (dst, contents) = match file_system_edit {
 +                    FileSystemEdit::CreateFile { dst, initial_contents } => (dst, initial_contents),
 +                    FileSystemEdit::MoveFile { src, dst } => {
 +                        (dst, db.file_text(src).as_ref().to_owned())
 +                    }
 +                    FileSystemEdit::MoveDir { src, src_id, dst } => {
 +                        // temporary placeholder for MoveDir since we are not using MoveDir in ide assists yet.
 +                        (dst, format!("{:?}\n{:?}", src_id, src))
 +                    }
 +                };
 +                let sr = db.file_source_root(dst.anchor);
 +                let sr = db.source_root(sr);
 +                let mut base = sr.path_for_file(&dst.anchor).unwrap().clone();
 +                base.pop();
 +                let created_file_path = base.join(&dst.path).unwrap();
 +                format_to!(buf, "//- {}\n", created_file_path);
 +                buf.push_str(&contents);
 +            }
 +
 +            assert_eq_text!(after, &buf);
 +        }
 +        (Some(assist), ExpectedResult::Target(target)) => {
 +            let range = assist.target;
 +            assert_eq_text!(&text_without_caret[range], target);
 +        }
 +        (Some(assist), ExpectedResult::Unresolved) => assert!(
 +            assist.source_change.is_none(),
 +            "unresolved assist should not contain source changes"
 +        ),
 +        (Some(_), ExpectedResult::NotApplicable) => panic!("assist should not be applicable!"),
 +        (
 +            None,
 +            ExpectedResult::After(_) | ExpectedResult::Target(_) | ExpectedResult::Unresolved,
 +        ) => {
 +            panic!("code action is not applicable")
 +        }
 +        (None, ExpectedResult::NotApplicable) => (),
 +    };
 +}
 +
 +fn labels(assists: &[Assist]) -> String {
 +    let mut labels = assists
 +        .iter()
 +        .map(|assist| {
 +            let mut label = match &assist.group {
 +                Some(g) => g.0.clone(),
 +                None => assist.label.to_string(),
 +            };
 +            label.push('\n');
 +            label
 +        })
 +        .collect::<Vec<_>>();
 +    labels.dedup();
 +    labels.into_iter().collect::<String>()
 +}
 +
 +#[test]
 +fn assist_order_field_struct() {
 +    let before = "struct Foo { $0bar: u32 }";
 +    let (before_cursor_pos, before) = extract_offset(before);
 +    let (db, file_id) = with_single_file(&before);
 +    let frange = FileRange { file_id, range: TextRange::empty(before_cursor_pos) };
 +    let assists = assists(&db, &TEST_CONFIG, AssistResolveStrategy::None, frange);
 +    let mut assists = assists.iter();
 +
 +    assert_eq!(assists.next().expect("expected assist").label, "Change visibility to pub(crate)");
 +    assert_eq!(assists.next().expect("expected assist").label, "Generate a getter method");
 +    assert_eq!(assists.next().expect("expected assist").label, "Generate a mut getter method");
 +    assert_eq!(assists.next().expect("expected assist").label, "Generate a setter method");
 +    assert_eq!(assists.next().expect("expected assist").label, "Convert to tuple struct");
 +    assert_eq!(assists.next().expect("expected assist").label, "Add `#[derive]`");
 +}
 +
 +#[test]
 +fn assist_order_if_expr() {
 +    let (db, frange) = RootDatabase::with_range(
 +        r#"
 +pub fn test_some_range(a: int) -> bool {
 +    if let 2..6 = $05$0 {
 +        true
 +    } else {
 +        false
 +    }
 +}
 +"#,
 +    );
 +
 +    let assists = assists(&db, &TEST_CONFIG, AssistResolveStrategy::None, frange);
 +    let expected = labels(&assists);
 +
 +    expect![[r#"
 +        Convert integer base
 +        Extract into variable
 +        Extract into function
 +        Replace if let with match
 +    "#]]
 +    .assert_eq(&expected);
 +}
 +
 +#[test]
 +fn assist_filter_works() {
 +    let (db, frange) = RootDatabase::with_range(
 +        r#"
 +pub fn test_some_range(a: int) -> bool {
 +    if let 2..6 = $05$0 {
 +        true
 +    } else {
 +        false
 +    }
 +}
 +"#,
 +    );
 +    {
 +        let mut cfg = TEST_CONFIG;
 +        cfg.allowed = Some(vec![AssistKind::Refactor]);
 +
 +        let assists = assists(&db, &cfg, AssistResolveStrategy::None, frange);
 +        let expected = labels(&assists);
 +
 +        expect![[r#"
 +            Convert integer base
 +            Extract into variable
 +            Extract into function
 +            Replace if let with match
 +        "#]]
 +        .assert_eq(&expected);
 +    }
 +
 +    {
 +        let mut cfg = TEST_CONFIG;
 +        cfg.allowed = Some(vec![AssistKind::RefactorExtract]);
 +        let assists = assists(&db, &cfg, AssistResolveStrategy::None, frange);
 +        let expected = labels(&assists);
 +
 +        expect![[r#"
 +            Extract into variable
 +            Extract into function
 +        "#]]
 +        .assert_eq(&expected);
 +    }
 +
 +    {
 +        let mut cfg = TEST_CONFIG;
 +        cfg.allowed = Some(vec![AssistKind::QuickFix]);
 +        let assists = assists(&db, &cfg, AssistResolveStrategy::None, frange);
 +        let expected = labels(&assists);
 +
 +        expect![[r#""#]].assert_eq(&expected);
 +    }
 +}
 +
 +#[test]
 +fn various_resolve_strategies() {
 +    let (db, frange) = RootDatabase::with_range(
 +        r#"
 +pub fn test_some_range(a: int) -> bool {
 +    if let 2..6 = $05$0 {
 +        true
 +    } else {
 +        false
 +    }
 +}
 +"#,
 +    );
 +
 +    let mut cfg = TEST_CONFIG;
 +    cfg.allowed = Some(vec![AssistKind::RefactorExtract]);
 +
 +    {
 +        let assists = assists(&db, &cfg, AssistResolveStrategy::None, frange);
 +        assert_eq!(2, assists.len());
 +        let mut assists = assists.into_iter();
 +
 +        let extract_into_variable_assist = assists.next().unwrap();
 +        expect![[r#"
 +            Assist {
 +                id: AssistId(
 +                    "extract_variable",
 +                    RefactorExtract,
 +                ),
 +                label: "Extract into variable",
 +                group: None,
 +                target: 59..60,
 +                source_change: None,
 +                trigger_signature_help: false,
 +            }
 +        "#]]
 +        .assert_debug_eq(&extract_into_variable_assist);
 +
 +        let extract_into_function_assist = assists.next().unwrap();
 +        expect![[r#"
 +            Assist {
 +                id: AssistId(
 +                    "extract_function",
 +                    RefactorExtract,
 +                ),
 +                label: "Extract into function",
 +                group: None,
 +                target: 59..60,
 +                source_change: None,
 +                trigger_signature_help: false,
 +            }
 +        "#]]
 +        .assert_debug_eq(&extract_into_function_assist);
 +    }
 +
 +    {
 +        let assists = assists(
 +            &db,
 +            &cfg,
 +            AssistResolveStrategy::Single(SingleResolve {
 +                assist_id: "SOMETHING_MISMATCHING".to_string(),
 +                assist_kind: AssistKind::RefactorExtract,
 +            }),
 +            frange,
 +        );
 +        assert_eq!(2, assists.len());
 +        let mut assists = assists.into_iter();
 +
 +        let extract_into_variable_assist = assists.next().unwrap();
 +        expect![[r#"
 +            Assist {
 +                id: AssistId(
 +                    "extract_variable",
 +                    RefactorExtract,
 +                ),
 +                label: "Extract into variable",
 +                group: None,
 +                target: 59..60,
 +                source_change: None,
 +                trigger_signature_help: false,
 +            }
 +        "#]]
 +        .assert_debug_eq(&extract_into_variable_assist);
 +
 +        let extract_into_function_assist = assists.next().unwrap();
 +        expect![[r#"
 +            Assist {
 +                id: AssistId(
 +                    "extract_function",
 +                    RefactorExtract,
 +                ),
 +                label: "Extract into function",
 +                group: None,
 +                target: 59..60,
 +                source_change: None,
 +                trigger_signature_help: false,
 +            }
 +        "#]]
 +        .assert_debug_eq(&extract_into_function_assist);
 +    }
 +
 +    {
 +        let assists = assists(
 +            &db,
 +            &cfg,
 +            AssistResolveStrategy::Single(SingleResolve {
 +                assist_id: "extract_variable".to_string(),
 +                assist_kind: AssistKind::RefactorExtract,
 +            }),
 +            frange,
 +        );
 +        assert_eq!(2, assists.len());
 +        let mut assists = assists.into_iter();
 +
 +        let extract_into_variable_assist = assists.next().unwrap();
 +        expect![[r#"
 +            Assist {
 +                id: AssistId(
 +                    "extract_variable",
 +                    RefactorExtract,
 +                ),
 +                label: "Extract into variable",
 +                group: None,
 +                target: 59..60,
 +                source_change: Some(
 +                    SourceChange {
 +                        source_file_edits: {
 +                            FileId(
 +                                0,
 +                            ): TextEdit {
 +                                indels: [
 +                                    Indel {
 +                                        insert: "let $0var_name = 5;\n    ",
 +                                        delete: 45..45,
 +                                    },
 +                                    Indel {
 +                                        insert: "var_name",
 +                                        delete: 59..60,
 +                                    },
 +                                ],
 +                            },
 +                        },
 +                        file_system_edits: [],
 +                        is_snippet: true,
 +                    },
 +                ),
 +                trigger_signature_help: false,
 +            }
 +        "#]]
 +        .assert_debug_eq(&extract_into_variable_assist);
 +
 +        let extract_into_function_assist = assists.next().unwrap();
 +        expect![[r#"
 +            Assist {
 +                id: AssistId(
 +                    "extract_function",
 +                    RefactorExtract,
 +                ),
 +                label: "Extract into function",
 +                group: None,
 +                target: 59..60,
 +                source_change: None,
 +                trigger_signature_help: false,
 +            }
 +        "#]]
 +        .assert_debug_eq(&extract_into_function_assist);
 +    }
 +
 +    {
 +        let assists = assists(&db, &cfg, AssistResolveStrategy::All, frange);
 +        assert_eq!(2, assists.len());
 +        let mut assists = assists.into_iter();
 +
 +        let extract_into_variable_assist = assists.next().unwrap();
 +        expect![[r#"
 +            Assist {
 +                id: AssistId(
 +                    "extract_variable",
 +                    RefactorExtract,
 +                ),
 +                label: "Extract into variable",
 +                group: None,
 +                target: 59..60,
 +                source_change: Some(
 +                    SourceChange {
 +                        source_file_edits: {
 +                            FileId(
 +                                0,
 +                            ): TextEdit {
 +                                indels: [
 +                                    Indel {
 +                                        insert: "let $0var_name = 5;\n    ",
 +                                        delete: 45..45,
 +                                    },
 +                                    Indel {
 +                                        insert: "var_name",
 +                                        delete: 59..60,
 +                                    },
 +                                ],
 +                            },
 +                        },
 +                        file_system_edits: [],
 +                        is_snippet: true,
 +                    },
 +                ),
 +                trigger_signature_help: false,
 +            }
 +        "#]]
 +        .assert_debug_eq(&extract_into_variable_assist);
 +
 +        let extract_into_function_assist = assists.next().unwrap();
 +        expect![[r#"
 +            Assist {
 +                id: AssistId(
 +                    "extract_function",
 +                    RefactorExtract,
 +                ),
 +                label: "Extract into function",
 +                group: None,
 +                target: 59..60,
 +                source_change: Some(
 +                    SourceChange {
 +                        source_file_edits: {
 +                            FileId(
 +                                0,
 +                            ): TextEdit {
 +                                indels: [
 +                                    Indel {
 +                                        insert: "fun_name()",
 +                                        delete: 59..60,
 +                                    },
 +                                    Indel {
 +                                        insert: "\n\nfn $0fun_name() -> i32 {\n    5\n}",
 +                                        delete: 110..110,
 +                                    },
 +                                ],
 +                            },
 +                        },
 +                        file_system_edits: [],
 +                        is_snippet: true,
 +                    },
 +                ),
 +                trigger_signature_help: false,
 +            }
 +        "#]]
 +        .assert_debug_eq(&extract_into_function_assist);
 +    }
 +}
index 2c4000efe0fa25a9f0c159d0c5f2f046ab04c9b5,0000000000000000000000000000000000000000..029d169899bb44a9e5ee76f49b1891fabdd3f991
mode 100644,000000..100644
--- /dev/null
@@@ -1,2461 -1,0 +1,2482 @@@
 +//! Generated by `sourcegen_assists_docs`, do not edit by hand.
 +
 +use super::check_doc_test;
 +
 +#[test]
 +fn doctest_add_explicit_type() {
 +    check_doc_test(
 +        "add_explicit_type",
 +        r#####"
 +fn main() {
 +    let x$0 = 92;
 +}
 +"#####,
 +        r#####"
 +fn main() {
 +    let x: i32 = 92;
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_add_hash() {
 +    check_doc_test(
 +        "add_hash",
 +        r#####"
 +fn main() {
 +    r#"Hello,$0 World!"#;
 +}
 +"#####,
 +        r#####"
 +fn main() {
 +    r##"Hello, World!"##;
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_add_impl_default_members() {
 +    check_doc_test(
 +        "add_impl_default_members",
 +        r#####"
 +trait Trait {
 +    type X;
 +    fn foo(&self);
 +    fn bar(&self) {}
 +}
 +
 +impl Trait for () {
 +    type X = ();
 +    fn foo(&self) {}$0
 +}
 +"#####,
 +        r#####"
 +trait Trait {
 +    type X;
 +    fn foo(&self);
 +    fn bar(&self) {}
 +}
 +
 +impl Trait for () {
 +    type X = ();
 +    fn foo(&self) {}
 +
 +    $0fn bar(&self) {}
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_add_impl_missing_members() {
 +    check_doc_test(
 +        "add_impl_missing_members",
 +        r#####"
 +trait Trait<T> {
 +    type X;
 +    fn foo(&self) -> T;
 +    fn bar(&self) {}
 +}
 +
 +impl Trait<u32> for () {$0
 +
 +}
 +"#####,
 +        r#####"
 +trait Trait<T> {
 +    type X;
 +    fn foo(&self) -> T;
 +    fn bar(&self) {}
 +}
 +
 +impl Trait<u32> for () {
 +    $0type X;
 +
 +    fn foo(&self) -> u32 {
 +        todo!()
 +    }
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_add_label_to_loop() {
 +    check_doc_test(
 +        "add_label_to_loop",
 +        r#####"
 +fn main() {
 +    loop$0 {
 +        break;
 +        continue;
 +    }
 +}
 +"#####,
 +        r#####"
 +fn main() {
 +    'l: loop {
 +        break 'l;
 +        continue 'l;
 +    }
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_add_lifetime_to_type() {
 +    check_doc_test(
 +        "add_lifetime_to_type",
 +        r#####"
 +struct Point {
 +    x: &$0u32,
 +    y: u32,
 +}
 +"#####,
 +        r#####"
 +struct Point<'a> {
 +    x: &'a u32,
 +    y: u32,
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_add_missing_match_arms() {
 +    check_doc_test(
 +        "add_missing_match_arms",
 +        r#####"
 +enum Action { Move { distance: u32 }, Stop }
 +
 +fn handle(action: Action) {
 +    match action {
 +        $0
 +    }
 +}
 +"#####,
 +        r#####"
 +enum Action { Move { distance: u32 }, Stop }
 +
 +fn handle(action: Action) {
 +    match action {
 +        $0Action::Move { distance } => todo!(),
 +        Action::Stop => todo!(),
 +    }
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_add_return_type() {
 +    check_doc_test(
 +        "add_return_type",
 +        r#####"
 +fn foo() { 4$02i32 }
 +"#####,
 +        r#####"
 +fn foo() -> i32 { 42i32 }
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_add_turbo_fish() {
 +    check_doc_test(
 +        "add_turbo_fish",
 +        r#####"
 +fn make<T>() -> T { todo!() }
 +fn main() {
 +    let x = make$0();
 +}
 +"#####,
 +        r#####"
 +fn make<T>() -> T { todo!() }
 +fn main() {
 +    let x = make::<${0:_}>();
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_apply_demorgan() {
 +    check_doc_test(
 +        "apply_demorgan",
 +        r#####"
 +fn main() {
 +    if x != 4 ||$0 y < 3.14 {}
 +}
 +"#####,
 +        r#####"
 +fn main() {
 +    if !(x == 4 && y >= 3.14) {}
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_auto_import() {
 +    check_doc_test(
 +        "auto_import",
 +        r#####"
 +fn main() {
 +    let map = HashMap$0::new();
 +}
 +pub mod std { pub mod collections { pub struct HashMap { } } }
 +"#####,
 +        r#####"
 +use std::collections::HashMap;
 +
 +fn main() {
 +    let map = HashMap::new();
 +}
 +pub mod std { pub mod collections { pub struct HashMap { } } }
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_change_visibility() {
 +    check_doc_test(
 +        "change_visibility",
 +        r#####"
 +$0fn frobnicate() {}
 +"#####,
 +        r#####"
 +pub(crate) fn frobnicate() {}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_convert_bool_then_to_if() {
 +    check_doc_test(
 +        "convert_bool_then_to_if",
 +        r#####"
 +//- minicore: bool_impl
 +fn main() {
 +    (0 == 0).then$0(|| val)
 +}
 +"#####,
 +        r#####"
 +fn main() {
 +    if 0 == 0 {
 +        Some(val)
 +    } else {
 +        None
 +    }
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_convert_for_loop_with_for_each() {
 +    check_doc_test(
 +        "convert_for_loop_with_for_each",
 +        r#####"
 +fn main() {
 +    let x = vec![1, 2, 3];
 +    for$0 v in x {
 +        let y = v * 2;
 +    }
 +}
 +"#####,
 +        r#####"
 +fn main() {
 +    let x = vec![1, 2, 3];
 +    x.into_iter().for_each(|v| {
 +        let y = v * 2;
 +    });
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_convert_if_to_bool_then() {
 +    check_doc_test(
 +        "convert_if_to_bool_then",
 +        r#####"
 +//- minicore: option
 +fn main() {
 +    if$0 cond {
 +        Some(val)
 +    } else {
 +        None
 +    }
 +}
 +"#####,
 +        r#####"
 +fn main() {
 +    cond.then(|| val)
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_convert_integer_literal() {
 +    check_doc_test(
 +        "convert_integer_literal",
 +        r#####"
 +const _: i32 = 10$0;
 +"#####,
 +        r#####"
 +const _: i32 = 0b1010;
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_convert_into_to_from() {
 +    check_doc_test(
 +        "convert_into_to_from",
 +        r#####"
 +//- minicore: from
 +impl $0Into<Thing> for usize {
 +    fn into(self) -> Thing {
 +        Thing {
 +            b: self.to_string(),
 +            a: self
 +        }
 +    }
 +}
 +"#####,
 +        r#####"
 +impl From<usize> for Thing {
 +    fn from(val: usize) -> Self {
 +        Thing {
 +            b: val.to_string(),
 +            a: val
 +        }
 +    }
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_convert_iter_for_each_to_for() {
 +    check_doc_test(
 +        "convert_iter_for_each_to_for",
 +        r#####"
 +//- minicore: iterators
 +use core::iter;
 +fn main() {
 +    let iter = iter::repeat((9, 2));
 +    iter.for_each$0(|(x, y)| {
 +        println!("x: {}, y: {}", x, y);
 +    });
 +}
 +"#####,
 +        r#####"
 +use core::iter;
 +fn main() {
 +    let iter = iter::repeat((9, 2));
 +    for (x, y) in iter {
 +        println!("x: {}, y: {}", x, y);
 +    }
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_convert_let_else_to_match() {
 +    check_doc_test(
 +        "convert_let_else_to_match",
 +        r#####"
 +fn main() {
 +    let Ok(mut x) = f() else$0 { return };
 +}
 +"#####,
 +        r#####"
 +fn main() {
 +    let mut x = match f() {
 +        Ok(x) => x,
 +        _ => return,
 +    };
 +}
 +"#####,
 +    )
 +}
 +
++#[test]
++fn doctest_convert_match_to_let_else() {
++    check_doc_test(
++        "convert_match_to_let_else",
++        r#####"
++//- minicore: option
++fn foo(opt: Option<()>) {
++    let val = $0match opt {
++        Some(it) => it,
++        None => return,
++    };
++}
++"#####,
++        r#####"
++fn foo(opt: Option<()>) {
++    let Some(val) = opt else { return };
++}
++"#####,
++    )
++}
++
 +#[test]
 +fn doctest_convert_named_struct_to_tuple_struct() {
 +    check_doc_test(
 +        "convert_named_struct_to_tuple_struct",
 +        r#####"
 +struct Point$0 { x: f32, y: f32 }
 +
 +impl Point {
 +    pub fn new(x: f32, y: f32) -> Self {
 +        Point { x, y }
 +    }
 +
 +    pub fn x(&self) -> f32 {
 +        self.x
 +    }
 +
 +    pub fn y(&self) -> f32 {
 +        self.y
 +    }
 +}
 +"#####,
 +        r#####"
 +struct Point(f32, f32);
 +
 +impl Point {
 +    pub fn new(x: f32, y: f32) -> Self {
 +        Point(x, y)
 +    }
 +
 +    pub fn x(&self) -> f32 {
 +        self.0
 +    }
 +
 +    pub fn y(&self) -> f32 {
 +        self.1
 +    }
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_convert_to_guarded_return() {
 +    check_doc_test(
 +        "convert_to_guarded_return",
 +        r#####"
 +fn main() {
 +    $0if cond {
 +        foo();
 +        bar();
 +    }
 +}
 +"#####,
 +        r#####"
 +fn main() {
 +    if !cond {
 +        return;
 +    }
 +    foo();
 +    bar();
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_convert_tuple_struct_to_named_struct() {
 +    check_doc_test(
 +        "convert_tuple_struct_to_named_struct",
 +        r#####"
 +struct Point$0(f32, f32);
 +
 +impl Point {
 +    pub fn new(x: f32, y: f32) -> Self {
 +        Point(x, y)
 +    }
 +
 +    pub fn x(&self) -> f32 {
 +        self.0
 +    }
 +
 +    pub fn y(&self) -> f32 {
 +        self.1
 +    }
 +}
 +"#####,
 +        r#####"
 +struct Point { field1: f32, field2: f32 }
 +
 +impl Point {
 +    pub fn new(x: f32, y: f32) -> Self {
 +        Point { field1: x, field2: y }
 +    }
 +
 +    pub fn x(&self) -> f32 {
 +        self.field1
 +    }
 +
 +    pub fn y(&self) -> f32 {
 +        self.field2
 +    }
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_convert_two_arm_bool_match_to_matches_macro() {
 +    check_doc_test(
 +        "convert_two_arm_bool_match_to_matches_macro",
 +        r#####"
 +fn main() {
 +    match scrutinee$0 {
 +        Some(val) if val.cond() => true,
 +        _ => false,
 +    }
 +}
 +"#####,
 +        r#####"
 +fn main() {
 +    matches!(scrutinee, Some(val) if val.cond())
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_convert_while_to_loop() {
 +    check_doc_test(
 +        "convert_while_to_loop",
 +        r#####"
 +fn main() {
 +    $0while cond {
 +        foo();
 +    }
 +}
 +"#####,
 +        r#####"
 +fn main() {
 +    loop {
 +        if !cond {
 +            break;
 +        }
 +        foo();
 +    }
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_destructure_tuple_binding() {
 +    check_doc_test(
 +        "destructure_tuple_binding",
 +        r#####"
 +fn main() {
 +    let $0t = (1,2);
 +    let v = t.0;
 +}
 +"#####,
 +        r#####"
 +fn main() {
 +    let ($0_0, _1) = (1,2);
 +    let v = _0;
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_expand_glob_import() {
 +    check_doc_test(
 +        "expand_glob_import",
 +        r#####"
 +mod foo {
 +    pub struct Bar;
 +    pub struct Baz;
 +}
 +
 +use foo::*$0;
 +
 +fn qux(bar: Bar, baz: Baz) {}
 +"#####,
 +        r#####"
 +mod foo {
 +    pub struct Bar;
 +    pub struct Baz;
 +}
 +
 +use foo::{Bar, Baz};
 +
 +fn qux(bar: Bar, baz: Baz) {}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_extract_function() {
 +    check_doc_test(
 +        "extract_function",
 +        r#####"
 +fn main() {
 +    let n = 1;
 +    $0let m = n + 2;
 +    // calculate
 +    let k = m + n;$0
 +    let g = 3;
 +}
 +"#####,
 +        r#####"
 +fn main() {
 +    let n = 1;
 +    fun_name(n);
 +    let g = 3;
 +}
 +
 +fn $0fun_name(n: i32) {
 +    let m = n + 2;
 +    // calculate
 +    let k = m + n;
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_extract_module() {
 +    check_doc_test(
 +        "extract_module",
 +        r#####"
 +$0fn foo(name: i32) -> i32 {
 +    name + 1
 +}$0
 +
 +fn bar(name: i32) -> i32 {
 +    name + 2
 +}
 +"#####,
 +        r#####"
 +mod modname {
 +    pub(crate) fn foo(name: i32) -> i32 {
 +        name + 1
 +    }
 +}
 +
 +fn bar(name: i32) -> i32 {
 +    name + 2
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_extract_struct_from_enum_variant() {
 +    check_doc_test(
 +        "extract_struct_from_enum_variant",
 +        r#####"
 +enum A { $0One(u32, u32) }
 +"#####,
 +        r#####"
 +struct One(u32, u32);
 +
 +enum A { One(One) }
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_extract_type_alias() {
 +    check_doc_test(
 +        "extract_type_alias",
 +        r#####"
 +struct S {
 +    field: $0(u8, u8, u8)$0,
 +}
 +"#####,
 +        r#####"
 +type $0Type = (u8, u8, u8);
 +
 +struct S {
 +    field: Type,
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_extract_variable() {
 +    check_doc_test(
 +        "extract_variable",
 +        r#####"
 +fn main() {
 +    $0(1 + 2)$0 * 4;
 +}
 +"#####,
 +        r#####"
 +fn main() {
 +    let $0var_name = (1 + 2);
 +    var_name * 4;
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_fix_visibility() {
 +    check_doc_test(
 +        "fix_visibility",
 +        r#####"
 +mod m {
 +    fn frobnicate() {}
 +}
 +fn main() {
 +    m::frobnicate$0() {}
 +}
 +"#####,
 +        r#####"
 +mod m {
 +    $0pub(crate) fn frobnicate() {}
 +}
 +fn main() {
 +    m::frobnicate() {}
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_flip_binexpr() {
 +    check_doc_test(
 +        "flip_binexpr",
 +        r#####"
 +fn main() {
 +    let _ = 90 +$0 2;
 +}
 +"#####,
 +        r#####"
 +fn main() {
 +    let _ = 2 + 90;
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_flip_comma() {
 +    check_doc_test(
 +        "flip_comma",
 +        r#####"
 +fn main() {
 +    ((1, 2),$0 (3, 4));
 +}
 +"#####,
 +        r#####"
 +fn main() {
 +    ((3, 4), (1, 2));
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_flip_trait_bound() {
 +    check_doc_test(
 +        "flip_trait_bound",
 +        r#####"
 +fn foo<T: Clone +$0 Copy>() { }
 +"#####,
 +        r#####"
 +fn foo<T: Copy + Clone>() { }
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_generate_constant() {
 +    check_doc_test(
 +        "generate_constant",
 +        r#####"
 +struct S { i: usize }
 +impl S { pub fn new(n: usize) {} }
 +fn main() {
 +    let v = S::new(CAPA$0CITY);
 +}
 +"#####,
 +        r#####"
 +struct S { i: usize }
 +impl S { pub fn new(n: usize) {} }
 +fn main() {
 +    const CAPACITY: usize = $0;
 +    let v = S::new(CAPACITY);
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_generate_default_from_enum_variant() {
 +    check_doc_test(
 +        "generate_default_from_enum_variant",
 +        r#####"
 +enum Version {
 + Undefined,
 + Minor$0,
 + Major,
 +}
 +"#####,
 +        r#####"
 +enum Version {
 + Undefined,
 + Minor,
 + Major,
 +}
 +
 +impl Default for Version {
 +    fn default() -> Self {
 +        Self::Minor
 +    }
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_generate_default_from_new() {
 +    check_doc_test(
 +        "generate_default_from_new",
 +        r#####"
 +struct Example { _inner: () }
 +
 +impl Example {
 +    pub fn n$0ew() -> Self {
 +        Self { _inner: () }
 +    }
 +}
 +"#####,
 +        r#####"
 +struct Example { _inner: () }
 +
 +impl Example {
 +    pub fn new() -> Self {
 +        Self { _inner: () }
 +    }
 +}
 +
 +impl Default for Example {
 +    fn default() -> Self {
 +        Self::new()
 +    }
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_generate_delegate_methods() {
 +    check_doc_test(
 +        "generate_delegate_methods",
 +        r#####"
 +struct Age(u8);
 +impl Age {
 +    fn age(&self) -> u8 {
 +        self.0
 +    }
 +}
 +
 +struct Person {
 +    ag$0e: Age,
 +}
 +"#####,
 +        r#####"
 +struct Age(u8);
 +impl Age {
 +    fn age(&self) -> u8 {
 +        self.0
 +    }
 +}
 +
 +struct Person {
 +    age: Age,
 +}
 +
 +impl Person {
 +    $0fn age(&self) -> u8 {
 +        self.age.age()
 +    }
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_generate_deref() {
 +    check_doc_test(
 +        "generate_deref",
 +        r#####"
 +//- minicore: deref, deref_mut
 +struct A;
 +struct B {
 +   $0a: A
 +}
 +"#####,
 +        r#####"
 +struct A;
 +struct B {
 +   a: A
 +}
 +
 +impl core::ops::Deref for B {
 +    type Target = A;
 +
 +    fn deref(&self) -> &Self::Target {
 +        &self.a
 +    }
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_generate_derive() {
 +    check_doc_test(
 +        "generate_derive",
 +        r#####"
 +struct Point {
 +    x: u32,
 +    y: u32,$0
 +}
 +"#####,
 +        r#####"
 +#[derive($0)]
 +struct Point {
 +    x: u32,
 +    y: u32,
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_generate_doc_example() {
 +    check_doc_test(
 +        "generate_doc_example",
 +        r#####"
 +/// Adds two numbers.$0
 +pub fn add(a: i32, b: i32) -> i32 { a + b }
 +"#####,
 +        r#####"
 +/// Adds two numbers.
 +///
 +/// # Examples
 +///
 +/// ```
 +/// use test::add;
 +///
 +/// assert_eq!(add(a, b), );
 +/// ```
 +pub fn add(a: i32, b: i32) -> i32 { a + b }
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_generate_documentation_template() {
 +    check_doc_test(
 +        "generate_documentation_template",
 +        r#####"
 +pub struct S;
 +impl S {
 +    pub unsafe fn set_len$0(&mut self, len: usize) -> Result<(), std::io::Error> {
 +        /* ... */
 +    }
 +}
 +"#####,
 +        r#####"
 +pub struct S;
 +impl S {
 +    /// Sets the length of this [`S`].
 +    ///
 +    /// # Errors
 +    ///
 +    /// This function will return an error if .
 +    ///
 +    /// # Safety
 +    ///
 +    /// .
 +    pub unsafe fn set_len(&mut self, len: usize) -> Result<(), std::io::Error> {
 +        /* ... */
 +    }
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_generate_enum_as_method() {
 +    check_doc_test(
 +        "generate_enum_as_method",
 +        r#####"
 +enum Value {
 + Number(i32),
 + Text(String)$0,
 +}
 +"#####,
 +        r#####"
 +enum Value {
 + Number(i32),
 + Text(String),
 +}
 +
 +impl Value {
 +    fn as_text(&self) -> Option<&String> {
 +        if let Self::Text(v) = self {
 +            Some(v)
 +        } else {
 +            None
 +        }
 +    }
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_generate_enum_is_method() {
 +    check_doc_test(
 +        "generate_enum_is_method",
 +        r#####"
 +enum Version {
 + Undefined,
 + Minor$0,
 + Major,
 +}
 +"#####,
 +        r#####"
 +enum Version {
 + Undefined,
 + Minor,
 + Major,
 +}
 +
 +impl Version {
 +    /// Returns `true` if the version is [`Minor`].
 +    ///
 +    /// [`Minor`]: Version::Minor
 +    #[must_use]
 +    fn is_minor(&self) -> bool {
 +        matches!(self, Self::Minor)
 +    }
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_generate_enum_try_into_method() {
 +    check_doc_test(
 +        "generate_enum_try_into_method",
 +        r#####"
 +enum Value {
 + Number(i32),
 + Text(String)$0,
 +}
 +"#####,
 +        r#####"
 +enum Value {
 + Number(i32),
 + Text(String),
 +}
 +
 +impl Value {
 +    fn try_into_text(self) -> Result<String, Self> {
 +        if let Self::Text(v) = self {
 +            Ok(v)
 +        } else {
 +            Err(self)
 +        }
 +    }
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_generate_enum_variant() {
 +    check_doc_test(
 +        "generate_enum_variant",
 +        r#####"
 +enum Countries {
 +    Ghana,
 +}
 +
 +fn main() {
 +    let country = Countries::Lesotho$0;
 +}
 +"#####,
 +        r#####"
 +enum Countries {
 +    Ghana,
 +    Lesotho,
 +}
 +
 +fn main() {
 +    let country = Countries::Lesotho;
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_generate_from_impl_for_enum() {
 +    check_doc_test(
 +        "generate_from_impl_for_enum",
 +        r#####"
 +enum A { $0One(u32) }
 +"#####,
 +        r#####"
 +enum A { One(u32) }
 +
 +impl From<u32> for A {
 +    fn from(v: u32) -> Self {
 +        Self::One(v)
 +    }
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_generate_function() {
 +    check_doc_test(
 +        "generate_function",
 +        r#####"
 +struct Baz;
 +fn baz() -> Baz { Baz }
 +fn foo() {
 +    bar$0("", baz());
 +}
 +
 +"#####,
 +        r#####"
 +struct Baz;
 +fn baz() -> Baz { Baz }
 +fn foo() {
 +    bar("", baz());
 +}
 +
 +fn bar(arg: &str, baz: Baz) ${0:-> _} {
 +    todo!()
 +}
 +
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_generate_getter() {
 +    check_doc_test(
 +        "generate_getter",
 +        r#####"
 +//- minicore: as_ref
 +pub struct String;
 +impl AsRef<str> for String {
 +    fn as_ref(&self) -> &str {
 +        ""
 +    }
 +}
 +
 +struct Person {
 +    nam$0e: String,
 +}
 +"#####,
 +        r#####"
 +pub struct String;
 +impl AsRef<str> for String {
 +    fn as_ref(&self) -> &str {
 +        ""
 +    }
 +}
 +
 +struct Person {
 +    name: String,
 +}
 +
 +impl Person {
 +    fn $0name(&self) -> &str {
 +        self.name.as_ref()
 +    }
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_generate_getter_mut() {
 +    check_doc_test(
 +        "generate_getter_mut",
 +        r#####"
 +struct Person {
 +    nam$0e: String,
 +}
 +"#####,
 +        r#####"
 +struct Person {
 +    name: String,
 +}
 +
 +impl Person {
 +    fn $0name_mut(&mut self) -> &mut String {
 +        &mut self.name
 +    }
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_generate_impl() {
 +    check_doc_test(
 +        "generate_impl",
 +        r#####"
 +struct Ctx<T: Clone> {
 +    data: T,$0
 +}
 +"#####,
 +        r#####"
 +struct Ctx<T: Clone> {
 +    data: T,
 +}
 +
 +impl<T: Clone> Ctx<T> {
 +    $0
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_generate_is_empty_from_len() {
 +    check_doc_test(
 +        "generate_is_empty_from_len",
 +        r#####"
 +struct MyStruct { data: Vec<String> }
 +
 +impl MyStruct {
 +    #[must_use]
 +    p$0ub fn len(&self) -> usize {
 +        self.data.len()
 +    }
 +}
 +"#####,
 +        r#####"
 +struct MyStruct { data: Vec<String> }
 +
 +impl MyStruct {
 +    #[must_use]
 +    pub fn len(&self) -> usize {
 +        self.data.len()
 +    }
 +
 +    #[must_use]
 +    pub fn is_empty(&self) -> bool {
 +        self.len() == 0
 +    }
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_generate_new() {
 +    check_doc_test(
 +        "generate_new",
 +        r#####"
 +struct Ctx<T: Clone> {
 +     data: T,$0
 +}
 +"#####,
 +        r#####"
 +struct Ctx<T: Clone> {
 +     data: T,
 +}
 +
 +impl<T: Clone> Ctx<T> {
 +    fn $0new(data: T) -> Self { Self { data } }
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_generate_setter() {
 +    check_doc_test(
 +        "generate_setter",
 +        r#####"
 +struct Person {
 +    nam$0e: String,
 +}
 +"#####,
 +        r#####"
 +struct Person {
 +    name: String,
 +}
 +
 +impl Person {
 +    fn set_name(&mut self, name: String) {
 +        self.name = name;
 +    }
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_inline_call() {
 +    check_doc_test(
 +        "inline_call",
 +        r#####"
 +//- minicore: option
 +fn foo(name: Option<&str>) {
 +    let name = name.unwrap$0();
 +}
 +"#####,
 +        r#####"
 +fn foo(name: Option<&str>) {
 +    let name = match name {
 +            Some(val) => val,
 +            None => panic!("called `Option::unwrap()` on a `None` value"),
 +        };
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_inline_into_callers() {
 +    check_doc_test(
 +        "inline_into_callers",
 +        r#####"
 +fn print(_: &str) {}
 +fn foo$0(word: &str) {
 +    if !word.is_empty() {
 +        print(word);
 +    }
 +}
 +fn bar() {
 +    foo("안녕하세요");
 +    foo("여러분");
 +}
 +"#####,
 +        r#####"
 +fn print(_: &str) {}
 +
 +fn bar() {
 +    {
 +        let word = "안녕하세요";
 +        if !word.is_empty() {
 +            print(word);
 +        }
 +    };
 +    {
 +        let word = "여러분";
 +        if !word.is_empty() {
 +            print(word);
 +        }
 +    };
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_inline_local_variable() {
 +    check_doc_test(
 +        "inline_local_variable",
 +        r#####"
 +fn main() {
 +    let x$0 = 1 + 2;
 +    x * 4;
 +}
 +"#####,
 +        r#####"
 +fn main() {
 +    (1 + 2) * 4;
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_inline_type_alias() {
 +    check_doc_test(
 +        "inline_type_alias",
 +        r#####"
 +type A<T = u32> = Vec<T>;
 +
 +fn main() {
 +    let a: $0A;
 +}
 +"#####,
 +        r#####"
 +type A<T = u32> = Vec<T>;
 +
 +fn main() {
 +    let a: Vec<u32>;
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_inline_type_alias_uses() {
 +    check_doc_test(
 +        "inline_type_alias_uses",
 +        r#####"
 +type $0A = i32;
 +fn id(x: A) -> A {
 +    x
 +};
 +fn foo() {
 +    let _: A = 3;
 +}
 +"#####,
 +        r#####"
 +
 +fn id(x: i32) -> i32 {
 +    x
 +};
 +fn foo() {
 +    let _: i32 = 3;
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_introduce_named_generic() {
 +    check_doc_test(
 +        "introduce_named_generic",
 +        r#####"
 +fn foo(bar: $0impl Bar) {}
 +"#####,
 +        r#####"
 +fn foo<B: Bar>(bar: B) {}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_introduce_named_lifetime() {
 +    check_doc_test(
 +        "introduce_named_lifetime",
 +        r#####"
 +impl Cursor<'_$0> {
 +    fn node(self) -> &SyntaxNode {
 +        match self {
 +            Cursor::Replace(node) | Cursor::Before(node) => node,
 +        }
 +    }
 +}
 +"#####,
 +        r#####"
 +impl<'a> Cursor<'a> {
 +    fn node(self) -> &SyntaxNode {
 +        match self {
 +            Cursor::Replace(node) | Cursor::Before(node) => node,
 +        }
 +    }
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_invert_if() {
 +    check_doc_test(
 +        "invert_if",
 +        r#####"
 +fn main() {
 +    if$0 !y { A } else { B }
 +}
 +"#####,
 +        r#####"
 +fn main() {
 +    if y { B } else { A }
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_line_to_block() {
 +    check_doc_test(
 +        "line_to_block",
 +        r#####"
 +   // Multi-line$0
 +   // comment
 +"#####,
 +        r#####"
 +  /*
 +  Multi-line
 +  comment
 +  */
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_make_raw_string() {
 +    check_doc_test(
 +        "make_raw_string",
 +        r#####"
 +fn main() {
 +    "Hello,$0 World!";
 +}
 +"#####,
 +        r#####"
 +fn main() {
 +    r#"Hello, World!"#;
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_make_usual_string() {
 +    check_doc_test(
 +        "make_usual_string",
 +        r#####"
 +fn main() {
 +    r#"Hello,$0 "World!""#;
 +}
 +"#####,
 +        r#####"
 +fn main() {
 +    "Hello, \"World!\"";
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_merge_imports() {
 +    check_doc_test(
 +        "merge_imports",
 +        r#####"
 +use std::$0fmt::Formatter;
 +use std::io;
 +"#####,
 +        r#####"
 +use std::{fmt::Formatter, io};
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_merge_match_arms() {
 +    check_doc_test(
 +        "merge_match_arms",
 +        r#####"
 +enum Action { Move { distance: u32 }, Stop }
 +
 +fn handle(action: Action) {
 +    match action {
 +        $0Action::Move(..) => foo(),
 +        Action::Stop => foo(),
 +    }
 +}
 +"#####,
 +        r#####"
 +enum Action { Move { distance: u32 }, Stop }
 +
 +fn handle(action: Action) {
 +    match action {
 +        Action::Move(..) | Action::Stop => foo(),
 +    }
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_move_arm_cond_to_match_guard() {
 +    check_doc_test(
 +        "move_arm_cond_to_match_guard",
 +        r#####"
 +enum Action { Move { distance: u32 }, Stop }
 +
 +fn handle(action: Action) {
 +    match action {
 +        Action::Move { distance } => $0if distance > 10 { foo() },
 +        _ => (),
 +    }
 +}
 +"#####,
 +        r#####"
 +enum Action { Move { distance: u32 }, Stop }
 +
 +fn handle(action: Action) {
 +    match action {
 +        Action::Move { distance } if distance > 10 => foo(),
 +        _ => (),
 +    }
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_move_bounds_to_where_clause() {
 +    check_doc_test(
 +        "move_bounds_to_where_clause",
 +        r#####"
 +fn apply<T, U, $0F: FnOnce(T) -> U>(f: F, x: T) -> U {
 +    f(x)
 +}
 +"#####,
 +        r#####"
 +fn apply<T, U, F>(f: F, x: T) -> U where F: FnOnce(T) -> U {
 +    f(x)
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_move_format_string_arg() {
 +    check_doc_test(
 +        "move_format_string_arg",
 +        r#####"
 +macro_rules! format_args {
 +    ($lit:literal $(tt:tt)*) => { 0 },
 +}
 +macro_rules! print {
 +    ($($arg:tt)*) => (std::io::_print(format_args!($($arg)*)));
 +}
 +
 +fn main() {
 +    print!("{x + 1}$0");
 +}
 +"#####,
 +        r#####"
 +macro_rules! format_args {
 +    ($lit:literal $(tt:tt)*) => { 0 },
 +}
 +macro_rules! print {
 +    ($($arg:tt)*) => (std::io::_print(format_args!($($arg)*)));
 +}
 +
 +fn main() {
 +    print!("{}"$0, x + 1);
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_move_from_mod_rs() {
 +    check_doc_test(
 +        "move_from_mod_rs",
 +        r#####"
 +//- /main.rs
 +mod a;
 +//- /a/mod.rs
 +$0fn t() {}$0
 +"#####,
 +        r#####"
 +fn t() {}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_move_guard_to_arm_body() {
 +    check_doc_test(
 +        "move_guard_to_arm_body",
 +        r#####"
 +enum Action { Move { distance: u32 }, Stop }
 +
 +fn handle(action: Action) {
 +    match action {
 +        Action::Move { distance } $0if distance > 10 => foo(),
 +        _ => (),
 +    }
 +}
 +"#####,
 +        r#####"
 +enum Action { Move { distance: u32 }, Stop }
 +
 +fn handle(action: Action) {
 +    match action {
 +        Action::Move { distance } => if distance > 10 {
 +            foo()
 +        },
 +        _ => (),
 +    }
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_move_module_to_file() {
 +    check_doc_test(
 +        "move_module_to_file",
 +        r#####"
 +mod $0foo {
 +    fn t() {}
 +}
 +"#####,
 +        r#####"
 +mod foo;
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_move_to_mod_rs() {
 +    check_doc_test(
 +        "move_to_mod_rs",
 +        r#####"
 +//- /main.rs
 +mod a;
 +//- /a.rs
 +$0fn t() {}$0
 +"#####,
 +        r#####"
 +fn t() {}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_promote_local_to_const() {
 +    check_doc_test(
 +        "promote_local_to_const",
 +        r#####"
 +fn main() {
 +    let foo$0 = true;
 +
 +    if foo {
 +        println!("It's true");
 +    } else {
 +        println!("It's false");
 +    }
 +}
 +"#####,
 +        r#####"
 +fn main() {
 +    const $0FOO: bool = true;
 +
 +    if FOO {
 +        println!("It's true");
 +    } else {
 +        println!("It's false");
 +    }
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_pull_assignment_up() {
 +    check_doc_test(
 +        "pull_assignment_up",
 +        r#####"
 +fn main() {
 +    let mut foo = 6;
 +
 +    if true {
 +        $0foo = 5;
 +    } else {
 +        foo = 4;
 +    }
 +}
 +"#####,
 +        r#####"
 +fn main() {
 +    let mut foo = 6;
 +
 +    foo = if true {
 +        5
 +    } else {
 +        4
 +    };
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_qualify_method_call() {
 +    check_doc_test(
 +        "qualify_method_call",
 +        r#####"
 +struct Foo;
 +impl Foo {
 +    fn foo(&self) {}
 +}
 +fn main() {
 +    let foo = Foo;
 +    foo.fo$0o();
 +}
 +"#####,
 +        r#####"
 +struct Foo;
 +impl Foo {
 +    fn foo(&self) {}
 +}
 +fn main() {
 +    let foo = Foo;
 +    Foo::foo(&foo);
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_qualify_path() {
 +    check_doc_test(
 +        "qualify_path",
 +        r#####"
 +fn main() {
 +    let map = HashMap$0::new();
 +}
 +pub mod std { pub mod collections { pub struct HashMap { } } }
 +"#####,
 +        r#####"
 +fn main() {
 +    let map = std::collections::HashMap::new();
 +}
 +pub mod std { pub mod collections { pub struct HashMap { } } }
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_reformat_number_literal() {
 +    check_doc_test(
 +        "reformat_number_literal",
 +        r#####"
 +const _: i32 = 1012345$0;
 +"#####,
 +        r#####"
 +const _: i32 = 1_012_345;
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_remove_dbg() {
 +    check_doc_test(
 +        "remove_dbg",
 +        r#####"
 +fn main() {
 +    $0dbg!(92);
 +}
 +"#####,
 +        r#####"
 +fn main() {
 +    92;
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_remove_hash() {
 +    check_doc_test(
 +        "remove_hash",
 +        r#####"
 +fn main() {
 +    r#"Hello,$0 World!"#;
 +}
 +"#####,
 +        r#####"
 +fn main() {
 +    r"Hello, World!";
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_remove_mut() {
 +    check_doc_test(
 +        "remove_mut",
 +        r#####"
 +impl Walrus {
 +    fn feed(&mut$0 self, amount: u32) {}
 +}
 +"#####,
 +        r#####"
 +impl Walrus {
 +    fn feed(&self, amount: u32) {}
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_remove_unused_param() {
 +    check_doc_test(
 +        "remove_unused_param",
 +        r#####"
 +fn frobnicate(x: i32$0) {}
 +
 +fn main() {
 +    frobnicate(92);
 +}
 +"#####,
 +        r#####"
 +fn frobnicate() {}
 +
 +fn main() {
 +    frobnicate();
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_reorder_fields() {
 +    check_doc_test(
 +        "reorder_fields",
 +        r#####"
 +struct Foo {foo: i32, bar: i32};
 +const test: Foo = $0Foo {bar: 0, foo: 1}
 +"#####,
 +        r#####"
 +struct Foo {foo: i32, bar: i32};
 +const test: Foo = Foo {foo: 1, bar: 0}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_reorder_impl_items() {
 +    check_doc_test(
 +        "reorder_impl_items",
 +        r#####"
 +trait Foo {
 +    type A;
 +    const B: u8;
 +    fn c();
 +}
 +
 +struct Bar;
 +$0impl Foo for Bar {
 +    const B: u8 = 17;
 +    fn c() {}
 +    type A = String;
 +}
 +"#####,
 +        r#####"
 +trait Foo {
 +    type A;
 +    const B: u8;
 +    fn c();
 +}
 +
 +struct Bar;
 +impl Foo for Bar {
 +    type A = String;
 +    const B: u8 = 17;
 +    fn c() {}
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_replace_char_with_string() {
 +    check_doc_test(
 +        "replace_char_with_string",
 +        r#####"
 +fn main() {
 +    find('{$0');
 +}
 +"#####,
 +        r#####"
 +fn main() {
 +    find("{");
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_replace_derive_with_manual_impl() {
 +    check_doc_test(
 +        "replace_derive_with_manual_impl",
 +        r#####"
 +//- minicore: derive
 +trait Debug { fn fmt(&self, f: &mut Formatter) -> Result<()>; }
 +#[derive(Deb$0ug, Display)]
 +struct S;
 +"#####,
 +        r#####"
 +trait Debug { fn fmt(&self, f: &mut Formatter) -> Result<()>; }
 +#[derive(Display)]
 +struct S;
 +
 +impl Debug for S {
 +    $0fn fmt(&self, f: &mut Formatter) -> Result<()> {
 +        f.debug_struct("S").finish()
 +    }
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_replace_if_let_with_match() {
 +    check_doc_test(
 +        "replace_if_let_with_match",
 +        r#####"
 +enum Action { Move { distance: u32 }, Stop }
 +
 +fn handle(action: Action) {
 +    $0if let Action::Move { distance } = action {
 +        foo(distance)
 +    } else {
 +        bar()
 +    }
 +}
 +"#####,
 +        r#####"
 +enum Action { Move { distance: u32 }, Stop }
 +
 +fn handle(action: Action) {
 +    match action {
 +        Action::Move { distance } => foo(distance),
 +        _ => bar(),
 +    }
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_replace_let_with_if_let() {
 +    check_doc_test(
 +        "replace_let_with_if_let",
 +        r#####"
 +enum Option<T> { Some(T), None }
 +
 +fn main(action: Action) {
 +    $0let x = compute();
 +}
 +
 +fn compute() -> Option<i32> { None }
 +"#####,
 +        r#####"
 +enum Option<T> { Some(T), None }
 +
 +fn main(action: Action) {
 +    if let Some(x) = compute() {
 +    }
 +}
 +
 +fn compute() -> Option<i32> { None }
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_replace_match_with_if_let() {
 +    check_doc_test(
 +        "replace_match_with_if_let",
 +        r#####"
 +enum Action { Move { distance: u32 }, Stop }
 +
 +fn handle(action: Action) {
 +    $0match action {
 +        Action::Move { distance } => foo(distance),
 +        _ => bar(),
 +    }
 +}
 +"#####,
 +        r#####"
 +enum Action { Move { distance: u32 }, Stop }
 +
 +fn handle(action: Action) {
 +    if let Action::Move { distance } = action {
 +        foo(distance)
 +    } else {
 +        bar()
 +    }
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_replace_or_else_with_or() {
 +    check_doc_test(
 +        "replace_or_else_with_or",
 +        r#####"
 +//- minicore:option
 +fn foo() {
 +    let a = Some(1);
 +    a.unwra$0p_or_else(|| 2);
 +}
 +"#####,
 +        r#####"
 +fn foo() {
 +    let a = Some(1);
 +    a.unwrap_or(2);
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_replace_or_with_or_else() {
 +    check_doc_test(
 +        "replace_or_with_or_else",
 +        r#####"
 +//- minicore:option
 +fn foo() {
 +    let a = Some(1);
 +    a.unwra$0p_or(2);
 +}
 +"#####,
 +        r#####"
 +fn foo() {
 +    let a = Some(1);
 +    a.unwrap_or_else(|| 2);
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_replace_qualified_name_with_use() {
 +    check_doc_test(
 +        "replace_qualified_name_with_use",
 +        r#####"
 +mod std { pub mod collections { pub struct HashMap<T, U>(T, U); } }
 +fn process(map: std::collections::$0HashMap<String, String>) {}
 +"#####,
 +        r#####"
 +use std::collections::HashMap;
 +
 +mod std { pub mod collections { pub struct HashMap<T, U>(T, U); } }
 +fn process(map: HashMap<String, String>) {}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_replace_string_with_char() {
 +    check_doc_test(
 +        "replace_string_with_char",
 +        r#####"
 +fn main() {
 +    find("{$0");
 +}
 +"#####,
 +        r#####"
 +fn main() {
 +    find('{');
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_replace_try_expr_with_match() {
 +    check_doc_test(
 +        "replace_try_expr_with_match",
 +        r#####"
 +//- minicore:option
 +fn handle() {
 +    let pat = Some(true)$0?;
 +}
 +"#####,
 +        r#####"
 +fn handle() {
 +    let pat = match Some(true) {
 +        Some(it) => it,
 +        None => return None,
 +    };
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_replace_turbofish_with_explicit_type() {
 +    check_doc_test(
 +        "replace_turbofish_with_explicit_type",
 +        r#####"
 +fn make<T>() -> T { ) }
 +fn main() {
 +    let a = make$0::<i32>();
 +}
 +"#####,
 +        r#####"
 +fn make<T>() -> T { ) }
 +fn main() {
 +    let a: i32 = make();
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_sort_items() {
 +    check_doc_test(
 +        "sort_items",
 +        r#####"
 +struct $0Foo$0 { second: u32, first: String }
 +"#####,
 +        r#####"
 +struct Foo { first: String, second: u32 }
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_sort_items_1() {
 +    check_doc_test(
 +        "sort_items",
 +        r#####"
 +trait $0Bar$0 {
 +    fn second(&self) -> u32;
 +    fn first(&self) -> String;
 +}
 +"#####,
 +        r#####"
 +trait Bar {
 +    fn first(&self) -> String;
 +    fn second(&self) -> u32;
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_sort_items_2() {
 +    check_doc_test(
 +        "sort_items",
 +        r#####"
 +struct Baz;
 +impl $0Baz$0 {
 +    fn second(&self) -> u32;
 +    fn first(&self) -> String;
 +}
 +"#####,
 +        r#####"
 +struct Baz;
 +impl Baz {
 +    fn first(&self) -> String;
 +    fn second(&self) -> u32;
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_sort_items_3() {
 +    check_doc_test(
 +        "sort_items",
 +        r#####"
 +enum $0Animal$0 {
 +  Dog(String, f64),
 +  Cat { weight: f64, name: String },
 +}
 +"#####,
 +        r#####"
 +enum Animal {
 +  Cat { weight: f64, name: String },
 +  Dog(String, f64),
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_sort_items_4() {
 +    check_doc_test(
 +        "sort_items",
 +        r#####"
 +enum Animal {
 +  Dog(String, f64),
 +  Cat $0{ weight: f64, name: String }$0,
 +}
 +"#####,
 +        r#####"
 +enum Animal {
 +  Dog(String, f64),
 +  Cat { name: String, weight: f64 },
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_split_import() {
 +    check_doc_test(
 +        "split_import",
 +        r#####"
 +use std::$0collections::HashMap;
 +"#####,
 +        r#####"
 +use std::{collections::HashMap};
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_toggle_ignore() {
 +    check_doc_test(
 +        "toggle_ignore",
 +        r#####"
 +$0#[test]
 +fn arithmetics {
 +    assert_eq!(2 + 2, 5);
 +}
 +"#####,
 +        r#####"
 +#[test]
 +#[ignore]
 +fn arithmetics {
 +    assert_eq!(2 + 2, 5);
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_unmerge_match_arm() {
 +    check_doc_test(
 +        "unmerge_match_arm",
 +        r#####"
 +enum Action { Move { distance: u32 }, Stop }
 +
 +fn handle(action: Action) {
 +    match action {
 +        Action::Move(..) $0| Action::Stop => foo(),
 +    }
 +}
 +"#####,
 +        r#####"
 +enum Action { Move { distance: u32 }, Stop }
 +
 +fn handle(action: Action) {
 +    match action {
 +        Action::Move(..) => foo(),
 +        Action::Stop => foo(),
 +    }
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_unmerge_use() {
 +    check_doc_test(
 +        "unmerge_use",
 +        r#####"
 +use std::fmt::{Debug, Display$0};
 +"#####,
 +        r#####"
 +use std::fmt::{Debug};
 +use std::fmt::Display;
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_unnecessary_async() {
 +    check_doc_test(
 +        "unnecessary_async",
 +        r#####"
 +pub async f$0n foo() {}
 +pub async fn bar() { foo().await }
 +"#####,
 +        r#####"
 +pub fn foo() {}
 +pub async fn bar() { foo() }
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_unwrap_block() {
 +    check_doc_test(
 +        "unwrap_block",
 +        r#####"
 +fn foo() {
 +    if true {$0
 +        println!("foo");
 +    }
 +}
 +"#####,
 +        r#####"
 +fn foo() {
 +    println!("foo");
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_unwrap_result_return_type() {
 +    check_doc_test(
 +        "unwrap_result_return_type",
 +        r#####"
 +//- minicore: result
 +fn foo() -> Result<i32>$0 { Ok(42i32) }
 +"#####,
 +        r#####"
 +fn foo() -> i32 { 42i32 }
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_unwrap_tuple() {
 +    check_doc_test(
 +        "unwrap_tuple",
 +        r#####"
 +//- minicore: result
 +fn main() {
 +    $0let (foo, bar) = ("Foo", "Bar");
 +}
 +"#####,
 +        r#####"
 +fn main() {
 +    let foo = "Foo";
 +    let bar = "Bar";
 +}
 +"#####,
 +    )
 +}
 +
 +#[test]
 +fn doctest_wrap_return_type_in_result() {
 +    check_doc_test(
 +        "wrap_return_type_in_result",
 +        r#####"
 +//- minicore: result
 +fn foo() -> i32$0 { 42i32 }
 +"#####,
 +        r#####"
 +fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }
 +"#####,
 +    )
 +}
index db32e7182c44d76ab78db38123862d8fe9f84257,0000000000000000000000000000000000000000..307e67927056ba9a4eefce68b72bef495c63ac1a
mode 100644,000000..100644
--- /dev/null
@@@ -1,707 -1,0 +1,707 @@@
-         Cursor::Replace(placeholder) => format!("${{0:{}}}", placeholder),
-         Cursor::Before(placeholder) => format!("$0{}", placeholder),
 +//! Assorted functions shared by several assists.
 +
 +use std::ops;
 +
 +pub(crate) use gen_trait_fn_body::gen_trait_fn_body;
 +use hir::{db::HirDatabase, HirDisplay, Semantics};
 +use ide_db::{famous_defs::FamousDefs, path_transform::PathTransform, RootDatabase, SnippetCap};
 +use stdx::format_to;
 +use syntax::{
 +    ast::{
 +        self,
 +        edit::{self, AstNodeEdit},
 +        edit_in_place::{AttrsOwnerEdit, Removable},
 +        make, HasArgList, HasAttrs, HasGenericParams, HasName, HasTypeBounds, Whitespace,
 +    },
 +    ted, AstNode, AstToken, Direction, SourceFile,
 +    SyntaxKind::*,
 +    SyntaxNode, TextRange, TextSize, T,
 +};
 +
 +use crate::assist_context::{AssistContext, SourceChangeBuilder};
 +
 +pub(crate) mod suggest_name;
 +mod gen_trait_fn_body;
 +
 +pub(crate) fn unwrap_trivial_block(block_expr: ast::BlockExpr) -> ast::Expr {
 +    extract_trivial_expression(&block_expr)
 +        .filter(|expr| !expr.syntax().text().contains_char('\n'))
 +        .unwrap_or_else(|| block_expr.into())
 +}
 +
 +pub fn extract_trivial_expression(block_expr: &ast::BlockExpr) -> Option<ast::Expr> {
 +    if block_expr.modifier().is_some() {
 +        return None;
 +    }
 +    let stmt_list = block_expr.stmt_list()?;
 +    let has_anything_else = |thing: &SyntaxNode| -> bool {
 +        let mut non_trivial_children =
 +            stmt_list.syntax().children_with_tokens().filter(|it| match it.kind() {
 +                WHITESPACE | T!['{'] | T!['}'] => false,
 +                _ => it.as_node() != Some(thing),
 +            });
 +        non_trivial_children.next().is_some()
 +    };
 +
 +    if let Some(expr) = stmt_list.tail_expr() {
 +        if has_anything_else(expr.syntax()) {
 +            return None;
 +        }
 +        return Some(expr);
 +    }
 +    // Unwrap `{ continue; }`
 +    let stmt = stmt_list.statements().next()?;
 +    if let ast::Stmt::ExprStmt(expr_stmt) = stmt {
 +        if has_anything_else(expr_stmt.syntax()) {
 +            return None;
 +        }
 +        let expr = expr_stmt.expr()?;
 +        if matches!(expr.syntax().kind(), CONTINUE_EXPR | BREAK_EXPR | RETURN_EXPR) {
 +            return Some(expr);
 +        }
 +    }
 +    None
 +}
 +
 +/// This is a method with a heuristics to support test methods annotated with custom test annotations, such as
 +/// `#[test_case(...)]`, `#[tokio::test]` and similar.
 +/// Also a regular `#[test]` annotation is supported.
 +///
 +/// It may produce false positives, for example, `#[wasm_bindgen_test]` requires a different command to run the test,
 +/// but it's better than not to have the runnables for the tests at all.
 +pub fn test_related_attribute(fn_def: &ast::Fn) -> Option<ast::Attr> {
 +    fn_def.attrs().find_map(|attr| {
 +        let path = attr.path()?;
 +        let text = path.syntax().text().to_string();
 +        if text.starts_with("test") || text.ends_with("test") {
 +            Some(attr)
 +        } else {
 +            None
 +        }
 +    })
 +}
 +
 +#[derive(Copy, Clone, PartialEq)]
 +pub enum DefaultMethods {
 +    Only,
 +    No,
 +}
 +
 +pub fn filter_assoc_items(
 +    sema: &Semantics<'_, RootDatabase>,
 +    items: &[hir::AssocItem],
 +    default_methods: DefaultMethods,
 +) -> Vec<ast::AssocItem> {
 +    fn has_def_name(item: &ast::AssocItem) -> bool {
 +        match item {
 +            ast::AssocItem::Fn(def) => def.name(),
 +            ast::AssocItem::TypeAlias(def) => def.name(),
 +            ast::AssocItem::Const(def) => def.name(),
 +            ast::AssocItem::MacroCall(_) => None,
 +        }
 +        .is_some()
 +    }
 +
 +    items
 +        .iter()
 +        // Note: This throws away items with no source.
 +        .filter_map(|&i| {
 +            let item = match i {
 +                hir::AssocItem::Function(i) => ast::AssocItem::Fn(sema.source(i)?.value),
 +                hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAlias(sema.source(i)?.value),
 +                hir::AssocItem::Const(i) => ast::AssocItem::Const(sema.source(i)?.value),
 +            };
 +            Some(item)
 +        })
 +        .filter(has_def_name)
 +        .filter(|it| match it {
 +            ast::AssocItem::Fn(def) => matches!(
 +                (default_methods, def.body()),
 +                (DefaultMethods::Only, Some(_)) | (DefaultMethods::No, None)
 +            ),
 +            _ => default_methods == DefaultMethods::No,
 +        })
 +        .collect::<Vec<_>>()
 +}
 +
 +pub fn add_trait_assoc_items_to_impl(
 +    sema: &Semantics<'_, RootDatabase>,
 +    items: Vec<ast::AssocItem>,
 +    trait_: hir::Trait,
 +    impl_: ast::Impl,
 +    target_scope: hir::SemanticsScope<'_>,
 +) -> (ast::Impl, ast::AssocItem) {
 +    let source_scope = sema.scope_for_def(trait_);
 +
 +    let transform = PathTransform::trait_impl(&target_scope, &source_scope, trait_, impl_.clone());
 +
 +    let items = items.into_iter().map(|assoc_item| {
 +        transform.apply(assoc_item.syntax());
 +        assoc_item.remove_attrs_and_docs();
 +        assoc_item
 +    });
 +
 +    let res = impl_.clone_for_update();
 +
 +    let assoc_item_list = res.get_or_create_assoc_item_list();
 +    let mut first_item = None;
 +    for item in items {
 +        first_item.get_or_insert_with(|| item.clone());
 +        match &item {
 +            ast::AssocItem::Fn(fn_) if fn_.body().is_none() => {
 +                let body = make::block_expr(None, Some(make::ext::expr_todo()))
 +                    .indent(edit::IndentLevel(1));
 +                ted::replace(fn_.get_or_create_body().syntax(), body.clone_for_update().syntax())
 +            }
 +            ast::AssocItem::TypeAlias(type_alias) => {
 +                if let Some(type_bound_list) = type_alias.type_bound_list() {
 +                    type_bound_list.remove()
 +                }
 +            }
 +            _ => {}
 +        }
 +
 +        assoc_item_list.add_item(item)
 +    }
 +
 +    (res, first_item.unwrap())
 +}
 +
 +#[derive(Clone, Copy, Debug)]
 +pub(crate) enum Cursor<'a> {
 +    Replace(&'a SyntaxNode),
 +    Before(&'a SyntaxNode),
 +}
 +
 +impl<'a> Cursor<'a> {
 +    fn node(self) -> &'a SyntaxNode {
 +        match self {
 +            Cursor::Replace(node) | Cursor::Before(node) => node,
 +        }
 +    }
 +}
 +
 +pub(crate) fn render_snippet(_cap: SnippetCap, node: &SyntaxNode, cursor: Cursor<'_>) -> String {
 +    assert!(cursor.node().ancestors().any(|it| it == *node));
 +    let range = cursor.node().text_range() - node.text_range().start();
 +    let range: ops::Range<usize> = range.into();
 +
 +    let mut placeholder = cursor.node().to_string();
 +    escape(&mut placeholder);
 +    let tab_stop = match cursor {
-                 format!("&[{}]", type_argument_name)
++        Cursor::Replace(placeholder) => format!("${{0:{placeholder}}}"),
++        Cursor::Before(placeholder) => format!("$0{placeholder}"),
 +    };
 +
 +    let mut buf = node.to_string();
 +    buf.replace_range(range, &tab_stop);
 +    return buf;
 +
 +    fn escape(buf: &mut String) {
 +        stdx::replace(buf, '{', r"\{");
 +        stdx::replace(buf, '}', r"\}");
 +        stdx::replace(buf, '$', r"\$");
 +    }
 +}
 +
 +pub(crate) fn vis_offset(node: &SyntaxNode) -> TextSize {
 +    node.children_with_tokens()
 +        .find(|it| !matches!(it.kind(), WHITESPACE | COMMENT | ATTR))
 +        .map(|it| it.text_range().start())
 +        .unwrap_or_else(|| node.text_range().start())
 +}
 +
 +pub(crate) fn invert_boolean_expression(expr: ast::Expr) -> ast::Expr {
 +    invert_special_case(&expr).unwrap_or_else(|| make::expr_prefix(T![!], expr))
 +}
 +
 +fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> {
 +    match expr {
 +        ast::Expr::BinExpr(bin) => {
 +            let bin = bin.clone_for_update();
 +            let op_token = bin.op_token()?;
 +            let rev_token = match op_token.kind() {
 +                T![==] => T![!=],
 +                T![!=] => T![==],
 +                T![<] => T![>=],
 +                T![<=] => T![>],
 +                T![>] => T![<=],
 +                T![>=] => T![<],
 +                // Parenthesize other expressions before prefixing `!`
 +                _ => return Some(make::expr_prefix(T![!], make::expr_paren(expr.clone()))),
 +            };
 +            ted::replace(op_token, make::token(rev_token));
 +            Some(bin.into())
 +        }
 +        ast::Expr::MethodCallExpr(mce) => {
 +            let receiver = mce.receiver()?;
 +            let method = mce.name_ref()?;
 +            let arg_list = mce.arg_list()?;
 +
 +            let method = match method.text().as_str() {
 +                "is_some" => "is_none",
 +                "is_none" => "is_some",
 +                "is_ok" => "is_err",
 +                "is_err" => "is_ok",
 +                _ => return None,
 +            };
 +            Some(make::expr_method_call(receiver, make::name_ref(method), arg_list))
 +        }
 +        ast::Expr::PrefixExpr(pe) if pe.op_kind()? == ast::UnaryOp::Not => match pe.expr()? {
 +            ast::Expr::ParenExpr(parexpr) => parexpr.expr(),
 +            _ => pe.expr(),
 +        },
 +        ast::Expr::Literal(lit) => match lit.kind() {
 +            ast::LiteralKind::Bool(b) => match b {
 +                true => Some(ast::Expr::Literal(make::expr_literal("false"))),
 +                false => Some(ast::Expr::Literal(make::expr_literal("true"))),
 +            },
 +            _ => None,
 +        },
 +        _ => None,
 +    }
 +}
 +
 +pub(crate) fn next_prev() -> impl Iterator<Item = Direction> {
 +    [Direction::Next, Direction::Prev].into_iter()
 +}
 +
 +pub(crate) fn does_pat_match_variant(pat: &ast::Pat, var: &ast::Pat) -> bool {
 +    let first_node_text = |pat: &ast::Pat| pat.syntax().first_child().map(|node| node.text());
 +
 +    let pat_head = match pat {
 +        ast::Pat::IdentPat(bind_pat) => match bind_pat.pat() {
 +            Some(p) => first_node_text(&p),
 +            None => return pat.syntax().text() == var.syntax().text(),
 +        },
 +        pat => first_node_text(pat),
 +    };
 +
 +    let var_head = first_node_text(var);
 +
 +    pat_head == var_head
 +}
 +
 +pub(crate) fn does_nested_pattern(pat: &ast::Pat) -> bool {
 +    let depth = calc_depth(pat, 0);
 +
 +    if 1 < depth {
 +        return true;
 +    }
 +    false
 +}
 +
 +fn calc_depth(pat: &ast::Pat, depth: usize) -> usize {
 +    match pat {
 +        ast::Pat::IdentPat(_)
 +        | ast::Pat::BoxPat(_)
 +        | ast::Pat::RestPat(_)
 +        | ast::Pat::LiteralPat(_)
 +        | ast::Pat::MacroPat(_)
 +        | ast::Pat::OrPat(_)
 +        | ast::Pat::ParenPat(_)
 +        | ast::Pat::PathPat(_)
 +        | ast::Pat::WildcardPat(_)
 +        | ast::Pat::RangePat(_)
 +        | ast::Pat::RecordPat(_)
 +        | ast::Pat::RefPat(_)
 +        | ast::Pat::SlicePat(_)
 +        | ast::Pat::TuplePat(_)
 +        | ast::Pat::ConstBlockPat(_) => depth,
 +
 +        // FIXME: Other patterns may also be nested. Currently it simply supports only `TupleStructPat`
 +        ast::Pat::TupleStructPat(pat) => {
 +            let mut max_depth = depth;
 +            for p in pat.fields() {
 +                let d = calc_depth(&p, depth + 1);
 +                if d > max_depth {
 +                    max_depth = d
 +                }
 +            }
 +            max_depth
 +        }
 +    }
 +}
 +
 +// Uses a syntax-driven approach to find any impl blocks for the struct that
 +// exist within the module/file
 +//
 +// Returns `None` if we've found an existing fn
 +//
 +// FIXME: change the new fn checking to a more semantic approach when that's more
 +// viable (e.g. we process proc macros, etc)
 +// FIXME: this partially overlaps with `find_impl_block_*`
 +
 +/// `find_struct_impl` looks for impl of a struct, but this also has additional feature
 +/// where it takes a list of function names and check if they exist inside impl_, if
 +/// even one match is found, it returns None
 +pub(crate) fn find_struct_impl(
 +    ctx: &AssistContext<'_>,
 +    adt: &ast::Adt,
 +    names: &[String],
 +) -> Option<Option<ast::Impl>> {
 +    let db = ctx.db();
 +    let module = adt.syntax().parent()?;
 +
 +    let struct_def = ctx.sema.to_def(adt)?;
 +
 +    let block = module.descendants().filter_map(ast::Impl::cast).find_map(|impl_blk| {
 +        let blk = ctx.sema.to_def(&impl_blk)?;
 +
 +        // FIXME: handle e.g. `struct S<T>; impl<U> S<U> {}`
 +        // (we currently use the wrong type parameter)
 +        // also we wouldn't want to use e.g. `impl S<u32>`
 +
 +        let same_ty = match blk.self_ty(db).as_adt() {
 +            Some(def) => def == struct_def,
 +            None => false,
 +        };
 +        let not_trait_impl = blk.trait_(db).is_none();
 +
 +        if !(same_ty && not_trait_impl) {
 +            None
 +        } else {
 +            Some(impl_blk)
 +        }
 +    });
 +
 +    if let Some(ref impl_blk) = block {
 +        if has_any_fn(impl_blk, names) {
 +            return None;
 +        }
 +    }
 +
 +    Some(block)
 +}
 +
 +fn has_any_fn(imp: &ast::Impl, names: &[String]) -> bool {
 +    if let Some(il) = imp.assoc_item_list() {
 +        for item in il.assoc_items() {
 +            if let ast::AssocItem::Fn(f) = item {
 +                if let Some(name) = f.name() {
 +                    if names.iter().any(|n| n.eq_ignore_ascii_case(&name.text())) {
 +                        return true;
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    false
 +}
 +
 +/// Find the start of the `impl` block for the given `ast::Impl`.
 +//
 +// FIXME: this partially overlaps with `find_struct_impl`
 +pub(crate) fn find_impl_block_start(impl_def: ast::Impl, buf: &mut String) -> Option<TextSize> {
 +    buf.push('\n');
 +    let start = impl_def.assoc_item_list().and_then(|it| it.l_curly_token())?.text_range().end();
 +    Some(start)
 +}
 +
 +/// Find the end of the `impl` block for the given `ast::Impl`.
 +//
 +// FIXME: this partially overlaps with `find_struct_impl`
 +pub(crate) fn find_impl_block_end(impl_def: ast::Impl, buf: &mut String) -> Option<TextSize> {
 +    buf.push('\n');
 +    let end = impl_def
 +        .assoc_item_list()
 +        .and_then(|it| it.r_curly_token())?
 +        .prev_sibling_or_token()?
 +        .text_range()
 +        .end();
 +    Some(end)
 +}
 +
 +// Generates the surrounding `impl Type { <code> }` including type and lifetime
 +// parameters
 +pub(crate) fn generate_impl_text(adt: &ast::Adt, code: &str) -> String {
 +    generate_impl_text_inner(adt, None, code)
 +}
 +
 +// Generates the surrounding `impl <trait> for Type { <code> }` including type
 +// and lifetime parameters
 +pub(crate) fn generate_trait_impl_text(adt: &ast::Adt, trait_text: &str, code: &str) -> String {
 +    generate_impl_text_inner(adt, Some(trait_text), code)
 +}
 +
 +fn generate_impl_text_inner(adt: &ast::Adt, trait_text: Option<&str>, code: &str) -> String {
 +    // Ensure lifetime params are before type & const params
 +    let generic_params = adt.generic_param_list().map(|generic_params| {
 +        let lifetime_params =
 +            generic_params.lifetime_params().map(ast::GenericParam::LifetimeParam);
 +        let ty_or_const_params = generic_params.type_or_const_params().filter_map(|param| {
 +            // remove defaults since they can't be specified in impls
 +            match param {
 +                ast::TypeOrConstParam::Type(param) => {
 +                    let param = param.clone_for_update();
 +                    param.remove_default();
 +                    Some(ast::GenericParam::TypeParam(param))
 +                }
 +                ast::TypeOrConstParam::Const(param) => {
 +                    let param = param.clone_for_update();
 +                    param.remove_default();
 +                    Some(ast::GenericParam::ConstParam(param))
 +                }
 +            }
 +        });
 +
 +        make::generic_param_list(itertools::chain(lifetime_params, ty_or_const_params))
 +    });
 +
 +    // FIXME: use syntax::make & mutable AST apis instead
 +    // `trait_text` and `code` can't be opaque blobs of text
 +    let mut buf = String::with_capacity(code.len());
 +
 +    // Copy any cfg attrs from the original adt
 +    buf.push_str("\n\n");
 +    let cfg_attrs = adt
 +        .attrs()
 +        .filter(|attr| attr.as_simple_call().map(|(name, _arg)| name == "cfg").unwrap_or(false));
 +    cfg_attrs.for_each(|attr| buf.push_str(&format!("{attr}\n")));
 +
 +    // `impl{generic_params} {trait_text} for {name}{generic_params.to_generic_args()}`
 +    buf.push_str("impl");
 +    if let Some(generic_params) = &generic_params {
 +        format_to!(buf, "{generic_params}");
 +    }
 +    buf.push(' ');
 +    if let Some(trait_text) = trait_text {
 +        buf.push_str(trait_text);
 +        buf.push_str(" for ");
 +    }
 +    buf.push_str(&adt.name().unwrap().text());
 +    if let Some(generic_params) = generic_params {
 +        format_to!(buf, "{}", generic_params.to_generic_args());
 +    }
 +
 +    match adt.where_clause() {
 +        Some(where_clause) => {
 +            format_to!(buf, "\n{where_clause}\n{{\n{code}\n}}");
 +        }
 +        None => {
 +            format_to!(buf, " {{\n{code}\n}}");
 +        }
 +    }
 +
 +    buf
 +}
 +
 +pub(crate) fn add_method_to_adt(
 +    builder: &mut SourceChangeBuilder,
 +    adt: &ast::Adt,
 +    impl_def: Option<ast::Impl>,
 +    method: &str,
 +) {
 +    let mut buf = String::with_capacity(method.len() + 2);
 +    if impl_def.is_some() {
 +        buf.push('\n');
 +    }
 +    buf.push_str(method);
 +
 +    let start_offset = impl_def
 +        .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf))
 +        .unwrap_or_else(|| {
 +            buf = generate_impl_text(adt, &buf);
 +            adt.syntax().text_range().end()
 +        });
 +
 +    builder.insert(start_offset, buf);
 +}
 +
 +#[derive(Debug)]
 +pub(crate) struct ReferenceConversion {
 +    conversion: ReferenceConversionType,
 +    ty: hir::Type,
 +}
 +
 +#[derive(Debug)]
 +enum ReferenceConversionType {
 +    // reference can be stripped if the type is Copy
 +    Copy,
 +    // &String -> &str
 +    AsRefStr,
 +    // &Vec<T> -> &[T]
 +    AsRefSlice,
 +    // &Box<T> -> &T
 +    Dereferenced,
 +    // &Option<T> -> Option<&T>
 +    Option,
 +    // &Result<T, E> -> Result<&T, &E>
 +    Result,
 +}
 +
 +impl ReferenceConversion {
 +    pub(crate) fn convert_type(&self, db: &dyn HirDatabase) -> String {
 +        match self.conversion {
 +            ReferenceConversionType::Copy => self.ty.display(db).to_string(),
 +            ReferenceConversionType::AsRefStr => "&str".to_string(),
 +            ReferenceConversionType::AsRefSlice => {
 +                let type_argument_name =
 +                    self.ty.type_arguments().next().unwrap().display(db).to_string();
-                 format!("&{}", type_argument_name)
++                format!("&[{type_argument_name}]")
 +            }
 +            ReferenceConversionType::Dereferenced => {
 +                let type_argument_name =
 +                    self.ty.type_arguments().next().unwrap().display(db).to_string();
-                 format!("Option<&{}>", type_argument_name)
++                format!("&{type_argument_name}")
 +            }
 +            ReferenceConversionType::Option => {
 +                let type_argument_name =
 +                    self.ty.type_arguments().next().unwrap().display(db).to_string();
-                 format!("Result<&{}, &{}>", first_type_argument_name, second_type_argument_name)
++                format!("Option<&{type_argument_name}>")
 +            }
 +            ReferenceConversionType::Result => {
 +                let mut type_arguments = self.ty.type_arguments();
 +                let first_type_argument_name =
 +                    type_arguments.next().unwrap().display(db).to_string();
 +                let second_type_argument_name =
 +                    type_arguments.next().unwrap().display(db).to_string();
-             ReferenceConversionType::Copy => format!("self.{}", field_name),
++                format!("Result<&{first_type_argument_name}, &{second_type_argument_name}>")
 +            }
 +        }
 +    }
 +
 +    pub(crate) fn getter(&self, field_name: String) -> String {
 +        match self.conversion {
-             | ReferenceConversionType::Result => format!("self.{}.as_ref()", field_name),
++            ReferenceConversionType::Copy => format!("self.{field_name}"),
 +            ReferenceConversionType::AsRefStr
 +            | ReferenceConversionType::AsRefSlice
 +            | ReferenceConversionType::Dereferenced
 +            | ReferenceConversionType::Option
++            | ReferenceConversionType::Result => format!("self.{field_name}.as_ref()"),
 +        }
 +    }
 +}
 +
 +// FIXME: It should return a new hir::Type, but currently constructing new types is too cumbersome
 +//        and all users of this function operate on string type names, so they can do the conversion
 +//        itself themselves.
 +pub(crate) fn convert_reference_type(
 +    ty: hir::Type,
 +    db: &RootDatabase,
 +    famous_defs: &FamousDefs<'_, '_>,
 +) -> Option<ReferenceConversion> {
 +    handle_copy(&ty, db)
 +        .or_else(|| handle_as_ref_str(&ty, db, famous_defs))
 +        .or_else(|| handle_as_ref_slice(&ty, db, famous_defs))
 +        .or_else(|| handle_dereferenced(&ty, db, famous_defs))
 +        .or_else(|| handle_option_as_ref(&ty, db, famous_defs))
 +        .or_else(|| handle_result_as_ref(&ty, db, famous_defs))
 +        .map(|conversion| ReferenceConversion { ty, conversion })
 +}
 +
 +fn handle_copy(ty: &hir::Type, db: &dyn HirDatabase) -> Option<ReferenceConversionType> {
 +    ty.is_copy(db).then(|| ReferenceConversionType::Copy)
 +}
 +
 +fn handle_as_ref_str(
 +    ty: &hir::Type,
 +    db: &dyn HirDatabase,
 +    famous_defs: &FamousDefs<'_, '_>,
 +) -> Option<ReferenceConversionType> {
 +    let str_type = hir::BuiltinType::str().ty(db);
 +
 +    ty.impls_trait(db, famous_defs.core_convert_AsRef()?, &[str_type])
 +        .then(|| ReferenceConversionType::AsRefStr)
 +}
 +
 +fn handle_as_ref_slice(
 +    ty: &hir::Type,
 +    db: &dyn HirDatabase,
 +    famous_defs: &FamousDefs<'_, '_>,
 +) -> Option<ReferenceConversionType> {
 +    let type_argument = ty.type_arguments().next()?;
 +    let slice_type = hir::Type::new_slice(type_argument);
 +
 +    ty.impls_trait(db, famous_defs.core_convert_AsRef()?, &[slice_type])
 +        .then(|| ReferenceConversionType::AsRefSlice)
 +}
 +
 +fn handle_dereferenced(
 +    ty: &hir::Type,
 +    db: &dyn HirDatabase,
 +    famous_defs: &FamousDefs<'_, '_>,
 +) -> Option<ReferenceConversionType> {
 +    let type_argument = ty.type_arguments().next()?;
 +
 +    ty.impls_trait(db, famous_defs.core_convert_AsRef()?, &[type_argument])
 +        .then(|| ReferenceConversionType::Dereferenced)
 +}
 +
 +fn handle_option_as_ref(
 +    ty: &hir::Type,
 +    db: &dyn HirDatabase,
 +    famous_defs: &FamousDefs<'_, '_>,
 +) -> Option<ReferenceConversionType> {
 +    if ty.as_adt() == famous_defs.core_option_Option()?.ty(db).as_adt() {
 +        Some(ReferenceConversionType::Option)
 +    } else {
 +        None
 +    }
 +}
 +
 +fn handle_result_as_ref(
 +    ty: &hir::Type,
 +    db: &dyn HirDatabase,
 +    famous_defs: &FamousDefs<'_, '_>,
 +) -> Option<ReferenceConversionType> {
 +    if ty.as_adt() == famous_defs.core_result_Result()?.ty(db).as_adt() {
 +        Some(ReferenceConversionType::Result)
 +    } else {
 +        None
 +    }
 +}
 +
 +pub(crate) fn get_methods(items: &ast::AssocItemList) -> Vec<ast::Fn> {
 +    items
 +        .assoc_items()
 +        .flat_map(|i| match i {
 +            ast::AssocItem::Fn(f) => Some(f),
 +            _ => None,
 +        })
 +        .filter(|f| f.name().is_some())
 +        .collect()
 +}
 +
 +/// Trim(remove leading and trailing whitespace) `initial_range` in `source_file`, return the trimmed range.
 +pub(crate) fn trimmed_text_range(source_file: &SourceFile, initial_range: TextRange) -> TextRange {
 +    let mut trimmed_range = initial_range;
 +    while source_file
 +        .syntax()
 +        .token_at_offset(trimmed_range.start())
 +        .find_map(Whitespace::cast)
 +        .is_some()
 +        && trimmed_range.start() < trimmed_range.end()
 +    {
 +        let start = trimmed_range.start() + TextSize::from(1);
 +        trimmed_range = TextRange::new(start, trimmed_range.end());
 +    }
 +    while source_file
 +        .syntax()
 +        .token_at_offset(trimmed_range.end())
 +        .find_map(Whitespace::cast)
 +        .is_some()
 +        && trimmed_range.start() < trimmed_range.end()
 +    {
 +        let end = trimmed_range.end() - TextSize::from(1);
 +        trimmed_range = TextRange::new(trimmed_range.start(), end);
 +    }
 +    trimmed_range
 +}
 +
 +/// Convert a list of function params to a list of arguments that can be passed
 +/// into a function call.
 +pub(crate) fn convert_param_list_to_arg_list(list: ast::ParamList) -> ast::ArgList {
 +    let mut args = vec![];
 +    for param in list.params() {
 +        if let Some(ast::Pat::IdentPat(pat)) = param.pat() {
 +            if let Some(name) = pat.name() {
 +                let name = name.to_string();
 +                let expr = make::expr_path(make::ext::ident_path(&name));
 +                args.push(expr);
 +            }
 +        }
 +    }
 +    make::arg_list(args)
 +}
index 7a0c912959a12c53b9b909225accc56e866d3ca5,0000000000000000000000000000000000000000..6c87e66c134d72a28642c8e614d0e372e63073fc
mode 100644,000000..100644
--- /dev/null
@@@ -1,661 -1,0 +1,661 @@@
-                 let variant_name = make::ext::path_from_idents(["Self", &format!("{}", name)])?;
 +//! This module contains functions to generate default trait impl function bodies where possible.
 +
 +use syntax::{
 +    ast::{self, edit::AstNodeEdit, make, AstNode, BinaryOp, CmpOp, HasName, LogicOp},
 +    ted,
 +};
 +
 +/// Generate custom trait bodies without default implementation where possible.
 +///
 +/// Returns `Option` so that we can use `?` rather than `if let Some`. Returning
 +/// `None` means that generating a custom trait body failed, and the body will remain
 +/// as `todo!` instead.
 +pub(crate) fn gen_trait_fn_body(
 +    func: &ast::Fn,
 +    trait_path: &ast::Path,
 +    adt: &ast::Adt,
 +) -> Option<()> {
 +    match trait_path.segment()?.name_ref()?.text().as_str() {
 +        "Clone" => gen_clone_impl(adt, func),
 +        "Debug" => gen_debug_impl(adt, func),
 +        "Default" => gen_default_impl(adt, func),
 +        "Hash" => gen_hash_impl(adt, func),
 +        "PartialEq" => gen_partial_eq(adt, func),
 +        "PartialOrd" => gen_partial_ord(adt, func),
 +        _ => None,
 +    }
 +}
 +
 +/// Generate a `Clone` impl based on the fields and members of the target type.
 +fn gen_clone_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
 +    stdx::always!(func.name().map_or(false, |name| name.text() == "clone"));
 +    fn gen_clone_call(target: ast::Expr) -> ast::Expr {
 +        let method = make::name_ref("clone");
 +        make::expr_method_call(target, method, make::arg_list(None))
 +    }
 +    let expr = match adt {
 +        // `Clone` cannot be derived for unions, so no default impl can be provided.
 +        ast::Adt::Union(_) => return None,
 +        ast::Adt::Enum(enum_) => {
 +            let list = enum_.variant_list()?;
 +            let mut arms = vec![];
 +            for variant in list.variants() {
 +                let name = variant.name()?;
-                             let field_name = format!("arg{}", i);
++                let variant_name = make::ext::path_from_idents(["Self", &format!("{name}")])?;
 +
 +                match variant.field_list() {
 +                    // => match self { Self::Name { x } => Self::Name { x: x.clone() } }
 +                    Some(ast::FieldList::RecordFieldList(list)) => {
 +                        let mut pats = vec![];
 +                        let mut fields = vec![];
 +                        for field in list.fields() {
 +                            let field_name = field.name()?;
 +                            let pat = make::ident_pat(false, false, field_name.clone());
 +                            pats.push(pat.into());
 +
 +                            let path = make::ext::ident_path(&field_name.to_string());
 +                            let method_call = gen_clone_call(make::expr_path(path));
 +                            let name_ref = make::name_ref(&field_name.to_string());
 +                            let field = make::record_expr_field(name_ref, Some(method_call));
 +                            fields.push(field);
 +                        }
 +                        let pat = make::record_pat(variant_name.clone(), pats.into_iter());
 +                        let fields = make::record_expr_field_list(fields);
 +                        let record_expr = make::record_expr(variant_name, fields).into();
 +                        arms.push(make::match_arm(Some(pat.into()), None, record_expr));
 +                    }
 +
 +                    // => match self { Self::Name(arg1) => Self::Name(arg1.clone()) }
 +                    Some(ast::FieldList::TupleFieldList(list)) => {
 +                        let mut pats = vec![];
 +                        let mut fields = vec![];
 +                        for (i, _) in list.fields().enumerate() {
-                         let target = make::expr_field(f_path, &format!("{}", i));
++                            let field_name = format!("arg{i}");
 +                            let pat = make::ident_pat(false, false, make::name(&field_name));
 +                            pats.push(pat.into());
 +
 +                            let f_path = make::expr_path(make::ext::ident_path(&field_name));
 +                            fields.push(gen_clone_call(f_path));
 +                        }
 +                        let pat = make::tuple_struct_pat(variant_name.clone(), pats.into_iter());
 +                        let struct_name = make::expr_path(variant_name);
 +                        let tuple_expr = make::expr_call(struct_name, make::arg_list(fields));
 +                        arms.push(make::match_arm(Some(pat.into()), None, tuple_expr));
 +                    }
 +
 +                    // => match self { Self::Name => Self::Name }
 +                    None => {
 +                        let pattern = make::path_pat(variant_name.clone());
 +                        let variant_expr = make::expr_path(variant_name);
 +                        arms.push(make::match_arm(Some(pattern), None, variant_expr));
 +                    }
 +                }
 +            }
 +
 +            let match_target = make::expr_path(make::ext::ident_path("self"));
 +            let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1));
 +            make::expr_match(match_target, list)
 +        }
 +        ast::Adt::Struct(strukt) => {
 +            match strukt.field_list() {
 +                // => Self { name: self.name.clone() }
 +                Some(ast::FieldList::RecordFieldList(field_list)) => {
 +                    let mut fields = vec![];
 +                    for field in field_list.fields() {
 +                        let base = make::expr_path(make::ext::ident_path("self"));
 +                        let target = make::expr_field(base, &field.name()?.to_string());
 +                        let method_call = gen_clone_call(target);
 +                        let name_ref = make::name_ref(&field.name()?.to_string());
 +                        let field = make::record_expr_field(name_ref, Some(method_call));
 +                        fields.push(field);
 +                    }
 +                    let struct_name = make::ext::ident_path("Self");
 +                    let fields = make::record_expr_field_list(fields);
 +                    make::record_expr(struct_name, fields).into()
 +                }
 +                // => Self(self.0.clone(), self.1.clone())
 +                Some(ast::FieldList::TupleFieldList(field_list)) => {
 +                    let mut fields = vec![];
 +                    for (i, _) in field_list.fields().enumerate() {
 +                        let f_path = make::expr_path(make::ext::ident_path("self"));
-                 let variant_name = make::ext::path_from_idents(["Self", &format!("{}", name)])?;
++                        let target = make::expr_field(f_path, &format!("{i}"));
 +                        fields.push(gen_clone_call(target));
 +                    }
 +                    let struct_name = make::expr_path(make::ext::ident_path("Self"));
 +                    make::expr_call(struct_name, make::arg_list(fields))
 +                }
 +                // => Self { }
 +                None => {
 +                    let struct_name = make::ext::ident_path("Self");
 +                    let fields = make::record_expr_field_list(None);
 +                    make::record_expr(struct_name, fields).into()
 +                }
 +            }
 +        }
 +    };
 +    let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1));
 +    ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
 +    Some(())
 +}
 +
 +/// Generate a `Debug` impl based on the fields and members of the target type.
 +fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
 +    let annotated_name = adt.name()?;
 +    match adt {
 +        // `Debug` cannot be derived for unions, so no default impl can be provided.
 +        ast::Adt::Union(_) => None,
 +
 +        // => match self { Self::Variant => write!(f, "Variant") }
 +        ast::Adt::Enum(enum_) => {
 +            let list = enum_.variant_list()?;
 +            let mut arms = vec![];
 +            for variant in list.variants() {
 +                let name = variant.name()?;
-                         let struct_name = format!("\"{}\"", name);
++                let variant_name = make::ext::path_from_idents(["Self", &format!("{name}")])?;
 +                let target = make::expr_path(make::ext::ident_path("f"));
 +
 +                match variant.field_list() {
 +                    Some(ast::FieldList::RecordFieldList(list)) => {
 +                        // => f.debug_struct(name)
 +                        let target = make::expr_path(make::ext::ident_path("f"));
 +                        let method = make::name_ref("debug_struct");
-                             let name = make::expr_literal(&(format!("\"{}\"", field_name))).into();
-                             let path = &format!("{}", field_name);
++                        let struct_name = format!("\"{name}\"");
 +                        let args = make::arg_list(Some(make::expr_literal(&struct_name).into()));
 +                        let mut expr = make::expr_method_call(target, method, args);
 +
 +                        let mut pats = vec![];
 +                        for field in list.fields() {
 +                            let field_name = field.name()?;
 +
 +                            // create a field pattern for use in `MyStruct { fields.. }`
 +                            let pat = make::ident_pat(false, false, field_name.clone());
 +                            pats.push(pat.into());
 +
 +                            // => <expr>.field("field_name", field)
 +                            let method_name = make::name_ref("field");
-                         let struct_name = format!("\"{}\"", name);
++                            let name = make::expr_literal(&(format!("\"{field_name}\""))).into();
++                            let path = &format!("{field_name}");
 +                            let path = make::expr_path(make::ext::ident_path(path));
 +                            let args = make::arg_list(vec![name, path]);
 +                            expr = make::expr_method_call(expr, method_name, args);
 +                        }
 +
 +                        // => <expr>.finish()
 +                        let method = make::name_ref("finish");
 +                        let expr = make::expr_method_call(expr, method, make::arg_list(None));
 +
 +                        // => MyStruct { fields.. } => f.debug_struct("MyStruct")...finish(),
 +                        let pat = make::record_pat(variant_name.clone(), pats.into_iter());
 +                        arms.push(make::match_arm(Some(pat.into()), None, expr));
 +                    }
 +                    Some(ast::FieldList::TupleFieldList(list)) => {
 +                        // => f.debug_tuple(name)
 +                        let target = make::expr_path(make::ext::ident_path("f"));
 +                        let method = make::name_ref("debug_tuple");
-                             let name = format!("arg{}", i);
++                        let struct_name = format!("\"{name}\"");
 +                        let args = make::arg_list(Some(make::expr_literal(&struct_name).into()));
 +                        let mut expr = make::expr_method_call(target, method, args);
 +
 +                        let mut pats = vec![];
 +                        for (i, _) in list.fields().enumerate() {
-                         let fmt_string = make::expr_literal(&(format!("\"{}\"", name))).into();
++                            let name = format!("arg{i}");
 +
 +                            // create a field pattern for use in `MyStruct(fields..)`
 +                            let field_name = make::name(&name);
 +                            let pat = make::ident_pat(false, false, field_name.clone());
 +                            pats.push(pat.into());
 +
 +                            // => <expr>.field(field)
 +                            let method_name = make::name_ref("field");
 +                            let field_path = &name.to_string();
 +                            let field_path = make::expr_path(make::ext::ident_path(field_path));
 +                            let args = make::arg_list(vec![field_path]);
 +                            expr = make::expr_method_call(expr, method_name, args);
 +                        }
 +
 +                        // => <expr>.finish()
 +                        let method = make::name_ref("finish");
 +                        let expr = make::expr_method_call(expr, method, make::arg_list(None));
 +
 +                        // => MyStruct (fields..) => f.debug_tuple("MyStruct")...finish(),
 +                        let pat = make::tuple_struct_pat(variant_name.clone(), pats.into_iter());
 +                        arms.push(make::match_arm(Some(pat.into()), None, expr));
 +                    }
 +                    None => {
-             let name = format!("\"{}\"", annotated_name);
++                        let fmt_string = make::expr_literal(&(format!("\"{name}\""))).into();
 +                        let args = make::arg_list([target, fmt_string]);
 +                        let macro_name = make::expr_path(make::ext::ident_path("write"));
 +                        let macro_call = make::expr_macro_call(macro_name, args);
 +
 +                        let variant_name = make::path_pat(variant_name);
 +                        arms.push(make::match_arm(Some(variant_name), None, macro_call));
 +                    }
 +                }
 +            }
 +
 +            let match_target = make::expr_path(make::ext::ident_path("self"));
 +            let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1));
 +            let match_expr = make::expr_match(match_target, list);
 +
 +            let body = make::block_expr(None, Some(match_expr));
 +            let body = body.indent(ast::edit::IndentLevel(1));
 +            ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
 +            Some(())
 +        }
 +
 +        ast::Adt::Struct(strukt) => {
-                         let f_name = make::expr_literal(&(format!("\"{}\"", name))).into();
++            let name = format!("\"{annotated_name}\"");
 +            let args = make::arg_list(Some(make::expr_literal(&name).into()));
 +            let target = make::expr_path(make::ext::ident_path("f"));
 +
 +            let expr = match strukt.field_list() {
 +                // => f.debug_struct("Name").finish()
 +                None => make::expr_method_call(target, make::name_ref("debug_struct"), args),
 +
 +                // => f.debug_struct("Name").field("foo", &self.foo).finish()
 +                Some(ast::FieldList::RecordFieldList(field_list)) => {
 +                    let method = make::name_ref("debug_struct");
 +                    let mut expr = make::expr_method_call(target, method, args);
 +                    for field in field_list.fields() {
 +                        let name = field.name()?;
-                         let f_path = make::expr_field(f_path, &format!("{}", name));
++                        let f_name = make::expr_literal(&(format!("\"{name}\""))).into();
 +                        let f_path = make::expr_path(make::ext::ident_path("self"));
 +                        let f_path = make::expr_ref(f_path, false);
-                         let f_path = make::expr_field(f_path, &format!("{}", i));
++                        let f_path = make::expr_field(f_path, &format!("{name}"));
 +                        let args = make::arg_list([f_name, f_path]);
 +                        expr = make::expr_method_call(expr, make::name_ref("field"), args);
 +                    }
 +                    expr
 +                }
 +
 +                // => f.debug_tuple("Name").field(self.0).finish()
 +                Some(ast::FieldList::TupleFieldList(field_list)) => {
 +                    let method = make::name_ref("debug_tuple");
 +                    let mut expr = make::expr_method_call(target, method, args);
 +                    for (i, _) in field_list.fields().enumerate() {
 +                        let f_path = make::expr_path(make::ext::ident_path("self"));
 +                        let f_path = make::expr_ref(f_path, false);
-                     let target = make::expr_field(base, &format!("{}", i));
++                        let f_path = make::expr_field(f_path, &format!("{i}"));
 +                        let method = make::name_ref("field");
 +                        expr = make::expr_method_call(expr, method, make::arg_list(Some(f_path)));
 +                    }
 +                    expr
 +                }
 +            };
 +
 +            let method = make::name_ref("finish");
 +            let expr = make::expr_method_call(expr, method, make::arg_list(None));
 +            let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1));
 +            ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
 +            Some(())
 +        }
 +    }
 +}
 +
 +/// Generate a `Debug` impl based on the fields and members of the target type.
 +fn gen_default_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
 +    fn gen_default_call() -> Option<ast::Expr> {
 +        let fn_name = make::ext::path_from_idents(["Default", "default"])?;
 +        Some(make::expr_call(make::expr_path(fn_name), make::arg_list(None)))
 +    }
 +    match adt {
 +        // `Debug` cannot be derived for unions, so no default impl can be provided.
 +        ast::Adt::Union(_) => None,
 +        // Deriving `Debug` for enums is not stable yet.
 +        ast::Adt::Enum(_) => None,
 +        ast::Adt::Struct(strukt) => {
 +            let expr = match strukt.field_list() {
 +                Some(ast::FieldList::RecordFieldList(field_list)) => {
 +                    let mut fields = vec![];
 +                    for field in field_list.fields() {
 +                        let method_call = gen_default_call()?;
 +                        let name_ref = make::name_ref(&field.name()?.to_string());
 +                        let field = make::record_expr_field(name_ref, Some(method_call));
 +                        fields.push(field);
 +                    }
 +                    let struct_name = make::ext::ident_path("Self");
 +                    let fields = make::record_expr_field_list(fields);
 +                    make::record_expr(struct_name, fields).into()
 +                }
 +                Some(ast::FieldList::TupleFieldList(field_list)) => {
 +                    let struct_name = make::expr_path(make::ext::ident_path("Self"));
 +                    let fields = field_list
 +                        .fields()
 +                        .map(|_| gen_default_call())
 +                        .collect::<Option<Vec<ast::Expr>>>()?;
 +                    make::expr_call(struct_name, make::arg_list(fields))
 +                }
 +                None => {
 +                    let struct_name = make::ext::ident_path("Self");
 +                    let fields = make::record_expr_field_list(None);
 +                    make::record_expr(struct_name, fields).into()
 +                }
 +            };
 +            let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1));
 +            ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
 +            Some(())
 +        }
 +    }
 +}
 +
 +/// Generate a `Hash` impl based on the fields and members of the target type.
 +fn gen_hash_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
 +    stdx::always!(func.name().map_or(false, |name| name.text() == "hash"));
 +    fn gen_hash_call(target: ast::Expr) -> ast::Stmt {
 +        let method = make::name_ref("hash");
 +        let arg = make::expr_path(make::ext::ident_path("state"));
 +        let expr = make::expr_method_call(target, method, make::arg_list(Some(arg)));
 +        make::expr_stmt(expr).into()
 +    }
 +
 +    let body = match adt {
 +        // `Hash` cannot be derived for unions, so no default impl can be provided.
 +        ast::Adt::Union(_) => return None,
 +
 +        // => std::mem::discriminant(self).hash(state);
 +        ast::Adt::Enum(_) => {
 +            let fn_name = make_discriminant()?;
 +
 +            let arg = make::expr_path(make::ext::ident_path("self"));
 +            let fn_call = make::expr_call(fn_name, make::arg_list(Some(arg)));
 +            let stmt = gen_hash_call(fn_call);
 +
 +            make::block_expr(Some(stmt), None).indent(ast::edit::IndentLevel(1))
 +        }
 +        ast::Adt::Struct(strukt) => match strukt.field_list() {
 +            // => self.<field>.hash(state);
 +            Some(ast::FieldList::RecordFieldList(field_list)) => {
 +                let mut stmts = vec![];
 +                for field in field_list.fields() {
 +                    let base = make::expr_path(make::ext::ident_path("self"));
 +                    let target = make::expr_field(base, &field.name()?.to_string());
 +                    stmts.push(gen_hash_call(target));
 +                }
 +                make::block_expr(stmts, None).indent(ast::edit::IndentLevel(1))
 +            }
 +
 +            // => self.<field_index>.hash(state);
 +            Some(ast::FieldList::TupleFieldList(field_list)) => {
 +                let mut stmts = vec![];
 +                for (i, _) in field_list.fields().enumerate() {
 +                    let base = make::expr_path(make::ext::ident_path("self"));
-                             let l_name = &format!("l_{}", field_name);
++                    let target = make::expr_field(base, &format!("{i}"));
 +                    stmts.push(gen_hash_call(target));
 +                }
 +                make::block_expr(stmts, None).indent(ast::edit::IndentLevel(1))
 +            }
 +
 +            // No fields in the body means there's nothing to hash.
 +            None => return None,
 +        },
 +    };
 +
 +    ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
 +    Some(())
 +}
 +
 +/// Generate a `PartialEq` impl based on the fields and members of the target type.
 +fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
 +    stdx::always!(func.name().map_or(false, |name| name.text() == "eq"));
 +    fn gen_eq_chain(expr: Option<ast::Expr>, cmp: ast::Expr) -> Option<ast::Expr> {
 +        match expr {
 +            Some(expr) => Some(make::expr_bin_op(expr, BinaryOp::LogicOp(LogicOp::And), cmp)),
 +            None => Some(cmp),
 +        }
 +    }
 +
 +    fn gen_record_pat_field(field_name: &str, pat_name: &str) -> ast::RecordPatField {
 +        let pat = make::ext::simple_ident_pat(make::name(pat_name));
 +        let name_ref = make::name_ref(field_name);
 +        make::record_pat_field(name_ref, pat.into())
 +    }
 +
 +    fn gen_record_pat(record_name: ast::Path, fields: Vec<ast::RecordPatField>) -> ast::RecordPat {
 +        let list = make::record_pat_field_list(fields);
 +        make::record_pat_with_fields(record_name, list)
 +    }
 +
 +    fn gen_variant_path(variant: &ast::Variant) -> Option<ast::Path> {
 +        make::ext::path_from_idents(["Self", &variant.name()?.to_string()])
 +    }
 +
 +    fn gen_tuple_field(field_name: &String) -> ast::Pat {
 +        ast::Pat::IdentPat(make::ident_pat(false, false, make::name(field_name)))
 +    }
 +
 +    // FIXME: return `None` if the trait carries a generic type; we can only
 +    // generate this code `Self` for the time being.
 +
 +    let body = match adt {
 +        // `PartialEq` cannot be derived for unions, so no default impl can be provided.
 +        ast::Adt::Union(_) => return None,
 +
 +        ast::Adt::Enum(enum_) => {
 +            // => std::mem::discriminant(self) == std::mem::discriminant(other)
 +            let lhs_name = make::expr_path(make::ext::ident_path("self"));
 +            let lhs = make::expr_call(make_discriminant()?, make::arg_list(Some(lhs_name.clone())));
 +            let rhs_name = make::expr_path(make::ext::ident_path("other"));
 +            let rhs = make::expr_call(make_discriminant()?, make::arg_list(Some(rhs_name.clone())));
 +            let eq_check =
 +                make::expr_bin_op(lhs, BinaryOp::CmpOp(CmpOp::Eq { negated: false }), rhs);
 +
 +            let mut n_cases = 0;
 +            let mut arms = vec![];
 +            for variant in enum_.variant_list()?.variants() {
 +                n_cases += 1;
 +                match variant.field_list() {
 +                    // => (Self::Bar { bin: l_bin }, Self::Bar { bin: r_bin }) => l_bin == r_bin,
 +                    Some(ast::FieldList::RecordFieldList(list)) => {
 +                        let mut expr = None;
 +                        let mut l_fields = vec![];
 +                        let mut r_fields = vec![];
 +
 +                        for field in list.fields() {
 +                            let field_name = field.name()?.to_string();
 +
-                             let r_name = &format!("r_{}", field_name);
++                            let l_name = &format!("l_{field_name}");
 +                            l_fields.push(gen_record_pat_field(&field_name, l_name));
 +
-                             let field_name = format!("{}", i);
++                            let r_name = &format!("r_{field_name}");
 +                            r_fields.push(gen_record_pat_field(&field_name, r_name));
 +
 +                            let lhs = make::expr_path(make::ext::ident_path(l_name));
 +                            let rhs = make::expr_path(make::ext::ident_path(r_name));
 +                            let cmp = make::expr_bin_op(
 +                                lhs,
 +                                BinaryOp::CmpOp(CmpOp::Eq { negated: false }),
 +                                rhs,
 +                            );
 +                            expr = gen_eq_chain(expr, cmp);
 +                        }
 +
 +                        let left = gen_record_pat(gen_variant_path(&variant)?, l_fields);
 +                        let right = gen_record_pat(gen_variant_path(&variant)?, r_fields);
 +                        let tuple = make::tuple_pat(vec![left.into(), right.into()]);
 +
 +                        if let Some(expr) = expr {
 +                            arms.push(make::match_arm(Some(tuple.into()), None, expr));
 +                        }
 +                    }
 +
 +                    Some(ast::FieldList::TupleFieldList(list)) => {
 +                        let mut expr = None;
 +                        let mut l_fields = vec![];
 +                        let mut r_fields = vec![];
 +
 +                        for (i, _) in list.fields().enumerate() {
-                             let l_name = format!("l{}", field_name);
++                            let field_name = format!("{i}");
 +
-                             let r_name = format!("r{}", field_name);
++                            let l_name = format!("l{field_name}");
 +                            l_fields.push(gen_tuple_field(&l_name));
 +
-                     let idx = format!("{}", i);
++                            let r_name = format!("r{field_name}");
 +                            r_fields.push(gen_tuple_field(&r_name));
 +
 +                            let lhs = make::expr_path(make::ext::ident_path(&l_name));
 +                            let rhs = make::expr_path(make::ext::ident_path(&r_name));
 +                            let cmp = make::expr_bin_op(
 +                                lhs,
 +                                BinaryOp::CmpOp(CmpOp::Eq { negated: false }),
 +                                rhs,
 +                            );
 +                            expr = gen_eq_chain(expr, cmp);
 +                        }
 +
 +                        let left = make::tuple_struct_pat(gen_variant_path(&variant)?, l_fields);
 +                        let right = make::tuple_struct_pat(gen_variant_path(&variant)?, r_fields);
 +                        let tuple = make::tuple_pat(vec![left.into(), right.into()]);
 +
 +                        if let Some(expr) = expr {
 +                            arms.push(make::match_arm(Some(tuple.into()), None, expr));
 +                        }
 +                    }
 +                    None => continue,
 +                }
 +            }
 +
 +            let expr = match arms.len() {
 +                0 => eq_check,
 +                _ => {
 +                    if n_cases > arms.len() {
 +                        let lhs = make::wildcard_pat().into();
 +                        arms.push(make::match_arm(Some(lhs), None, eq_check));
 +                    }
 +
 +                    let match_target = make::expr_tuple(vec![lhs_name, rhs_name]);
 +                    let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1));
 +                    make::expr_match(match_target, list)
 +                }
 +            };
 +
 +            make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1))
 +        }
 +        ast::Adt::Struct(strukt) => match strukt.field_list() {
 +            Some(ast::FieldList::RecordFieldList(field_list)) => {
 +                let mut expr = None;
 +                for field in field_list.fields() {
 +                    let lhs = make::expr_path(make::ext::ident_path("self"));
 +                    let lhs = make::expr_field(lhs, &field.name()?.to_string());
 +                    let rhs = make::expr_path(make::ext::ident_path("other"));
 +                    let rhs = make::expr_field(rhs, &field.name()?.to_string());
 +                    let cmp =
 +                        make::expr_bin_op(lhs, BinaryOp::CmpOp(CmpOp::Eq { negated: false }), rhs);
 +                    expr = gen_eq_chain(expr, cmp);
 +                }
 +                make::block_expr(None, expr).indent(ast::edit::IndentLevel(1))
 +            }
 +
 +            Some(ast::FieldList::TupleFieldList(field_list)) => {
 +                let mut expr = None;
 +                for (i, _) in field_list.fields().enumerate() {
-                     let idx = format!("{}", i);
++                    let idx = format!("{i}");
 +                    let lhs = make::expr_path(make::ext::ident_path("self"));
 +                    let lhs = make::expr_field(lhs, &idx);
 +                    let rhs = make::expr_path(make::ext::ident_path("other"));
 +                    let rhs = make::expr_field(rhs, &idx);
 +                    let cmp =
 +                        make::expr_bin_op(lhs, BinaryOp::CmpOp(CmpOp::Eq { negated: false }), rhs);
 +                    expr = gen_eq_chain(expr, cmp);
 +                }
 +                make::block_expr(None, expr).indent(ast::edit::IndentLevel(1))
 +            }
 +
 +            // No fields in the body means there's nothing to hash.
 +            None => {
 +                let expr = make::expr_literal("true").into();
 +                make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1))
 +            }
 +        },
 +    };
 +
 +    ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
 +    Some(())
 +}
 +
 +fn gen_partial_ord(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
 +    stdx::always!(func.name().map_or(false, |name| name.text() == "partial_cmp"));
 +    fn gen_partial_eq_match(match_target: ast::Expr) -> Option<ast::Stmt> {
 +        let mut arms = vec![];
 +
 +        let variant_name =
 +            make::path_pat(make::ext::path_from_idents(["core", "cmp", "Ordering", "Equal"])?);
 +        let lhs = make::tuple_struct_pat(make::ext::path_from_idents(["Some"])?, [variant_name]);
 +        arms.push(make::match_arm(Some(lhs.into()), None, make::expr_empty_block()));
 +
 +        arms.push(make::match_arm(
 +            [make::ident_pat(false, false, make::name("ord")).into()],
 +            None,
 +            make::expr_return(Some(make::expr_path(make::ext::ident_path("ord")))),
 +        ));
 +        let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1));
 +        Some(make::expr_stmt(make::expr_match(match_target, list)).into())
 +    }
 +
 +    fn gen_partial_cmp_call(lhs: ast::Expr, rhs: ast::Expr) -> ast::Expr {
 +        let rhs = make::expr_ref(rhs, false);
 +        let method = make::name_ref("partial_cmp");
 +        make::expr_method_call(lhs, method, make::arg_list(Some(rhs)))
 +    }
 +
 +    // FIXME: return `None` if the trait carries a generic type; we can only
 +    // generate this code `Self` for the time being.
 +
 +    let body = match adt {
 +        // `PartialOrd` cannot be derived for unions, so no default impl can be provided.
 +        ast::Adt::Union(_) => return None,
 +        // `core::mem::Discriminant` does not implement `PartialOrd` in stable Rust today.
 +        ast::Adt::Enum(_) => return None,
 +        ast::Adt::Struct(strukt) => match strukt.field_list() {
 +            Some(ast::FieldList::RecordFieldList(field_list)) => {
 +                let mut exprs = vec![];
 +                for field in field_list.fields() {
 +                    let lhs = make::expr_path(make::ext::ident_path("self"));
 +                    let lhs = make::expr_field(lhs, &field.name()?.to_string());
 +                    let rhs = make::expr_path(make::ext::ident_path("other"));
 +                    let rhs = make::expr_field(rhs, &field.name()?.to_string());
 +                    let ord = gen_partial_cmp_call(lhs, rhs);
 +                    exprs.push(ord);
 +                }
 +
 +                let tail = exprs.pop();
 +                let stmts = exprs
 +                    .into_iter()
 +                    .map(gen_partial_eq_match)
 +                    .collect::<Option<Vec<ast::Stmt>>>()?;
 +                make::block_expr(stmts.into_iter(), tail).indent(ast::edit::IndentLevel(1))
 +            }
 +
 +            Some(ast::FieldList::TupleFieldList(field_list)) => {
 +                let mut exprs = vec![];
 +                for (i, _) in field_list.fields().enumerate() {
++                    let idx = format!("{i}");
 +                    let lhs = make::expr_path(make::ext::ident_path("self"));
 +                    let lhs = make::expr_field(lhs, &idx);
 +                    let rhs = make::expr_path(make::ext::ident_path("other"));
 +                    let rhs = make::expr_field(rhs, &idx);
 +                    let ord = gen_partial_cmp_call(lhs, rhs);
 +                    exprs.push(ord);
 +                }
 +                let tail = exprs.pop();
 +                let stmts = exprs
 +                    .into_iter()
 +                    .map(gen_partial_eq_match)
 +                    .collect::<Option<Vec<ast::Stmt>>>()?;
 +                make::block_expr(stmts.into_iter(), tail).indent(ast::edit::IndentLevel(1))
 +            }
 +
 +            // No fields in the body means there's nothing to compare.
 +            None => {
 +                let expr = make::expr_literal("true").into();
 +                make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1))
 +            }
 +        },
 +    };
 +
 +    ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
 +    Some(())
 +}
 +
 +fn make_discriminant() -> Option<ast::Expr> {
 +    Some(make::expr_path(make::ext::path_from_idents(["core", "mem", "discriminant"])?))
 +}
index 9a891cea2d458491979475f1753b1f3f67b69645,0000000000000000000000000000000000000000..b9bd47f7da504609b4c3b78947bd00bc9774164a
mode 100644,000000..100644
--- /dev/null
@@@ -1,616 -1,0 +1,637 @@@
-     if !ctx.config.snippets.is_empty() {
-         add_custom_postfix_completions(acc, ctx, &postfix_snippet, &receiver_text);
-     }
 +//! Postfix completions, like `Ok(10).ifl$0` => `if let Ok() = Ok(10) { $0 }`.
 +
 +mod format_like;
 +
 +use hir::{Documentation, HasAttrs};
 +use ide_db::{imports::insert_use::ImportScope, ty_filter::TryEnum, SnippetCap};
 +use syntax::{
 +    ast::{self, AstNode, AstToken},
 +    SyntaxKind::{EXPR_STMT, STMT_LIST},
 +    TextRange, TextSize,
 +};
 +use text_edit::TextEdit;
 +
 +use crate::{
 +    completions::postfix::format_like::add_format_like_completions,
 +    context::{CompletionContext, DotAccess, DotAccessKind},
 +    item::{Builder, CompletionRelevancePostfixMatch},
 +    CompletionItem, CompletionItemKind, CompletionRelevance, Completions, SnippetScope,
 +};
 +
 +pub(crate) fn complete_postfix(
 +    acc: &mut Completions,
 +    ctx: &CompletionContext<'_>,
 +    dot_access: &DotAccess,
 +) {
 +    if !ctx.config.enable_postfix_completions {
 +        return;
 +    }
 +
 +    let (dot_receiver, receiver_ty, receiver_is_ambiguous_float_literal) = match dot_access {
 +        DotAccess { receiver_ty: Some(ty), receiver: Some(it), kind, .. } => (
 +            it,
 +            &ty.original,
 +            match *kind {
 +                DotAccessKind::Field { receiver_is_ambiguous_float_literal } => {
 +                    receiver_is_ambiguous_float_literal
 +                }
 +                DotAccessKind::Method { .. } => false,
 +            },
 +        ),
 +        _ => return,
 +    };
 +
 +    let receiver_text = get_receiver_text(dot_receiver, receiver_is_ambiguous_float_literal);
 +
 +    let cap = match ctx.config.snippet_cap {
 +        Some(it) => it,
 +        None => return,
 +    };
 +
 +    let postfix_snippet = match build_postfix_snippet_builder(ctx, cap, dot_receiver) {
 +        Some(it) => it,
 +        None => return,
 +    };
 +
 +    if let Some(drop_trait) = ctx.famous_defs().core_ops_Drop() {
 +        if receiver_ty.impls_trait(ctx.db, drop_trait, &[]) {
 +            if let &[hir::AssocItem::Function(drop_fn)] = &*drop_trait.items(ctx.db) {
 +                cov_mark::hit!(postfix_drop_completion);
 +                // FIXME: check that `drop` is in scope, use fully qualified path if it isn't/if shadowed
 +                let mut item = postfix_snippet(
 +                    "drop",
 +                    "fn drop(&mut self)",
 +                    &format!("drop($0{})", receiver_text),
 +                );
 +                item.set_documentation(drop_fn.docs(ctx.db));
 +                item.add_to(acc);
 +            }
 +        }
 +    }
 +
 +    let try_enum = TryEnum::from_ty(&ctx.sema, &receiver_ty.strip_references());
 +    if let Some(try_enum) = &try_enum {
 +        match try_enum {
 +            TryEnum::Result => {
 +                postfix_snippet(
 +                    "ifl",
 +                    "if let Ok {}",
 +                    &format!("if let Ok($1) = {} {{\n    $0\n}}", receiver_text),
 +                )
 +                .add_to(acc);
 +
 +                postfix_snippet(
 +                    "while",
 +                    "while let Ok {}",
 +                    &format!("while let Ok($1) = {} {{\n    $0\n}}", receiver_text),
 +                )
 +                .add_to(acc);
 +            }
 +            TryEnum::Option => {
 +                postfix_snippet(
 +                    "ifl",
 +                    "if let Some {}",
 +                    &format!("if let Some($1) = {} {{\n    $0\n}}", receiver_text),
 +                )
 +                .add_to(acc);
 +
 +                postfix_snippet(
 +                    "while",
 +                    "while let Some {}",
 +                    &format!("while let Some($1) = {} {{\n    $0\n}}", receiver_text),
 +                )
 +                .add_to(acc);
 +            }
 +        }
 +    } else if receiver_ty.is_bool() || receiver_ty.is_unknown() {
 +        postfix_snippet("if", "if expr {}", &format!("if {} {{\n    $0\n}}", receiver_text))
 +            .add_to(acc);
 +        postfix_snippet(
 +            "while",
 +            "while expr {}",
 +            &format!("while {} {{\n    $0\n}}", receiver_text),
 +        )
 +        .add_to(acc);
 +        postfix_snippet("not", "!expr", &format!("!{}", receiver_text)).add_to(acc);
 +    } else if let Some(trait_) = ctx.famous_defs().core_iter_IntoIterator() {
 +        if receiver_ty.impls_trait(ctx.db, trait_, &[]) {
 +            postfix_snippet(
 +                "for",
 +                "for ele in expr {}",
 +                &format!("for ele in {} {{\n    $0\n}}", receiver_text),
 +            )
 +            .add_to(acc);
 +        }
 +    }
 +
 +    postfix_snippet("ref", "&expr", &format!("&{}", receiver_text)).add_to(acc);
 +    postfix_snippet("refm", "&mut expr", &format!("&mut {}", receiver_text)).add_to(acc);
 +
 +    // The rest of the postfix completions create an expression that moves an argument,
 +    // so it's better to consider references now to avoid breaking the compilation
 +    let dot_receiver = include_references(dot_receiver);
 +    let receiver_text = get_receiver_text(&dot_receiver, receiver_is_ambiguous_float_literal);
 +    let postfix_snippet = match build_postfix_snippet_builder(ctx, cap, &dot_receiver) {
 +        Some(it) => it,
 +        None => return,
 +    };
 +
++    if !ctx.config.snippets.is_empty() {
++        add_custom_postfix_completions(acc, ctx, &postfix_snippet, &receiver_text);
++    }
++
 +    match try_enum {
 +        Some(try_enum) => match try_enum {
 +            TryEnum::Result => {
 +                postfix_snippet(
 +                    "match",
 +                    "match expr {}",
 +                    &format!("match {} {{\n    Ok(${{1:_}}) => {{$2}},\n    Err(${{3:_}}) => {{$0}},\n}}", receiver_text),
 +                )
 +                .add_to(acc);
 +            }
 +            TryEnum::Option => {
 +                postfix_snippet(
 +                    "match",
 +                    "match expr {}",
 +                    &format!(
 +                        "match {} {{\n    Some(${{1:_}}) => {{$2}},\n    None => {{$0}},\n}}",
 +                        receiver_text
 +                    ),
 +                )
 +                .add_to(acc);
 +            }
 +        },
 +        None => {
 +            postfix_snippet(
 +                "match",
 +                "match expr {}",
 +                &format!("match {} {{\n    ${{1:_}} => {{$0}},\n}}", receiver_text),
 +            )
 +            .add_to(acc);
 +        }
 +    }
 +
 +    postfix_snippet("box", "Box::new(expr)", &format!("Box::new({})", receiver_text)).add_to(acc);
 +    postfix_snippet("dbg", "dbg!(expr)", &format!("dbg!({})", receiver_text)).add_to(acc); // fixme
 +    postfix_snippet("dbgr", "dbg!(&expr)", &format!("dbg!(&{})", receiver_text)).add_to(acc);
 +    postfix_snippet("call", "function(expr)", &format!("${{1}}({})", receiver_text)).add_to(acc);
 +
 +    if let Some(parent) = dot_receiver.syntax().parent().and_then(|p| p.parent()) {
 +        if matches!(parent.kind(), STMT_LIST | EXPR_STMT) {
 +            postfix_snippet("let", "let", &format!("let $0 = {};", receiver_text)).add_to(acc);
 +            postfix_snippet("letm", "let mut", &format!("let mut $0 = {};", receiver_text))
 +                .add_to(acc);
 +        }
 +    }
 +
 +    if let ast::Expr::Literal(literal) = dot_receiver.clone() {
 +        if let Some(literal_text) = ast::String::cast(literal.token()) {
 +            add_format_like_completions(acc, ctx, &dot_receiver, cap, &literal_text);
 +        }
 +    }
 +}
 +
 +fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String {
 +    let text = if receiver_is_ambiguous_float_literal {
 +        let text = receiver.syntax().text();
 +        let without_dot = ..text.len() - TextSize::of('.');
 +        text.slice(without_dot).to_string()
 +    } else {
 +        receiver.to_string()
 +    };
 +
 +    // The receiver texts should be interpreted as-is, as they are expected to be
 +    // normal Rust expressions. We escape '\' and '$' so they don't get treated as
 +    // snippet-specific constructs.
 +    //
 +    // Note that we don't need to escape the other characters that can be escaped,
 +    // because they wouldn't be treated as snippet-specific constructs without '$'.
 +    text.replace('\\', "\\\\").replace('$', "\\$")
 +}
 +
 +fn include_references(initial_element: &ast::Expr) -> ast::Expr {
 +    let mut resulting_element = initial_element.clone();
 +    while let Some(parent_ref_element) =
 +        resulting_element.syntax().parent().and_then(ast::RefExpr::cast)
 +    {
 +        resulting_element = ast::Expr::from(parent_ref_element);
 +    }
 +    resulting_element
 +}
 +
 +fn build_postfix_snippet_builder<'ctx>(
 +    ctx: &'ctx CompletionContext<'_>,
 +    cap: SnippetCap,
 +    receiver: &'ctx ast::Expr,
 +) -> Option<impl Fn(&str, &str, &str) -> Builder + 'ctx> {
 +    let receiver_syntax = receiver.syntax();
 +    let receiver_range = ctx.sema.original_range_opt(receiver_syntax)?.range;
 +    if ctx.source_range().end() < receiver_range.start() {
 +        // This shouldn't happen, yet it does. I assume this might be due to an incorrect token mapping.
 +        return None;
 +    }
 +    let delete_range = TextRange::new(receiver_range.start(), ctx.source_range().end());
 +
 +    // Wrapping impl Fn in an option ruins lifetime inference for the parameters in a way that
 +    // can't be annotated for the closure, hence fix it by constructing it without the Option first
 +    fn build<'ctx>(
 +        ctx: &'ctx CompletionContext<'_>,
 +        cap: SnippetCap,
 +        delete_range: TextRange,
 +    ) -> impl Fn(&str, &str, &str) -> Builder + 'ctx {
 +        move |label, detail, snippet| {
 +            let edit = TextEdit::replace(delete_range, snippet.to_string());
 +            let mut item =
 +                CompletionItem::new(CompletionItemKind::Snippet, ctx.source_range(), label);
 +            item.detail(detail).snippet_edit(cap, edit);
 +            let postfix_match = if ctx.original_token.text() == label {
 +                cov_mark::hit!(postfix_exact_match_is_high_priority);
 +                Some(CompletionRelevancePostfixMatch::Exact)
 +            } else {
 +                cov_mark::hit!(postfix_inexact_match_is_low_priority);
 +                Some(CompletionRelevancePostfixMatch::NonExact)
 +            };
 +            let relevance = CompletionRelevance { postfix_match, ..Default::default() };
 +            item.set_relevance(relevance);
 +            item
 +        }
 +    }
 +    Some(build(ctx, cap, delete_range))
 +}
 +
 +fn add_custom_postfix_completions(
 +    acc: &mut Completions,
 +    ctx: &CompletionContext<'_>,
 +    postfix_snippet: impl Fn(&str, &str, &str) -> Builder,
 +    receiver_text: &str,
 +) -> Option<()> {
 +    if ImportScope::find_insert_use_container(&ctx.token.parent()?, &ctx.sema).is_none() {
 +        return None;
 +    }
 +    ctx.config.postfix_snippets().filter(|(_, snip)| snip.scope == SnippetScope::Expr).for_each(
 +        |(trigger, snippet)| {
 +            let imports = match snippet.imports(ctx) {
 +                Some(imports) => imports,
 +                None => return,
 +            };
 +            let body = snippet.postfix_snippet(receiver_text);
 +            let mut builder =
 +                postfix_snippet(trigger, snippet.description.as_deref().unwrap_or_default(), &body);
 +            builder.documentation(Documentation::new(format!("```rust\n{}\n```", body)));
 +            for import in imports.into_iter() {
 +                builder.add_import(import);
 +            }
 +            builder.add_to(acc);
 +        },
 +    );
 +    None
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use expect_test::{expect, Expect};
 +
 +    use crate::{
 +        tests::{check_edit, check_edit_with_config, completion_list, TEST_CONFIG},
 +        CompletionConfig, Snippet,
 +    };
 +
 +    fn check(ra_fixture: &str, expect: Expect) {
 +        let actual = completion_list(ra_fixture);
 +        expect.assert_eq(&actual)
 +    }
 +
 +    #[test]
 +    fn postfix_completion_works_for_trivial_path_expression() {
 +        check(
 +            r#"
 +fn main() {
 +    let bar = true;
 +    bar.$0
 +}
 +"#,
 +            expect![[r#"
 +                sn box   Box::new(expr)
 +                sn call  function(expr)
 +                sn dbg   dbg!(expr)
 +                sn dbgr  dbg!(&expr)
 +                sn if    if expr {}
 +                sn let   let
 +                sn letm  let mut
 +                sn match match expr {}
 +                sn not   !expr
 +                sn ref   &expr
 +                sn refm  &mut expr
 +                sn while while expr {}
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn postfix_completion_works_for_function_calln() {
 +        check(
 +            r#"
 +fn foo(elt: bool) -> bool {
 +    !elt
 +}
 +
 +fn main() {
 +    let bar = true;
 +    foo(bar.$0)
 +}
 +"#,
 +            expect![[r#"
 +                sn box   Box::new(expr)
 +                sn call  function(expr)
 +                sn dbg   dbg!(expr)
 +                sn dbgr  dbg!(&expr)
 +                sn if    if expr {}
 +                sn match match expr {}
 +                sn not   !expr
 +                sn ref   &expr
 +                sn refm  &mut expr
 +                sn while while expr {}
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn postfix_type_filtering() {
 +        check(
 +            r#"
 +fn main() {
 +    let bar: u8 = 12;
 +    bar.$0
 +}
 +"#,
 +            expect![[r#"
 +                sn box   Box::new(expr)
 +                sn call  function(expr)
 +                sn dbg   dbg!(expr)
 +                sn dbgr  dbg!(&expr)
 +                sn let   let
 +                sn letm  let mut
 +                sn match match expr {}
 +                sn ref   &expr
 +                sn refm  &mut expr
 +            "#]],
 +        )
 +    }
 +
 +    #[test]
 +    fn let_middle_block() {
 +        check(
 +            r#"
 +fn main() {
 +    baz.l$0
 +    res
 +}
 +"#,
 +            expect![[r#"
 +                sn box   Box::new(expr)
 +                sn call  function(expr)
 +                sn dbg   dbg!(expr)
 +                sn dbgr  dbg!(&expr)
 +                sn if    if expr {}
 +                sn let   let
 +                sn letm  let mut
 +                sn match match expr {}
 +                sn not   !expr
 +                sn ref   &expr
 +                sn refm  &mut expr
 +                sn while while expr {}
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn option_iflet() {
 +        check_edit(
 +            "ifl",
 +            r#"
 +//- minicore: option
 +fn main() {
 +    let bar = Some(true);
 +    bar.$0
 +}
 +"#,
 +            r#"
 +fn main() {
 +    let bar = Some(true);
 +    if let Some($1) = bar {
 +    $0
 +}
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn result_match() {
 +        check_edit(
 +            "match",
 +            r#"
 +//- minicore: result
 +fn main() {
 +    let bar = Ok(true);
 +    bar.$0
 +}
 +"#,
 +            r#"
 +fn main() {
 +    let bar = Ok(true);
 +    match bar {
 +    Ok(${1:_}) => {$2},
 +    Err(${3:_}) => {$0},
 +}
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn postfix_completion_works_for_ambiguous_float_literal() {
 +        check_edit("refm", r#"fn main() { 42.$0 }"#, r#"fn main() { &mut 42 }"#)
 +    }
 +
 +    #[test]
 +    fn works_in_simple_macro() {
 +        check_edit(
 +            "dbg",
 +            r#"
 +macro_rules! m { ($e:expr) => { $e } }
 +fn main() {
 +    let bar: u8 = 12;
 +    m!(bar.d$0)
 +}
 +"#,
 +            r#"
 +macro_rules! m { ($e:expr) => { $e } }
 +fn main() {
 +    let bar: u8 = 12;
 +    m!(dbg!(bar))
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn postfix_completion_for_references() {
 +        check_edit("dbg", r#"fn main() { &&42.$0 }"#, r#"fn main() { dbg!(&&42) }"#);
 +        check_edit("refm", r#"fn main() { &&42.$0 }"#, r#"fn main() { &&&mut 42 }"#);
 +        check_edit(
 +            "ifl",
 +            r#"
 +//- minicore: option
 +fn main() {
 +    let bar = &Some(true);
 +    bar.$0
 +}
 +"#,
 +            r#"
 +fn main() {
 +    let bar = &Some(true);
 +    if let Some($1) = bar {
 +    $0
 +}
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn custom_postfix_completion() {
 +        let config = CompletionConfig {
 +            snippets: vec![Snippet::new(
 +                &[],
 +                &["break".into()],
 +                &["ControlFlow::Break(${receiver})".into()],
 +                "",
 +                &["core::ops::ControlFlow".into()],
 +                crate::SnippetScope::Expr,
 +            )
 +            .unwrap()],
 +            ..TEST_CONFIG
 +        };
 +
 +        check_edit_with_config(
 +            config.clone(),
 +            "break",
 +            r#"
 +//- minicore: try
 +fn main() { 42.$0 }
 +"#,
 +            r#"
 +use core::ops::ControlFlow;
 +
 +fn main() { ControlFlow::Break(42) }
 +"#,
 +        );
 +
 +        // The receiver texts should be escaped, see comments in `get_receiver_text()`
 +        // for detail.
 +        //
 +        // Note that the last argument is what *lsp clients would see* rather than
 +        // what users would see. Unescaping happens thereafter.
 +        check_edit_with_config(
 +            config.clone(),
 +            "break",
 +            r#"
 +//- minicore: try
 +fn main() { '\\'.$0 }
 +"#,
 +            r#"
 +use core::ops::ControlFlow;
 +
 +fn main() { ControlFlow::Break('\\\\') }
 +"#,
 +        );
 +
 +        check_edit_with_config(
 +            config.clone(),
 +            "break",
 +            r#"
 +//- minicore: try
 +fn main() {
 +    match true {
 +        true => "${1:placeholder}",
 +        false => "\$",
 +    }.$0
 +}
 +"#,
 +            r#"
 +use core::ops::ControlFlow;
 +
 +fn main() {
 +    ControlFlow::Break(match true {
 +        true => "\${1:placeholder}",
 +        false => "\\\$",
 +    })
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn postfix_completion_for_format_like_strings() {
 +        check_edit(
 +            "format",
 +            r#"fn main() { "{some_var:?}".$0 }"#,
 +            r#"fn main() { format!("{:?}", some_var) }"#,
 +        );
 +        check_edit(
 +            "panic",
 +            r#"fn main() { "Panic with {a}".$0 }"#,
 +            r#"fn main() { panic!("Panic with {}", a) }"#,
 +        );
 +        check_edit(
 +            "println",
 +            r#"fn main() { "{ 2+2 } { SomeStruct { val: 1, other: 32 } :?}".$0 }"#,
 +            r#"fn main() { println!("{} {:?}", 2+2, SomeStruct { val: 1, other: 32 }) }"#,
 +        );
 +        check_edit(
 +            "loge",
 +            r#"fn main() { "{2+2}".$0 }"#,
 +            r#"fn main() { log::error!("{}", 2+2) }"#,
 +        );
 +        check_edit(
 +            "logt",
 +            r#"fn main() { "{2+2}".$0 }"#,
 +            r#"fn main() { log::trace!("{}", 2+2) }"#,
 +        );
 +        check_edit(
 +            "logd",
 +            r#"fn main() { "{2+2}".$0 }"#,
 +            r#"fn main() { log::debug!("{}", 2+2) }"#,
 +        );
 +        check_edit("logi", r#"fn main() { "{2+2}".$0 }"#, r#"fn main() { log::info!("{}", 2+2) }"#);
 +        check_edit("logw", r#"fn main() { "{2+2}".$0 }"#, r#"fn main() { log::warn!("{}", 2+2) }"#);
 +        check_edit(
 +            "loge",
 +            r#"fn main() { "{2+2}".$0 }"#,
 +            r#"fn main() { log::error!("{}", 2+2) }"#,
 +        );
 +    }
++
++    #[test]
++    fn postfix_custom_snippets_completion_for_references() {
++        check_edit_with_config(
++            CompletionConfig {
++                snippets: vec![Snippet::new(
++                    &[],
++                    &["ok".into()],
++                    &["Ok(${receiver})".into()],
++                    "",
++                    &[],
++                    crate::SnippetScope::Expr,
++                )
++                .unwrap()],
++                ..TEST_CONFIG
++            },
++            "ok",
++            r#"fn main() { &&42.$0 }"#,
++            r#"fn main() { Ok(&&42) }"#,
++        );
++    }
 +}
index 82b85f2fa5edd58ed58c59aa172c1599bb7c994d,0000000000000000000000000000000000000000..aa5d7e9beb54f155c9b1753d1ca6283f11f0b99a
mode 100644,000000..100644
--- /dev/null
@@@ -1,805 -1,0 +1,825 @@@
-         // FIXME: There should be optimization potential here
-         // Currently we try to descend everything we find which
-         // means we call `Semantics::descend_into_macros` on
-         // every textual hit. That function is notoriously
-         // expensive even for things that do not get down mapped
-         // into macros.
 +//! Implementation of find-usages functionality.
 +//!
 +//! It is based on the standard ide trick: first, we run a fast text search to
 +//! get a super-set of matches. Then, we we confirm each match using precise
 +//! name resolution.
 +
 +use std::{mem, sync::Arc};
 +
 +use base_db::{FileId, FileRange, SourceDatabase, SourceDatabaseExt};
 +use hir::{DefWithBody, HasAttrs, HasSource, InFile, ModuleSource, Semantics, Visibility};
 +use memchr::memmem::Finder;
 +use once_cell::unsync::Lazy;
 +use parser::SyntaxKind;
 +use stdx::hash::NoHashHashMap;
 +use syntax::{ast, match_ast, AstNode, TextRange, TextSize};
 +
 +use crate::{
 +    defs::{Definition, NameClass, NameRefClass},
 +    traits::{as_trait_assoc_def, convert_to_def_in_trait},
 +    RootDatabase,
 +};
 +
 +#[derive(Debug, Default, Clone)]
 +pub struct UsageSearchResult {
 +    pub references: NoHashHashMap<FileId, Vec<FileReference>>,
 +}
 +
 +impl UsageSearchResult {
 +    pub fn is_empty(&self) -> bool {
 +        self.references.is_empty()
 +    }
 +
 +    pub fn len(&self) -> usize {
 +        self.references.len()
 +    }
 +
 +    pub fn iter(&self) -> impl Iterator<Item = (&FileId, &[FileReference])> + '_ {
 +        self.references.iter().map(|(file_id, refs)| (file_id, &**refs))
 +    }
 +
 +    pub fn file_ranges(&self) -> impl Iterator<Item = FileRange> + '_ {
 +        self.references.iter().flat_map(|(&file_id, refs)| {
 +            refs.iter().map(move |&FileReference { range, .. }| FileRange { file_id, range })
 +        })
 +    }
 +}
 +
 +impl IntoIterator for UsageSearchResult {
 +    type Item = (FileId, Vec<FileReference>);
 +    type IntoIter = <NoHashHashMap<FileId, Vec<FileReference>> as IntoIterator>::IntoIter;
 +
 +    fn into_iter(self) -> Self::IntoIter {
 +        self.references.into_iter()
 +    }
 +}
 +
 +#[derive(Debug, Clone)]
 +pub struct FileReference {
 +    /// The range of the reference in the original file
 +    pub range: TextRange,
 +    /// The node of the reference in the (macro-)file
 +    pub name: ast::NameLike,
 +    pub category: Option<ReferenceCategory>,
 +}
 +
 +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
 +pub enum ReferenceCategory {
 +    // FIXME: Add this variant and delete the `retain_adt_literal_usages` function.
 +    // Create
 +    Write,
 +    Read,
 +    Import,
 +    // FIXME: Some day should be able to search in doc comments. Would probably
 +    // need to switch from enum to bitflags then?
 +    // DocComment
 +}
 +
 +/// Generally, `search_scope` returns files that might contain references for the element.
 +/// For `pub(crate)` things it's a crate, for `pub` things it's a crate and dependant crates.
 +/// In some cases, the location of the references is known to within a `TextRange`,
 +/// e.g. for things like local variables.
 +#[derive(Clone, Debug)]
 +pub struct SearchScope {
 +    entries: NoHashHashMap<FileId, Option<TextRange>>,
 +}
 +
 +impl SearchScope {
 +    fn new(entries: NoHashHashMap<FileId, Option<TextRange>>) -> SearchScope {
 +        SearchScope { entries }
 +    }
 +
 +    /// Build a search scope spanning the entire crate graph of files.
 +    fn crate_graph(db: &RootDatabase) -> SearchScope {
 +        let mut entries = NoHashHashMap::default();
 +
 +        let graph = db.crate_graph();
 +        for krate in graph.iter() {
 +            let root_file = graph[krate].root_file_id;
 +            let source_root_id = db.file_source_root(root_file);
 +            let source_root = db.source_root(source_root_id);
 +            entries.extend(source_root.iter().map(|id| (id, None)));
 +        }
 +        SearchScope { entries }
 +    }
 +
 +    /// Build a search scope spanning all the reverse dependencies of the given crate.
 +    fn reverse_dependencies(db: &RootDatabase, of: hir::Crate) -> SearchScope {
 +        let mut entries = NoHashHashMap::default();
 +        for rev_dep in of.transitive_reverse_dependencies(db) {
 +            let root_file = rev_dep.root_file(db);
 +            let source_root_id = db.file_source_root(root_file);
 +            let source_root = db.source_root(source_root_id);
 +            entries.extend(source_root.iter().map(|id| (id, None)));
 +        }
 +        SearchScope { entries }
 +    }
 +
 +    /// Build a search scope spanning the given crate.
 +    fn krate(db: &RootDatabase, of: hir::Crate) -> SearchScope {
 +        let root_file = of.root_file(db);
 +        let source_root_id = db.file_source_root(root_file);
 +        let source_root = db.source_root(source_root_id);
 +        SearchScope { entries: source_root.iter().map(|id| (id, None)).collect() }
 +    }
 +
 +    /// Build a search scope spanning the given module and all its submodules.
 +    fn module_and_children(db: &RootDatabase, module: hir::Module) -> SearchScope {
 +        let mut entries = NoHashHashMap::default();
 +
 +        let (file_id, range) = {
 +            let InFile { file_id, value } = module.definition_source(db);
 +            if let Some((file_id, call_source)) = file_id.original_call_node(db) {
 +                (file_id, Some(call_source.text_range()))
 +            } else {
 +                (
 +                    file_id.original_file(db),
 +                    match value {
 +                        ModuleSource::SourceFile(_) => None,
 +                        ModuleSource::Module(it) => Some(it.syntax().text_range()),
 +                        ModuleSource::BlockExpr(it) => Some(it.syntax().text_range()),
 +                    },
 +                )
 +            }
 +        };
 +        entries.insert(file_id, range);
 +
 +        let mut to_visit: Vec<_> = module.children(db).collect();
 +        while let Some(module) = to_visit.pop() {
 +            if let InFile { file_id, value: ModuleSource::SourceFile(_) } =
 +                module.definition_source(db)
 +            {
 +                entries.insert(file_id.original_file(db), None);
 +            }
 +            to_visit.extend(module.children(db));
 +        }
 +        SearchScope { entries }
 +    }
 +
 +    /// Build an empty search scope.
 +    pub fn empty() -> SearchScope {
 +        SearchScope::new(NoHashHashMap::default())
 +    }
 +
 +    /// Build a empty search scope spanning the given file.
 +    pub fn single_file(file: FileId) -> SearchScope {
 +        SearchScope::new(std::iter::once((file, None)).collect())
 +    }
 +
 +    /// Build a empty search scope spanning the text range of the given file.
 +    pub fn file_range(range: FileRange) -> SearchScope {
 +        SearchScope::new(std::iter::once((range.file_id, Some(range.range))).collect())
 +    }
 +
 +    /// Build a empty search scope spanning the given files.
 +    pub fn files(files: &[FileId]) -> SearchScope {
 +        SearchScope::new(files.iter().map(|f| (*f, None)).collect())
 +    }
 +
 +    pub fn intersection(&self, other: &SearchScope) -> SearchScope {
 +        let (mut small, mut large) = (&self.entries, &other.entries);
 +        if small.len() > large.len() {
 +            mem::swap(&mut small, &mut large)
 +        }
 +
 +        let intersect_ranges =
 +            |r1: Option<TextRange>, r2: Option<TextRange>| -> Option<Option<TextRange>> {
 +                match (r1, r2) {
 +                    (None, r) | (r, None) => Some(r),
 +                    (Some(r1), Some(r2)) => r1.intersect(r2).map(Some),
 +                }
 +            };
 +        let res = small
 +            .iter()
 +            .filter_map(|(&file_id, &r1)| {
 +                let &r2 = large.get(&file_id)?;
 +                let r = intersect_ranges(r1, r2)?;
 +                Some((file_id, r))
 +            })
 +            .collect();
 +
 +        SearchScope::new(res)
 +    }
 +}
 +
 +impl IntoIterator for SearchScope {
 +    type Item = (FileId, Option<TextRange>);
 +    type IntoIter = std::collections::hash_map::IntoIter<FileId, Option<TextRange>>;
 +
 +    fn into_iter(self) -> Self::IntoIter {
 +        self.entries.into_iter()
 +    }
 +}
 +
 +impl Definition {
 +    fn search_scope(&self, db: &RootDatabase) -> SearchScope {
 +        let _p = profile::span("search_scope");
 +
 +        if let Definition::BuiltinType(_) = self {
 +            return SearchScope::crate_graph(db);
 +        }
 +
 +        // def is crate root
 +        // FIXME: We don't do searches for crates currently, as a crate does not actually have a single name
 +        if let &Definition::Module(module) = self {
 +            if module.is_crate_root(db) {
 +                return SearchScope::reverse_dependencies(db, module.krate());
 +            }
 +        }
 +
 +        let module = match self.module(db) {
 +            Some(it) => it,
 +            None => return SearchScope::empty(),
 +        };
 +        let InFile { file_id, value: module_source } = module.definition_source(db);
 +        let file_id = file_id.original_file(db);
 +
 +        if let Definition::Local(var) = self {
 +            let def = match var.parent(db) {
 +                DefWithBody::Function(f) => f.source(db).map(|src| src.syntax().cloned()),
 +                DefWithBody::Const(c) => c.source(db).map(|src| src.syntax().cloned()),
 +                DefWithBody::Static(s) => s.source(db).map(|src| src.syntax().cloned()),
 +                DefWithBody::Variant(v) => v.source(db).map(|src| src.syntax().cloned()),
 +            };
 +            return match def {
 +                Some(def) => SearchScope::file_range(def.as_ref().original_file_range(db)),
 +                None => SearchScope::single_file(file_id),
 +            };
 +        }
 +
 +        if let Definition::SelfType(impl_) = self {
 +            return match impl_.source(db).map(|src| src.syntax().cloned()) {
 +                Some(def) => SearchScope::file_range(def.as_ref().original_file_range(db)),
 +                None => SearchScope::single_file(file_id),
 +            };
 +        }
 +
 +        if let Definition::GenericParam(hir::GenericParam::LifetimeParam(param)) = self {
 +            let def = match param.parent(db) {
 +                hir::GenericDef::Function(it) => it.source(db).map(|src| src.syntax().cloned()),
 +                hir::GenericDef::Adt(it) => it.source(db).map(|src| src.syntax().cloned()),
 +                hir::GenericDef::Trait(it) => it.source(db).map(|src| src.syntax().cloned()),
 +                hir::GenericDef::TypeAlias(it) => it.source(db).map(|src| src.syntax().cloned()),
 +                hir::GenericDef::Impl(it) => it.source(db).map(|src| src.syntax().cloned()),
 +                hir::GenericDef::Variant(it) => it.source(db).map(|src| src.syntax().cloned()),
 +                hir::GenericDef::Const(it) => it.source(db).map(|src| src.syntax().cloned()),
 +            };
 +            return match def {
 +                Some(def) => SearchScope::file_range(def.as_ref().original_file_range(db)),
 +                None => SearchScope::single_file(file_id),
 +            };
 +        }
 +
 +        if let Definition::Macro(macro_def) = self {
 +            return match macro_def.kind(db) {
 +                hir::MacroKind::Declarative => {
 +                    if macro_def.attrs(db).by_key("macro_export").exists() {
 +                        SearchScope::reverse_dependencies(db, module.krate())
 +                    } else {
 +                        SearchScope::krate(db, module.krate())
 +                    }
 +                }
 +                hir::MacroKind::BuiltIn => SearchScope::crate_graph(db),
 +                hir::MacroKind::Derive | hir::MacroKind::Attr | hir::MacroKind::ProcMacro => {
 +                    SearchScope::reverse_dependencies(db, module.krate())
 +                }
 +            };
 +        }
 +
 +        if let Definition::DeriveHelper(_) = self {
 +            return SearchScope::reverse_dependencies(db, module.krate());
 +        }
 +
 +        let vis = self.visibility(db);
 +        if let Some(Visibility::Public) = vis {
 +            return SearchScope::reverse_dependencies(db, module.krate());
 +        }
 +        if let Some(Visibility::Module(module)) = vis {
 +            return SearchScope::module_and_children(db, module.into());
 +        }
 +
 +        let range = match module_source {
 +            ModuleSource::Module(m) => Some(m.syntax().text_range()),
 +            ModuleSource::BlockExpr(b) => Some(b.syntax().text_range()),
 +            ModuleSource::SourceFile(_) => None,
 +        };
 +        match range {
 +            Some(range) => SearchScope::file_range(FileRange { file_id, range }),
 +            None => SearchScope::single_file(file_id),
 +        }
 +    }
 +
 +    pub fn usages<'a>(self, sema: &'a Semantics<'_, RootDatabase>) -> FindUsages<'a> {
 +        FindUsages {
 +            local_repr: match self {
 +                Definition::Local(local) => Some(local.representative(sema.db)),
 +                _ => None,
 +            },
 +            def: self,
 +            trait_assoc_def: as_trait_assoc_def(sema.db, self),
 +            sema,
 +            scope: None,
 +            include_self_kw_refs: None,
 +            search_self_mod: false,
 +        }
 +    }
 +}
 +
 +#[derive(Clone)]
 +pub struct FindUsages<'a> {
 +    def: Definition,
 +    /// If def is an assoc item from a trait or trait impl, this is the corresponding item of the trait definition
 +    trait_assoc_def: Option<Definition>,
 +    sema: &'a Semantics<'a, RootDatabase>,
 +    scope: Option<SearchScope>,
 +    include_self_kw_refs: Option<hir::Type>,
 +    local_repr: Option<hir::Local>,
 +    search_self_mod: bool,
 +}
 +
 +impl<'a> FindUsages<'a> {
 +    /// Enable searching for `Self` when the definition is a type or `self` for modules.
 +    pub fn include_self_refs(mut self) -> FindUsages<'a> {
 +        self.include_self_kw_refs = def_to_ty(self.sema, &self.def);
 +        self.search_self_mod = true;
 +        self
 +    }
 +
 +    /// Limit the search to a given [`SearchScope`].
 +    pub fn in_scope(self, scope: SearchScope) -> FindUsages<'a> {
 +        self.set_scope(Some(scope))
 +    }
 +
 +    /// Limit the search to a given [`SearchScope`].
 +    pub fn set_scope(mut self, scope: Option<SearchScope>) -> FindUsages<'a> {
 +        assert!(self.scope.is_none());
 +        self.scope = scope;
 +        self
 +    }
 +
 +    pub fn at_least_one(&self) -> bool {
 +        let mut found = false;
 +        self.search(&mut |_, _| {
 +            found = true;
 +            true
 +        });
 +        found
 +    }
 +
 +    pub fn all(self) -> UsageSearchResult {
 +        let mut res = UsageSearchResult::default();
 +        self.search(&mut |file_id, reference| {
 +            res.references.entry(file_id).or_default().push(reference);
 +            false
 +        });
 +        res
 +    }
 +
 +    fn search(&self, sink: &mut dyn FnMut(FileId, FileReference) -> bool) {
 +        let _p = profile::span("FindUsages:search");
 +        let sema = self.sema;
 +
 +        let search_scope = {
 +            let base = self.trait_assoc_def.unwrap_or(self.def).search_scope(sema.db);
 +            match &self.scope {
 +                None => base,
 +                Some(scope) => base.intersection(scope),
 +            }
 +        };
 +
 +        let name = match self.def {
 +            // special case crate modules as these do not have a proper name
 +            Definition::Module(module) if module.is_crate_root(self.sema.db) => {
 +                // FIXME: This assumes the crate name is always equal to its display name when it really isn't
 +                module
 +                    .krate()
 +                    .display_name(self.sema.db)
 +                    .map(|crate_name| crate_name.crate_name().as_smol_str().clone())
 +            }
 +            _ => {
 +                let self_kw_refs = || {
 +                    self.include_self_kw_refs.as_ref().and_then(|ty| {
 +                        ty.as_adt()
 +                            .map(|adt| adt.name(self.sema.db))
 +                            .or_else(|| ty.as_builtin().map(|builtin| builtin.name()))
 +                    })
 +                };
 +                // We need to unescape the name in case it is written without "r#" in earlier
 +                // editions of Rust where it isn't a keyword.
 +                self.def.name(sema.db).or_else(self_kw_refs).map(|it| it.unescaped().to_smol_str())
 +            }
 +        };
 +        let name = match &name {
 +            Some(s) => s.as_str(),
 +            None => return,
 +        };
 +        let finder = &Finder::new(name);
 +        let include_self_kw_refs =
 +            self.include_self_kw_refs.as_ref().map(|ty| (ty, Finder::new("Self")));
 +
 +        // for<'a> |text: &'a str, name: &'a str, search_range: TextRange| -> impl Iterator<Item = TextSize> + 'a { ... }
 +        fn match_indices<'a>(
 +            text: &'a str,
 +            finder: &'a Finder<'a>,
 +            search_range: TextRange,
 +        ) -> impl Iterator<Item = TextSize> + 'a {
 +            finder.find_iter(text.as_bytes()).filter_map(move |idx| {
 +                let offset: TextSize = idx.try_into().unwrap();
 +                if !search_range.contains_inclusive(offset) {
 +                    return None;
 +                }
 +                Some(offset)
 +            })
 +        }
 +
 +        // for<'a> |scope: &'a SearchScope| -> impl Iterator<Item = (Arc<String>, FileId, TextRange)> + 'a { ... }
 +        fn scope_files<'a>(
 +            sema: &'a Semantics<'_, RootDatabase>,
 +            scope: &'a SearchScope,
 +        ) -> impl Iterator<Item = (Arc<String>, FileId, TextRange)> + 'a {
 +            scope.entries.iter().map(|(&file_id, &search_range)| {
 +                let text = sema.db.file_text(file_id);
 +                let search_range =
 +                    search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(text.as_str())));
 +
 +                (text, file_id, search_range)
 +            })
 +        }
 +
-                 for name in sema.find_nodes_at_offset_with_descend(&tree, offset) {
-                     if match name {
-                         ast::NameLike::NameRef(name_ref) => self.found_name_ref(&name_ref, sink),
-                         ast::NameLike::Name(name) => self.found_name(&name, sink),
-                         ast::NameLike::Lifetime(lifetime) => self.found_lifetime(&lifetime, sink),
-                     } {
-                         return;
++        let find_nodes = move |name: &str, node: &syntax::SyntaxNode, offset: TextSize| {
++            node.token_at_offset(offset).find(|it| it.text() == name).map(|token| {
++                // FIXME: There should be optimization potential here
++                // Currently we try to descend everything we find which
++                // means we call `Semantics::descend_into_macros` on
++                // every textual hit. That function is notoriously
++                // expensive even for things that do not get down mapped
++                // into macros.
++                sema.descend_into_macros(token).into_iter().filter_map(|it| it.parent())
++            })
++        };
++
 +        for (text, file_id, search_range) in scope_files(sema, &search_scope) {
 +            let tree = Lazy::new(move || sema.parse(file_id).syntax().clone());
 +
 +            // Search for occurrences of the items name
 +            for offset in match_indices(&text, finder, search_range) {
-                     for name_ref in sema.find_nodes_at_offset_with_descend(&tree, offset) {
-                         if self.found_self_ty_name_ref(self_ty, &name_ref, sink) {
-                             return;
++                if let Some(iter) = find_nodes(name, &tree, offset) {
++                    for name in iter.filter_map(ast::NameLike::cast) {
++                        if match name {
++                            ast::NameLike::NameRef(name_ref) => {
++                                self.found_name_ref(&name_ref, sink)
++                            }
++                            ast::NameLike::Name(name) => self.found_name(&name, sink),
++                            ast::NameLike::Lifetime(lifetime) => {
++                                self.found_lifetime(&lifetime, sink)
++                            }
++                        } {
++                            return;
++                        }
 +                    }
 +                }
 +            }
 +            // Search for occurrences of the `Self` referring to our type
 +            if let Some((self_ty, finder)) = &include_self_kw_refs {
 +                for offset in match_indices(&text, finder, search_range) {
-                         for name_ref in sema.find_nodes_at_offset_with_descend(&tree, offset) {
-                             if self.found_name_ref(&name_ref, sink) {
-                                 return;
++                    if let Some(iter) = find_nodes("Self", &tree, offset) {
++                        for name_ref in iter.filter_map(ast::NameRef::cast) {
++                            if self.found_self_ty_name_ref(self_ty, &name_ref, sink) {
++                                return;
++                            }
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +
 +        // Search for `super` and `crate` resolving to our module
 +        match self.def {
 +            Definition::Module(module) => {
 +                let scope = search_scope
 +                    .intersection(&SearchScope::module_and_children(self.sema.db, module));
 +
 +                let is_crate_root =
 +                    module.is_crate_root(self.sema.db).then(|| Finder::new("crate"));
 +                let finder = &Finder::new("super");
 +
 +                for (text, file_id, search_range) in scope_files(sema, &scope) {
 +                    let tree = Lazy::new(move || sema.parse(file_id).syntax().clone());
 +
 +                    for offset in match_indices(&text, finder, search_range) {
-                             for name_ref in sema.find_nodes_at_offset_with_descend(&tree, offset) {
-                                 if self.found_name_ref(&name_ref, sink) {
-                                     return;
++                        if let Some(iter) = find_nodes("super", &tree, offset) {
++                            for name_ref in iter.filter_map(ast::NameRef::cast) {
++                                if self.found_name_ref(&name_ref, sink) {
++                                    return;
++                                }
 +                            }
 +                        }
 +                    }
 +                    if let Some(finder) = &is_crate_root {
 +                        for offset in match_indices(&text, finder, search_range) {
-                     for name_ref in sema.find_nodes_at_offset_with_descend(&tree, offset) {
-                         if self.found_self_module_name_ref(&name_ref, sink) {
-                             return;
++                            if let Some(iter) = find_nodes("crate", &tree, offset) {
++                                for name_ref in iter.filter_map(ast::NameRef::cast) {
++                                    if self.found_name_ref(&name_ref, sink) {
++                                        return;
++                                    }
 +                                }
 +                            }
 +                        }
 +                    }
 +                }
 +            }
 +            _ => (),
 +        }
 +
 +        // search for module `self` references in our module's definition source
 +        match self.def {
 +            Definition::Module(module) if self.search_self_mod => {
 +                let src = module.definition_source(sema.db);
 +                let file_id = src.file_id.original_file(sema.db);
 +                let (file_id, search_range) = match src.value {
 +                    ModuleSource::Module(m) => (file_id, Some(m.syntax().text_range())),
 +                    ModuleSource::BlockExpr(b) => (file_id, Some(b.syntax().text_range())),
 +                    ModuleSource::SourceFile(_) => (file_id, None),
 +                };
 +
 +                let search_range = if let Some(&range) = search_scope.entries.get(&file_id) {
 +                    match (range, search_range) {
 +                        (None, range) | (range, None) => range,
 +                        (Some(range), Some(search_range)) => match range.intersect(search_range) {
 +                            Some(range) => Some(range),
 +                            None => return,
 +                        },
 +                    }
 +                } else {
 +                    return;
 +                };
 +
 +                let text = sema.db.file_text(file_id);
 +                let search_range =
 +                    search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(text.as_str())));
 +
 +                let tree = Lazy::new(|| sema.parse(file_id).syntax().clone());
 +                let finder = &Finder::new("self");
 +
 +                for offset in match_indices(&text, finder, search_range) {
++                    if let Some(iter) = find_nodes("self", &tree, offset) {
++                        for name_ref in iter.filter_map(ast::NameRef::cast) {
++                            if self.found_self_module_name_ref(&name_ref, sink) {
++                                return;
++                            }
 +                        }
 +                    }
 +                }
 +            }
 +            _ => {}
 +        }
 +    }
 +
 +    fn found_self_ty_name_ref(
 +        &self,
 +        self_ty: &hir::Type,
 +        name_ref: &ast::NameRef,
 +        sink: &mut dyn FnMut(FileId, FileReference) -> bool,
 +    ) -> bool {
 +        match NameRefClass::classify(self.sema, name_ref) {
 +            Some(NameRefClass::Definition(Definition::SelfType(impl_)))
 +                if impl_.self_ty(self.sema.db) == *self_ty =>
 +            {
 +                let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
 +                let reference = FileReference {
 +                    range,
 +                    name: ast::NameLike::NameRef(name_ref.clone()),
 +                    category: None,
 +                };
 +                sink(file_id, reference)
 +            }
 +            _ => false,
 +        }
 +    }
 +
 +    fn found_self_module_name_ref(
 +        &self,
 +        name_ref: &ast::NameRef,
 +        sink: &mut dyn FnMut(FileId, FileReference) -> bool,
 +    ) -> bool {
 +        match NameRefClass::classify(self.sema, name_ref) {
 +            Some(NameRefClass::Definition(def @ Definition::Module(_))) if def == self.def => {
 +                let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
 +                let reference = FileReference {
 +                    range,
 +                    name: ast::NameLike::NameRef(name_ref.clone()),
 +                    category: is_name_ref_in_import(name_ref).then(|| ReferenceCategory::Import),
 +                };
 +                sink(file_id, reference)
 +            }
 +            _ => false,
 +        }
 +    }
 +
 +    fn found_lifetime(
 +        &self,
 +        lifetime: &ast::Lifetime,
 +        sink: &mut dyn FnMut(FileId, FileReference) -> bool,
 +    ) -> bool {
 +        match NameRefClass::classify_lifetime(self.sema, lifetime) {
 +            Some(NameRefClass::Definition(def)) if def == self.def => {
 +                let FileRange { file_id, range } = self.sema.original_range(lifetime.syntax());
 +                let reference = FileReference {
 +                    range,
 +                    name: ast::NameLike::Lifetime(lifetime.clone()),
 +                    category: None,
 +                };
 +                sink(file_id, reference)
 +            }
 +            _ => false,
 +        }
 +    }
 +
 +    fn found_name_ref(
 +        &self,
 +        name_ref: &ast::NameRef,
 +        sink: &mut dyn FnMut(FileId, FileReference) -> bool,
 +    ) -> bool {
 +        match NameRefClass::classify(self.sema, name_ref) {
 +            Some(NameRefClass::Definition(def @ Definition::Local(local)))
 +                if matches!(
 +                    self.local_repr, Some(repr) if repr == local.representative(self.sema.db)
 +                ) =>
 +            {
 +                let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
 +                let reference = FileReference {
 +                    range,
 +                    name: ast::NameLike::NameRef(name_ref.clone()),
 +                    category: ReferenceCategory::new(&def, name_ref),
 +                };
 +                sink(file_id, reference)
 +            }
 +            Some(NameRefClass::Definition(def))
 +                if match self.trait_assoc_def {
 +                    Some(trait_assoc_def) => {
 +                        // we have a trait assoc item, so force resolve all assoc items to their trait version
 +                        convert_to_def_in_trait(self.sema.db, def) == trait_assoc_def
 +                    }
 +                    None => self.def == def,
 +                } =>
 +            {
 +                let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
 +                let reference = FileReference {
 +                    range,
 +                    name: ast::NameLike::NameRef(name_ref.clone()),
 +                    category: ReferenceCategory::new(&def, name_ref),
 +                };
 +                sink(file_id, reference)
 +            }
 +            Some(NameRefClass::Definition(def)) if self.include_self_kw_refs.is_some() => {
 +                if self.include_self_kw_refs == def_to_ty(self.sema, &def) {
 +                    let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
 +                    let reference = FileReference {
 +                        range,
 +                        name: ast::NameLike::NameRef(name_ref.clone()),
 +                        category: ReferenceCategory::new(&def, name_ref),
 +                    };
 +                    sink(file_id, reference)
 +                } else {
 +                    false
 +                }
 +            }
 +            Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => {
 +                let field = Definition::Field(field);
 +                let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
 +                let access = match self.def {
 +                    Definition::Field(_) if field == self.def => {
 +                        ReferenceCategory::new(&field, name_ref)
 +                    }
 +                    Definition::Local(_) if matches!(self.local_repr, Some(repr) if repr == local.representative(self.sema.db)) => {
 +                        ReferenceCategory::new(&Definition::Local(local), name_ref)
 +                    }
 +                    _ => return false,
 +                };
 +                let reference = FileReference {
 +                    range,
 +                    name: ast::NameLike::NameRef(name_ref.clone()),
 +                    category: access,
 +                };
 +                sink(file_id, reference)
 +            }
 +            _ => false,
 +        }
 +    }
 +
 +    fn found_name(
 +        &self,
 +        name: &ast::Name,
 +        sink: &mut dyn FnMut(FileId, FileReference) -> bool,
 +    ) -> bool {
 +        match NameClass::classify(self.sema, name) {
 +            Some(NameClass::PatFieldShorthand { local_def: _, field_ref })
 +                if matches!(
 +                    self.def, Definition::Field(_) if Definition::Field(field_ref) == self.def
 +                ) =>
 +            {
 +                let FileRange { file_id, range } = self.sema.original_range(name.syntax());
 +                let reference = FileReference {
 +                    range,
 +                    name: ast::NameLike::Name(name.clone()),
 +                    // FIXME: mutable patterns should have `Write` access
 +                    category: Some(ReferenceCategory::Read),
 +                };
 +                sink(file_id, reference)
 +            }
 +            Some(NameClass::ConstReference(def)) if self.def == def => {
 +                let FileRange { file_id, range } = self.sema.original_range(name.syntax());
 +                let reference = FileReference {
 +                    range,
 +                    name: ast::NameLike::Name(name.clone()),
 +                    category: None,
 +                };
 +                sink(file_id, reference)
 +            }
 +            Some(NameClass::Definition(def @ Definition::Local(local))) if def != self.def => {
 +                if matches!(
 +                    self.local_repr,
 +                    Some(repr) if local.representative(self.sema.db) == repr
 +                ) {
 +                    let FileRange { file_id, range } = self.sema.original_range(name.syntax());
 +                    let reference = FileReference {
 +                        range,
 +                        name: ast::NameLike::Name(name.clone()),
 +                        category: None,
 +                    };
 +                    return sink(file_id, reference);
 +                }
 +                false
 +            }
 +            Some(NameClass::Definition(def)) if def != self.def => {
 +                // if the def we are looking for is a trait (impl) assoc item, we'll have to resolve the items to trait definition assoc item
 +                if !matches!(
 +                    self.trait_assoc_def,
 +                    Some(trait_assoc_def)
 +                        if convert_to_def_in_trait(self.sema.db, def) == trait_assoc_def
 +                ) {
 +                    return false;
 +                }
 +                let FileRange { file_id, range } = self.sema.original_range(name.syntax());
 +                let reference = FileReference {
 +                    range,
 +                    name: ast::NameLike::Name(name.clone()),
 +                    category: None,
 +                };
 +                sink(file_id, reference)
 +            }
 +            _ => false,
 +        }
 +    }
 +}
 +
 +fn def_to_ty(sema: &Semantics<'_, RootDatabase>, def: &Definition) -> Option<hir::Type> {
 +    match def {
 +        Definition::Adt(adt) => Some(adt.ty(sema.db)),
 +        Definition::TypeAlias(it) => Some(it.ty(sema.db)),
 +        Definition::BuiltinType(it) => Some(it.ty(sema.db)),
 +        Definition::SelfType(it) => Some(it.self_ty(sema.db)),
 +        _ => None,
 +    }
 +}
 +
 +impl ReferenceCategory {
 +    fn new(def: &Definition, r: &ast::NameRef) -> Option<ReferenceCategory> {
 +        // Only Locals and Fields have accesses for now.
 +        if !matches!(def, Definition::Local(_) | Definition::Field(_)) {
 +            return is_name_ref_in_import(r).then(|| ReferenceCategory::Import);
 +        }
 +
 +        let mode = r.syntax().ancestors().find_map(|node| {
 +        match_ast! {
 +            match node {
 +                ast::BinExpr(expr) => {
 +                    if matches!(expr.op_kind()?, ast::BinaryOp::Assignment { .. }) {
 +                        // If the variable or field ends on the LHS's end then it's a Write (covers fields and locals).
 +                        // FIXME: This is not terribly accurate.
 +                        if let Some(lhs) = expr.lhs() {
 +                            if lhs.syntax().text_range().end() == r.syntax().text_range().end() {
 +                                return Some(ReferenceCategory::Write);
 +                            }
 +                        }
 +                    }
 +                    Some(ReferenceCategory::Read)
 +                },
 +                _ => None
 +            }
 +        }
 +    });
 +
 +        // Default Locals and Fields to read
 +        mode.or(Some(ReferenceCategory::Read))
 +    }
 +}
 +
 +fn is_name_ref_in_import(name_ref: &ast::NameRef) -> bool {
 +    name_ref
 +        .syntax()
 +        .parent()
 +        .and_then(ast::PathSegment::cast)
 +        .and_then(|it| it.parent_path().top_path().syntax().parent())
 +        .map_or(false, |it| it.kind() == SyntaxKind::USE_TREE)
 +}
index 852a8fd837616ab3c0a2fb4c1bdf75e99774fe8d,0000000000000000000000000000000000000000..07d117aff10bd8fa9bbdc3dff5ee5aaed8e38153
mode 100644,000000..100644
--- /dev/null
@@@ -1,451 -1,0 +1,451 @@@
-     pub repo: String,
-     pub version: String,
 +//! This module generates [moniker](https://microsoft.github.io/language-server-protocol/specifications/lsif/0.6.0/specification/#exportsImports)
 +//! for LSIF and LSP.
 +
 +use hir::{db::DefDatabase, AsAssocItem, AssocItemContainer, Crate, Name, Semantics};
 +use ide_db::{
 +    base_db::{CrateOrigin, FileId, FileLoader, FilePosition, LangCrateOrigin},
 +    defs::{Definition, IdentClass},
 +    helpers::pick_best_token,
 +    RootDatabase,
 +};
 +use itertools::Itertools;
 +use syntax::{AstNode, SyntaxKind::*, T};
 +
 +use crate::{doc_links::token_as_doc_comment, RangeInfo};
 +
 +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 +pub enum MonikerDescriptorKind {
 +    Namespace,
 +    Type,
 +    Term,
 +    Method,
 +    TypeParameter,
 +    Parameter,
 +    Macro,
 +    Meta,
 +}
 +
 +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 +pub struct MonikerDescriptor {
 +    pub name: Name,
 +    pub desc: MonikerDescriptorKind,
 +}
 +
 +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 +pub struct MonikerIdentifier {
 +    pub crate_name: String,
 +    pub description: Vec<MonikerDescriptor>,
 +}
 +
 +impl ToString for MonikerIdentifier {
 +    fn to_string(&self) -> String {
 +        match self {
 +            MonikerIdentifier { description, crate_name } => {
 +                format!(
 +                    "{}::{}",
 +                    crate_name,
 +                    description.iter().map(|x| x.name.to_string()).join("::")
 +                )
 +            }
 +        }
 +    }
 +}
 +
 +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 +pub enum MonikerKind {
 +    Import,
 +    Export,
 +}
 +
 +#[derive(Debug, Clone, PartialEq, Eq, Hash)]
 +pub struct MonikerResult {
 +    pub identifier: MonikerIdentifier,
 +    pub kind: MonikerKind,
 +    pub package_information: PackageInformation,
 +}
 +
 +impl MonikerResult {
 +    pub fn from_def(db: &RootDatabase, def: Definition, from_crate: Crate) -> Option<Self> {
 +        def_to_moniker(db, def, from_crate)
 +    }
 +}
 +
 +#[derive(Debug, Clone, PartialEq, Eq, Hash)]
 +pub struct PackageInformation {
 +    pub name: String,
-                     repo?,
-                     krate.version(db)?,
++    pub repo: Option<String>,
++    pub version: Option<String>,
 +}
 +
 +pub(crate) fn crate_for_file(db: &RootDatabase, file_id: FileId) -> Option<Crate> {
 +    for &krate in db.relevant_crates(file_id).iter() {
 +        let crate_def_map = db.crate_def_map(krate);
 +        for (_, data) in crate_def_map.modules() {
 +            if data.origin.file_id() == Some(file_id) {
 +                return Some(krate.into());
 +            }
 +        }
 +    }
 +    None
 +}
 +
 +pub(crate) fn moniker(
 +    db: &RootDatabase,
 +    FilePosition { file_id, offset }: FilePosition,
 +) -> Option<RangeInfo<Vec<MonikerResult>>> {
 +    let sema = &Semantics::new(db);
 +    let file = sema.parse(file_id).syntax().clone();
 +    let current_crate = crate_for_file(db, file_id)?;
 +    let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
 +        IDENT
 +        | INT_NUMBER
 +        | LIFETIME_IDENT
 +        | T![self]
 +        | T![super]
 +        | T![crate]
 +        | T![Self]
 +        | COMMENT => 2,
 +        kind if kind.is_trivia() => 0,
 +        _ => 1,
 +    })?;
 +    if let Some(doc_comment) = token_as_doc_comment(&original_token) {
 +        return doc_comment.get_definition_with_descend_at(sema, offset, |def, _, _| {
 +            let m = def_to_moniker(db, def, current_crate)?;
 +            Some(RangeInfo::new(original_token.text_range(), vec![m]))
 +        });
 +    }
 +    let navs = sema
 +        .descend_into_macros(original_token.clone())
 +        .into_iter()
 +        .filter_map(|token| {
 +            IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops).map(|it| {
 +                it.into_iter().flat_map(|def| def_to_moniker(sema.db, def, current_crate))
 +            })
 +        })
 +        .flatten()
 +        .unique()
 +        .collect::<Vec<_>>();
 +    Some(RangeInfo::new(original_token.text_range(), navs))
 +}
 +
 +pub(crate) fn def_to_moniker(
 +    db: &RootDatabase,
 +    def: Definition,
 +    from_crate: Crate,
 +) -> Option<MonikerResult> {
 +    if matches!(
 +        def,
 +        Definition::GenericParam(_)
 +            | Definition::Label(_)
 +            | Definition::DeriveHelper(_)
 +            | Definition::BuiltinAttr(_)
 +            | Definition::ToolModule(_)
 +    ) {
 +        return None;
 +    }
 +
 +    let module = def.module(db)?;
 +    let krate = module.krate();
 +    let mut description = vec![];
 +    description.extend(module.path_to_root(db).into_iter().filter_map(|x| {
 +        Some(MonikerDescriptor { name: x.name(db)?, desc: MonikerDescriptorKind::Namespace })
 +    }));
 +
 +    // Handle associated items within a trait
 +    if let Some(assoc) = def.as_assoc_item(db) {
 +        let container = assoc.container(db);
 +        match container {
 +            AssocItemContainer::Trait(trait_) => {
 +                // Because different traits can have functions with the same name,
 +                // we have to include the trait name as part of the moniker for uniqueness.
 +                description.push(MonikerDescriptor {
 +                    name: trait_.name(db),
 +                    desc: MonikerDescriptorKind::Type,
 +                });
 +            }
 +            AssocItemContainer::Impl(impl_) => {
 +                // Because a struct can implement multiple traits, for implementations
 +                // we add both the struct name and the trait name to the path
 +                if let Some(adt) = impl_.self_ty(db).as_adt() {
 +                    description.push(MonikerDescriptor {
 +                        name: adt.name(db),
 +                        desc: MonikerDescriptorKind::Type,
 +                    });
 +                }
 +
 +                if let Some(trait_) = impl_.trait_(db) {
 +                    description.push(MonikerDescriptor {
 +                        name: trait_.name(db),
 +                        desc: MonikerDescriptorKind::Type,
 +                    });
 +                }
 +            }
 +        }
 +    }
 +
 +    if let Definition::Field(it) = def {
 +        description.push(MonikerDescriptor {
 +            name: it.parent_def(db).name(db),
 +            desc: MonikerDescriptorKind::Type,
 +        });
 +    }
 +
 +    let name_desc = match def {
 +        // These are handled by top-level guard (for performance).
 +        Definition::GenericParam(_)
 +        | Definition::Label(_)
 +        | Definition::DeriveHelper(_)
 +        | Definition::BuiltinAttr(_)
 +        | Definition::ToolModule(_) => return None,
 +
 +        Definition::Local(local) => {
 +            if !local.is_param(db) {
 +                return None;
 +            }
 +
 +            MonikerDescriptor { name: local.name(db), desc: MonikerDescriptorKind::Parameter }
 +        }
 +        Definition::Macro(m) => {
 +            MonikerDescriptor { name: m.name(db), desc: MonikerDescriptorKind::Macro }
 +        }
 +        Definition::Function(f) => {
 +            MonikerDescriptor { name: f.name(db), desc: MonikerDescriptorKind::Method }
 +        }
 +        Definition::Variant(v) => {
 +            MonikerDescriptor { name: v.name(db), desc: MonikerDescriptorKind::Type }
 +        }
 +        Definition::Const(c) => {
 +            MonikerDescriptor { name: c.name(db)?, desc: MonikerDescriptorKind::Term }
 +        }
 +        Definition::Trait(trait_) => {
 +            MonikerDescriptor { name: trait_.name(db), desc: MonikerDescriptorKind::Type }
 +        }
 +        Definition::TypeAlias(ta) => {
 +            MonikerDescriptor { name: ta.name(db), desc: MonikerDescriptorKind::TypeParameter }
 +        }
 +        Definition::Module(m) => {
 +            MonikerDescriptor { name: m.name(db)?, desc: MonikerDescriptorKind::Namespace }
 +        }
 +        Definition::BuiltinType(b) => {
 +            MonikerDescriptor { name: b.name(), desc: MonikerDescriptorKind::Type }
 +        }
 +        Definition::SelfType(imp) => MonikerDescriptor {
 +            name: imp.self_ty(db).as_adt()?.name(db),
 +            desc: MonikerDescriptorKind::Type,
 +        },
 +        Definition::Field(it) => {
 +            MonikerDescriptor { name: it.name(db), desc: MonikerDescriptorKind::Term }
 +        }
 +        Definition::Adt(adt) => {
 +            MonikerDescriptor { name: adt.name(db), desc: MonikerDescriptorKind::Type }
 +        }
 +        Definition::Static(s) => {
 +            MonikerDescriptor { name: s.name(db), desc: MonikerDescriptorKind::Meta }
 +        }
 +    };
 +
 +    description.push(name_desc);
 +
 +    Some(MonikerResult {
 +        identifier: MonikerIdentifier {
 +            crate_name: krate.display_name(db)?.crate_name().to_string(),
 +            description,
 +        },
 +        kind: if krate == from_crate { MonikerKind::Export } else { MonikerKind::Import },
 +        package_information: {
 +            let (name, repo, version) = match krate.origin(db) {
 +                CrateOrigin::CratesIo { repo, name } => (
 +                    name.unwrap_or(krate.display_name(db)?.canonical_name().to_string()),
-                     "https://github.com/rust-lang/rust/".to_string(),
-                     match lang {
++                    repo,
++                    krate.version(db),
 +                ),
 +                CrateOrigin::Lang(lang) => (
 +                    krate.display_name(db)?.canonical_name().to_string(),
-                     },
++                    Some("https://github.com/rust-lang/rust/".to_string()),
++                    Some(match lang {
 +                        LangCrateOrigin::Other => {
 +                            "https://github.com/rust-lang/rust/library/".into()
 +                        }
 +                        lang => format!("https://github.com/rust-lang/rust/library/{lang}",),
-             r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
++                    }),
 +                ),
 +            };
 +            PackageInformation { name, repo, version }
 +        },
 +    })
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use crate::fixture;
 +
 +    use super::MonikerKind;
 +
 +    #[track_caller]
 +    fn no_moniker(ra_fixture: &str) {
 +        let (analysis, position) = fixture::position(ra_fixture);
 +        if let Some(x) = analysis.moniker(position).unwrap() {
 +            assert_eq!(x.info.len(), 0, "Moniker founded but no moniker expected: {:?}", x);
 +        }
 +    }
 +
 +    #[track_caller]
 +    fn check_moniker(ra_fixture: &str, identifier: &str, package: &str, kind: MonikerKind) {
 +        let (analysis, position) = fixture::position(ra_fixture);
 +        let x = analysis.moniker(position).unwrap().expect("no moniker found").info;
 +        assert_eq!(x.len(), 1);
 +        let x = x.into_iter().next().unwrap();
 +        assert_eq!(identifier, x.identifier.to_string());
 +        assert_eq!(package, format!("{:?}", x.package_information));
 +        assert_eq!(kind, x.kind);
 +    }
 +
 +    #[test]
 +    fn basic() {
 +        check_moniker(
 +            r#"
 +//- /lib.rs crate:main deps:foo
 +use foo::module::func;
 +fn main() {
 +    func$0();
 +}
 +//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
 +pub mod module {
 +    pub fn func() {}
 +}
 +"#,
 +            "foo::module::func",
-             r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
++            r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
 +            MonikerKind::Import,
 +        );
 +        check_moniker(
 +            r#"
 +//- /lib.rs crate:main deps:foo
 +use foo::module::func;
 +fn main() {
 +    func();
 +}
 +//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
 +pub mod module {
 +    pub fn func$0() {}
 +}
 +"#,
 +            "foo::module::func",
-             r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
++            r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
 +            MonikerKind::Export,
 +        );
 +    }
 +
 +    #[test]
 +    fn moniker_for_trait() {
 +        check_moniker(
 +            r#"
 +//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
 +pub mod module {
 +    pub trait MyTrait {
 +        pub fn func$0() {}
 +    }
 +}
 +"#,
 +            "foo::module::MyTrait::func",
-             r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
++            r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
 +            MonikerKind::Export,
 +        );
 +    }
 +
 +    #[test]
 +    fn moniker_for_trait_constant() {
 +        check_moniker(
 +            r#"
 +//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
 +pub mod module {
 +    pub trait MyTrait {
 +        const MY_CONST$0: u8;
 +    }
 +}
 +"#,
 +            "foo::module::MyTrait::MY_CONST",
-             r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
++            r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
 +            MonikerKind::Export,
 +        );
 +    }
 +
 +    #[test]
 +    fn moniker_for_trait_type() {
 +        check_moniker(
 +            r#"
 +//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
 +pub mod module {
 +    pub trait MyTrait {
 +        type MyType$0;
 +    }
 +}
 +"#,
 +            "foo::module::MyTrait::MyType",
-             r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
++            r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
 +            MonikerKind::Export,
 +        );
 +    }
 +
 +    #[test]
 +    fn moniker_for_trait_impl_function() {
 +        check_moniker(
 +            r#"
 +//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
 +pub mod module {
 +    pub trait MyTrait {
 +        pub fn func() {}
 +    }
 +
 +    struct MyStruct {}
 +
 +    impl MyTrait for MyStruct {
 +        pub fn func$0() {}
 +    }
 +}
 +"#,
 +            "foo::module::MyStruct::MyTrait::func",
-             r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
++            r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
 +            MonikerKind::Export,
 +        );
 +    }
 +
 +    #[test]
 +    fn moniker_for_field() {
 +        check_moniker(
 +            r#"
 +//- /lib.rs crate:main deps:foo
 +use foo::St;
 +fn main() {
 +    let x = St { a$0: 2 };
 +}
 +//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
 +pub struct St {
 +    pub a: i32,
 +}
 +"#,
 +            "foo::St::a",
++            r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
 +            MonikerKind::Import,
 +        );
 +    }
 +
 +    #[test]
 +    fn no_moniker_for_local() {
 +        no_moniker(
 +            r#"
 +//- /lib.rs crate:main deps:foo
 +use foo::module::func;
 +fn main() {
 +    func();
 +}
 +//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
 +pub mod module {
 +    pub fn func() {
 +        let x$0 = 2;
 +    }
 +}
 +"#,
 +        );
 +    }
 +}
index fe44856dcad2a9a0f8abc49d6ca980cbea49df78,0000000000000000000000000000000000000000..b4df0437050f4d4de0be9b6886c1b0b4d66e00b4
mode 100644,000000..100644
--- /dev/null
@@@ -1,2252 -1,0 +1,2283 @@@
-             let frange = sema.original_range(name_like.syntax());
 +//! Renaming functionality.
 +//!
 +//! This is mostly front-end for [`ide_db::rename`], but it also includes the
 +//! tests. This module also implements a couple of magic tricks, like renaming
 +//! `self` and to `self` (to switch between associated function and method).
 +
 +use hir::{AsAssocItem, InFile, Semantics};
 +use ide_db::{
 +    base_db::FileId,
 +    defs::{Definition, NameClass, NameRefClass},
 +    rename::{bail, format_err, source_edit_from_references, IdentifierKind},
 +    RootDatabase,
 +};
 +use itertools::Itertools;
 +use stdx::{always, never};
 +use syntax::{ast, AstNode, SyntaxNode};
 +
 +use text_edit::TextEdit;
 +
 +use crate::{FilePosition, RangeInfo, SourceChange};
 +
 +pub use ide_db::rename::RenameError;
 +
 +type RenameResult<T> = Result<T, RenameError>;
 +
 +/// Prepares a rename. The sole job of this function is to return the TextRange of the thing that is
 +/// being targeted for a rename.
 +pub(crate) fn prepare_rename(
 +    db: &RootDatabase,
 +    position: FilePosition,
 +) -> RenameResult<RangeInfo<()>> {
 +    let sema = Semantics::new(db);
 +    let source_file = sema.parse(position.file_id);
 +    let syntax = source_file.syntax();
 +
 +    let res = find_definitions(&sema, syntax, position)?
 +        .map(|(name_like, def)| {
 +            // ensure all ranges are valid
 +
 +            if def.range_for_rename(&sema).is_none() {
 +                bail!("No references found at position")
 +            }
-             (Err(e), _) => Err(e),
++            let Some(frange) = sema.original_range_opt(name_like.syntax()) else {
++                bail!("No references found at position");
++            };
 +
 +            always!(
 +                frange.range.contains_inclusive(position.offset)
 +                    && frange.file_id == position.file_id
 +            );
 +            Ok(frange.range)
 +        })
 +        .reduce(|acc, cur| match (acc, cur) {
 +            // ensure all ranges are the same
 +            (Ok(acc_inner), Ok(cur_inner)) if acc_inner == cur_inner => Ok(acc_inner),
++            (e @ Err(_), _) | (_, e @ Err(_)) => e,
 +            _ => bail!("inconsistent text range"),
 +        });
 +
 +    match res {
 +        // ensure at least one definition was found
 +        Some(res) => res.map(|range| RangeInfo::new(range, ())),
 +        None => bail!("No references found at position"),
 +    }
 +}
 +
 +// Feature: Rename
 +//
 +// Renames the item below the cursor and all of its references
 +//
 +// |===
 +// | Editor  | Shortcut
 +//
 +// | VS Code | kbd:[F2]
 +// |===
 +//
 +// image::https://user-images.githubusercontent.com/48062697/113065582-055aae80-91b1-11eb-8ade-2b58e6d81883.gif[]
 +pub(crate) fn rename(
 +    db: &RootDatabase,
 +    position: FilePosition,
 +    new_name: &str,
 +) -> RenameResult<SourceChange> {
 +    let sema = Semantics::new(db);
 +    let source_file = sema.parse(position.file_id);
 +    let syntax = source_file.syntax();
 +
 +    let defs = find_definitions(&sema, syntax, position)?;
 +
 +    let ops: RenameResult<Vec<SourceChange>> = defs
 +        .map(|(_namelike, def)| {
 +            if let Definition::Local(local) = def {
 +                if let Some(self_param) = local.as_self_param(sema.db) {
 +                    cov_mark::hit!(rename_self_to_param);
 +                    return rename_self_to_param(&sema, local, self_param, new_name);
 +                }
 +                if new_name == "self" {
 +                    cov_mark::hit!(rename_to_self);
 +                    return rename_to_self(&sema, local);
 +                }
 +            }
 +            def.rename(&sema, new_name)
 +        })
 +        .collect();
 +
 +    ops?.into_iter()
 +        .reduce(|acc, elem| acc.merge(elem))
 +        .ok_or_else(|| format_err!("No references found at position"))
 +}
 +
 +/// Called by the client when it is about to rename a file.
 +pub(crate) fn will_rename_file(
 +    db: &RootDatabase,
 +    file_id: FileId,
 +    new_name_stem: &str,
 +) -> Option<SourceChange> {
 +    let sema = Semantics::new(db);
 +    let module = sema.to_module_def(file_id)?;
 +    let def = Definition::Module(module);
 +    let mut change = def.rename(&sema, new_name_stem).ok()?;
 +    change.file_system_edits.clear();
 +    Some(change)
 +}
 +
 +fn find_definitions(
 +    sema: &Semantics<'_, RootDatabase>,
 +    syntax: &SyntaxNode,
 +    position: FilePosition,
 +) -> RenameResult<impl Iterator<Item = (ast::NameLike, Definition)>> {
 +    let symbols = sema
 +        .find_nodes_at_offset_with_descend::<ast::NameLike>(syntax, position.offset)
 +        .map(|name_like| {
 +            let res = match &name_like {
 +                // renaming aliases would rename the item being aliased as the HIR doesn't track aliases yet
 +                ast::NameLike::Name(name)
 +                    if name
 +                        .syntax()
 +                        .parent()
 +                        .map_or(false, |it| ast::Rename::can_cast(it.kind())) =>
 +                {
 +                    bail!("Renaming aliases is currently unsupported")
 +                }
 +                ast::NameLike::Name(name) => NameClass::classify(sema, name)
 +                    .map(|class| match class {
 +                        NameClass::Definition(it) | NameClass::ConstReference(it) => it,
 +                        NameClass::PatFieldShorthand { local_def, field_ref: _ } => {
 +                            Definition::Local(local_def)
 +                        }
 +                    })
 +                    .map(|def| (name_like.clone(), def))
 +                    .ok_or_else(|| format_err!("No references found at position")),
 +                ast::NameLike::NameRef(name_ref) => {
 +                    NameRefClass::classify(sema, name_ref)
 +                        .map(|class| match class {
 +                            NameRefClass::Definition(def) => def,
 +                            NameRefClass::FieldShorthand { local_ref, field_ref: _ } => {
 +                                Definition::Local(local_ref)
 +                            }
 +                        })
 +                        .ok_or_else(|| format_err!("No references found at position"))
 +                        .and_then(|def| {
 +                            // if the name differs from the definitions name it has to be an alias
 +                            if def
 +                                .name(sema.db)
 +                                .map_or(false, |it| it.to_smol_str() != name_ref.text().as_str())
 +                            {
 +                                Err(format_err!("Renaming aliases is currently unsupported"))
 +                            } else {
 +                                Ok((name_like.clone(), def))
 +                            }
 +                        })
 +                }
 +                ast::NameLike::Lifetime(lifetime) => {
 +                    NameRefClass::classify_lifetime(sema, lifetime)
 +                        .and_then(|class| match class {
 +                            NameRefClass::Definition(def) => Some(def),
 +                            _ => None,
 +                        })
 +                        .or_else(|| {
 +                            NameClass::classify_lifetime(sema, lifetime).and_then(|it| match it {
 +                                NameClass::Definition(it) => Some(it),
 +                                _ => None,
 +                            })
 +                        })
 +                        .map(|def| (name_like, def))
 +                        .ok_or_else(|| format_err!("No references found at position"))
 +                }
 +            };
 +            res
 +        });
 +
 +    let res: RenameResult<Vec<_>> = symbols.collect();
 +    match res {
 +        Ok(v) => {
 +            if v.is_empty() {
 +                // FIXME: some semantic duplication between "empty vec" and "Err()"
 +                Err(format_err!("No references found at position"))
 +            } else {
 +                // remove duplicates, comparing `Definition`s
 +                Ok(v.into_iter().unique_by(|t| t.1))
 +            }
 +        }
 +        Err(e) => Err(e),
 +    }
 +}
 +
 +fn rename_to_self(
 +    sema: &Semantics<'_, RootDatabase>,
 +    local: hir::Local,
 +) -> RenameResult<SourceChange> {
 +    if never!(local.is_self(sema.db)) {
 +        bail!("rename_to_self invoked on self");
 +    }
 +
 +    let fn_def = match local.parent(sema.db) {
 +        hir::DefWithBody::Function(func) => func,
 +        _ => bail!("Cannot rename local to self outside of function"),
 +    };
 +
 +    if fn_def.self_param(sema.db).is_some() {
 +        bail!("Method already has a self parameter");
 +    }
 +
 +    let params = fn_def.assoc_fn_params(sema.db);
 +    let first_param = params
 +        .first()
 +        .ok_or_else(|| format_err!("Cannot rename local to self unless it is a parameter"))?;
 +    match first_param.as_local(sema.db) {
 +        Some(plocal) => {
 +            if plocal != local {
 +                bail!("Only the first parameter may be renamed to self");
 +            }
 +        }
 +        None => bail!("rename_to_self invoked on destructuring parameter"),
 +    }
 +
 +    let assoc_item = fn_def
 +        .as_assoc_item(sema.db)
 +        .ok_or_else(|| format_err!("Cannot rename parameter to self for free function"))?;
 +    let impl_ = match assoc_item.container(sema.db) {
 +        hir::AssocItemContainer::Trait(_) => {
 +            bail!("Cannot rename parameter to self for trait functions");
 +        }
 +        hir::AssocItemContainer::Impl(impl_) => impl_,
 +    };
 +    let first_param_ty = first_param.ty();
 +    let impl_ty = impl_.self_ty(sema.db);
 +    let (ty, self_param) = if impl_ty.remove_ref().is_some() {
 +        // if the impl is a ref to the type we can just match the `&T` with self directly
 +        (first_param_ty.clone(), "self")
 +    } else {
 +        first_param_ty.remove_ref().map_or((first_param_ty.clone(), "self"), |ty| {
 +            (ty, if first_param_ty.is_mutable_reference() { "&mut self" } else { "&self" })
 +        })
 +    };
 +
 +    if ty != impl_ty {
 +        bail!("Parameter type differs from impl block type");
 +    }
 +
 +    let InFile { file_id, value: param_source } =
 +        first_param.source(sema.db).ok_or_else(|| format_err!("No source for parameter found"))?;
 +
 +    let def = Definition::Local(local);
 +    let usages = def.usages(sema).all();
 +    let mut source_change = SourceChange::default();
 +    source_change.extend(usages.iter().map(|(&file_id, references)| {
 +        (file_id, source_edit_from_references(references, def, "self"))
 +    }));
 +    source_change.insert_source_edit(
 +        file_id.original_file(sema.db),
 +        TextEdit::replace(param_source.syntax().text_range(), String::from(self_param)),
 +    );
 +    Ok(source_change)
 +}
 +
 +fn rename_self_to_param(
 +    sema: &Semantics<'_, RootDatabase>,
 +    local: hir::Local,
 +    self_param: hir::SelfParam,
 +    new_name: &str,
 +) -> RenameResult<SourceChange> {
 +    if new_name == "self" {
 +        // Let's do nothing rather than complain.
 +        cov_mark::hit!(rename_self_to_self);
 +        return Ok(SourceChange::default());
 +    }
 +
 +    let identifier_kind = IdentifierKind::classify(new_name)?;
 +
 +    let InFile { file_id, value: self_param } =
 +        self_param.source(sema.db).ok_or_else(|| format_err!("cannot find function source"))?;
 +
 +    let def = Definition::Local(local);
 +    let usages = def.usages(sema).all();
 +    let edit = text_edit_from_self_param(&self_param, new_name)
 +        .ok_or_else(|| format_err!("No target type found"))?;
 +    if usages.len() > 1 && identifier_kind == IdentifierKind::Underscore {
 +        bail!("Cannot rename reference to `_` as it is being referenced multiple times");
 +    }
 +    let mut source_change = SourceChange::default();
 +    source_change.insert_source_edit(file_id.original_file(sema.db), edit);
 +    source_change.extend(usages.iter().map(|(&file_id, references)| {
 +        (file_id, source_edit_from_references(references, def, new_name))
 +    }));
 +    Ok(source_change)
 +}
 +
 +fn text_edit_from_self_param(self_param: &ast::SelfParam, new_name: &str) -> Option<TextEdit> {
 +    fn target_type_name(impl_def: &ast::Impl) -> Option<String> {
 +        if let Some(ast::Type::PathType(p)) = impl_def.self_ty() {
 +            return Some(p.path()?.segment()?.name_ref()?.text().to_string());
 +        }
 +        None
 +    }
 +
 +    let impl_def = self_param.syntax().ancestors().find_map(ast::Impl::cast)?;
 +    let type_name = target_type_name(&impl_def)?;
 +
 +    let mut replacement_text = String::from(new_name);
 +    replacement_text.push_str(": ");
 +    match (self_param.amp_token(), self_param.mut_token()) {
 +        (Some(_), None) => replacement_text.push('&'),
 +        (Some(_), Some(_)) => replacement_text.push_str("&mut "),
 +        (_, _) => (),
 +    };
 +    replacement_text.push_str(type_name.as_str());
 +
 +    Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text))
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use expect_test::{expect, Expect};
 +    use stdx::trim_indent;
 +    use test_utils::assert_eq_text;
 +    use text_edit::TextEdit;
 +
 +    use crate::{fixture, FileId};
 +
 +    use super::{RangeInfo, RenameError};
 +
 +    #[track_caller]
 +    fn check(new_name: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
 +        let ra_fixture_after = &trim_indent(ra_fixture_after);
 +        let (analysis, position) = fixture::position(ra_fixture_before);
 +        let rename_result = analysis
 +            .rename(position, new_name)
 +            .unwrap_or_else(|err| panic!("Rename to '{}' was cancelled: {}", new_name, err));
 +        match rename_result {
 +            Ok(source_change) => {
 +                let mut text_edit_builder = TextEdit::builder();
 +                let mut file_id: Option<FileId> = None;
 +                for edit in source_change.source_file_edits {
 +                    file_id = Some(edit.0);
 +                    for indel in edit.1.into_iter() {
 +                        text_edit_builder.replace(indel.delete, indel.insert);
 +                    }
 +                }
 +                if let Some(file_id) = file_id {
 +                    let mut result = analysis.file_text(file_id).unwrap().to_string();
 +                    text_edit_builder.finish().apply(&mut result);
 +                    assert_eq_text!(ra_fixture_after, &*result);
 +                }
 +            }
 +            Err(err) => {
 +                if ra_fixture_after.starts_with("error:") {
 +                    let error_message = ra_fixture_after
 +                        .chars()
 +                        .into_iter()
 +                        .skip("error:".len())
 +                        .collect::<String>();
 +                    assert_eq!(error_message.trim(), err.to_string());
 +                } else {
 +                    panic!("Rename to '{}' failed unexpectedly: {}", new_name, err)
 +                }
 +            }
 +        };
 +    }
 +
 +    fn check_expect(new_name: &str, ra_fixture: &str, expect: Expect) {
 +        let (analysis, position) = fixture::position(ra_fixture);
 +        let source_change =
 +            analysis.rename(position, new_name).unwrap().expect("Expect returned a RenameError");
 +        expect.assert_debug_eq(&source_change)
 +    }
 +
 +    fn check_expect_will_rename_file(new_name: &str, ra_fixture: &str, expect: Expect) {
 +        let (analysis, position) = fixture::position(ra_fixture);
 +        let source_change = analysis
 +            .will_rename_file(position.file_id, new_name)
 +            .unwrap()
 +            .expect("Expect returned a RenameError");
 +        expect.assert_debug_eq(&source_change)
 +    }
 +
 +    fn check_prepare(ra_fixture: &str, expect: Expect) {
 +        let (analysis, position) = fixture::position(ra_fixture);
 +        let result = analysis
 +            .prepare_rename(position)
 +            .unwrap_or_else(|err| panic!("PrepareRename was cancelled: {}", err));
 +        match result {
 +            Ok(RangeInfo { range, info: () }) => {
 +                let source = analysis.file_text(position.file_id).unwrap();
 +                expect.assert_eq(&format!("{:?}: {}", range, &source[range]))
 +            }
 +            Err(RenameError(err)) => expect.assert_eq(&err),
 +        };
 +    }
 +
 +    #[test]
 +    fn test_prepare_rename_namelikes() {
 +        check_prepare(r"fn name$0<'lifetime>() {}", expect![[r#"3..7: name"#]]);
 +        check_prepare(r"fn name<'lifetime$0>() {}", expect![[r#"8..17: 'lifetime"#]]);
 +        check_prepare(r"fn name<'lifetime>() { name$0(); }", expect![[r#"23..27: name"#]]);
 +    }
 +
 +    #[test]
 +    fn test_prepare_rename_in_macro() {
 +        check_prepare(
 +            r"macro_rules! foo {
 +    ($ident:ident) => {
 +        pub struct $ident;
 +    }
 +}
 +foo!(Foo$0);",
 +            expect![[r#"83..86: Foo"#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn test_prepare_rename_keyword() {
 +        check_prepare(r"struct$0 Foo;", expect![[r#"No references found at position"#]]);
 +    }
 +
 +    #[test]
 +    fn test_prepare_rename_tuple_field() {
 +        check_prepare(
 +            r#"
 +struct Foo(i32);
 +
 +fn baz() {
 +    let mut x = Foo(4);
 +    x.0$0 = 5;
 +}
 +"#,
 +            expect![[r#"No references found at position"#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn test_prepare_rename_builtin() {
 +        check_prepare(
 +            r#"
 +fn foo() {
 +    let x: i32$0 = 0;
 +}
 +"#,
 +            expect![[r#"No references found at position"#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn test_prepare_rename_self() {
 +        check_prepare(
 +            r#"
 +struct Foo {}
 +
 +impl Foo {
 +    fn foo(self) -> Self$0 {
 +        self
 +    }
 +}
 +"#,
 +            expect![[r#"No references found at position"#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn test_rename_to_underscore() {
 +        check("_", r#"fn main() { let i$0 = 1; }"#, r#"fn main() { let _ = 1; }"#);
 +    }
 +
 +    #[test]
 +    fn test_rename_to_raw_identifier() {
 +        check("r#fn", r#"fn main() { let i$0 = 1; }"#, r#"fn main() { let r#fn = 1; }"#);
 +    }
 +
 +    #[test]
 +    fn test_rename_to_invalid_identifier1() {
 +        check(
 +            "invalid!",
 +            r#"fn main() { let i$0 = 1; }"#,
 +            "error: Invalid name `invalid!`: not an identifier",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_rename_to_invalid_identifier2() {
 +        check(
 +            "multiple tokens",
 +            r#"fn main() { let i$0 = 1; }"#,
 +            "error: Invalid name `multiple tokens`: not an identifier",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_rename_to_invalid_identifier3() {
 +        check(
 +            "let",
 +            r#"fn main() { let i$0 = 1; }"#,
 +            "error: Invalid name `let`: not an identifier",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_rename_to_invalid_identifier_lifetime() {
 +        cov_mark::check!(rename_not_an_ident_ref);
 +        check(
 +            "'foo",
 +            r#"fn main() { let i$0 = 1; }"#,
 +            "error: Invalid name `'foo`: not an identifier",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_rename_to_invalid_identifier_lifetime2() {
 +        cov_mark::check!(rename_not_a_lifetime_ident_ref);
 +        check(
 +            "foo",
 +            r#"fn main<'a>(_: &'a$0 ()) {}"#,
 +            "error: Invalid name `foo`: not a lifetime identifier",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_rename_to_underscore_invalid() {
 +        cov_mark::check!(rename_underscore_multiple);
 +        check(
 +            "_",
 +            r#"fn main(foo$0: ()) {foo;}"#,
 +            "error: Cannot rename reference to `_` as it is being referenced multiple times",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_rename_mod_invalid() {
 +        check(
 +            "'foo",
 +            r#"mod foo$0 {}"#,
 +            "error: Invalid name `'foo`: cannot rename module to 'foo",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_rename_for_local() {
 +        check(
 +            "k",
 +            r#"
 +fn main() {
 +    let mut i = 1;
 +    let j = 1;
 +    i = i$0 + j;
 +
 +    { i = 0; }
 +
 +    i = 5;
 +}
 +"#,
 +            r#"
 +fn main() {
 +    let mut k = 1;
 +    let j = 1;
 +    k = k + j;
 +
 +    { k = 0; }
 +
 +    k = 5;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_rename_unresolved_reference() {
 +        check(
 +            "new_name",
 +            r#"fn main() { let _ = unresolved_ref$0; }"#,
 +            "error: No references found at position",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_rename_macro_multiple_occurrences() {
 +        check(
 +            "Baaah",
 +            r#"macro_rules! foo {
 +    ($ident:ident) => {
 +        const $ident: () = ();
 +        struct $ident {}
 +    };
 +}
 +
 +foo!($0Foo);
 +const _: () = Foo;
 +const _: Foo = Foo {};
 +    "#,
 +            r#"
 +macro_rules! foo {
 +    ($ident:ident) => {
 +        const $ident: () = ();
 +        struct $ident {}
 +    };
 +}
 +
 +foo!(Baaah);
 +const _: () = Baaah;
 +const _: Baaah = Baaah {};
 +    "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn test_rename_for_macro_args() {
 +        check(
 +            "b",
 +            r#"
 +macro_rules! foo {($i:ident) => {$i} }
 +fn main() {
 +    let a$0 = "test";
 +    foo!(a);
 +}
 +"#,
 +            r#"
 +macro_rules! foo {($i:ident) => {$i} }
 +fn main() {
 +    let b = "test";
 +    foo!(b);
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_rename_for_macro_args_rev() {
 +        check(
 +            "b",
 +            r#"
 +macro_rules! foo {($i:ident) => {$i} }
 +fn main() {
 +    let a = "test";
 +    foo!(a$0);
 +}
 +"#,
 +            r#"
 +macro_rules! foo {($i:ident) => {$i} }
 +fn main() {
 +    let b = "test";
 +    foo!(b);
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_rename_for_macro_define_fn() {
 +        check(
 +            "bar",
 +            r#"
 +macro_rules! define_fn {($id:ident) => { fn $id{} }}
 +define_fn!(foo);
 +fn main() {
 +    fo$0o();
 +}
 +"#,
 +            r#"
 +macro_rules! define_fn {($id:ident) => { fn $id{} }}
 +define_fn!(bar);
 +fn main() {
 +    bar();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_rename_for_macro_define_fn_rev() {
 +        check(
 +            "bar",
 +            r#"
 +macro_rules! define_fn {($id:ident) => { fn $id{} }}
 +define_fn!(fo$0o);
 +fn main() {
 +    foo();
 +}
 +"#,
 +            r#"
 +macro_rules! define_fn {($id:ident) => { fn $id{} }}
 +define_fn!(bar);
 +fn main() {
 +    bar();
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_rename_for_param_inside() {
 +        check("j", r#"fn foo(i : u32) -> u32 { i$0 }"#, r#"fn foo(j : u32) -> u32 { j }"#);
 +    }
 +
 +    #[test]
 +    fn test_rename_refs_for_fn_param() {
 +        check("j", r#"fn foo(i$0 : u32) -> u32 { i }"#, r#"fn foo(j : u32) -> u32 { j }"#);
 +    }
 +
 +    #[test]
 +    fn test_rename_for_mut_param() {
 +        check("j", r#"fn foo(mut i$0 : u32) -> u32 { i }"#, r#"fn foo(mut j : u32) -> u32 { j }"#);
 +    }
 +
 +    #[test]
 +    fn test_rename_struct_field() {
 +        check(
 +            "foo",
 +            r#"
 +struct Foo { field$0: i32 }
 +
 +impl Foo {
 +    fn new(i: i32) -> Self {
 +        Self { field: i }
 +    }
 +}
 +"#,
 +            r#"
 +struct Foo { foo: i32 }
 +
 +impl Foo {
 +    fn new(i: i32) -> Self {
 +        Self { foo: i }
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_rename_field_in_field_shorthand() {
 +        cov_mark::check!(test_rename_field_in_field_shorthand);
 +        check(
 +            "field",
 +            r#"
 +struct Foo { foo$0: i32 }
 +
 +impl Foo {
 +    fn new(foo: i32) -> Self {
 +        Self { foo }
 +    }
 +}
 +"#,
 +            r#"
 +struct Foo { field: i32 }
 +
 +impl Foo {
 +    fn new(foo: i32) -> Self {
 +        Self { field: foo }
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_rename_local_in_field_shorthand() {
 +        cov_mark::check!(test_rename_local_in_field_shorthand);
 +        check(
 +            "j",
 +            r#"
 +struct Foo { i: i32 }
 +
 +impl Foo {
 +    fn new(i$0: i32) -> Self {
 +        Self { i }
 +    }
 +}
 +"#,
 +            r#"
 +struct Foo { i: i32 }
 +
 +impl Foo {
 +    fn new(j: i32) -> Self {
 +        Self { i: j }
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_field_shorthand_correct_struct() {
 +        check(
 +            "j",
 +            r#"
 +struct Foo { i$0: i32 }
 +struct Bar { i: i32 }
 +
 +impl Bar {
 +    fn new(i: i32) -> Self {
 +        Self { i }
 +    }
 +}
 +"#,
 +            r#"
 +struct Foo { j: i32 }
 +struct Bar { i: i32 }
 +
 +impl Bar {
 +    fn new(i: i32) -> Self {
 +        Self { i }
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_shadow_local_for_struct_shorthand() {
 +        check(
 +            "j",
 +            r#"
 +struct Foo { i: i32 }
 +
 +fn baz(i$0: i32) -> Self {
 +     let x = Foo { i };
 +     {
 +         let i = 0;
 +         Foo { i }
 +     }
 +}
 +"#,
 +            r#"
 +struct Foo { i: i32 }
 +
 +fn baz(j: i32) -> Self {
 +     let x = Foo { i: j };
 +     {
 +         let i = 0;
 +         Foo { i }
 +     }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_rename_mod() {
 +        check_expect(
 +            "foo2",
 +            r#"
 +//- /lib.rs
 +mod bar;
 +
 +//- /bar.rs
 +mod foo$0;
 +
 +//- /bar/foo.rs
 +// empty
 +"#,
 +            expect![[r#"
 +                SourceChange {
 +                    source_file_edits: {
 +                        FileId(
 +                            1,
 +                        ): TextEdit {
 +                            indels: [
 +                                Indel {
 +                                    insert: "foo2",
 +                                    delete: 4..7,
 +                                },
 +                            ],
 +                        },
 +                    },
 +                    file_system_edits: [
 +                        MoveFile {
 +                            src: FileId(
 +                                2,
 +                            ),
 +                            dst: AnchoredPathBuf {
 +                                anchor: FileId(
 +                                    2,
 +                                ),
 +                                path: "foo2.rs",
 +                            },
 +                        },
 +                    ],
 +                    is_snippet: false,
 +                }
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn test_rename_mod_in_use_tree() {
 +        check_expect(
 +            "quux",
 +            r#"
 +//- /main.rs
 +pub mod foo;
 +pub mod bar;
 +fn main() {}
 +
 +//- /foo.rs
 +pub struct FooContent;
 +
 +//- /bar.rs
 +use crate::foo$0::FooContent;
 +"#,
 +            expect![[r#"
 +                SourceChange {
 +                    source_file_edits: {
 +                        FileId(
 +                            0,
 +                        ): TextEdit {
 +                            indels: [
 +                                Indel {
 +                                    insert: "quux",
 +                                    delete: 8..11,
 +                                },
 +                            ],
 +                        },
 +                        FileId(
 +                            2,
 +                        ): TextEdit {
 +                            indels: [
 +                                Indel {
 +                                    insert: "quux",
 +                                    delete: 11..14,
 +                                },
 +                            ],
 +                        },
 +                    },
 +                    file_system_edits: [
 +                        MoveFile {
 +                            src: FileId(
 +                                1,
 +                            ),
 +                            dst: AnchoredPathBuf {
 +                                anchor: FileId(
 +                                    1,
 +                                ),
 +                                path: "quux.rs",
 +                            },
 +                        },
 +                    ],
 +                    is_snippet: false,
 +                }
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn test_rename_mod_in_dir() {
 +        check_expect(
 +            "foo2",
 +            r#"
 +//- /lib.rs
 +mod fo$0o;
 +//- /foo/mod.rs
 +// empty
 +"#,
 +            expect![[r#"
 +                SourceChange {
 +                    source_file_edits: {
 +                        FileId(
 +                            0,
 +                        ): TextEdit {
 +                            indels: [
 +                                Indel {
 +                                    insert: "foo2",
 +                                    delete: 4..7,
 +                                },
 +                            ],
 +                        },
 +                    },
 +                    file_system_edits: [
 +                        MoveDir {
 +                            src: AnchoredPathBuf {
 +                                anchor: FileId(
 +                                    1,
 +                                ),
 +                                path: "../foo",
 +                            },
 +                            src_id: FileId(
 +                                1,
 +                            ),
 +                            dst: AnchoredPathBuf {
 +                                anchor: FileId(
 +                                    1,
 +                                ),
 +                                path: "../foo2",
 +                            },
 +                        },
 +                    ],
 +                    is_snippet: false,
 +                }
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn test_rename_unusually_nested_mod() {
 +        check_expect(
 +            "bar",
 +            r#"
 +//- /lib.rs
 +mod outer { mod fo$0o; }
 +
 +//- /outer/foo.rs
 +// empty
 +"#,
 +            expect![[r#"
 +                SourceChange {
 +                    source_file_edits: {
 +                        FileId(
 +                            0,
 +                        ): TextEdit {
 +                            indels: [
 +                                Indel {
 +                                    insert: "bar",
 +                                    delete: 16..19,
 +                                },
 +                            ],
 +                        },
 +                    },
 +                    file_system_edits: [
 +                        MoveFile {
 +                            src: FileId(
 +                                1,
 +                            ),
 +                            dst: AnchoredPathBuf {
 +                                anchor: FileId(
 +                                    1,
 +                                ),
 +                                path: "bar.rs",
 +                            },
 +                        },
 +                    ],
 +                    is_snippet: false,
 +                }
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn test_module_rename_in_path() {
 +        check(
 +            "baz",
 +            r#"
 +mod $0foo {
 +    pub use self::bar as qux;
 +    pub fn bar() {}
 +}
 +
 +fn main() { foo::bar(); }
 +"#,
 +            r#"
 +mod baz {
 +    pub use self::bar as qux;
 +    pub fn bar() {}
 +}
 +
 +fn main() { baz::bar(); }
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_rename_mod_filename_and_path() {
 +        check_expect(
 +            "foo2",
 +            r#"
 +//- /lib.rs
 +mod bar;
 +fn f() {
 +    bar::foo::fun()
 +}
 +
 +//- /bar.rs
 +pub mod foo$0;
 +
 +//- /bar/foo.rs
 +// pub fn fun() {}
 +"#,
 +            expect![[r#"
 +                SourceChange {
 +                    source_file_edits: {
 +                        FileId(
 +                            0,
 +                        ): TextEdit {
 +                            indels: [
 +                                Indel {
 +                                    insert: "foo2",
 +                                    delete: 27..30,
 +                                },
 +                            ],
 +                        },
 +                        FileId(
 +                            1,
 +                        ): TextEdit {
 +                            indels: [
 +                                Indel {
 +                                    insert: "foo2",
 +                                    delete: 8..11,
 +                                },
 +                            ],
 +                        },
 +                    },
 +                    file_system_edits: [
 +                        MoveFile {
 +                            src: FileId(
 +                                2,
 +                            ),
 +                            dst: AnchoredPathBuf {
 +                                anchor: FileId(
 +                                    2,
 +                                ),
 +                                path: "foo2.rs",
 +                            },
 +                        },
 +                    ],
 +                    is_snippet: false,
 +                }
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn test_rename_mod_recursive() {
 +        check_expect(
 +            "foo2",
 +            r#"
 +//- /lib.rs
 +mod foo$0;
 +
 +//- /foo.rs
 +mod bar;
 +mod corge;
 +
 +//- /foo/bar.rs
 +mod qux;
 +
 +//- /foo/bar/qux.rs
 +mod quux;
 +
 +//- /foo/bar/qux/quux/mod.rs
 +// empty
 +
 +//- /foo/corge.rs
 +// empty
 +"#,
 +            expect![[r#"
 +                SourceChange {
 +                    source_file_edits: {
 +                        FileId(
 +                            0,
 +                        ): TextEdit {
 +                            indels: [
 +                                Indel {
 +                                    insert: "foo2",
 +                                    delete: 4..7,
 +                                },
 +                            ],
 +                        },
 +                    },
 +                    file_system_edits: [
 +                        MoveFile {
 +                            src: FileId(
 +                                1,
 +                            ),
 +                            dst: AnchoredPathBuf {
 +                                anchor: FileId(
 +                                    1,
 +                                ),
 +                                path: "foo2.rs",
 +                            },
 +                        },
 +                        MoveDir {
 +                            src: AnchoredPathBuf {
 +                                anchor: FileId(
 +                                    1,
 +                                ),
 +                                path: "foo",
 +                            },
 +                            src_id: FileId(
 +                                1,
 +                            ),
 +                            dst: AnchoredPathBuf {
 +                                anchor: FileId(
 +                                    1,
 +                                ),
 +                                path: "foo2",
 +                            },
 +                        },
 +                    ],
 +                    is_snippet: false,
 +                }
 +            "#]],
 +        )
 +    }
 +    #[test]
 +    fn test_rename_mod_ref_by_super() {
 +        check(
 +            "baz",
 +            r#"
 +        mod $0foo {
 +        struct X;
 +
 +        mod bar {
 +            use super::X;
 +        }
 +    }
 +            "#,
 +            r#"
 +        mod baz {
 +        struct X;
 +
 +        mod bar {
 +            use super::X;
 +        }
 +    }
 +            "#,
 +        )
 +    }
 +
 +    #[test]
 +    fn test_rename_mod_in_macro() {
 +        check(
 +            "bar",
 +            r#"
 +//- /foo.rs
 +
 +//- /lib.rs
 +macro_rules! submodule {
 +    ($name:ident) => {
 +        mod $name;
 +    };
 +}
 +
 +submodule!($0foo);
 +"#,
 +            r#"
 +macro_rules! submodule {
 +    ($name:ident) => {
 +        mod $name;
 +    };
 +}
 +
 +submodule!(bar);
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn test_rename_mod_for_crate_root() {
 +        check_expect_will_rename_file(
 +            "main",
 +            r#"
 +//- /lib.rs
 +use crate::foo as bar;
 +fn foo() {}
 +mod bar$0;
 +"#,
 +            expect![[r#"
 +                SourceChange {
 +                    source_file_edits: {},
 +                    file_system_edits: [],
 +                    is_snippet: false,
 +                }
 +                "#]],
 +        )
 +    }
 +
 +    #[test]
 +    fn test_enum_variant_from_module_1() {
 +        cov_mark::check!(rename_non_local);
 +        check(
 +            "Baz",
 +            r#"
 +mod foo {
 +    pub enum Foo { Bar$0 }
 +}
 +
 +fn func(f: foo::Foo) {
 +    match f {
 +        foo::Foo::Bar => {}
 +    }
 +}
 +"#,
 +            r#"
 +mod foo {
 +    pub enum Foo { Baz }
 +}
 +
 +fn func(f: foo::Foo) {
 +    match f {
 +        foo::Foo::Baz => {}
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_enum_variant_from_module_2() {
 +        check(
 +            "baz",
 +            r#"
 +mod foo {
 +    pub struct Foo { pub bar$0: uint }
 +}
 +
 +fn foo(f: foo::Foo) {
 +    let _ = f.bar;
 +}
 +"#,
 +            r#"
 +mod foo {
 +    pub struct Foo { pub baz: uint }
 +}
 +
 +fn foo(f: foo::Foo) {
 +    let _ = f.baz;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_parameter_to_self() {
 +        cov_mark::check!(rename_to_self);
 +        check(
 +            "self",
 +            r#"
 +struct Foo { i: i32 }
 +
 +impl Foo {
 +    fn f(foo$0: &mut Foo) -> i32 {
 +        foo.i
 +    }
 +}
 +"#,
 +            r#"
 +struct Foo { i: i32 }
 +
 +impl Foo {
 +    fn f(&mut self) -> i32 {
 +        self.i
 +    }
 +}
 +"#,
 +        );
 +        check(
 +            "self",
 +            r#"
 +struct Foo { i: i32 }
 +
 +impl Foo {
 +    fn f(foo$0: Foo) -> i32 {
 +        foo.i
 +    }
 +}
 +"#,
 +            r#"
 +struct Foo { i: i32 }
 +
 +impl Foo {
 +    fn f(self) -> i32 {
 +        self.i
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_parameter_to_self_error_no_impl() {
 +        check(
 +            "self",
 +            r#"
 +struct Foo { i: i32 }
 +
 +fn f(foo$0: &mut Foo) -> i32 {
 +    foo.i
 +}
 +"#,
 +            "error: Cannot rename parameter to self for free function",
 +        );
 +        check(
 +            "self",
 +            r#"
 +struct Foo { i: i32 }
 +struct Bar;
 +
 +impl Bar {
 +    fn f(foo$0: &mut Foo) -> i32 {
 +        foo.i
 +    }
 +}
 +"#,
 +            "error: Parameter type differs from impl block type",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_parameter_to_self_error_not_first() {
 +        check(
 +            "self",
 +            r#"
 +struct Foo { i: i32 }
 +impl Foo {
 +    fn f(x: (), foo$0: &mut Foo) -> i32 {
 +        foo.i
 +    }
 +}
 +"#,
 +            "error: Only the first parameter may be renamed to self",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_parameter_to_self_impl_ref() {
 +        check(
 +            "self",
 +            r#"
 +struct Foo { i: i32 }
 +impl &Foo {
 +    fn f(foo$0: &Foo) -> i32 {
 +        foo.i
 +    }
 +}
 +"#,
 +            r#"
 +struct Foo { i: i32 }
 +impl &Foo {
 +    fn f(self) -> i32 {
 +        self.i
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_self_to_parameter() {
 +        check(
 +            "foo",
 +            r#"
 +struct Foo { i: i32 }
 +
 +impl Foo {
 +    fn f(&mut $0self) -> i32 {
 +        self.i
 +    }
 +}
 +"#,
 +            r#"
 +struct Foo { i: i32 }
 +
 +impl Foo {
 +    fn f(foo: &mut Foo) -> i32 {
 +        foo.i
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_owned_self_to_parameter() {
 +        cov_mark::check!(rename_self_to_param);
 +        check(
 +            "foo",
 +            r#"
 +struct Foo { i: i32 }
 +
 +impl Foo {
 +    fn f($0self) -> i32 {
 +        self.i
 +    }
 +}
 +"#,
 +            r#"
 +struct Foo { i: i32 }
 +
 +impl Foo {
 +    fn f(foo: Foo) -> i32 {
 +        foo.i
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_self_in_path_to_parameter() {
 +        check(
 +            "foo",
 +            r#"
 +struct Foo { i: i32 }
 +
 +impl Foo {
 +    fn f(&self) -> i32 {
 +        let self_var = 1;
 +        self$0.i
 +    }
 +}
 +"#,
 +            r#"
 +struct Foo { i: i32 }
 +
 +impl Foo {
 +    fn f(foo: &Foo) -> i32 {
 +        let self_var = 1;
 +        foo.i
 +    }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_rename_field_put_init_shorthand() {
 +        cov_mark::check!(test_rename_field_put_init_shorthand);
 +        check(
 +            "bar",
 +            r#"
 +struct Foo { i$0: i32 }
 +
 +fn foo(bar: i32) -> Foo {
 +    Foo { i: bar }
 +}
 +"#,
 +            r#"
 +struct Foo { bar: i32 }
 +
 +fn foo(bar: i32) -> Foo {
 +    Foo { bar }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_rename_local_put_init_shorthand() {
 +        cov_mark::check!(test_rename_local_put_init_shorthand);
 +        check(
 +            "i",
 +            r#"
 +struct Foo { i: i32 }
 +
 +fn foo(bar$0: i32) -> Foo {
 +    Foo { i: bar }
 +}
 +"#,
 +            r#"
 +struct Foo { i: i32 }
 +
 +fn foo(i: i32) -> Foo {
 +    Foo { i }
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_struct_field_pat_into_shorthand() {
 +        cov_mark::check!(test_rename_field_put_init_shorthand_pat);
 +        check(
 +            "baz",
 +            r#"
 +struct Foo { i$0: i32 }
 +
 +fn foo(foo: Foo) {
 +    let Foo { i: ref baz @ qux } = foo;
 +    let _ = qux;
 +}
 +"#,
 +            r#"
 +struct Foo { baz: i32 }
 +
 +fn foo(foo: Foo) {
 +    let Foo { baz: ref baz @ qux } = foo;
 +    let _ = qux;
 +}
 +"#,
 +        );
 +        check(
 +            "baz",
 +            r#"
 +struct Foo { i$0: i32 }
 +
 +fn foo(foo: Foo) {
 +    let Foo { i: ref baz } = foo;
 +    let _ = qux;
 +}
 +"#,
 +            r#"
 +struct Foo { baz: i32 }
 +
 +fn foo(foo: Foo) {
 +    let Foo { ref baz } = foo;
 +    let _ = qux;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_struct_local_pat_into_shorthand() {
 +        cov_mark::check!(test_rename_local_put_init_shorthand_pat);
 +        check(
 +            "field",
 +            r#"
 +struct Foo { field: i32 }
 +
 +fn foo(foo: Foo) {
 +    let Foo { field: qux$0 } = foo;
 +    let _ = qux;
 +}
 +"#,
 +            r#"
 +struct Foo { field: i32 }
 +
 +fn foo(foo: Foo) {
 +    let Foo { field } = foo;
 +    let _ = field;
 +}
 +"#,
 +        );
 +        check(
 +            "field",
 +            r#"
 +struct Foo { field: i32 }
 +
 +fn foo(foo: Foo) {
 +    let Foo { field: x @ qux$0 } = foo;
 +    let _ = qux;
 +}
 +"#,
 +            r#"
 +struct Foo { field: i32 }
 +
 +fn foo(foo: Foo) {
 +    let Foo { field: x @ field } = foo;
 +    let _ = field;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_rename_binding_in_destructure_pat() {
 +        let expected_fixture = r#"
 +struct Foo {
 +    i: i32,
 +}
 +
 +fn foo(foo: Foo) {
 +    let Foo { i: bar } = foo;
 +    let _ = bar;
 +}
 +"#;
 +        check(
 +            "bar",
 +            r#"
 +struct Foo {
 +    i: i32,
 +}
 +
 +fn foo(foo: Foo) {
 +    let Foo { i: b } = foo;
 +    let _ = b$0;
 +}
 +"#,
 +            expected_fixture,
 +        );
 +        check(
 +            "bar",
 +            r#"
 +struct Foo {
 +    i: i32,
 +}
 +
 +fn foo(foo: Foo) {
 +    let Foo { i } = foo;
 +    let _ = i$0;
 +}
 +"#,
 +            expected_fixture,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_rename_binding_in_destructure_param_pat() {
 +        check(
 +            "bar",
 +            r#"
 +struct Foo {
 +    i: i32
 +}
 +
 +fn foo(Foo { i }: Foo) -> i32 {
 +    i$0
 +}
 +"#,
 +            r#"
 +struct Foo {
 +    i: i32
 +}
 +
 +fn foo(Foo { i: bar }: Foo) -> i32 {
 +    bar
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn test_struct_field_complex_ident_pat() {
 +        cov_mark::check!(rename_record_pat_field_name_split);
 +        check(
 +            "baz",
 +            r#"
 +struct Foo { i$0: i32 }
 +
 +fn foo(foo: Foo) {
 +    let Foo { ref i } = foo;
 +}
 +"#,
 +            r#"
 +struct Foo { baz: i32 }
 +
 +fn foo(foo: Foo) {
 +    let Foo { baz: ref i } = foo;
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_rename_lifetimes() {
 +        cov_mark::check!(rename_lifetime);
 +        check(
 +            "'yeeee",
 +            r#"
 +trait Foo<'a> {
 +    fn foo() -> &'a ();
 +}
 +impl<'a> Foo<'a> for &'a () {
 +    fn foo() -> &'a$0 () {
 +        unimplemented!()
 +    }
 +}
 +"#,
 +            r#"
 +trait Foo<'a> {
 +    fn foo() -> &'a ();
 +}
 +impl<'yeeee> Foo<'yeeee> for &'yeeee () {
 +    fn foo() -> &'yeeee () {
 +        unimplemented!()
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn test_rename_bind_pat() {
 +        check(
 +            "new_name",
 +            r#"
 +fn main() {
 +    enum CustomOption<T> {
 +        None,
 +        Some(T),
 +    }
 +
 +    let test_variable = CustomOption::Some(22);
 +
 +    match test_variable {
 +        CustomOption::Some(foo$0) if foo == 11 => {}
 +        _ => (),
 +    }
 +}"#,
 +            r#"
 +fn main() {
 +    enum CustomOption<T> {
 +        None,
 +        Some(T),
 +    }
 +
 +    let test_variable = CustomOption::Some(22);
 +
 +    match test_variable {
 +        CustomOption::Some(new_name) if new_name == 11 => {}
 +        _ => (),
 +    }
 +}"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_rename_label() {
 +        check(
 +            "'foo",
 +            r#"
 +fn foo<'a>() -> &'a () {
 +    'a: {
 +        'b: loop {
 +            break 'a$0;
 +        }
 +    }
 +}
 +"#,
 +            r#"
 +fn foo<'a>() -> &'a () {
 +    'foo: {
 +        'b: loop {
 +            break 'foo;
 +        }
 +    }
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn test_self_to_self() {
 +        cov_mark::check!(rename_self_to_self);
 +        check(
 +            "self",
 +            r#"
 +struct Foo;
 +impl Foo {
 +    fn foo(self$0) {}
 +}
 +"#,
 +            r#"
 +struct Foo;
 +impl Foo {
 +    fn foo(self) {}
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn test_rename_field_in_pat_in_macro_doesnt_shorthand() {
 +        // ideally we would be able to make this emit a short hand, but I doubt this is easily possible
 +        check(
 +            "baz",
 +            r#"
 +macro_rules! foo {
 +    ($pattern:pat) => {
 +        let $pattern = loop {};
 +    };
 +}
 +struct Foo {
 +    bar$0: u32,
 +}
 +fn foo() {
 +    foo!(Foo { bar: baz });
 +}
 +"#,
 +            r#"
 +macro_rules! foo {
 +    ($pattern:pat) => {
 +        let $pattern = loop {};
 +    };
 +}
 +struct Foo {
 +    baz: u32,
 +}
 +fn foo() {
 +    foo!(Foo { baz: baz });
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn test_rename_tuple_field() {
 +        check(
 +            "foo",
 +            r#"
 +struct Foo(i32);
 +
 +fn baz() {
 +    let mut x = Foo(4);
 +    x.0$0 = 5;
 +}
 +"#,
 +            "error: No identifier available to rename",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_rename_builtin() {
 +        check(
 +            "foo",
 +            r#"
 +fn foo() {
 +    let x: i32$0 = 0;
 +}
 +"#,
 +            "error: Cannot rename builtin type",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_rename_self() {
 +        check(
 +            "foo",
 +            r#"
 +struct Foo {}
 +
 +impl Foo {
 +    fn foo(self) -> Self$0 {
 +        self
 +    }
 +}
 +"#,
 +            "error: Cannot rename `Self`",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_rename_ignores_self_ty() {
 +        check(
 +            "Fo0",
 +            r#"
 +struct $0Foo;
 +
 +impl Foo where Self: {}
 +"#,
 +            r#"
 +struct Fo0;
 +
 +impl Fo0 where Self: {}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_rename_fails_on_aliases() {
 +        check(
 +            "Baz",
 +            r#"
 +struct Foo;
 +use Foo as Bar$0;
 +"#,
 +            "error: Renaming aliases is currently unsupported",
 +        );
 +        check(
 +            "Baz",
 +            r#"
 +struct Foo;
 +use Foo as Bar;
 +use Bar$0;
 +"#,
 +            "error: Renaming aliases is currently unsupported",
 +        );
 +    }
 +
 +    #[test]
 +    fn test_rename_trait_method() {
 +        let res = r"
 +trait Foo {
 +    fn foo(&self) {
 +        self.foo();
 +    }
 +}
 +
 +impl Foo for () {
 +    fn foo(&self) {
 +        self.foo();
 +    }
 +}";
 +        check(
 +            "foo",
 +            r#"
 +trait Foo {
 +    fn bar$0(&self) {
 +        self.bar();
 +    }
 +}
 +
 +impl Foo for () {
 +    fn bar(&self) {
 +        self.bar();
 +    }
 +}"#,
 +            res,
 +        );
 +        check(
 +            "foo",
 +            r#"
 +trait Foo {
 +    fn bar(&self) {
 +        self.bar$0();
 +    }
 +}
 +
 +impl Foo for () {
 +    fn bar(&self) {
 +        self.bar();
 +    }
 +}"#,
 +            res,
 +        );
 +        check(
 +            "foo",
 +            r#"
 +trait Foo {
 +    fn bar(&self) {
 +        self.bar();
 +    }
 +}
 +
 +impl Foo for () {
 +    fn bar$0(&self) {
 +        self.bar();
 +    }
 +}"#,
 +            res,
 +        );
 +        check(
 +            "foo",
 +            r#"
 +trait Foo {
 +    fn bar(&self) {
 +        self.bar();
 +    }
 +}
 +
 +impl Foo for () {
 +    fn bar(&self) {
 +        self.bar$0();
 +    }
 +}"#,
 +            res,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_rename_trait_method_prefix_of_second() {
 +        check(
 +            "qux",
 +            r#"
 +trait Foo {
 +    fn foo$0() {}
 +    fn foobar() {}
 +}
 +"#,
 +            r#"
 +trait Foo {
 +    fn qux() {}
 +    fn foobar() {}
 +}
 +"#,
 +        );
 +    }
 +
 +    #[test]
 +    fn test_rename_trait_const() {
 +        let res = r"
 +trait Foo {
 +    const FOO: ();
 +}
 +
 +impl Foo for () {
 +    const FOO: ();
 +}
 +fn f() { <()>::FOO; }";
 +        check(
 +            "FOO",
 +            r#"
 +trait Foo {
 +    const BAR$0: ();
 +}
 +
 +impl Foo for () {
 +    const BAR: ();
 +}
 +fn f() { <()>::BAR; }"#,
 +            res,
 +        );
 +        check(
 +            "FOO",
 +            r#"
 +trait Foo {
 +    const BAR: ();
 +}
 +
 +impl Foo for () {
 +    const BAR$0: ();
 +}
 +fn f() { <()>::BAR; }"#,
 +            res,
 +        );
 +        check(
 +            "FOO",
 +            r#"
 +trait Foo {
 +    const BAR: ();
 +}
 +
 +impl Foo for () {
 +    const BAR: ();
 +}
 +fn f() { <()>::BAR$0; }"#,
 +            res,
 +        );
 +    }
 +
 +    #[test]
 +    fn defs_from_macros_arent_renamed() {
 +        check(
 +            "lol",
 +            r#"
 +macro_rules! m { () => { fn f() {} } }
 +m!();
 +fn main() { f$0()  }
 +"#,
 +            "error: No identifier available to rename",
 +        )
 +    }
 +
 +    #[test]
 +    fn attributed_item() {
 +        check(
 +            "function",
 +            r#"
 +//- proc_macros: identity
 +
 +#[proc_macros::identity]
 +fn func$0() {
 +    func();
 +}
 +"#,
 +            r#"
 +
 +#[proc_macros::identity]
 +fn function() {
 +    function();
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn in_macro_multi_mapping() {
 +        check(
 +            "a",
 +            r#"
 +fn foo() {
 +    macro_rules! match_ast2 {
 +        ($node:ident {
 +            $( $res:expr, )*
 +        }) => {{
 +            $( if $node { $res } else )*
 +            { loop {} }
 +        }};
 +    }
 +    let $0d = 3;
 +    match_ast2! {
 +        d {
 +            d,
 +            d,
 +        }
 +    };
 +}
 +"#,
 +            r#"
 +fn foo() {
 +    macro_rules! match_ast2 {
 +        ($node:ident {
 +            $( $res:expr, )*
 +        }) => {{
 +            $( if $node { $res } else )*
 +            { loop {} }
 +        }};
 +    }
 +    let a = 3;
 +    match_ast2! {
 +        a {
 +            a,
 +            a,
 +        }
 +    };
 +}
 +"#,
 +        )
 +    }
 +
 +    #[test]
 +    fn rename_multi_local() {
 +        check(
 +            "bar",
 +            r#"
 +fn foo((foo$0 | foo | foo): ()) {
 +    foo;
 +    let foo;
 +}
 +"#,
 +            r#"
 +fn foo((bar | bar | bar): ()) {
 +    bar;
 +    let foo;
 +}
 +"#,
 +        );
 +        check(
 +            "bar",
 +            r#"
 +fn foo((foo | foo$0 | foo): ()) {
 +    foo;
 +    let foo;
 +}
 +"#,
 +            r#"
 +fn foo((bar | bar | bar): ()) {
 +    bar;
 +    let foo;
 +}
 +"#,
 +        );
 +        check(
 +            "bar",
 +            r#"
 +fn foo((foo | foo | foo): ()) {
 +    foo$0;
 +    let foo;
 +}
 +"#,
 +            r#"
 +fn foo((bar | bar | bar): ()) {
 +    bar;
 +    let foo;
 +}
 +"#,
 +        );
 +    }
++
++    #[test]
++    fn regression_13498() {
++        check(
++            "Testing",
++            r"
++mod foo {
++    pub struct Test$0;
++}
++
++use foo::Test as Tester;
++
++fn main() {
++    let t = Tester;
++}
++",
++            r"
++mod foo {
++    pub struct Testing;
++}
++
++use foo::Testing as Tester;
++
++fn main() {
++    let t = Tester;
++}
++",
++        )
++    }
 +}
index fedc1a435827233ac24135b0918167efa4238dc6,0000000000000000000000000000000000000000..7486b20293a664b7fc048dc247332ff7118f8491
mode 100644,000000..100644
--- /dev/null
@@@ -1,1334 -1,0 +1,1349 @@@
-         hir::CallableKind::Closure | hir::CallableKind::FnPtr => (),
 +//! This module provides primitives for showing type and function parameter information when editing
 +//! a call or use-site.
 +
 +use std::collections::BTreeSet;
 +
 +use either::Either;
 +use hir::{AssocItem, GenericParam, HasAttrs, HirDisplay, Semantics, Trait};
 +use ide_db::{active_parameter::callable_for_node, base_db::FilePosition};
 +use stdx::format_to;
 +use syntax::{
 +    algo,
 +    ast::{self, HasArgList},
 +    match_ast, AstNode, Direction, SyntaxToken, TextRange, TextSize,
 +};
 +
 +use crate::RootDatabase;
 +
 +/// Contains information about an item signature as seen from a use site.
 +///
 +/// This includes the "active parameter", which is the parameter whose value is currently being
 +/// edited.
 +#[derive(Debug)]
 +pub struct SignatureHelp {
 +    pub doc: Option<String>,
 +    pub signature: String,
 +    pub active_parameter: Option<usize>,
 +    parameters: Vec<TextRange>,
 +}
 +
 +impl SignatureHelp {
 +    pub fn parameter_labels(&self) -> impl Iterator<Item = &str> + '_ {
 +        self.parameters.iter().map(move |&it| &self.signature[it])
 +    }
 +
 +    pub fn parameter_ranges(&self) -> &[TextRange] {
 +        &self.parameters
 +    }
 +
 +    fn push_call_param(&mut self, param: &str) {
 +        self.push_param('(', param);
 +    }
 +
 +    fn push_generic_param(&mut self, param: &str) {
 +        self.push_param('<', param);
 +    }
 +
 +    fn push_param(&mut self, opening_delim: char, param: &str) {
 +        if !self.signature.ends_with(opening_delim) {
 +            self.signature.push_str(", ");
 +        }
 +        let start = TextSize::of(&self.signature);
 +        self.signature.push_str(param);
 +        let end = TextSize::of(&self.signature);
 +        self.parameters.push(TextRange::new(start, end))
 +    }
 +}
 +
 +/// Computes parameter information for the given position.
 +pub(crate) fn signature_help(db: &RootDatabase, position: FilePosition) -> Option<SignatureHelp> {
 +    let sema = Semantics::new(db);
 +    let file = sema.parse(position.file_id);
 +    let file = file.syntax();
 +    let token = file
 +        .token_at_offset(position.offset)
 +        .left_biased()
 +        // if the cursor is sandwiched between two space tokens and the call is unclosed
 +        // this prevents us from leaving the CallExpression
 +        .and_then(|tok| algo::skip_trivia_token(tok, Direction::Prev))?;
 +    let token = sema.descend_into_macros_single(token);
 +
 +    for node in token.parent_ancestors() {
 +        match_ast! {
 +            match node {
 +                ast::ArgList(arg_list) => {
 +                    let cursor_outside = arg_list.r_paren_token().as_ref() == Some(&token);
 +                    if cursor_outside {
 +                        return None;
 +                    }
 +                    return signature_help_for_call(&sema, token);
 +                },
 +                ast::GenericArgList(garg_list) => {
 +                    let cursor_outside = garg_list.r_angle_token().as_ref() == Some(&token);
 +                    if cursor_outside {
 +                        return None;
 +                    }
 +                    return signature_help_for_generics(&sema, token);
 +                },
 +                _ => (),
 +            }
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn signature_help_for_call(
 +    sema: &Semantics<'_, RootDatabase>,
 +    token: SyntaxToken,
 +) -> Option<SignatureHelp> {
 +    // Find the calling expression and its NameRef
 +    let mut node = token.parent()?;
 +    let calling_node = loop {
 +        if let Some(callable) = ast::CallableExpr::cast(node.clone()) {
 +            if callable
 +                .arg_list()
 +                .map_or(false, |it| it.syntax().text_range().contains(token.text_range().start()))
 +            {
 +                break callable;
 +            }
 +        }
 +
 +        // Stop at multi-line expressions, since the signature of the outer call is not very
 +        // helpful inside them.
 +        if let Some(expr) = ast::Expr::cast(node.clone()) {
 +            if expr.syntax().text().contains_char('\n') {
 +                return None;
 +            }
 +        }
 +
 +        node = node.parent()?;
 +    };
 +
 +    let (callable, active_parameter) = callable_for_node(sema, &calling_node, &token)?;
 +
 +    let mut res =
 +        SignatureHelp { doc: None, signature: String::new(), parameters: vec![], active_parameter };
 +
 +    let db = sema.db;
 +    let mut fn_params = None;
 +    match callable.kind() {
 +        hir::CallableKind::Function(func) => {
 +            res.doc = func.docs(db).map(|it| it.into());
 +            format_to!(res.signature, "fn {}", func.name(db));
 +            fn_params = Some(match callable.receiver_param(db) {
 +                Some(_self) => func.params_without_self(db),
 +                None => func.assoc_fn_params(db),
 +            });
 +        }
 +        hir::CallableKind::TupleStruct(strukt) => {
 +            res.doc = strukt.docs(db).map(|it| it.into());
 +            format_to!(res.signature, "struct {}", strukt.name(db));
 +        }
 +        hir::CallableKind::TupleEnumVariant(variant) => {
 +            res.doc = variant.docs(db).map(|it| it.into());
 +            format_to!(
 +                res.signature,
 +                "enum {}::{}",
 +                variant.parent_enum(db).name(db),
 +                variant.name(db)
 +            );
 +        }
-         hir::CallableKind::Function(_) | hir::CallableKind::Closure | hir::CallableKind::FnPtr => {
-             render(callable.return_type())
-         }
++        hir::CallableKind::Closure | hir::CallableKind::FnPtr | hir::CallableKind::Other => (),
 +    }
 +
 +    res.signature.push('(');
 +    {
 +        if let Some(self_param) = callable.receiver_param(db) {
 +            format_to!(res.signature, "{}", self_param)
 +        }
 +        let mut buf = String::new();
 +        for (idx, (pat, ty)) in callable.params(db).into_iter().enumerate() {
 +            buf.clear();
 +            if let Some(pat) = pat {
 +                match pat {
 +                    Either::Left(_self) => format_to!(buf, "self: "),
 +                    Either::Right(pat) => format_to!(buf, "{}: ", pat),
 +                }
 +            }
 +            // APITs (argument position `impl Trait`s) are inferred as {unknown} as the user is
 +            // in the middle of entering call arguments.
 +            // In that case, fall back to render definitions of the respective parameters.
 +            // This is overly conservative: we do not substitute known type vars
 +            // (see FIXME in tests::impl_trait) and falling back on any unknowns.
 +            match (ty.contains_unknown(), fn_params.as_deref()) {
 +                (true, Some(fn_params)) => format_to!(buf, "{}", fn_params[idx].ty().display(db)),
 +                _ => format_to!(buf, "{}", ty.display(db)),
 +            }
 +            res.push_call_param(&buf);
 +        }
 +    }
 +    res.signature.push(')');
 +
 +    let mut render = |ret_type: hir::Type| {
 +        if !ret_type.is_unit() {
 +            format_to!(res.signature, " -> {}", ret_type.display(db));
 +        }
 +    };
 +    match callable.kind() {
 +        hir::CallableKind::Function(func) if callable.return_type().contains_unknown() => {
 +            render(func.ret_type(db))
 +        }
-         // Implicitly add `Sized` to avoid noisy `T: ?Sized` in the results.
++        hir::CallableKind::Function(_)
++        | hir::CallableKind::Closure
++        | hir::CallableKind::FnPtr
++        | hir::CallableKind::Other => render(callable.return_type()),
 +        hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_) => {}
 +    }
 +    Some(res)
 +}
 +
 +fn signature_help_for_generics(
 +    sema: &Semantics<'_, RootDatabase>,
 +    token: SyntaxToken,
 +) -> Option<SignatureHelp> {
 +    let parent = token.parent()?;
 +    let arg_list = parent
 +        .ancestors()
 +        .filter_map(ast::GenericArgList::cast)
 +        .find(|list| list.syntax().text_range().contains(token.text_range().start()))?;
 +
 +    let mut active_parameter = arg_list
 +        .generic_args()
 +        .take_while(|arg| arg.syntax().text_range().end() <= token.text_range().start())
 +        .count();
 +
 +    let first_arg_is_non_lifetime = arg_list
 +        .generic_args()
 +        .next()
 +        .map_or(false, |arg| !matches!(arg, ast::GenericArg::LifetimeArg(_)));
 +
 +    let mut generics_def = if let Some(path) =
 +        arg_list.syntax().ancestors().find_map(ast::Path::cast)
 +    {
 +        let res = sema.resolve_path(&path)?;
 +        let generic_def: hir::GenericDef = match res {
 +            hir::PathResolution::Def(hir::ModuleDef::Adt(it)) => it.into(),
 +            hir::PathResolution::Def(hir::ModuleDef::Function(it)) => it.into(),
 +            hir::PathResolution::Def(hir::ModuleDef::Trait(it)) => it.into(),
 +            hir::PathResolution::Def(hir::ModuleDef::TypeAlias(it)) => it.into(),
 +            hir::PathResolution::Def(hir::ModuleDef::Variant(it)) => it.into(),
 +            hir::PathResolution::Def(hir::ModuleDef::BuiltinType(_))
 +            | hir::PathResolution::Def(hir::ModuleDef::Const(_))
 +            | hir::PathResolution::Def(hir::ModuleDef::Macro(_))
 +            | hir::PathResolution::Def(hir::ModuleDef::Module(_))
 +            | hir::PathResolution::Def(hir::ModuleDef::Static(_)) => return None,
 +            hir::PathResolution::BuiltinAttr(_)
 +            | hir::PathResolution::ToolModule(_)
 +            | hir::PathResolution::Local(_)
 +            | hir::PathResolution::TypeParam(_)
 +            | hir::PathResolution::ConstParam(_)
 +            | hir::PathResolution::SelfType(_)
 +            | hir::PathResolution::DeriveHelper(_) => return None,
 +        };
 +
 +        generic_def
 +    } else if let Some(method_call) = arg_list.syntax().parent().and_then(ast::MethodCallExpr::cast)
 +    {
 +        // recv.method::<$0>()
 +        let method = sema.resolve_method_call(&method_call)?;
 +        method.into()
 +    } else {
 +        return None;
 +    };
 +
 +    let mut res = SignatureHelp {
 +        doc: None,
 +        signature: String::new(),
 +        parameters: vec![],
 +        active_parameter: None,
 +    };
 +
 +    let db = sema.db;
 +    match generics_def {
 +        hir::GenericDef::Function(it) => {
 +            res.doc = it.docs(db).map(|it| it.into());
 +            format_to!(res.signature, "fn {}", it.name(db));
 +        }
 +        hir::GenericDef::Adt(hir::Adt::Enum(it)) => {
 +            res.doc = it.docs(db).map(|it| it.into());
 +            format_to!(res.signature, "enum {}", it.name(db));
 +        }
 +        hir::GenericDef::Adt(hir::Adt::Struct(it)) => {
 +            res.doc = it.docs(db).map(|it| it.into());
 +            format_to!(res.signature, "struct {}", it.name(db));
 +        }
 +        hir::GenericDef::Adt(hir::Adt::Union(it)) => {
 +            res.doc = it.docs(db).map(|it| it.into());
 +            format_to!(res.signature, "union {}", it.name(db));
 +        }
 +        hir::GenericDef::Trait(it) => {
 +            res.doc = it.docs(db).map(|it| it.into());
 +            format_to!(res.signature, "trait {}", it.name(db));
 +        }
 +        hir::GenericDef::TypeAlias(it) => {
 +            res.doc = it.docs(db).map(|it| it.into());
 +            format_to!(res.signature, "type {}", it.name(db));
 +        }
 +        hir::GenericDef::Variant(it) => {
 +            // In paths, generics of an enum can be specified *after* one of its variants.
 +            // eg. `None::<u8>`
 +            // We'll use the signature of the enum, but include the docs of the variant.
 +            res.doc = it.docs(db).map(|it| it.into());
 +            let it = it.parent_enum(db);
 +            format_to!(res.signature, "enum {}", it.name(db));
 +            generics_def = it.into();
 +        }
 +        // These don't have generic args that can be specified
 +        hir::GenericDef::Impl(_) | hir::GenericDef::Const(_) => return None,
 +    }
 +
 +    let params = generics_def.params(sema.db);
 +    let num_lifetime_params =
 +        params.iter().take_while(|param| matches!(param, GenericParam::LifetimeParam(_))).count();
 +    if first_arg_is_non_lifetime {
 +        // Lifetime parameters were omitted.
 +        active_parameter += num_lifetime_params;
 +    }
 +    res.active_parameter = Some(active_parameter);
 +
 +    res.signature.push('<');
 +    let mut buf = String::new();
 +    for param in params {
 +        if let hir::GenericParam::TypeParam(ty) = param {
 +            if ty.is_implicit(db) {
 +                continue;
 +            }
 +        }
 +
 +        buf.clear();
 +        format_to!(buf, "{}", param.display(db));
 +        res.push_generic_param(&buf);
 +    }
 +    if let hir::GenericDef::Trait(tr) = generics_def {
 +        add_assoc_type_bindings(db, &mut res, tr, arg_list);
 +    }
 +    res.signature.push('>');
 +
 +    Some(res)
 +}
 +
 +fn add_assoc_type_bindings(
 +    db: &RootDatabase,
 +    res: &mut SignatureHelp,
 +    tr: Trait,
 +    args: ast::GenericArgList,
 +) {
 +    if args.syntax().ancestors().find_map(ast::TypeBound::cast).is_none() {
 +        // Assoc type bindings are only valid in type bound position.
 +        return;
 +    }
 +
 +    let present_bindings = args
 +        .generic_args()
 +        .filter_map(|arg| match arg {
 +            ast::GenericArg::AssocTypeArg(arg) => arg.name_ref().map(|n| n.to_string()),
 +            _ => None,
 +        })
 +        .collect::<BTreeSet<_>>();
 +
 +    let mut buf = String::new();
 +    for binding in &present_bindings {
 +        buf.clear();
 +        format_to!(buf, "{} = …", binding);
 +        res.push_generic_param(&buf);
 +    }
 +
 +    for item in tr.items_with_supertraits(db) {
 +        if let AssocItem::TypeAlias(ty) = item {
 +            let name = ty.name(db).to_smol_str();
 +            if !present_bindings.contains(&*name) {
 +                buf.clear();
 +                format_to!(buf, "{} = …", name);
 +                res.push_generic_param(&buf);
 +            }
 +        }
 +    }
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use std::iter;
 +
 +    use expect_test::{expect, Expect};
 +    use ide_db::base_db::{fixture::ChangeFixture, FilePosition};
 +    use stdx::format_to;
 +
 +    use crate::RootDatabase;
 +
 +    /// Creates analysis from a multi-file fixture, returns positions marked with $0.
 +    pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
 +        let change_fixture = ChangeFixture::parse(ra_fixture);
 +        let mut database = RootDatabase::default();
 +        database.apply_change(change_fixture.change);
 +        let (file_id, range_or_offset) =
 +            change_fixture.file_position.expect("expected a marker ($0)");
 +        let offset = range_or_offset.expect_offset();
 +        (database, FilePosition { file_id, offset })
 +    }
 +
 +    fn check(ra_fixture: &str, expect: Expect) {
- #[lang = "sized"] trait Sized {{}}
 +        let fixture = format!(
 +            r#"
++//- minicore: sized, fn
 +{ra_fixture}
 +            "#
 +        );
 +        let (db, position) = position(&fixture);
 +        let sig_help = crate::signature_help::signature_help(&db, position);
 +        let actual = match sig_help {
 +            Some(sig_help) => {
 +                let mut rendered = String::new();
 +                if let Some(docs) = &sig_help.doc {
 +                    format_to!(rendered, "{}\n------\n", docs.as_str());
 +                }
 +                format_to!(rendered, "{}\n", sig_help.signature);
 +                let mut offset = 0;
 +                for (i, range) in sig_help.parameter_ranges().iter().enumerate() {
 +                    let is_active = sig_help.active_parameter == Some(i);
 +
 +                    let start = u32::from(range.start());
 +                    let gap = start.checked_sub(offset).unwrap_or_else(|| {
 +                        panic!("parameter ranges out of order: {:?}", sig_help.parameter_ranges())
 +                    });
 +                    rendered.extend(iter::repeat(' ').take(gap as usize));
 +                    let param_text = &sig_help.signature[*range];
 +                    let width = param_text.chars().count(); // …
 +                    let marker = if is_active { '^' } else { '-' };
 +                    rendered.extend(iter::repeat(marker).take(width));
 +                    offset += gap + u32::from(range.len());
 +                }
 +                if !sig_help.parameter_ranges().is_empty() {
 +                    format_to!(rendered, "\n");
 +                }
 +                rendered
 +            }
 +            None => String::new(),
 +        };
 +        expect.assert_eq(&actual);
 +    }
 +
 +    #[test]
 +    fn test_fn_signature_two_args() {
 +        check(
 +            r#"
 +fn foo(x: u32, y: u32) -> u32 {x + y}
 +fn bar() { foo($03, ); }
 +"#,
 +            expect![[r#"
 +                fn foo(x: u32, y: u32) -> u32
 +                       ^^^^^^  ------
 +            "#]],
 +        );
 +        check(
 +            r#"
 +fn foo(x: u32, y: u32) -> u32 {x + y}
 +fn bar() { foo(3$0, ); }
 +"#,
 +            expect![[r#"
 +                fn foo(x: u32, y: u32) -> u32
 +                       ^^^^^^  ------
 +            "#]],
 +        );
 +        check(
 +            r#"
 +fn foo(x: u32, y: u32) -> u32 {x + y}
 +fn bar() { foo(3,$0 ); }
 +"#,
 +            expect![[r#"
 +                fn foo(x: u32, y: u32) -> u32
 +                       ------  ^^^^^^
 +            "#]],
 +        );
 +        check(
 +            r#"
 +fn foo(x: u32, y: u32) -> u32 {x + y}
 +fn bar() { foo(3, $0); }
 +"#,
 +            expect![[r#"
 +                fn foo(x: u32, y: u32) -> u32
 +                       ------  ^^^^^^
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn test_fn_signature_two_args_empty() {
 +        check(
 +            r#"
 +fn foo(x: u32, y: u32) -> u32 {x + y}
 +fn bar() { foo($0); }
 +"#,
 +            expect![[r#"
 +                fn foo(x: u32, y: u32) -> u32
 +                       ^^^^^^  ------
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn test_fn_signature_two_args_first_generics() {
 +        check(
 +            r#"
 +fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
 +    where T: Copy + Display, U: Debug
 +{ x + y }
 +
 +fn bar() { foo($03, ); }
 +"#,
 +            expect![[r#"
 +                fn foo(x: i32, y: U) -> u32
 +                       ^^^^^^  ----
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn test_fn_signature_no_params() {
 +        check(
 +            r#"
 +fn foo<T>() -> T where T: Copy + Display {}
 +fn bar() { foo($0); }
 +"#,
 +            expect![[r#"
 +                fn foo() -> T
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn test_fn_signature_for_impl() {
 +        check(
 +            r#"
 +struct F;
 +impl F { pub fn new() { } }
 +fn bar() {
 +    let _ : F = F::new($0);
 +}
 +"#,
 +            expect![[r#"
 +                fn new()
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn test_fn_signature_for_method_self() {
 +        check(
 +            r#"
 +struct S;
 +impl S { pub fn do_it(&self) {} }
 +
 +fn bar() {
 +    let s: S = S;
 +    s.do_it($0);
 +}
 +"#,
 +            expect![[r#"
 +                fn do_it(&self)
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn test_fn_signature_for_method_with_arg() {
 +        check(
 +            r#"
 +struct S;
 +impl S {
 +    fn foo(&self, x: i32) {}
 +}
 +
 +fn main() { S.foo($0); }
 +"#,
 +            expect![[r#"
 +                fn foo(&self, x: i32)
 +                              ^^^^^^
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn test_fn_signature_for_generic_method() {
 +        check(
 +            r#"
 +struct S<T>(T);
 +impl<T> S<T> {
 +    fn foo(&self, x: T) {}
 +}
 +
 +fn main() { S(1u32).foo($0); }
 +"#,
 +            expect![[r#"
 +                fn foo(&self, x: u32)
 +                              ^^^^^^
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn test_fn_signature_for_method_with_arg_as_assoc_fn() {
 +        check(
 +            r#"
 +struct S;
 +impl S {
 +    fn foo(&self, x: i32) {}
 +}
 +
 +fn main() { S::foo($0); }
 +"#,
 +            expect![[r#"
 +                fn foo(self: &S, x: i32)
 +                       ^^^^^^^^  ------
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn test_fn_signature_with_docs_simple() {
 +        check(
 +            r#"
 +/// test
 +// non-doc-comment
 +fn foo(j: u32) -> u32 {
 +    j
 +}
 +
 +fn bar() {
 +    let _ = foo($0);
 +}
 +"#,
 +            expect![[r#"
 +                test
 +                ------
 +                fn foo(j: u32) -> u32
 +                       ^^^^^^
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn test_fn_signature_with_docs() {
 +        check(
 +            r#"
 +/// Adds one to the number given.
 +///
 +/// # Examples
 +///
 +/// ```
 +/// let five = 5;
 +///
 +/// assert_eq!(6, my_crate::add_one(5));
 +/// ```
 +pub fn add_one(x: i32) -> i32 {
 +    x + 1
 +}
 +
 +pub fn do() {
 +    add_one($0
 +}"#,
 +            expect![[r##"
 +                Adds one to the number given.
 +
 +                # Examples
 +
 +                ```
 +                let five = 5;
 +
 +                assert_eq!(6, my_crate::add_one(5));
 +                ```
 +                ------
 +                fn add_one(x: i32) -> i32
 +                           ^^^^^^
 +            "##]],
 +        );
 +    }
 +
 +    #[test]
 +    fn test_fn_signature_with_docs_impl() {
 +        check(
 +            r#"
 +struct addr;
 +impl addr {
 +    /// Adds one to the number given.
 +    ///
 +    /// # Examples
 +    ///
 +    /// ```
 +    /// let five = 5;
 +    ///
 +    /// assert_eq!(6, my_crate::add_one(5));
 +    /// ```
 +    pub fn add_one(x: i32) -> i32 {
 +        x + 1
 +    }
 +}
 +
 +pub fn do_it() {
 +    addr {};
 +    addr::add_one($0);
 +}
 +"#,
 +            expect![[r##"
 +                Adds one to the number given.
 +
 +                # Examples
 +
 +                ```
 +                let five = 5;
 +
 +                assert_eq!(6, my_crate::add_one(5));
 +                ```
 +                ------
 +                fn add_one(x: i32) -> i32
 +                           ^^^^^^
 +            "##]],
 +        );
 +    }
 +
 +    #[test]
 +    fn test_fn_signature_with_docs_from_actix() {
 +        check(
 +            r#"
 +trait Actor {
 +    /// Actor execution context type
 +    type Context;
 +}
 +trait WriteHandler<E>
 +where
 +    Self: Actor
 +{
 +    /// Method is called when writer finishes.
 +    ///
 +    /// By default this method stops actor's `Context`.
 +    fn finished(&mut self, ctx: &mut Self::Context) {}
 +}
 +
 +fn foo(mut r: impl WriteHandler<()>) {
 +    r.finished($0);
 +}
 +"#,
 +            expect![[r#"
 +                Method is called when writer finishes.
 +
 +                By default this method stops actor's `Context`.
 +                ------
 +                fn finished(&mut self, ctx: &mut <impl WriteHandler<()> as Actor>::Context)
 +                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn call_info_bad_offset() {
 +        check(
 +            r#"
 +fn foo(x: u32, y: u32) -> u32 {x + y}
 +fn bar() { foo $0 (3, ); }
 +"#,
 +            expect![[""]],
 +        );
 +    }
 +
 +    #[test]
 +    fn outside_of_arg_list() {
 +        check(
 +            r#"
 +fn foo(a: u8) {}
 +fn f() {
 +    foo(123)$0
 +}
 +"#,
 +            expect![[]],
 +        );
 +        check(
 +            r#"
 +fn foo<T>(a: u8) {}
 +fn f() {
 +    foo::<u32>$0()
 +}
 +"#,
 +            expect![[]],
 +        );
 +    }
 +
 +    #[test]
 +    fn test_nested_method_in_lambda() {
 +        check(
 +            r#"
 +struct Foo;
 +impl Foo { fn bar(&self, _: u32) { } }
 +
 +fn bar(_: u32) { }
 +
 +fn main() {
 +    let foo = Foo;
 +    std::thread::spawn(move || foo.bar($0));
 +}
 +"#,
 +            expect![[r#"
 +                fn bar(&self, _: u32)
 +                              ^^^^^^
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn works_for_tuple_structs() {
 +        check(
 +            r#"
 +/// A cool tuple struct
 +struct S(u32, i32);
 +fn main() {
 +    let s = S(0, $0);
 +}
 +"#,
 +            expect![[r#"
 +                A cool tuple struct
 +                ------
 +                struct S(u32, i32)
 +                         ---  ^^^
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn generic_struct() {
 +        check(
 +            r#"
 +struct S<T>(T);
 +fn main() {
 +    let s = S($0);
 +}
 +"#,
 +            expect![[r#"
 +                struct S({unknown})
 +                         ^^^^^^^^^
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn works_for_enum_variants() {
 +        check(
 +            r#"
 +enum E {
 +    /// A Variant
 +    A(i32),
 +    /// Another
 +    B,
 +    /// And C
 +    C { a: i32, b: i32 }
 +}
 +
 +fn main() {
 +    let a = E::A($0);
 +}
 +"#,
 +            expect![[r#"
 +                A Variant
 +                ------
 +                enum E::A(i32)
 +                          ^^^
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn cant_call_struct_record() {
 +        check(
 +            r#"
 +struct S { x: u32, y: i32 }
 +fn main() {
 +    let s = S($0);
 +}
 +"#,
 +            expect![[""]],
 +        );
 +    }
 +
 +    #[test]
 +    fn cant_call_enum_record() {
 +        check(
 +            r#"
 +enum E {
 +    /// A Variant
 +    A(i32),
 +    /// Another
 +    B,
 +    /// And C
 +    C { a: i32, b: i32 }
 +}
 +
 +fn main() {
 +    let a = E::C($0);
 +}
 +"#,
 +            expect![[""]],
 +        );
 +    }
 +
 +    #[test]
 +    fn fn_signature_for_call_in_macro() {
 +        check(
 +            r#"
 +macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
 +fn foo() { }
 +id! {
 +    fn bar() { foo($0); }
 +}
 +"#,
 +            expect![[r#"
 +                fn foo()
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn call_info_for_lambdas() {
 +        check(
 +            r#"
 +struct S;
 +fn foo(s: S) -> i32 { 92 }
 +fn main() {
 +    (|s| foo(s))($0)
 +}
 +        "#,
 +            expect![[r#"
 +                (s: S) -> i32
 +                 ^^^^
 +            "#]],
 +        )
 +    }
 +
 +    #[test]
 +    fn call_info_for_fn_ptr() {
 +        check(
 +            r#"
 +fn main(f: fn(i32, f64) -> char) {
 +    f(0, $0)
 +}
 +        "#,
 +            expect![[r#"
 +                (i32, f64) -> char
 +                 ---  ^^^
 +            "#]],
 +        )
 +    }
 +
 +    #[test]
 +    fn call_info_for_unclosed_call() {
 +        check(
 +            r#"
 +fn foo(foo: u32, bar: u32) {}
 +fn main() {
 +    foo($0
 +}"#,
 +            expect![[r#"
 +                fn foo(foo: u32, bar: u32)
 +                       ^^^^^^^^  --------
 +            "#]],
 +        );
 +        // check with surrounding space
 +        check(
 +            r#"
 +fn foo(foo: u32, bar: u32) {}
 +fn main() {
 +    foo( $0
 +}"#,
 +            expect![[r#"
 +                fn foo(foo: u32, bar: u32)
 +                       ^^^^^^^^  --------
 +            "#]],
 +        )
 +    }
 +
 +    #[test]
 +    fn test_multiline_argument() {
 +        check(
 +            r#"
 +fn callee(a: u8, b: u8) {}
 +fn main() {
 +    callee(match 0 {
 +        0 => 1,$0
 +    })
 +}"#,
 +            expect![[r#""#]],
 +        );
 +        check(
 +            r#"
 +fn callee(a: u8, b: u8) {}
 +fn main() {
 +    callee(match 0 {
 +        0 => 1,
 +    },$0)
 +}"#,
 +            expect![[r#"
 +                fn callee(a: u8, b: u8)
 +                          -----  ^^^^^
 +            "#]],
 +        );
 +        check(
 +            r#"
 +fn callee(a: u8, b: u8) {}
 +fn main() {
 +    callee($0match 0 {
 +        0 => 1,
 +    })
 +}"#,
 +            expect![[r#"
 +                fn callee(a: u8, b: u8)
 +                          ^^^^^  -----
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn test_generics_simple() {
 +        check(
 +            r#"
 +/// Option docs.
 +enum Option<T> {
 +    Some(T),
 +    None,
 +}
 +
 +fn f() {
 +    let opt: Option<$0
 +}
 +        "#,
 +            expect![[r#"
 +                Option docs.
 +                ------
 +                enum Option<T>
 +                            ^
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn test_generics_on_variant() {
 +        check(
 +            r#"
 +/// Option docs.
 +enum Option<T> {
 +    /// Some docs.
 +    Some(T),
 +    /// None docs.
 +    None,
 +}
 +
 +use Option::*;
 +
 +fn f() {
 +    None::<$0
 +}
 +        "#,
 +            expect![[r#"
 +                None docs.
 +                ------
 +                enum Option<T>
 +                            ^
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn test_lots_of_generics() {
 +        check(
 +            r#"
 +trait Tr<T> {}
 +
 +struct S<T>(T);
 +
 +impl<T> S<T> {
 +    fn f<G, H>(g: G, h: impl Tr<G>) where G: Tr<()> {}
 +}
 +
 +fn f() {
 +    S::<u8>::f::<(), $0
 +}
 +        "#,
 +            expect![[r#"
 +                fn f<G: Tr<()>, H>
 +                     ---------  ^
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn test_generics_in_trait_ufcs() {
 +        check(
 +            r#"
 +trait Tr {
 +    fn f<T: Tr, U>() {}
 +}
 +
 +struct S;
 +
 +impl Tr for S {}
 +
 +fn f() {
 +    <S as Tr>::f::<$0
 +}
 +        "#,
 +            expect![[r#"
 +                fn f<T: Tr, U>
 +                     ^^^^^  -
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn test_generics_in_method_call() {
 +        check(
 +            r#"
 +struct S;
 +
 +impl S {
 +    fn f<T>(&self) {}
 +}
 +
 +fn f() {
 +    S.f::<$0
 +}
 +        "#,
 +            expect![[r#"
 +                fn f<T>
 +                     ^
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn test_generic_param_in_method_call() {
 +        check(
 +            r#"
 +struct Foo;
 +impl Foo {
 +    fn test<V>(&mut self, val: V) {}
 +}
 +fn sup() {
 +    Foo.test($0)
 +}
 +"#,
 +            expect![[r#"
 +                fn test(&mut self, val: V)
 +                                   ^^^^^^
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn test_generic_kinds() {
 +        check(
 +            r#"
 +fn callee<'a, const A: u8, T, const C: u8>() {}
 +
 +fn f() {
 +    callee::<'static, $0
 +}
 +        "#,
 +            expect![[r#"
 +                fn callee<'a, const A: u8, T, const C: u8>
 +                          --  ^^^^^^^^^^^  -  -----------
 +            "#]],
 +        );
 +        check(
 +            r#"
 +fn callee<'a, const A: u8, T, const C: u8>() {}
 +
 +fn f() {
 +    callee::<NON_LIFETIME$0
 +}
 +        "#,
 +            expect![[r#"
 +                fn callee<'a, const A: u8, T, const C: u8>
 +                          --  ^^^^^^^^^^^  -  -----------
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn test_trait_assoc_types() {
 +        check(
 +            r#"
 +trait Trait<'a, T> {
 +    type Assoc;
 +}
 +fn f() -> impl Trait<(), $0
 +            "#,
 +            expect![[r#"
 +                trait Trait<'a, T, Assoc = …>
 +                            --  -  ^^^^^^^^^
 +            "#]],
 +        );
 +        check(
 +            r#"
 +trait Iterator {
 +    type Item;
 +}
 +fn f() -> impl Iterator<$0
 +            "#,
 +            expect![[r#"
 +                trait Iterator<Item = …>
 +                               ^^^^^^^^
 +            "#]],
 +        );
 +        check(
 +            r#"
 +trait Iterator {
 +    type Item;
 +}
 +fn f() -> impl Iterator<Item = $0
 +            "#,
 +            expect![[r#"
 +                trait Iterator<Item = …>
 +                               ^^^^^^^^
 +            "#]],
 +        );
 +        check(
 +            r#"
 +trait Tr {
 +    type A;
 +    type B;
 +}
 +fn f() -> impl Tr<$0
 +            "#,
 +            expect![[r#"
 +                trait Tr<A = …, B = …>
 +                         ^^^^^  -----
 +            "#]],
 +        );
 +        check(
 +            r#"
 +trait Tr {
 +    type A;
 +    type B;
 +}
 +fn f() -> impl Tr<B$0
 +            "#,
 +            expect![[r#"
 +                trait Tr<A = …, B = …>
 +                         ^^^^^  -----
 +            "#]],
 +        );
 +        check(
 +            r#"
 +trait Tr {
 +    type A;
 +    type B;
 +}
 +fn f() -> impl Tr<B = $0
 +            "#,
 +            expect![[r#"
 +                trait Tr<B = …, A = …>
 +                         ^^^^^  -----
 +            "#]],
 +        );
 +        check(
 +            r#"
 +trait Tr {
 +    type A;
 +    type B;
 +}
 +fn f() -> impl Tr<B = (), $0
 +            "#,
 +            expect![[r#"
 +                trait Tr<B = …, A = …>
 +                         -----  ^^^^^
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn test_supertrait_assoc() {
 +        check(
 +            r#"
 +trait Super {
 +    type SuperTy;
 +}
 +trait Sub: Super + Super {
 +    type SubTy;
 +}
 +fn f() -> impl Sub<$0
 +            "#,
 +            expect![[r#"
 +                trait Sub<SubTy = …, SuperTy = …>
 +                          ^^^^^^^^^  -----------
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn no_assoc_types_outside_type_bounds() {
 +        check(
 +            r#"
 +trait Tr<T> {
 +    type Assoc;
 +}
 +
 +impl Tr<$0
 +        "#,
 +            expect![[r#"
 +            trait Tr<T>
 +                     ^
 +        "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn impl_trait() {
 +        // FIXME: Substitute type vars in impl trait (`U` -> `i8`)
 +        check(
 +            r#"
 +trait Trait<T> {}
 +struct Wrap<T>(T);
 +fn foo<U>(x: Wrap<impl Trait<U>>) {}
 +fn f() {
 +    foo::<i8>($0)
 +}
 +"#,
 +            expect![[r#"
 +                fn foo(x: Wrap<impl Trait<U>>)
 +                       ^^^^^^^^^^^^^^^^^^^^^^
 +            "#]],
 +        );
 +    }
 +
 +    #[test]
 +    fn fully_qualified_syntax() {
 +        check(
 +            r#"
 +fn f() {
 +    trait A { fn foo(&self, other: Self); }
 +    A::foo(&self$0, other);
 +}
 +"#,
 +            expect![[r#"
 +                fn foo(self: &Self, other: Self)
 +                       ^^^^^^^^^^^  -----------
 +            "#]],
 +        );
 +    }
++
++    #[test]
++    fn help_for_generic_call() {
++        check(
++            r#"
++fn f<F: FnOnce(u8, u16) -> i32>(f: F) {
++    f($0)
++}
++"#,
++            expect![[r#"
++                (u8, u16) -> i32
++                 ^^  ---
++            "#]],
++        );
++    }
 +}
index 5ff347b9bd7227dd886e73bf2b7bec69ce012a30,0000000000000000000000000000000000000000..c74ddabb17770392dbb576059d61231285d7f3bb
mode 100644,000000..100644
--- /dev/null
@@@ -1,329 -1,0 +1,329 @@@
-                 repository: Some(lsif::Repository {
-                     url: pi.repo,
 +//! LSIF (language server index format) generator
 +
 +use std::collections::HashMap;
 +use std::env;
 +use std::time::Instant;
 +
 +use ide::{
 +    Analysis, FileId, FileRange, MonikerKind, PackageInformation, RootDatabase, StaticIndex,
 +    StaticIndexedFile, TokenId, TokenStaticData,
 +};
 +use ide_db::LineIndexDatabase;
 +
 +use ide_db::base_db::salsa::{self, ParallelDatabase};
 +use lsp_types::{self, lsif};
 +use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace};
 +use vfs::{AbsPathBuf, Vfs};
 +
 +use crate::cli::{
 +    flags,
 +    load_cargo::{load_workspace, LoadCargoConfig},
 +    Result,
 +};
 +use crate::line_index::{LineEndings, LineIndex, PositionEncoding};
 +use crate::to_proto;
 +use crate::version::version;
 +
 +/// Need to wrap Snapshot to provide `Clone` impl for `map_with`
 +struct Snap<DB>(DB);
 +impl<DB: ParallelDatabase> Clone for Snap<salsa::Snapshot<DB>> {
 +    fn clone(&self) -> Snap<salsa::Snapshot<DB>> {
 +        Snap(self.0.snapshot())
 +    }
 +}
 +
 +struct LsifManager<'a> {
 +    count: i32,
 +    token_map: HashMap<TokenId, Id>,
 +    range_map: HashMap<FileRange, Id>,
 +    file_map: HashMap<FileId, Id>,
 +    package_map: HashMap<PackageInformation, Id>,
 +    analysis: &'a Analysis,
 +    db: &'a RootDatabase,
 +    vfs: &'a Vfs,
 +}
 +
 +#[derive(Clone, Copy)]
 +struct Id(i32);
 +
 +impl From<Id> for lsp_types::NumberOrString {
 +    fn from(Id(x): Id) -> Self {
 +        lsp_types::NumberOrString::Number(x)
 +    }
 +}
 +
 +impl LsifManager<'_> {
 +    fn new<'a>(analysis: &'a Analysis, db: &'a RootDatabase, vfs: &'a Vfs) -> LsifManager<'a> {
 +        LsifManager {
 +            count: 0,
 +            token_map: HashMap::default(),
 +            range_map: HashMap::default(),
 +            file_map: HashMap::default(),
 +            package_map: HashMap::default(),
 +            analysis,
 +            db,
 +            vfs,
 +        }
 +    }
 +
 +    fn add(&mut self, data: lsif::Element) -> Id {
 +        let id = Id(self.count);
 +        self.emit(&serde_json::to_string(&lsif::Entry { id: id.into(), data }).unwrap());
 +        self.count += 1;
 +        id
 +    }
 +
 +    fn add_vertex(&mut self, vertex: lsif::Vertex) -> Id {
 +        self.add(lsif::Element::Vertex(vertex))
 +    }
 +
 +    fn add_edge(&mut self, edge: lsif::Edge) -> Id {
 +        self.add(lsif::Element::Edge(edge))
 +    }
 +
 +    // FIXME: support file in addition to stdout here
 +    fn emit(&self, data: &str) {
 +        println!("{}", data);
 +    }
 +
 +    fn get_token_id(&mut self, id: TokenId) -> Id {
 +        if let Some(x) = self.token_map.get(&id) {
 +            return *x;
 +        }
 +        let result_set_id = self.add_vertex(lsif::Vertex::ResultSet(lsif::ResultSet { key: None }));
 +        self.token_map.insert(id, result_set_id);
 +        result_set_id
 +    }
 +
 +    fn get_package_id(&mut self, package_information: PackageInformation) -> Id {
 +        if let Some(x) = self.package_map.get(&package_information) {
 +            return *x;
 +        }
 +        let pi = package_information.clone();
 +        let result_set_id =
 +            self.add_vertex(lsif::Vertex::PackageInformation(lsif::PackageInformation {
 +                name: pi.name,
 +                manager: "cargo".to_string(),
 +                uri: None,
 +                content: None,
-                 version: Some(pi.version),
++                repository: pi.repo.map(|url| lsif::Repository {
++                    url,
 +                    r#type: "git".to_string(),
 +                    commit_id: None,
 +                }),
++                version: pi.version,
 +            }));
 +        self.package_map.insert(package_information, result_set_id);
 +        result_set_id
 +    }
 +
 +    fn get_range_id(&mut self, id: FileRange) -> Id {
 +        if let Some(x) = self.range_map.get(&id) {
 +            return *x;
 +        }
 +        let file_id = id.file_id;
 +        let doc_id = self.get_file_id(file_id);
 +        let line_index = self.db.line_index(file_id);
 +        let line_index = LineIndex {
 +            index: line_index,
 +            encoding: PositionEncoding::Utf16,
 +            endings: LineEndings::Unix,
 +        };
 +        let range_id = self.add_vertex(lsif::Vertex::Range {
 +            range: to_proto::range(&line_index, id.range),
 +            tag: None,
 +        });
 +        self.add_edge(lsif::Edge::Contains(lsif::EdgeDataMultiIn {
 +            in_vs: vec![range_id.into()],
 +            out_v: doc_id.into(),
 +        }));
 +        range_id
 +    }
 +
 +    fn get_file_id(&mut self, id: FileId) -> Id {
 +        if let Some(x) = self.file_map.get(&id) {
 +            return *x;
 +        }
 +        let path = self.vfs.file_path(id);
 +        let path = path.as_path().unwrap();
 +        let doc_id = self.add_vertex(lsif::Vertex::Document(lsif::Document {
 +            language_id: "rust".to_string(),
 +            uri: lsp_types::Url::from_file_path(path).unwrap(),
 +        }));
 +        self.file_map.insert(id, doc_id);
 +        doc_id
 +    }
 +
 +    fn add_token(&mut self, id: TokenId, token: TokenStaticData) {
 +        let result_set_id = self.get_token_id(id);
 +        if let Some(hover) = token.hover {
 +            let hover_id = self.add_vertex(lsif::Vertex::HoverResult {
 +                result: lsp_types::Hover {
 +                    contents: lsp_types::HoverContents::Markup(to_proto::markup_content(
 +                        hover.markup,
 +                        ide::HoverDocFormat::Markdown,
 +                    )),
 +                    range: None,
 +                },
 +            });
 +            self.add_edge(lsif::Edge::Hover(lsif::EdgeData {
 +                in_v: hover_id.into(),
 +                out_v: result_set_id.into(),
 +            }));
 +        }
 +        if let Some(moniker) = token.moniker {
 +            let package_id = self.get_package_id(moniker.package_information);
 +            let moniker_id = self.add_vertex(lsif::Vertex::Moniker(lsp_types::Moniker {
 +                scheme: "rust-analyzer".to_string(),
 +                identifier: moniker.identifier.to_string(),
 +                unique: lsp_types::UniquenessLevel::Scheme,
 +                kind: Some(match moniker.kind {
 +                    MonikerKind::Import => lsp_types::MonikerKind::Import,
 +                    MonikerKind::Export => lsp_types::MonikerKind::Export,
 +                }),
 +            }));
 +            self.add_edge(lsif::Edge::PackageInformation(lsif::EdgeData {
 +                in_v: package_id.into(),
 +                out_v: moniker_id.into(),
 +            }));
 +            self.add_edge(lsif::Edge::Moniker(lsif::EdgeData {
 +                in_v: moniker_id.into(),
 +                out_v: result_set_id.into(),
 +            }));
 +        }
 +        if let Some(def) = token.definition {
 +            let result_id = self.add_vertex(lsif::Vertex::DefinitionResult);
 +            let def_vertex = self.get_range_id(def);
 +            self.add_edge(lsif::Edge::Item(lsif::Item {
 +                document: (*self.file_map.get(&def.file_id).unwrap()).into(),
 +                property: None,
 +                edge_data: lsif::EdgeDataMultiIn {
 +                    in_vs: vec![def_vertex.into()],
 +                    out_v: result_id.into(),
 +                },
 +            }));
 +            self.add_edge(lsif::Edge::Definition(lsif::EdgeData {
 +                in_v: result_id.into(),
 +                out_v: result_set_id.into(),
 +            }));
 +        }
 +        if !token.references.is_empty() {
 +            let result_id = self.add_vertex(lsif::Vertex::ReferenceResult);
 +            self.add_edge(lsif::Edge::References(lsif::EdgeData {
 +                in_v: result_id.into(),
 +                out_v: result_set_id.into(),
 +            }));
 +            let mut edges = token.references.iter().fold(
 +                HashMap::<_, Vec<lsp_types::NumberOrString>>::new(),
 +                |mut edges, x| {
 +                    let entry =
 +                        edges.entry((x.range.file_id, x.is_definition)).or_insert_with(Vec::new);
 +                    entry.push((*self.range_map.get(&x.range).unwrap()).into());
 +                    edges
 +                },
 +            );
 +            for x in token.references {
 +                if let Some(vertices) = edges.remove(&(x.range.file_id, x.is_definition)) {
 +                    self.add_edge(lsif::Edge::Item(lsif::Item {
 +                        document: (*self.file_map.get(&x.range.file_id).unwrap()).into(),
 +                        property: Some(if x.is_definition {
 +                            lsif::ItemKind::Definitions
 +                        } else {
 +                            lsif::ItemKind::References
 +                        }),
 +                        edge_data: lsif::EdgeDataMultiIn {
 +                            in_vs: vertices,
 +                            out_v: result_id.into(),
 +                        },
 +                    }));
 +                }
 +            }
 +        }
 +    }
 +
 +    fn add_file(&mut self, file: StaticIndexedFile) {
 +        let StaticIndexedFile { file_id, tokens, folds, .. } = file;
 +        let doc_id = self.get_file_id(file_id);
 +        let text = self.analysis.file_text(file_id).unwrap();
 +        let line_index = self.db.line_index(file_id);
 +        let line_index = LineIndex {
 +            index: line_index,
 +            encoding: PositionEncoding::Utf16,
 +            endings: LineEndings::Unix,
 +        };
 +        let result = folds
 +            .into_iter()
 +            .map(|it| to_proto::folding_range(&*text, &line_index, false, it))
 +            .collect();
 +        let folding_id = self.add_vertex(lsif::Vertex::FoldingRangeResult { result });
 +        self.add_edge(lsif::Edge::FoldingRange(lsif::EdgeData {
 +            in_v: folding_id.into(),
 +            out_v: doc_id.into(),
 +        }));
 +        let tokens_id = tokens
 +            .into_iter()
 +            .map(|(range, id)| {
 +                let range_id = self.add_vertex(lsif::Vertex::Range {
 +                    range: to_proto::range(&line_index, range),
 +                    tag: None,
 +                });
 +                self.range_map.insert(FileRange { file_id, range }, range_id);
 +                let result_set_id = self.get_token_id(id);
 +                self.add_edge(lsif::Edge::Next(lsif::EdgeData {
 +                    in_v: result_set_id.into(),
 +                    out_v: range_id.into(),
 +                }));
 +                range_id.into()
 +            })
 +            .collect();
 +        self.add_edge(lsif::Edge::Contains(lsif::EdgeDataMultiIn {
 +            in_vs: tokens_id,
 +            out_v: doc_id.into(),
 +        }));
 +    }
 +}
 +
 +impl flags::Lsif {
 +    pub fn run(self) -> Result<()> {
 +        eprintln!("Generating LSIF started...");
 +        let now = Instant::now();
 +        let cargo_config = CargoConfig::default();
 +        let no_progress = &|_| ();
 +        let load_cargo_config = LoadCargoConfig {
 +            load_out_dirs_from_check: true,
 +            with_proc_macro: true,
 +            prefill_caches: false,
 +        };
 +        let path = AbsPathBuf::assert(env::current_dir()?.join(&self.path));
 +        let manifest = ProjectManifest::discover_single(&path)?;
 +
 +        let workspace = ProjectWorkspace::load(manifest, &cargo_config, no_progress)?;
 +
 +        let (host, vfs, _proc_macro) =
 +            load_workspace(workspace, &cargo_config.extra_env, &load_cargo_config)?;
 +        let db = host.raw_database();
 +        let analysis = host.analysis();
 +
 +        let si = StaticIndex::compute(&analysis);
 +
 +        let mut lsif = LsifManager::new(&analysis, db, &vfs);
 +        lsif.add_vertex(lsif::Vertex::MetaData(lsif::MetaData {
 +            version: String::from("0.5.0"),
 +            project_root: lsp_types::Url::from_file_path(path).unwrap(),
 +            position_encoding: lsif::Encoding::Utf16,
 +            tool_info: Some(lsp_types::lsif::ToolInfo {
 +                name: "rust-analyzer".to_string(),
 +                args: vec![],
 +                version: Some(version().to_string()),
 +            }),
 +        }));
 +        for file in si.files {
 +            lsif.add_file(file);
 +        }
 +        for (id, token) in si.tokens.iter() {
 +            lsif.add_token(id, token);
 +        }
 +        eprintln!("Generating LSIF finished in {:?}", now.elapsed());
 +        Ok(())
 +    }
 +}
index 16298862b50f39fbaee254c76ccb8190ad4ec017,0000000000000000000000000000000000000000..ca7ba896b67cfc8bbbce7dca755bc520e3998beb
mode 100644,000000..100644
--- /dev/null
@@@ -1,418 -1,0 +1,456 @@@
-             version,
 +//! SCIP generator
 +
 +use std::{
 +    collections::{HashMap, HashSet},
 +    time::Instant,
 +};
 +
 +use crate::line_index::{LineEndings, LineIndex, PositionEncoding};
 +use hir::Name;
 +use ide::{
 +    LineCol, MonikerDescriptorKind, StaticIndex, StaticIndexedFile, TextRange, TokenId,
 +    TokenStaticData,
 +};
 +use ide_db::LineIndexDatabase;
 +use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace};
 +use scip::types as scip_types;
 +use std::env;
 +
 +use crate::cli::{
 +    flags,
 +    load_cargo::{load_workspace, LoadCargoConfig},
 +    Result,
 +};
 +
 +impl flags::Scip {
 +    pub fn run(self) -> Result<()> {
 +        eprintln!("Generating SCIP start...");
 +        let now = Instant::now();
 +        let cargo_config = CargoConfig::default();
 +
 +        let no_progress = &|s| (eprintln!("rust-analyzer: Loading {}", s));
 +        let load_cargo_config = LoadCargoConfig {
 +            load_out_dirs_from_check: true,
 +            with_proc_macro: true,
 +            prefill_caches: true,
 +        };
 +        let path = vfs::AbsPathBuf::assert(env::current_dir()?.join(&self.path));
 +        let rootpath = path.normalize();
 +        let manifest = ProjectManifest::discover_single(&path)?;
 +
 +        let workspace = ProjectWorkspace::load(manifest, &cargo_config, no_progress)?;
 +
 +        let (host, vfs, _) =
 +            load_workspace(workspace, &cargo_config.extra_env, &load_cargo_config)?;
 +        let db = host.raw_database();
 +        let analysis = host.analysis();
 +
 +        let si = StaticIndex::compute(&analysis);
 +
 +        let mut index = scip_types::Index {
 +            metadata: Some(scip_types::Metadata {
 +                version: scip_types::ProtocolVersion::UnspecifiedProtocolVersion.into(),
 +                tool_info: Some(scip_types::ToolInfo {
 +                    name: "rust-analyzer".to_owned(),
 +                    version: "0.1".to_owned(),
 +                    arguments: vec![],
 +                    ..Default::default()
 +                })
 +                .into(),
 +                project_root: format!(
 +                    "file://{}",
 +                    path.normalize()
 +                        .as_os_str()
 +                        .to_str()
 +                        .ok_or(anyhow::anyhow!("Unable to normalize project_root path"))?
 +                        .to_string()
 +                ),
 +                text_document_encoding: scip_types::TextEncoding::UTF8.into(),
 +                ..Default::default()
 +            })
 +            .into(),
 +            ..Default::default()
 +        };
 +
 +        let mut symbols_emitted: HashSet<TokenId> = HashSet::default();
 +        let mut tokens_to_symbol: HashMap<TokenId, String> = HashMap::new();
 +
 +        for StaticIndexedFile { file_id, tokens, .. } in si.files {
 +            let mut local_count = 0;
 +            let mut new_local_symbol = || {
 +                let new_symbol = scip::types::Symbol::new_local(local_count);
 +                local_count += 1;
 +
 +                new_symbol
 +            };
 +
 +            let relative_path = match get_relative_filepath(&vfs, &rootpath, file_id) {
 +                Some(relative_path) => relative_path,
 +                None => continue,
 +            };
 +
 +            let line_index = LineIndex {
 +                index: db.line_index(file_id),
 +                encoding: PositionEncoding::Utf8,
 +                endings: LineEndings::Unix,
 +            };
 +
 +            let mut doc = scip_types::Document {
 +                relative_path,
 +                language: "rust".to_string(),
 +                ..Default::default()
 +            };
 +
 +            tokens.into_iter().for_each(|(range, id)| {
 +                let token = si.tokens.get(id).unwrap();
 +
 +                let mut occurrence = scip_types::Occurrence::default();
 +                occurrence.range = text_range_to_scip_range(&line_index, range);
 +                occurrence.symbol = tokens_to_symbol
 +                    .entry(id)
 +                    .or_insert_with(|| {
 +                        let symbol = token_to_symbol(&token).unwrap_or_else(&mut new_local_symbol);
 +                        scip::symbol::format_symbol(symbol)
 +                    })
 +                    .clone();
 +
 +                if let Some(def) = token.definition {
 +                    if def.range == range {
 +                        occurrence.symbol_roles |= scip_types::SymbolRole::Definition as i32;
 +                    }
 +
 +                    if symbols_emitted.insert(id) {
 +                        let mut symbol_info = scip_types::SymbolInformation::default();
 +                        symbol_info.symbol = occurrence.symbol.clone();
 +                        if let Some(hover) = &token.hover {
 +                            if !hover.markup.as_str().is_empty() {
 +                                symbol_info.documentation = vec![hover.markup.as_str().to_string()];
 +                            }
 +                        }
 +
 +                        doc.symbols.push(symbol_info)
 +                    }
 +                }
 +
 +                doc.occurrences.push(occurrence);
 +            });
 +
 +            if doc.occurrences.is_empty() {
 +                continue;
 +            }
 +
 +            index.documents.push(doc);
 +        }
 +
 +        scip::write_message_to_file("index.scip", index)
 +            .map_err(|err| anyhow::anyhow!("Failed to write scip to file: {}", err))?;
 +
 +        eprintln!("Generating SCIP finished {:?}", now.elapsed());
 +        Ok(())
 +    }
 +}
 +
 +fn get_relative_filepath(
 +    vfs: &vfs::Vfs,
 +    rootpath: &vfs::AbsPathBuf,
 +    file_id: ide::FileId,
 +) -> Option<String> {
 +    Some(vfs.file_path(file_id).as_path()?.strip_prefix(&rootpath)?.as_ref().to_str()?.to_string())
 +}
 +
 +// SCIP Ranges have a (very large) optimization that ranges if they are on the same line
 +// only encode as a vector of [start_line, start_col, end_col].
 +//
 +// This transforms a line index into the optimized SCIP Range.
 +fn text_range_to_scip_range(line_index: &LineIndex, range: TextRange) -> Vec<i32> {
 +    let LineCol { line: start_line, col: start_col } = line_index.index.line_col(range.start());
 +    let LineCol { line: end_line, col: end_col } = line_index.index.line_col(range.end());
 +
 +    if start_line == end_line {
 +        vec![start_line as i32, start_col as i32, end_col as i32]
 +    } else {
 +        vec![start_line as i32, start_col as i32, end_line as i32, end_col as i32]
 +    }
 +}
 +
 +fn new_descriptor_str(
 +    name: &str,
 +    suffix: scip_types::descriptor::Suffix,
 +) -> scip_types::Descriptor {
 +    scip_types::Descriptor {
 +        name: name.to_string(),
 +        disambiguator: "".to_string(),
 +        suffix: suffix.into(),
 +        ..Default::default()
 +    }
 +}
 +
 +fn new_descriptor(name: Name, suffix: scip_types::descriptor::Suffix) -> scip_types::Descriptor {
 +    let mut name = name.to_string();
 +    if name.contains("'") {
 +        name = format!("`{}`", name);
 +    }
 +
 +    new_descriptor_str(name.as_str(), suffix)
 +}
 +
 +/// Loosely based on `def_to_moniker`
 +///
 +/// Only returns a Symbol when it's a non-local symbol.
 +///     So if the visibility isn't outside of a document, then it will return None
 +fn token_to_symbol(token: &TokenStaticData) -> Option<scip_types::Symbol> {
 +    use scip_types::descriptor::Suffix::*;
 +
 +    let moniker = token.moniker.as_ref()?;
 +
 +    let package_name = moniker.package_information.name.clone();
 +    let version = moniker.package_information.version.clone();
 +    let descriptors = moniker
 +        .identifier
 +        .description
 +        .iter()
 +        .map(|desc| {
 +            new_descriptor(
 +                desc.name.clone(),
 +                match desc.desc {
 +                    MonikerDescriptorKind::Namespace => Namespace,
 +                    MonikerDescriptorKind::Type => Type,
 +                    MonikerDescriptorKind::Term => Term,
 +                    MonikerDescriptorKind::Method => Method,
 +                    MonikerDescriptorKind::TypeParameter => TypeParameter,
 +                    MonikerDescriptorKind::Parameter => Parameter,
 +                    MonikerDescriptorKind::Macro => Macro,
 +                    MonikerDescriptorKind::Meta => Meta,
 +                },
 +            )
 +        })
 +        .collect();
 +
 +    Some(scip_types::Symbol {
 +        scheme: "rust-analyzer".into(),
 +        package: Some(scip_types::Package {
 +            manager: "cargo".to_string(),
 +            name: package_name,
++            version: version.unwrap_or_else(|| ".".to_string()),
 +            ..Default::default()
 +        })
 +        .into(),
 +        descriptors,
 +        ..Default::default()
 +    })
 +}
 +
 +#[cfg(test)]
 +mod test {
 +    use super::*;
 +    use ide::{AnalysisHost, FilePosition, StaticIndex, TextSize};
 +    use ide_db::base_db::fixture::ChangeFixture;
 +    use scip::symbol::format_symbol;
 +
 +    fn position(ra_fixture: &str) -> (AnalysisHost, FilePosition) {
 +        let mut host = AnalysisHost::default();
 +        let change_fixture = ChangeFixture::parse(ra_fixture);
 +        host.raw_database_mut().apply_change(change_fixture.change);
 +        let (file_id, range_or_offset) =
 +            change_fixture.file_position.expect("expected a marker ($0)");
 +        let offset = range_or_offset.expect_offset();
 +        (host, FilePosition { file_id, offset })
 +    }
 +
 +    /// If expected == "", then assert that there are no symbols (this is basically local symbol)
 +    #[track_caller]
 +    fn check_symbol(ra_fixture: &str, expected: &str) {
 +        let (host, position) = position(ra_fixture);
 +
 +        let analysis = host.analysis();
 +        let si = StaticIndex::compute(&analysis);
 +
 +        let FilePosition { file_id, offset } = position;
 +
 +        let mut found_symbol = None;
 +        for file in &si.files {
 +            if file.file_id != file_id {
 +                continue;
 +            }
 +            for &(range, id) in &file.tokens {
 +                if range.contains(offset - TextSize::from(1)) {
 +                    let token = si.tokens.get(id).unwrap();
 +                    found_symbol = token_to_symbol(token);
 +                    break;
 +                }
 +            }
 +        }
 +
 +        if expected == "" {
 +            assert!(found_symbol.is_none(), "must have no symbols {:?}", found_symbol);
 +            return;
 +        }
 +
 +        assert!(found_symbol.is_some(), "must have one symbol {:?}", found_symbol);
 +        let res = found_symbol.unwrap();
 +        let formatted = format_symbol(res);
 +        assert_eq!(formatted, expected);
 +    }
 +
 +    #[test]
 +    fn basic() {
 +        check_symbol(
 +            r#"
 +//- /lib.rs crate:main deps:foo
 +use foo::example_mod::func;
 +fn main() {
 +    func$0();
 +}
 +//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
 +pub mod example_mod {
 +    pub fn func() {}
 +}
 +"#,
 +            "rust-analyzer cargo foo 0.1.0 example_mod/func().",
 +        );
 +    }
 +
 +    #[test]
 +    fn symbol_for_trait() {
 +        check_symbol(
 +            r#"
 +//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
 +pub mod module {
 +    pub trait MyTrait {
 +        pub fn func$0() {}
 +    }
 +}
 +"#,
 +            "rust-analyzer cargo foo 0.1.0 module/MyTrait#func().",
 +        );
 +    }
 +
 +    #[test]
 +    fn symbol_for_trait_constant() {
 +        check_symbol(
 +            r#"
 +    //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
 +    pub mod module {
 +        pub trait MyTrait {
 +            const MY_CONST$0: u8;
 +        }
 +    }
 +    "#,
 +            "rust-analyzer cargo foo 0.1.0 module/MyTrait#MY_CONST.",
 +        );
 +    }
 +
 +    #[test]
 +    fn symbol_for_trait_type() {
 +        check_symbol(
 +            r#"
 +    //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
 +    pub mod module {
 +        pub trait MyTrait {
 +            type MyType$0;
 +        }
 +    }
 +    "#,
 +            // "foo::module::MyTrait::MyType",
 +            "rust-analyzer cargo foo 0.1.0 module/MyTrait#[MyType]",
 +        );
 +    }
 +
 +    #[test]
 +    fn symbol_for_trait_impl_function() {
 +        check_symbol(
 +            r#"
 +    //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
 +    pub mod module {
 +        pub trait MyTrait {
 +            pub fn func() {}
 +        }
 +
 +        struct MyStruct {}
 +
 +        impl MyTrait for MyStruct {
 +            pub fn func$0() {}
 +        }
 +    }
 +    "#,
 +            // "foo::module::MyStruct::MyTrait::func",
 +            "rust-analyzer cargo foo 0.1.0 module/MyStruct#MyTrait#func().",
 +        );
 +    }
 +
 +    #[test]
 +    fn symbol_for_field() {
 +        check_symbol(
 +            r#"
 +    //- /lib.rs crate:main deps:foo
 +    use foo::St;
 +    fn main() {
 +        let x = St { a$0: 2 };
 +    }
 +    //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
 +    pub struct St {
 +        pub a: i32,
 +    }
 +    "#,
 +            "rust-analyzer cargo foo 0.1.0 St#a.",
 +        );
 +    }
 +
 +    #[test]
 +    fn local_symbol_for_local() {
 +        check_symbol(
 +            r#"
 +    //- /lib.rs crate:main deps:foo
 +    use foo::module::func;
 +    fn main() {
 +        func();
 +    }
 +    //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
 +    pub mod module {
 +        pub fn func() {
 +            let x$0 = 2;
 +        }
 +    }
 +    "#,
 +            "",
 +        );
 +    }
++
++    #[test]
++    fn global_symbol_for_pub_struct() {
++        check_symbol(
++            r#"
++    //- /lib.rs crate:main
++    mod foo;
++
++    fn main() {
++        let _bar = foo::Bar { i: 0 };
++    }
++    //- /foo.rs
++    pub struct Bar$0 {
++        pub i: i32,
++    }
++    "#,
++            "rust-analyzer cargo main . foo/Bar#",
++        );
++    }
++
++    #[test]
++    fn global_symbol_for_pub_struct_reference() {
++        check_symbol(
++            r#"
++    //- /lib.rs crate:main
++    mod foo;
++
++    fn main() {
++        let _bar = foo::Bar$0 { i: 0 };
++    }
++    //- /foo.rs
++    pub struct Bar {
++        pub i: i32,
++    }
++    "#,
++            "rust-analyzer cargo main . foo/Bar#",
++        );
++    }
 +}
index 1ed8f2bb5f35a639e16d7a4b641677ce94ead90c,0000000000000000000000000000000000000000..4072ae585dbd913d036fbaad37a559db33af3117
mode 100644,000000..100644
--- /dev/null
@@@ -1,2193 -1,0 +1,2197 @@@
 +//! Config used by the language server.
 +//!
 +//! We currently get this config from `initialize` LSP request, which is not the
 +//! best way to do it, but was the simplest thing we could implement.
 +//!
 +//! Of particular interest is the `feature_flags` hash map: while other fields
 +//! configure the server itself, feature flags are passed into analysis, and
 +//! tweak things like automatic insertion of `()` in completions.
 +
 +use std::{fmt, iter, path::PathBuf};
 +
 +use flycheck::FlycheckConfig;
 +use ide::{
 +    AssistConfig, CallableSnippets, CompletionConfig, DiagnosticsConfig, ExprFillDefaultMode,
 +    HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayHintsConfig,
 +    JoinLinesConfig, Snippet, SnippetScope,
 +};
 +use ide_db::{
 +    imports::insert_use::{ImportGranularity, InsertUseConfig, PrefixKind},
 +    SnippetCap,
 +};
 +use itertools::Itertools;
 +use lsp_types::{ClientCapabilities, MarkupKind};
 +use project_model::{
 +    CargoConfig, CargoFeatures, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource,
 +    UnsetTestCrates,
 +};
 +use rustc_hash::{FxHashMap, FxHashSet};
 +use serde::{de::DeserializeOwned, Deserialize};
 +use vfs::AbsPathBuf;
 +
 +use crate::{
 +    caps::completion_item_edit_resolve,
 +    diagnostics::DiagnosticsMapConfig,
 +    line_index::PositionEncoding,
 +    lsp_ext::{self, supports_utf8, WorkspaceSymbolSearchKind, WorkspaceSymbolSearchScope},
 +};
 +
 +mod patch_old_style;
 +
 +// Conventions for configuration keys to preserve maximal extendability without breakage:
 +//  - Toggles (be it binary true/false or with more options in-between) should almost always suffix as `_enable`
 +//    This has the benefit of namespaces being extensible, and if the suffix doesn't fit later it can be changed without breakage.
 +//  - In general be wary of using the namespace of something verbatim, it prevents us from adding subkeys in the future
 +//  - Don't use abbreviations unless really necessary
 +//  - foo_command = overrides the subcommand, foo_overrideCommand allows full overwriting, extra args only applies for foo_command
 +
 +// Defines the server-side configuration of the rust-analyzer. We generate
 +// *parts* of VS Code's `package.json` config from this. Run `cargo test` to
 +// re-generate that file.
 +//
 +// However, editor specific config, which the server doesn't know about, should
 +// be specified directly in `package.json`.
 +//
 +// To deprecate an option by replacing it with another name use `new_name | `old_name` so that we keep
 +// parsing the old name.
 +config_data! {
 +    struct ConfigData {
++        /// Whether to insert #[must_use] when generating `as_` methods
++        /// for enum variants.
++        assist_emitMustUse: bool               = "false",
 +        /// Placeholder expression to use for missing expressions in assists.
 +        assist_expressionFillDefault: ExprFillDefaultDef              = "\"todo\"",
 +
 +        /// Warm up caches on project load.
 +        cachePriming_enable: bool = "true",
 +        /// How many worker threads to handle priming caches. The default `0` means to pick automatically.
 +        cachePriming_numThreads: ParallelCachePrimingNumThreads = "0",
 +
 +        /// Automatically refresh project info via `cargo metadata` on
 +        /// `Cargo.toml` or `.cargo/config.toml` changes.
 +        cargo_autoreload: bool           = "true",
 +        /// Run build scripts (`build.rs`) for more precise code analysis.
 +        cargo_buildScripts_enable: bool  = "true",
 +        /// Specifies the working directory for running build scripts.
 +        /// - "workspace": run build scripts for a workspace in the workspace's root directory.
 +        ///   This is incompatible with `#rust-analyzer.cargo.buildScripts.invocationStrategy#` set to `once`.
 +        /// - "root": run build scripts in the project's root directory.
 +        /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
 +        /// is set.
 +        cargo_buildScripts_invocationLocation: InvocationLocation = "\"workspace\"",
 +        /// Specifies the invocation strategy to use when running the build scripts command.
 +        /// If `per_workspace` is set, the command will be executed for each workspace.
 +        /// If `once` is set, the command will be executed once.
 +        /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
 +        /// is set.
 +        cargo_buildScripts_invocationStrategy: InvocationStrategy = "\"per_workspace\"",
 +        /// Override the command rust-analyzer uses to run build scripts and
 +        /// build procedural macros. The command is required to output json
 +        /// and should therefore include `--message-format=json` or a similar
 +        /// option.
 +        ///
 +        /// By default, a cargo invocation will be constructed for the configured
 +        /// targets and features, with the following base command line:
 +        ///
 +        /// ```bash
 +        /// cargo check --quiet --workspace --message-format=json --all-targets
 +        /// ```
 +        /// .
 +        cargo_buildScripts_overrideCommand: Option<Vec<String>> = "null",
 +        /// Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to
 +        /// avoid checking unnecessary things.
 +        cargo_buildScripts_useRustcWrapper: bool = "true",
 +        /// Extra environment variables that will be set when running cargo, rustc
 +        /// or other commands within the workspace. Useful for setting RUSTFLAGS.
 +        cargo_extraEnv: FxHashMap<String, String> = "{}",
 +        /// List of features to activate.
 +        ///
 +        /// Set this to `"all"` to pass `--all-features` to cargo.
 +        cargo_features: CargoFeaturesDef      = "[]",
 +        /// Whether to pass `--no-default-features` to cargo.
 +        cargo_noDefaultFeatures: bool    = "false",
 +        /// Relative path to the sysroot, or "discover" to try to automatically find it via
 +        /// "rustc --print sysroot".
 +        ///
 +        /// Unsetting this disables sysroot loading.
 +        ///
 +        /// This option does not take effect until rust-analyzer is restarted.
 +        cargo_sysroot: Option<String>    = "\"discover\"",
 +        /// Compilation target override (target triple).
 +        cargo_target: Option<String>     = "null",
 +        /// Unsets `#[cfg(test)]` for the specified crates.
 +        cargo_unsetTest: Vec<String>   = "[\"core\"]",
 +
 +        /// Check all targets and tests (`--all-targets`).
 +        checkOnSave_allTargets: bool                     = "true",
 +        /// Cargo command to use for `cargo check`.
 +        checkOnSave_command: String                      = "\"check\"",
 +        /// Run specified `cargo check` command for diagnostics on save.
 +        checkOnSave_enable: bool                         = "true",
 +        /// Extra arguments for `cargo check`.
 +        checkOnSave_extraArgs: Vec<String>               = "[]",
 +        /// Extra environment variables that will be set when running `cargo check`.
 +        /// Extends `#rust-analyzer.cargo.extraEnv#`.
 +        checkOnSave_extraEnv: FxHashMap<String, String> = "{}",
 +        /// List of features to activate. Defaults to
 +        /// `#rust-analyzer.cargo.features#`.
 +        ///
 +        /// Set to `"all"` to pass `--all-features` to Cargo.
 +        checkOnSave_features: Option<CargoFeaturesDef>      = "null",
 +        /// Specifies the working directory for running checks.
 +        /// - "workspace": run checks for workspaces in the corresponding workspaces' root directories.
 +        // FIXME: Ideally we would support this in some way
 +        ///   This falls back to "root" if `#rust-analyzer.cargo.checkOnSave.invocationStrategy#` is set to `once`.
 +        /// - "root": run checks in the project's root directory.
 +        /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
 +        /// is set.
 +        checkOnSave_invocationLocation: InvocationLocation = "\"workspace\"",
 +        /// Specifies the invocation strategy to use when running the checkOnSave command.
 +        /// If `per_workspace` is set, the command will be executed for each workspace.
 +        /// If `once` is set, the command will be executed once.
 +        /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
 +        /// is set.
 +        checkOnSave_invocationStrategy: InvocationStrategy = "\"per_workspace\"",
 +        /// Whether to pass `--no-default-features` to Cargo. Defaults to
 +        /// `#rust-analyzer.cargo.noDefaultFeatures#`.
 +        checkOnSave_noDefaultFeatures: Option<bool>      = "null",
 +        /// Override the command rust-analyzer uses instead of `cargo check` for
 +        /// diagnostics on save. The command is required to output json and
 +        /// should therefor include `--message-format=json` or a similar option.
 +        ///
 +        /// If you're changing this because you're using some tool wrapping
 +        /// Cargo, you might also want to change
 +        /// `#rust-analyzer.cargo.buildScripts.overrideCommand#`.
 +        ///
 +        /// If there are multiple linked projects, this command is invoked for
 +        /// each of them, with the working directory being the project root
 +        /// (i.e., the folder containing the `Cargo.toml`).
 +        ///
 +        /// An example command would be:
 +        ///
 +        /// ```bash
 +        /// cargo check --workspace --message-format=json --all-targets
 +        /// ```
 +        /// .
 +        checkOnSave_overrideCommand: Option<Vec<String>> = "null",
 +        /// Check for a specific target. Defaults to
 +        /// `#rust-analyzer.cargo.target#`.
 +        checkOnSave_target: Option<String>               = "null",
 +
 +        /// Toggles the additional completions that automatically add imports when completed.
 +        /// Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled.
 +        completion_autoimport_enable: bool       = "true",
 +        /// Toggles the additional completions that automatically show method calls and field accesses
 +        /// with `self` prefixed to them when inside a method.
 +        completion_autoself_enable: bool        = "true",
 +        /// Whether to add parenthesis and argument snippets when completing function.
 +        completion_callable_snippets: CallableCompletionDef  = "\"fill_arguments\"",
 +        /// Whether to show postfix snippets like `dbg`, `if`, `not`, etc.
 +        completion_postfix_enable: bool         = "true",
 +        /// Enables completions of private items and fields that are defined in the current workspace even if they are not visible at the current position.
 +        completion_privateEditable_enable: bool = "false",
 +        /// Custom completion snippets.
 +        // NOTE: Keep this list in sync with the feature docs of user snippets.
 +        completion_snippets_custom: FxHashMap<String, SnippetDef> = r#"{
 +            "Arc::new": {
 +                "postfix": "arc",
 +                "body": "Arc::new(${receiver})",
 +                "requires": "std::sync::Arc",
 +                "description": "Put the expression into an `Arc`",
 +                "scope": "expr"
 +            },
 +            "Rc::new": {
 +                "postfix": "rc",
 +                "body": "Rc::new(${receiver})",
 +                "requires": "std::rc::Rc",
 +                "description": "Put the expression into an `Rc`",
 +                "scope": "expr"
 +            },
 +            "Box::pin": {
 +                "postfix": "pinbox",
 +                "body": "Box::pin(${receiver})",
 +                "requires": "std::boxed::Box",
 +                "description": "Put the expression into a pinned `Box`",
 +                "scope": "expr"
 +            },
 +            "Ok": {
 +                "postfix": "ok",
 +                "body": "Ok(${receiver})",
 +                "description": "Wrap the expression in a `Result::Ok`",
 +                "scope": "expr"
 +            },
 +            "Err": {
 +                "postfix": "err",
 +                "body": "Err(${receiver})",
 +                "description": "Wrap the expression in a `Result::Err`",
 +                "scope": "expr"
 +            },
 +            "Some": {
 +                "postfix": "some",
 +                "body": "Some(${receiver})",
 +                "description": "Wrap the expression in an `Option::Some`",
 +                "scope": "expr"
 +            }
 +        }"#,
 +
 +        /// List of rust-analyzer diagnostics to disable.
 +        diagnostics_disabled: FxHashSet<String> = "[]",
 +        /// Whether to show native rust-analyzer diagnostics.
 +        diagnostics_enable: bool                = "true",
 +        /// Whether to show experimental rust-analyzer diagnostics that might
 +        /// have more false positives than usual.
 +        diagnostics_experimental_enable: bool    = "false",
 +        /// Map of prefixes to be substituted when parsing diagnostic file paths.
 +        /// This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`.
 +        diagnostics_remapPrefix: FxHashMap<String, String> = "{}",
 +        /// List of warnings that should be displayed with hint severity.
 +        ///
 +        /// The warnings will be indicated by faded text or three dots in code
 +        /// and will not show up in the `Problems Panel`.
 +        diagnostics_warningsAsHint: Vec<String> = "[]",
 +        /// List of warnings that should be displayed with info severity.
 +        ///
 +        /// The warnings will be indicated by a blue squiggly underline in code
 +        /// and a blue icon in the `Problems Panel`.
 +        diagnostics_warningsAsInfo: Vec<String> = "[]",
 +
 +        /// These directories will be ignored by rust-analyzer. They are
 +        /// relative to the workspace root, and globs are not supported. You may
 +        /// also need to add the folders to Code's `files.watcherExclude`.
 +        files_excludeDirs: Vec<PathBuf> = "[]",
 +        /// Controls file watching implementation.
 +        files_watcher: FilesWatcherDef = "\"client\"",
 +        /// Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords.
 +        highlightRelated_breakPoints_enable: bool = "true",
 +        /// Enables highlighting of all exit points while the cursor is on any `return`, `?`, `fn`, or return type arrow (`->`).
 +        highlightRelated_exitPoints_enable: bool = "true",
 +        /// Enables highlighting of related references while the cursor is on any identifier.
 +        highlightRelated_references_enable: bool = "true",
 +        /// Enables highlighting of all break points for a loop or block context while the cursor is on any `async` or `await` keywords.
 +        highlightRelated_yieldPoints_enable: bool = "true",
 +
 +        /// Whether to show `Debug` action. Only applies when
 +        /// `#rust-analyzer.hover.actions.enable#` is set.
 +        hover_actions_debug_enable: bool           = "true",
 +        /// Whether to show HoverActions in Rust files.
 +        hover_actions_enable: bool          = "true",
 +        /// Whether to show `Go to Type Definition` action. Only applies when
 +        /// `#rust-analyzer.hover.actions.enable#` is set.
 +        hover_actions_gotoTypeDef_enable: bool     = "true",
 +        /// Whether to show `Implementations` action. Only applies when
 +        /// `#rust-analyzer.hover.actions.enable#` is set.
 +        hover_actions_implementations_enable: bool = "true",
 +        /// Whether to show `References` action. Only applies when
 +        /// `#rust-analyzer.hover.actions.enable#` is set.
 +        hover_actions_references_enable: bool      = "false",
 +        /// Whether to show `Run` action. Only applies when
 +        /// `#rust-analyzer.hover.actions.enable#` is set.
 +        hover_actions_run_enable: bool             = "true",
 +
 +        /// Whether to show documentation on hover.
 +        hover_documentation_enable: bool           = "true",
 +        /// Whether to show keyword hover popups. Only applies when
 +        /// `#rust-analyzer.hover.documentation.enable#` is set.
 +        hover_documentation_keywords_enable: bool  = "true",
 +        /// Use markdown syntax for links in hover.
 +        hover_links_enable: bool = "true",
 +
 +        /// Whether to enforce the import granularity setting for all files. If set to false rust-analyzer will try to keep import styles consistent per file.
 +        imports_granularity_enforce: bool              = "false",
 +        /// How imports should be grouped into use statements.
 +        imports_granularity_group: ImportGranularityDef  = "\"crate\"",
 +        /// Group inserted imports by the https://rust-analyzer.github.io/manual.html#auto-import[following order]. Groups are separated by newlines.
 +        imports_group_enable: bool                           = "true",
 +        /// Whether to allow import insertion to merge new imports into single path glob imports like `use std::fmt::*;`.
 +        imports_merge_glob: bool           = "true",
 +        /// Prefer to unconditionally use imports of the core and alloc crate, over the std crate.
 +        imports_prefer_no_std: bool                     = "false",
 +        /// The path structure for newly inserted paths to use.
 +        imports_prefix: ImportPrefixDef               = "\"plain\"",
 +
 +        /// Whether to show inlay type hints for binding modes.
 +        inlayHints_bindingModeHints_enable: bool                   = "false",
 +        /// Whether to show inlay type hints for method chains.
 +        inlayHints_chainingHints_enable: bool                      = "true",
 +        /// Whether to show inlay hints after a closing `}` to indicate what item it belongs to.
 +        inlayHints_closingBraceHints_enable: bool                  = "true",
 +        /// Minimum number of lines required before the `}` until the hint is shown (set to 0 or 1
 +        /// to always show them).
 +        inlayHints_closingBraceHints_minLines: usize               = "25",
 +        /// Whether to show inlay type hints for return types of closures.
 +        inlayHints_closureReturnTypeHints_enable: ClosureReturnTypeHintsDef  = "\"never\"",
 +        /// Whether to show inlay type hints for elided lifetimes in function signatures.
 +        inlayHints_lifetimeElisionHints_enable: LifetimeElisionDef = "\"never\"",
 +        /// Whether to prefer using parameter names as the name for elided lifetime hints if possible.
 +        inlayHints_lifetimeElisionHints_useParameterNames: bool    = "false",
 +        /// Maximum length for inlay hints. Set to null to have an unlimited length.
 +        inlayHints_maxLength: Option<usize>                        = "25",
 +        /// Whether to show function parameter name inlay hints at the call
 +        /// site.
 +        inlayHints_parameterHints_enable: bool                     = "true",
 +        /// Whether to show inlay type hints for compiler inserted reborrows.
 +        inlayHints_reborrowHints_enable: ReborrowHintsDef          = "\"never\"",
 +        /// Whether to render leading colons for type hints, and trailing colons for parameter hints.
 +        inlayHints_renderColons: bool                              = "true",
 +        /// Whether to show inlay type hints for variables.
 +        inlayHints_typeHints_enable: bool                          = "true",
 +        /// Whether to hide inlay type hints for `let` statements that initialize to a closure.
 +        /// Only applies to closures with blocks, same as `#rust-analyzer.inlayHints.closureReturnTypeHints.enable#`.
 +        inlayHints_typeHints_hideClosureInitialization: bool       = "false",
 +        /// Whether to hide inlay type hints for constructors.
 +        inlayHints_typeHints_hideNamedConstructor: bool            = "false",
 +
 +        /// Join lines merges consecutive declaration and initialization of an assignment.
 +        joinLines_joinAssignments: bool = "true",
 +        /// Join lines inserts else between consecutive ifs.
 +        joinLines_joinElseIf: bool = "true",
 +        /// Join lines removes trailing commas.
 +        joinLines_removeTrailingComma: bool = "true",
 +        /// Join lines unwraps trivial blocks.
 +        joinLines_unwrapTrivialBlock: bool = "true",
 +
 +
 +        /// Whether to show `Debug` lens. Only applies when
 +        /// `#rust-analyzer.lens.enable#` is set.
 +        lens_debug_enable: bool            = "true",
 +        /// Whether to show CodeLens in Rust files.
 +        lens_enable: bool           = "true",
 +        /// Internal config: use custom client-side commands even when the
 +        /// client doesn't set the corresponding capability.
 +        lens_forceCustomCommands: bool = "true",
 +        /// Whether to show `Implementations` lens. Only applies when
 +        /// `#rust-analyzer.lens.enable#` is set.
 +        lens_implementations_enable: bool  = "true",
 +        /// Where to render annotations.
 +        lens_location: AnnotationLocation = "\"above_name\"",
 +        /// Whether to show `References` lens for Struct, Enum, and Union.
 +        /// Only applies when `#rust-analyzer.lens.enable#` is set.
 +        lens_references_adt_enable: bool = "false",
 +        /// Whether to show `References` lens for Enum Variants.
 +        /// Only applies when `#rust-analyzer.lens.enable#` is set.
 +        lens_references_enumVariant_enable: bool = "false",
 +        /// Whether to show `Method References` lens. Only applies when
 +        /// `#rust-analyzer.lens.enable#` is set.
 +        lens_references_method_enable: bool = "false",
 +        /// Whether to show `References` lens for Trait.
 +        /// Only applies when `#rust-analyzer.lens.enable#` is set.
 +        lens_references_trait_enable: bool = "false",
 +        /// Whether to show `Run` lens. Only applies when
 +        /// `#rust-analyzer.lens.enable#` is set.
 +        lens_run_enable: bool              = "true",
 +
 +        /// Disable project auto-discovery in favor of explicitly specified set
 +        /// of projects.
 +        ///
 +        /// Elements must be paths pointing to `Cargo.toml`,
 +        /// `rust-project.json`, or JSON objects in `rust-project.json` format.
 +        linkedProjects: Vec<ManifestOrProjectJson> = "[]",
 +
 +        /// Number of syntax trees rust-analyzer keeps in memory. Defaults to 128.
 +        lru_capacity: Option<usize>                 = "null",
 +
 +        /// Whether to show `can't find Cargo.toml` error message.
 +        notifications_cargoTomlNotFound: bool      = "true",
 +
 +        /// Expand attribute macros. Requires `#rust-analyzer.procMacro.enable#` to be set.
 +        procMacro_attributes_enable: bool = "true",
 +        /// Enable support for procedural macros, implies `#rust-analyzer.cargo.buildScripts.enable#`.
 +        procMacro_enable: bool                     = "true",
 +        /// These proc-macros will be ignored when trying to expand them.
 +        ///
 +        /// This config takes a map of crate names with the exported proc-macro names to ignore as values.
 +        procMacro_ignored: FxHashMap<Box<str>, Box<[Box<str>]>>          = "{}",
 +        /// Internal config, path to proc-macro server executable (typically,
 +        /// this is rust-analyzer itself, but we override this in tests).
 +        procMacro_server: Option<PathBuf>          = "null",
 +
 +        /// Exclude imports from find-all-references.
 +        references_excludeImports: bool = "false",
 +
 +        /// Command to be executed instead of 'cargo' for runnables.
 +        runnables_command: Option<String> = "null",
 +        /// Additional arguments to be passed to cargo for runnables such as
 +        /// tests or binaries. For example, it may be `--release`.
 +        runnables_extraArgs: Vec<String>   = "[]",
 +
 +        /// Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private
 +        /// projects, or "discover" to try to automatically find it if the `rustc-dev` component
 +        /// is installed.
 +        ///
 +        /// Any project which uses rust-analyzer with the rustcPrivate
 +        /// crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it.
 +        ///
 +        /// This option does not take effect until rust-analyzer is restarted.
 +        rustc_source: Option<String> = "null",
 +
 +        /// Additional arguments to `rustfmt`.
 +        rustfmt_extraArgs: Vec<String>               = "[]",
 +        /// Advanced option, fully override the command rust-analyzer uses for
 +        /// formatting.
 +        rustfmt_overrideCommand: Option<Vec<String>> = "null",
 +        /// Enables the use of rustfmt's unstable range formatting command for the
 +        /// `textDocument/rangeFormatting` request. The rustfmt option is unstable and only
 +        /// available on a nightly build.
 +        rustfmt_rangeFormatting_enable: bool = "false",
 +
 +        /// Inject additional highlighting into doc comments.
 +        ///
 +        /// When enabled, rust-analyzer will highlight rust source in doc comments as well as intra
 +        /// doc links.
 +        semanticHighlighting_doc_comment_inject_enable: bool = "true",
 +        /// Use semantic tokens for operators.
 +        ///
 +        /// When disabled, rust-analyzer will emit semantic tokens only for operator tokens when
 +        /// they are tagged with modifiers.
 +        semanticHighlighting_operator_enable: bool = "true",
 +        /// Use specialized semantic tokens for operators.
 +        ///
 +        /// When enabled, rust-analyzer will emit special token types for operator tokens instead
 +        /// of the generic `operator` token type.
 +        semanticHighlighting_operator_specialization_enable: bool = "false",
 +        /// Use semantic tokens for punctuations.
 +        ///
 +        /// When disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when
 +        /// they are tagged with modifiers or have a special role.
 +        semanticHighlighting_punctuation_enable: bool = "false",
 +        /// When enabled, rust-analyzer will emit a punctuation semantic token for the `!` of macro
 +        /// calls.
 +        semanticHighlighting_punctuation_separate_macro_bang: bool = "false",
 +        /// Use specialized semantic tokens for punctuations.
 +        ///
 +        /// When enabled, rust-analyzer will emit special token types for punctuation tokens instead
 +        /// of the generic `punctuation` token type.
 +        semanticHighlighting_punctuation_specialization_enable: bool = "false",
 +        /// Use semantic tokens for strings.
 +        ///
 +        /// In some editors (e.g. vscode) semantic tokens override other highlighting grammars.
 +        /// By disabling semantic tokens for strings, other grammars can be used to highlight
 +        /// their contents.
 +        semanticHighlighting_strings_enable: bool = "true",
 +
 +        /// Show full signature of the callable. Only shows parameters if disabled.
 +        signatureInfo_detail: SignatureDetail                           = "\"full\"",
 +        /// Show documentation.
 +        signatureInfo_documentation_enable: bool                       = "true",
 +
 +        /// Whether to insert closing angle brackets when typing an opening angle bracket of a generic argument list.
 +        typing_autoClosingAngleBrackets_enable: bool = "false",
 +
 +        /// Workspace symbol search kind.
 +        workspace_symbol_search_kind: WorkspaceSymbolSearchKindDef = "\"only_types\"",
 +        /// Limits the number of items returned from a workspace symbol search (Defaults to 128).
 +        /// Some clients like vs-code issue new searches on result filtering and don't require all results to be returned in the initial search.
 +        /// Other clients requires all results upfront and might require a higher limit.
 +        workspace_symbol_search_limit: usize = "128",
 +        /// Workspace symbol search scope.
 +        workspace_symbol_search_scope: WorkspaceSymbolSearchScopeDef = "\"workspace\"",
 +    }
 +}
 +
 +impl Default for ConfigData {
 +    fn default() -> Self {
 +        ConfigData::from_json(serde_json::Value::Null, &mut Vec::new())
 +    }
 +}
 +
 +#[derive(Debug, Clone)]
 +pub struct Config {
 +    pub discovered_projects: Option<Vec<ProjectManifest>>,
 +    caps: lsp_types::ClientCapabilities,
 +    root_path: AbsPathBuf,
 +    data: ConfigData,
 +    detached_files: Vec<AbsPathBuf>,
 +    snippets: Vec<Snippet>,
 +}
 +
 +type ParallelCachePrimingNumThreads = u8;
 +
 +#[derive(Debug, Clone, Eq, PartialEq)]
 +pub enum LinkedProject {
 +    ProjectManifest(ProjectManifest),
 +    InlineJsonProject(ProjectJson),
 +}
 +
 +impl From<ProjectManifest> for LinkedProject {
 +    fn from(v: ProjectManifest) -> Self {
 +        LinkedProject::ProjectManifest(v)
 +    }
 +}
 +
 +impl From<ProjectJson> for LinkedProject {
 +    fn from(v: ProjectJson) -> Self {
 +        LinkedProject::InlineJsonProject(v)
 +    }
 +}
 +
 +pub struct CallInfoConfig {
 +    pub params_only: bool,
 +    pub docs: bool,
 +}
 +
 +#[derive(Clone, Debug, PartialEq, Eq)]
 +pub struct LensConfig {
 +    // runnables
 +    pub run: bool,
 +    pub debug: bool,
 +
 +    // implementations
 +    pub implementations: bool,
 +
 +    // references
 +    pub method_refs: bool,
 +    pub refs_adt: bool,   // for Struct, Enum, Union and Trait
 +    pub refs_trait: bool, // for Struct, Enum, Union and Trait
 +    pub enum_variant_refs: bool,
 +
 +    // annotations
 +    pub location: AnnotationLocation,
 +}
 +
 +#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
 +#[serde(rename_all = "snake_case")]
 +pub enum AnnotationLocation {
 +    AboveName,
 +    AboveWholeItem,
 +}
 +
 +impl From<AnnotationLocation> for ide::AnnotationLocation {
 +    fn from(location: AnnotationLocation) -> Self {
 +        match location {
 +            AnnotationLocation::AboveName => ide::AnnotationLocation::AboveName,
 +            AnnotationLocation::AboveWholeItem => ide::AnnotationLocation::AboveWholeItem,
 +        }
 +    }
 +}
 +
 +impl LensConfig {
 +    pub fn any(&self) -> bool {
 +        self.run
 +            || self.debug
 +            || self.implementations
 +            || self.method_refs
 +            || self.refs_adt
 +            || self.refs_trait
 +            || self.enum_variant_refs
 +    }
 +
 +    pub fn none(&self) -> bool {
 +        !self.any()
 +    }
 +
 +    pub fn runnable(&self) -> bool {
 +        self.run || self.debug
 +    }
 +
 +    pub fn references(&self) -> bool {
 +        self.method_refs || self.refs_adt || self.refs_trait || self.enum_variant_refs
 +    }
 +}
 +
 +#[derive(Clone, Debug, PartialEq, Eq)]
 +pub struct HoverActionsConfig {
 +    pub implementations: bool,
 +    pub references: bool,
 +    pub run: bool,
 +    pub debug: bool,
 +    pub goto_type_def: bool,
 +}
 +
 +impl HoverActionsConfig {
 +    pub const NO_ACTIONS: Self = Self {
 +        implementations: false,
 +        references: false,
 +        run: false,
 +        debug: false,
 +        goto_type_def: false,
 +    };
 +
 +    pub fn any(&self) -> bool {
 +        self.implementations || self.references || self.runnable() || self.goto_type_def
 +    }
 +
 +    pub fn none(&self) -> bool {
 +        !self.any()
 +    }
 +
 +    pub fn runnable(&self) -> bool {
 +        self.run || self.debug
 +    }
 +}
 +
 +#[derive(Debug, Clone)]
 +pub struct FilesConfig {
 +    pub watcher: FilesWatcher,
 +    pub exclude: Vec<AbsPathBuf>,
 +}
 +
 +#[derive(Debug, Clone)]
 +pub enum FilesWatcher {
 +    Client,
 +    Server,
 +}
 +
 +#[derive(Debug, Clone)]
 +pub struct NotificationsConfig {
 +    pub cargo_toml_not_found: bool,
 +}
 +
 +#[derive(Debug, Clone)]
 +pub enum RustfmtConfig {
 +    Rustfmt { extra_args: Vec<String>, enable_range_formatting: bool },
 +    CustomCommand { command: String, args: Vec<String> },
 +}
 +
 +/// Configuration for runnable items, such as `main` function or tests.
 +#[derive(Debug, Clone)]
 +pub struct RunnablesConfig {
 +    /// Custom command to be executed instead of `cargo` for runnables.
 +    pub override_cargo: Option<String>,
 +    /// Additional arguments for the `cargo`, e.g. `--release`.
 +    pub cargo_extra_args: Vec<String>,
 +}
 +
 +/// Configuration for workspace symbol search requests.
 +#[derive(Debug, Clone)]
 +pub struct WorkspaceSymbolConfig {
 +    /// In what scope should the symbol be searched in.
 +    pub search_scope: WorkspaceSymbolSearchScope,
 +    /// What kind of symbol is being searched for.
 +    pub search_kind: WorkspaceSymbolSearchKind,
 +    /// How many items are returned at most.
 +    pub search_limit: usize,
 +}
 +
 +pub struct ClientCommandsConfig {
 +    pub run_single: bool,
 +    pub debug_single: bool,
 +    pub show_reference: bool,
 +    pub goto_location: bool,
 +    pub trigger_parameter_hints: bool,
 +}
 +
 +#[derive(Debug)]
 +pub struct ConfigUpdateError {
 +    errors: Vec<(String, serde_json::Error)>,
 +}
 +
 +impl fmt::Display for ConfigUpdateError {
 +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +        let errors = self.errors.iter().format_with("\n", |(key, e), f| {
 +            f(key)?;
 +            f(&": ")?;
 +            f(e)
 +        });
 +        write!(
 +            f,
 +            "rust-analyzer found {} invalid config value{}:\n{}",
 +            self.errors.len(),
 +            if self.errors.len() == 1 { "" } else { "s" },
 +            errors
 +        )
 +    }
 +}
 +
 +impl Config {
 +    pub fn new(root_path: AbsPathBuf, caps: ClientCapabilities) -> Self {
 +        Config {
 +            caps,
 +            data: ConfigData::default(),
 +            detached_files: Vec::new(),
 +            discovered_projects: None,
 +            root_path,
 +            snippets: Default::default(),
 +        }
 +    }
 +
 +    pub fn update(&mut self, mut json: serde_json::Value) -> Result<(), ConfigUpdateError> {
 +        tracing::info!("updating config from JSON: {:#}", json);
 +        if json.is_null() || json.as_object().map_or(false, |it| it.is_empty()) {
 +            return Ok(());
 +        }
 +        let mut errors = Vec::new();
 +        self.detached_files =
 +            get_field::<Vec<PathBuf>>(&mut json, &mut errors, "detachedFiles", None, "[]")
 +                .into_iter()
 +                .map(AbsPathBuf::assert)
 +                .collect();
 +        patch_old_style::patch_json_for_outdated_configs(&mut json);
 +        self.data = ConfigData::from_json(json, &mut errors);
 +        tracing::debug!("deserialized config data: {:#?}", self.data);
 +        self.snippets.clear();
 +        for (name, def) in self.data.completion_snippets_custom.iter() {
 +            if def.prefix.is_empty() && def.postfix.is_empty() {
 +                continue;
 +            }
 +            let scope = match def.scope {
 +                SnippetScopeDef::Expr => SnippetScope::Expr,
 +                SnippetScopeDef::Type => SnippetScope::Type,
 +                SnippetScopeDef::Item => SnippetScope::Item,
 +            };
 +            match Snippet::new(
 +                &def.prefix,
 +                &def.postfix,
 +                &def.body,
 +                def.description.as_ref().unwrap_or(name),
 +                &def.requires,
 +                scope,
 +            ) {
 +                Some(snippet) => self.snippets.push(snippet),
 +                None => errors.push((
 +                    format!("snippet {name} is invalid"),
 +                    <serde_json::Error as serde::de::Error>::custom(
 +                        "snippet path is invalid or triggers are missing",
 +                    ),
 +                )),
 +            }
 +        }
 +
 +        self.validate(&mut errors);
 +
 +        if errors.is_empty() {
 +            Ok(())
 +        } else {
 +            Err(ConfigUpdateError { errors })
 +        }
 +    }
 +
 +    fn validate(&self, error_sink: &mut Vec<(String, serde_json::Error)>) {
 +        use serde::de::Error;
 +        if self.data.checkOnSave_command.is_empty() {
 +            error_sink.push((
 +                "/checkOnSave/command".to_string(),
 +                serde_json::Error::custom("expected a non-empty string"),
 +            ));
 +        }
 +    }
 +
 +    pub fn json_schema() -> serde_json::Value {
 +        ConfigData::json_schema()
 +    }
 +
 +    pub fn root_path(&self) -> &AbsPathBuf {
 +        &self.root_path
 +    }
 +
 +    pub fn caps(&self) -> &lsp_types::ClientCapabilities {
 +        &self.caps
 +    }
 +
 +    pub fn detached_files(&self) -> &[AbsPathBuf] {
 +        &self.detached_files
 +    }
 +}
 +
 +macro_rules! try_ {
 +    ($expr:expr) => {
 +        || -> _ { Some($expr) }()
 +    };
 +}
 +macro_rules! try_or {
 +    ($expr:expr, $or:expr) => {
 +        try_!($expr).unwrap_or($or)
 +    };
 +}
 +
 +macro_rules! try_or_def {
 +    ($expr:expr) => {
 +        try_!($expr).unwrap_or_default()
 +    };
 +}
 +
 +impl Config {
 +    pub fn linked_projects(&self) -> Vec<LinkedProject> {
 +        match self.data.linkedProjects.as_slice() {
 +            [] => match self.discovered_projects.as_ref() {
 +                Some(discovered_projects) => {
 +                    let exclude_dirs: Vec<_> = self
 +                        .data
 +                        .files_excludeDirs
 +                        .iter()
 +                        .map(|p| self.root_path.join(p))
 +                        .collect();
 +                    discovered_projects
 +                        .iter()
 +                        .filter(|p| {
 +                            let (ProjectManifest::ProjectJson(path)
 +                            | ProjectManifest::CargoToml(path)) = p;
 +                            !exclude_dirs.iter().any(|p| path.starts_with(p))
 +                        })
 +                        .cloned()
 +                        .map(LinkedProject::from)
 +                        .collect()
 +                }
 +                None => Vec::new(),
 +            },
 +            linked_projects => linked_projects
 +                .iter()
 +                .filter_map(|linked_project| match linked_project {
 +                    ManifestOrProjectJson::Manifest(it) => {
 +                        let path = self.root_path.join(it);
 +                        ProjectManifest::from_manifest_file(path)
 +                            .map_err(|e| tracing::error!("failed to load linked project: {}", e))
 +                            .ok()
 +                            .map(Into::into)
 +                    }
 +                    ManifestOrProjectJson::ProjectJson(it) => {
 +                        Some(ProjectJson::new(&self.root_path, it.clone()).into())
 +                    }
 +                })
 +                .collect(),
 +        }
 +    }
 +
 +    pub fn did_save_text_document_dynamic_registration(&self) -> bool {
 +        let caps = try_or_def!(self.caps.text_document.as_ref()?.synchronization.clone()?);
 +        caps.did_save == Some(true) && caps.dynamic_registration == Some(true)
 +    }
 +
 +    pub fn did_change_watched_files_dynamic_registration(&self) -> bool {
 +        try_or_def!(
 +            self.caps.workspace.as_ref()?.did_change_watched_files.as_ref()?.dynamic_registration?
 +        )
 +    }
 +
 +    pub fn prefill_caches(&self) -> bool {
 +        self.data.cachePriming_enable
 +    }
 +
 +    pub fn location_link(&self) -> bool {
 +        try_or_def!(self.caps.text_document.as_ref()?.definition?.link_support?)
 +    }
 +
 +    pub fn line_folding_only(&self) -> bool {
 +        try_or_def!(self.caps.text_document.as_ref()?.folding_range.as_ref()?.line_folding_only?)
 +    }
 +
 +    pub fn hierarchical_symbols(&self) -> bool {
 +        try_or_def!(
 +            self.caps
 +                .text_document
 +                .as_ref()?
 +                .document_symbol
 +                .as_ref()?
 +                .hierarchical_document_symbol_support?
 +        )
 +    }
 +
 +    pub fn code_action_literals(&self) -> bool {
 +        try_!(self
 +            .caps
 +            .text_document
 +            .as_ref()?
 +            .code_action
 +            .as_ref()?
 +            .code_action_literal_support
 +            .as_ref()?)
 +        .is_some()
 +    }
 +
 +    pub fn work_done_progress(&self) -> bool {
 +        try_or_def!(self.caps.window.as_ref()?.work_done_progress?)
 +    }
 +
 +    pub fn will_rename(&self) -> bool {
 +        try_or_def!(self.caps.workspace.as_ref()?.file_operations.as_ref()?.will_rename?)
 +    }
 +
 +    pub fn change_annotation_support(&self) -> bool {
 +        try_!(self
 +            .caps
 +            .workspace
 +            .as_ref()?
 +            .workspace_edit
 +            .as_ref()?
 +            .change_annotation_support
 +            .as_ref()?)
 +        .is_some()
 +    }
 +
 +    pub fn code_action_resolve(&self) -> bool {
 +        try_or_def!(self
 +            .caps
 +            .text_document
 +            .as_ref()?
 +            .code_action
 +            .as_ref()?
 +            .resolve_support
 +            .as_ref()?
 +            .properties
 +            .as_slice())
 +        .iter()
 +        .any(|it| it == "edit")
 +    }
 +
 +    pub fn signature_help_label_offsets(&self) -> bool {
 +        try_or_def!(
 +            self.caps
 +                .text_document
 +                .as_ref()?
 +                .signature_help
 +                .as_ref()?
 +                .signature_information
 +                .as_ref()?
 +                .parameter_information
 +                .as_ref()?
 +                .label_offset_support?
 +        )
 +    }
 +
 +    pub fn completion_label_details_support(&self) -> bool {
 +        try_!(self
 +            .caps
 +            .text_document
 +            .as_ref()?
 +            .completion
 +            .as_ref()?
 +            .completion_item
 +            .as_ref()?
 +            .label_details_support
 +            .as_ref()?)
 +        .is_some()
 +    }
 +
 +    pub fn position_encoding(&self) -> PositionEncoding {
 +        if supports_utf8(&self.caps) {
 +            PositionEncoding::Utf8
 +        } else {
 +            PositionEncoding::Utf16
 +        }
 +    }
 +
 +    fn experimental(&self, index: &'static str) -> bool {
 +        try_or_def!(self.caps.experimental.as_ref()?.get(index)?.as_bool()?)
 +    }
 +
 +    pub fn code_action_group(&self) -> bool {
 +        self.experimental("codeActionGroup")
 +    }
 +
 +    pub fn server_status_notification(&self) -> bool {
 +        self.experimental("serverStatusNotification")
 +    }
 +
 +    pub fn publish_diagnostics(&self) -> bool {
 +        self.data.diagnostics_enable
 +    }
 +
 +    pub fn diagnostics(&self) -> DiagnosticsConfig {
 +        DiagnosticsConfig {
 +            proc_attr_macros_enabled: self.expand_proc_attr_macros(),
 +            proc_macros_enabled: self.data.procMacro_enable,
 +            disable_experimental: !self.data.diagnostics_experimental_enable,
 +            disabled: self.data.diagnostics_disabled.clone(),
 +            expr_fill_default: match self.data.assist_expressionFillDefault {
 +                ExprFillDefaultDef::Todo => ExprFillDefaultMode::Todo,
 +                ExprFillDefaultDef::Default => ExprFillDefaultMode::Default,
 +            },
 +            insert_use: self.insert_use_config(),
 +            prefer_no_std: self.data.imports_prefer_no_std,
 +        }
 +    }
 +
 +    pub fn diagnostics_map(&self) -> DiagnosticsMapConfig {
 +        DiagnosticsMapConfig {
 +            remap_prefix: self.data.diagnostics_remapPrefix.clone(),
 +            warnings_as_info: self.data.diagnostics_warningsAsInfo.clone(),
 +            warnings_as_hint: self.data.diagnostics_warningsAsHint.clone(),
 +        }
 +    }
 +
 +    pub fn extra_env(&self) -> &FxHashMap<String, String> {
 +        &self.data.cargo_extraEnv
 +    }
 +
 +    pub fn check_on_save_extra_env(&self) -> FxHashMap<String, String> {
 +        let mut extra_env = self.data.cargo_extraEnv.clone();
 +        extra_env.extend(self.data.checkOnSave_extraEnv.clone());
 +        extra_env
 +    }
 +
 +    pub fn lru_capacity(&self) -> Option<usize> {
 +        self.data.lru_capacity
 +    }
 +
 +    pub fn proc_macro_srv(&self) -> Option<(AbsPathBuf, /* is path explicitly set */ bool)> {
 +        if !self.data.procMacro_enable {
 +            return None;
 +        }
 +        Some(match &self.data.procMacro_server {
 +            Some(it) => (
 +                AbsPathBuf::try_from(it.clone()).unwrap_or_else(|path| self.root_path.join(path)),
 +                true,
 +            ),
 +            None => (AbsPathBuf::assert(std::env::current_exe().ok()?), false),
 +        })
 +    }
 +
 +    pub fn dummy_replacements(&self) -> &FxHashMap<Box<str>, Box<[Box<str>]>> {
 +        &self.data.procMacro_ignored
 +    }
 +
 +    pub fn expand_proc_attr_macros(&self) -> bool {
 +        self.data.procMacro_enable && self.data.procMacro_attributes_enable
 +    }
 +
 +    pub fn files(&self) -> FilesConfig {
 +        FilesConfig {
 +            watcher: match self.data.files_watcher {
 +                FilesWatcherDef::Client if self.did_change_watched_files_dynamic_registration() => {
 +                    FilesWatcher::Client
 +                }
 +                _ => FilesWatcher::Server,
 +            },
 +            exclude: self.data.files_excludeDirs.iter().map(|it| self.root_path.join(it)).collect(),
 +        }
 +    }
 +
 +    pub fn notifications(&self) -> NotificationsConfig {
 +        NotificationsConfig { cargo_toml_not_found: self.data.notifications_cargoTomlNotFound }
 +    }
 +
 +    pub fn cargo_autoreload(&self) -> bool {
 +        self.data.cargo_autoreload
 +    }
 +
 +    pub fn run_build_scripts(&self) -> bool {
 +        self.data.cargo_buildScripts_enable || self.data.procMacro_enable
 +    }
 +
 +    pub fn cargo(&self) -> CargoConfig {
 +        let rustc_source = self.data.rustc_source.as_ref().map(|rustc_src| {
 +            if rustc_src == "discover" {
 +                RustcSource::Discover
 +            } else {
 +                RustcSource::Path(self.root_path.join(rustc_src))
 +            }
 +        });
 +        let sysroot = self.data.cargo_sysroot.as_ref().map(|sysroot| {
 +            if sysroot == "discover" {
 +                RustcSource::Discover
 +            } else {
 +                RustcSource::Path(self.root_path.join(sysroot))
 +            }
 +        });
 +
 +        CargoConfig {
 +            features: match &self.data.cargo_features {
 +                CargoFeaturesDef::All => CargoFeatures::All,
 +                CargoFeaturesDef::Selected(features) => CargoFeatures::Selected {
 +                    features: features.clone(),
 +                    no_default_features: self.data.cargo_noDefaultFeatures,
 +                },
 +            },
 +            target: self.data.cargo_target.clone(),
 +            sysroot,
 +            rustc_source,
 +            unset_test_crates: UnsetTestCrates::Only(self.data.cargo_unsetTest.clone()),
 +            wrap_rustc_in_build_scripts: self.data.cargo_buildScripts_useRustcWrapper,
 +            invocation_strategy: match self.data.cargo_buildScripts_invocationStrategy {
 +                InvocationStrategy::Once => project_model::InvocationStrategy::Once,
 +                InvocationStrategy::PerWorkspace => project_model::InvocationStrategy::PerWorkspace,
 +            },
 +            invocation_location: match self.data.cargo_buildScripts_invocationLocation {
 +                InvocationLocation::Root => {
 +                    project_model::InvocationLocation::Root(self.root_path.clone())
 +                }
 +                InvocationLocation::Workspace => project_model::InvocationLocation::Workspace,
 +            },
 +            run_build_script_command: self.data.cargo_buildScripts_overrideCommand.clone(),
 +            extra_env: self.data.cargo_extraEnv.clone(),
 +        }
 +    }
 +
 +    pub fn rustfmt(&self) -> RustfmtConfig {
 +        match &self.data.rustfmt_overrideCommand {
 +            Some(args) if !args.is_empty() => {
 +                let mut args = args.clone();
 +                let command = args.remove(0);
 +                RustfmtConfig::CustomCommand { command, args }
 +            }
 +            Some(_) | None => RustfmtConfig::Rustfmt {
 +                extra_args: self.data.rustfmt_extraArgs.clone(),
 +                enable_range_formatting: self.data.rustfmt_rangeFormatting_enable,
 +            },
 +        }
 +    }
 +
 +    pub fn flycheck(&self) -> Option<FlycheckConfig> {
 +        if !self.data.checkOnSave_enable {
 +            return None;
 +        }
 +        let flycheck_config = match &self.data.checkOnSave_overrideCommand {
 +            Some(args) if !args.is_empty() => {
 +                let mut args = args.clone();
 +                let command = args.remove(0);
 +                FlycheckConfig::CustomCommand {
 +                    command,
 +                    args,
 +                    extra_env: self.check_on_save_extra_env(),
 +                    invocation_strategy: match self.data.checkOnSave_invocationStrategy {
 +                        InvocationStrategy::Once => flycheck::InvocationStrategy::Once,
 +                        InvocationStrategy::PerWorkspace => {
 +                            flycheck::InvocationStrategy::PerWorkspace
 +                        }
 +                    },
 +                    invocation_location: match self.data.checkOnSave_invocationLocation {
 +                        InvocationLocation::Root => {
 +                            flycheck::InvocationLocation::Root(self.root_path.clone())
 +                        }
 +                        InvocationLocation::Workspace => flycheck::InvocationLocation::Workspace,
 +                    },
 +                }
 +            }
 +            Some(_) | None => FlycheckConfig::CargoCommand {
 +                command: self.data.checkOnSave_command.clone(),
 +                target_triple: self
 +                    .data
 +                    .checkOnSave_target
 +                    .clone()
 +                    .or_else(|| self.data.cargo_target.clone()),
 +                all_targets: self.data.checkOnSave_allTargets,
 +                no_default_features: self
 +                    .data
 +                    .checkOnSave_noDefaultFeatures
 +                    .unwrap_or(self.data.cargo_noDefaultFeatures),
 +                all_features: matches!(
 +                    self.data.checkOnSave_features.as_ref().unwrap_or(&self.data.cargo_features),
 +                    CargoFeaturesDef::All
 +                ),
 +                features: match self
 +                    .data
 +                    .checkOnSave_features
 +                    .clone()
 +                    .unwrap_or_else(|| self.data.cargo_features.clone())
 +                {
 +                    CargoFeaturesDef::All => vec![],
 +                    CargoFeaturesDef::Selected(it) => it,
 +                },
 +                extra_args: self.data.checkOnSave_extraArgs.clone(),
 +                extra_env: self.check_on_save_extra_env(),
 +            },
 +        };
 +        Some(flycheck_config)
 +    }
 +
 +    pub fn runnables(&self) -> RunnablesConfig {
 +        RunnablesConfig {
 +            override_cargo: self.data.runnables_command.clone(),
 +            cargo_extra_args: self.data.runnables_extraArgs.clone(),
 +        }
 +    }
 +
 +    pub fn inlay_hints(&self) -> InlayHintsConfig {
 +        InlayHintsConfig {
 +            render_colons: self.data.inlayHints_renderColons,
 +            type_hints: self.data.inlayHints_typeHints_enable,
 +            parameter_hints: self.data.inlayHints_parameterHints_enable,
 +            chaining_hints: self.data.inlayHints_chainingHints_enable,
 +            closure_return_type_hints: match self.data.inlayHints_closureReturnTypeHints_enable {
 +                ClosureReturnTypeHintsDef::Always => ide::ClosureReturnTypeHints::Always,
 +                ClosureReturnTypeHintsDef::Never => ide::ClosureReturnTypeHints::Never,
 +                ClosureReturnTypeHintsDef::WithBlock => ide::ClosureReturnTypeHints::WithBlock,
 +            },
 +            lifetime_elision_hints: match self.data.inlayHints_lifetimeElisionHints_enable {
 +                LifetimeElisionDef::Always => ide::LifetimeElisionHints::Always,
 +                LifetimeElisionDef::Never => ide::LifetimeElisionHints::Never,
 +                LifetimeElisionDef::SkipTrivial => ide::LifetimeElisionHints::SkipTrivial,
 +            },
 +            hide_named_constructor_hints: self.data.inlayHints_typeHints_hideNamedConstructor,
 +            hide_closure_initialization_hints: self
 +                .data
 +                .inlayHints_typeHints_hideClosureInitialization,
 +            reborrow_hints: match self.data.inlayHints_reborrowHints_enable {
 +                ReborrowHintsDef::Always => ide::ReborrowHints::Always,
 +                ReborrowHintsDef::Never => ide::ReborrowHints::Never,
 +                ReborrowHintsDef::Mutable => ide::ReborrowHints::MutableOnly,
 +            },
 +            binding_mode_hints: self.data.inlayHints_bindingModeHints_enable,
 +            param_names_for_lifetime_elision_hints: self
 +                .data
 +                .inlayHints_lifetimeElisionHints_useParameterNames,
 +            max_length: self.data.inlayHints_maxLength,
 +            closing_brace_hints_min_lines: if self.data.inlayHints_closingBraceHints_enable {
 +                Some(self.data.inlayHints_closingBraceHints_minLines)
 +            } else {
 +                None
 +            },
 +        }
 +    }
 +
 +    fn insert_use_config(&self) -> InsertUseConfig {
 +        InsertUseConfig {
 +            granularity: match self.data.imports_granularity_group {
 +                ImportGranularityDef::Preserve => ImportGranularity::Preserve,
 +                ImportGranularityDef::Item => ImportGranularity::Item,
 +                ImportGranularityDef::Crate => ImportGranularity::Crate,
 +                ImportGranularityDef::Module => ImportGranularity::Module,
 +            },
 +            enforce_granularity: self.data.imports_granularity_enforce,
 +            prefix_kind: match self.data.imports_prefix {
 +                ImportPrefixDef::Plain => PrefixKind::Plain,
 +                ImportPrefixDef::ByCrate => PrefixKind::ByCrate,
 +                ImportPrefixDef::BySelf => PrefixKind::BySelf,
 +            },
 +            group: self.data.imports_group_enable,
 +            skip_glob_imports: !self.data.imports_merge_glob,
 +        }
 +    }
 +
 +    pub fn completion(&self) -> CompletionConfig {
 +        CompletionConfig {
 +            enable_postfix_completions: self.data.completion_postfix_enable,
 +            enable_imports_on_the_fly: self.data.completion_autoimport_enable
 +                && completion_item_edit_resolve(&self.caps),
 +            enable_self_on_the_fly: self.data.completion_autoself_enable,
 +            enable_private_editable: self.data.completion_privateEditable_enable,
 +            callable: match self.data.completion_callable_snippets {
 +                CallableCompletionDef::FillArguments => Some(CallableSnippets::FillArguments),
 +                CallableCompletionDef::AddParentheses => Some(CallableSnippets::AddParentheses),
 +                CallableCompletionDef::None => None,
 +            },
 +            insert_use: self.insert_use_config(),
 +            prefer_no_std: self.data.imports_prefer_no_std,
 +            snippet_cap: SnippetCap::new(try_or_def!(
 +                self.caps
 +                    .text_document
 +                    .as_ref()?
 +                    .completion
 +                    .as_ref()?
 +                    .completion_item
 +                    .as_ref()?
 +                    .snippet_support?
 +            )),
 +            snippets: self.snippets.clone(),
 +        }
 +    }
 +
 +    pub fn find_all_refs_exclude_imports(&self) -> bool {
 +        self.data.references_excludeImports
 +    }
 +
 +    pub fn snippet_cap(&self) -> bool {
 +        self.experimental("snippetTextEdit")
 +    }
 +
 +    pub fn assist(&self) -> AssistConfig {
 +        AssistConfig {
 +            snippet_cap: SnippetCap::new(self.experimental("snippetTextEdit")),
 +            allowed: None,
 +            insert_use: self.insert_use_config(),
 +            prefer_no_std: self.data.imports_prefer_no_std,
++            assist_emit_must_use: self.data.assist_emitMustUse,
 +        }
 +    }
 +
 +    pub fn join_lines(&self) -> JoinLinesConfig {
 +        JoinLinesConfig {
 +            join_else_if: self.data.joinLines_joinElseIf,
 +            remove_trailing_comma: self.data.joinLines_removeTrailingComma,
 +            unwrap_trivial_blocks: self.data.joinLines_unwrapTrivialBlock,
 +            join_assignments: self.data.joinLines_joinAssignments,
 +        }
 +    }
 +
 +    pub fn call_info(&self) -> CallInfoConfig {
 +        CallInfoConfig {
 +            params_only: matches!(self.data.signatureInfo_detail, SignatureDetail::Parameters),
 +            docs: self.data.signatureInfo_documentation_enable,
 +        }
 +    }
 +
 +    pub fn lens(&self) -> LensConfig {
 +        LensConfig {
 +            run: self.data.lens_enable && self.data.lens_run_enable,
 +            debug: self.data.lens_enable && self.data.lens_debug_enable,
 +            implementations: self.data.lens_enable && self.data.lens_implementations_enable,
 +            method_refs: self.data.lens_enable && self.data.lens_references_method_enable,
 +            refs_adt: self.data.lens_enable && self.data.lens_references_adt_enable,
 +            refs_trait: self.data.lens_enable && self.data.lens_references_trait_enable,
 +            enum_variant_refs: self.data.lens_enable
 +                && self.data.lens_references_enumVariant_enable,
 +            location: self.data.lens_location,
 +        }
 +    }
 +
 +    pub fn hover_actions(&self) -> HoverActionsConfig {
 +        let enable = self.experimental("hoverActions") && self.data.hover_actions_enable;
 +        HoverActionsConfig {
 +            implementations: enable && self.data.hover_actions_implementations_enable,
 +            references: enable && self.data.hover_actions_references_enable,
 +            run: enable && self.data.hover_actions_run_enable,
 +            debug: enable && self.data.hover_actions_debug_enable,
 +            goto_type_def: enable && self.data.hover_actions_gotoTypeDef_enable,
 +        }
 +    }
 +
 +    pub fn highlighting_config(&self) -> HighlightConfig {
 +        HighlightConfig {
 +            strings: self.data.semanticHighlighting_strings_enable,
 +            punctuation: self.data.semanticHighlighting_punctuation_enable,
 +            specialize_punctuation: self
 +                .data
 +                .semanticHighlighting_punctuation_specialization_enable,
 +            macro_bang: self.data.semanticHighlighting_punctuation_separate_macro_bang,
 +            operator: self.data.semanticHighlighting_operator_enable,
 +            specialize_operator: self.data.semanticHighlighting_operator_specialization_enable,
 +            inject_doc_comment: self.data.semanticHighlighting_doc_comment_inject_enable,
 +            syntactic_name_ref_highlighting: false,
 +        }
 +    }
 +
 +    pub fn hover(&self) -> HoverConfig {
 +        HoverConfig {
 +            links_in_hover: self.data.hover_links_enable,
 +            documentation: self.data.hover_documentation_enable.then(|| {
 +                let is_markdown = try_or_def!(self
 +                    .caps
 +                    .text_document
 +                    .as_ref()?
 +                    .hover
 +                    .as_ref()?
 +                    .content_format
 +                    .as_ref()?
 +                    .as_slice())
 +                .contains(&MarkupKind::Markdown);
 +                if is_markdown {
 +                    HoverDocFormat::Markdown
 +                } else {
 +                    HoverDocFormat::PlainText
 +                }
 +            }),
 +            keywords: self.data.hover_documentation_keywords_enable,
 +        }
 +    }
 +
 +    pub fn workspace_symbol(&self) -> WorkspaceSymbolConfig {
 +        WorkspaceSymbolConfig {
 +            search_scope: match self.data.workspace_symbol_search_scope {
 +                WorkspaceSymbolSearchScopeDef::Workspace => WorkspaceSymbolSearchScope::Workspace,
 +                WorkspaceSymbolSearchScopeDef::WorkspaceAndDependencies => {
 +                    WorkspaceSymbolSearchScope::WorkspaceAndDependencies
 +                }
 +            },
 +            search_kind: match self.data.workspace_symbol_search_kind {
 +                WorkspaceSymbolSearchKindDef::OnlyTypes => WorkspaceSymbolSearchKind::OnlyTypes,
 +                WorkspaceSymbolSearchKindDef::AllSymbols => WorkspaceSymbolSearchKind::AllSymbols,
 +            },
 +            search_limit: self.data.workspace_symbol_search_limit,
 +        }
 +    }
 +
 +    pub fn semantic_tokens_refresh(&self) -> bool {
 +        try_or_def!(self.caps.workspace.as_ref()?.semantic_tokens.as_ref()?.refresh_support?)
 +    }
 +
 +    pub fn code_lens_refresh(&self) -> bool {
 +        try_or_def!(self.caps.workspace.as_ref()?.code_lens.as_ref()?.refresh_support?)
 +    }
 +
 +    pub fn insert_replace_support(&self) -> bool {
 +        try_or_def!(
 +            self.caps
 +                .text_document
 +                .as_ref()?
 +                .completion
 +                .as_ref()?
 +                .completion_item
 +                .as_ref()?
 +                .insert_replace_support?
 +        )
 +    }
 +
 +    pub fn client_commands(&self) -> ClientCommandsConfig {
 +        let commands =
 +            try_or!(self.caps.experimental.as_ref()?.get("commands")?, &serde_json::Value::Null);
 +        let commands: Option<lsp_ext::ClientCommandOptions> =
 +            serde_json::from_value(commands.clone()).ok();
 +        let force = commands.is_none() && self.data.lens_forceCustomCommands;
 +        let commands = commands.map(|it| it.commands).unwrap_or_default();
 +
 +        let get = |name: &str| commands.iter().any(|it| it == name) || force;
 +
 +        ClientCommandsConfig {
 +            run_single: get("rust-analyzer.runSingle"),
 +            debug_single: get("rust-analyzer.debugSingle"),
 +            show_reference: get("rust-analyzer.showReferences"),
 +            goto_location: get("rust-analyzer.gotoLocation"),
 +            trigger_parameter_hints: get("editor.action.triggerParameterHints"),
 +        }
 +    }
 +
 +    pub fn highlight_related(&self) -> HighlightRelatedConfig {
 +        HighlightRelatedConfig {
 +            references: self.data.highlightRelated_references_enable,
 +            break_points: self.data.highlightRelated_breakPoints_enable,
 +            exit_points: self.data.highlightRelated_exitPoints_enable,
 +            yield_points: self.data.highlightRelated_yieldPoints_enable,
 +        }
 +    }
 +
 +    pub fn prime_caches_num_threads(&self) -> u8 {
 +        match self.data.cachePriming_numThreads {
 +            0 => num_cpus::get_physical().try_into().unwrap_or(u8::MAX),
 +            n => n,
 +        }
 +    }
 +
 +    pub fn typing_autoclose_angle(&self) -> bool {
 +        self.data.typing_autoClosingAngleBrackets_enable
 +    }
 +}
 +// Deserialization definitions
 +
 +macro_rules! create_bool_or_string_de {
 +    ($ident:ident<$bool:literal, $string:literal>) => {
 +        fn $ident<'de, D>(d: D) -> Result<(), D::Error>
 +        where
 +            D: serde::Deserializer<'de>,
 +        {
 +            struct V;
 +            impl<'de> serde::de::Visitor<'de> for V {
 +                type Value = ();
 +
 +                fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
 +                    formatter.write_str(concat!(
 +                        stringify!($bool),
 +                        " or \"",
 +                        stringify!($string),
 +                        "\""
 +                    ))
 +                }
 +
 +                fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
 +                where
 +                    E: serde::de::Error,
 +                {
 +                    match v {
 +                        $bool => Ok(()),
 +                        _ => Err(serde::de::Error::invalid_value(
 +                            serde::de::Unexpected::Bool(v),
 +                            &self,
 +                        )),
 +                    }
 +                }
 +
 +                fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
 +                where
 +                    E: serde::de::Error,
 +                {
 +                    match v {
 +                        $string => Ok(()),
 +                        _ => Err(serde::de::Error::invalid_value(
 +                            serde::de::Unexpected::Str(v),
 +                            &self,
 +                        )),
 +                    }
 +                }
 +
 +                fn visit_enum<A>(self, a: A) -> Result<Self::Value, A::Error>
 +                where
 +                    A: serde::de::EnumAccess<'de>,
 +                {
 +                    use serde::de::VariantAccess;
 +                    let (variant, va) = a.variant::<&'de str>()?;
 +                    va.unit_variant()?;
 +                    match variant {
 +                        $string => Ok(()),
 +                        _ => Err(serde::de::Error::invalid_value(
 +                            serde::de::Unexpected::Str(variant),
 +                            &self,
 +                        )),
 +                    }
 +                }
 +            }
 +            d.deserialize_any(V)
 +        }
 +    };
 +}
 +create_bool_or_string_de!(true_or_always<true, "always">);
 +create_bool_or_string_de!(false_or_never<false, "never">);
 +
 +macro_rules! named_unit_variant {
 +    ($variant:ident) => {
 +        pub(super) fn $variant<'de, D>(deserializer: D) -> Result<(), D::Error>
 +        where
 +            D: serde::Deserializer<'de>,
 +        {
 +            struct V;
 +            impl<'de> serde::de::Visitor<'de> for V {
 +                type Value = ();
 +                fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 +                    f.write_str(concat!("\"", stringify!($variant), "\""))
 +                }
 +                fn visit_str<E: serde::de::Error>(self, value: &str) -> Result<Self::Value, E> {
 +                    if value == stringify!($variant) {
 +                        Ok(())
 +                    } else {
 +                        Err(E::invalid_value(serde::de::Unexpected::Str(value), &self))
 +                    }
 +                }
 +            }
 +            deserializer.deserialize_str(V)
 +        }
 +    };
 +}
 +
 +mod de_unit_v {
 +    named_unit_variant!(all);
 +    named_unit_variant!(skip_trivial);
 +    named_unit_variant!(mutable);
 +    named_unit_variant!(with_block);
 +}
 +
 +#[derive(Deserialize, Debug, Clone, Copy)]
 +#[serde(rename_all = "snake_case")]
 +enum SnippetScopeDef {
 +    Expr,
 +    Item,
 +    Type,
 +}
 +
 +impl Default for SnippetScopeDef {
 +    fn default() -> Self {
 +        SnippetScopeDef::Expr
 +    }
 +}
 +
 +#[derive(Deserialize, Debug, Clone, Default)]
 +#[serde(default)]
 +struct SnippetDef {
 +    #[serde(deserialize_with = "single_or_array")]
 +    prefix: Vec<String>,
 +    #[serde(deserialize_with = "single_or_array")]
 +    postfix: Vec<String>,
 +    description: Option<String>,
 +    #[serde(deserialize_with = "single_or_array")]
 +    body: Vec<String>,
 +    #[serde(deserialize_with = "single_or_array")]
 +    requires: Vec<String>,
 +    scope: SnippetScopeDef,
 +}
 +
 +fn single_or_array<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
 +where
 +    D: serde::Deserializer<'de>,
 +{
 +    struct SingleOrVec;
 +
 +    impl<'de> serde::de::Visitor<'de> for SingleOrVec {
 +        type Value = Vec<String>;
 +
 +        fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 +            formatter.write_str("string or array of strings")
 +        }
 +
 +        fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
 +        where
 +            E: serde::de::Error,
 +        {
 +            Ok(vec![value.to_owned()])
 +        }
 +
 +        fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
 +        where
 +            A: serde::de::SeqAccess<'de>,
 +        {
 +            Deserialize::deserialize(serde::de::value::SeqAccessDeserializer::new(seq))
 +        }
 +    }
 +
 +    deserializer.deserialize_any(SingleOrVec)
 +}
 +
 +#[derive(Deserialize, Debug, Clone)]
 +#[serde(untagged)]
 +enum ManifestOrProjectJson {
 +    Manifest(PathBuf),
 +    ProjectJson(ProjectJsonData),
 +}
 +
 +#[derive(Deserialize, Debug, Clone)]
 +#[serde(rename_all = "snake_case")]
 +enum ExprFillDefaultDef {
 +    Todo,
 +    Default,
 +}
 +
 +#[derive(Deserialize, Debug, Clone)]
 +#[serde(rename_all = "snake_case")]
 +enum ImportGranularityDef {
 +    Preserve,
 +    Item,
 +    Crate,
 +    Module,
 +}
 +
 +#[derive(Deserialize, Debug, Copy, Clone)]
 +#[serde(rename_all = "snake_case")]
 +enum CallableCompletionDef {
 +    FillArguments,
 +    AddParentheses,
 +    None,
 +}
 +
 +#[derive(Deserialize, Debug, Clone)]
 +#[serde(untagged)]
 +enum CargoFeaturesDef {
 +    #[serde(deserialize_with = "de_unit_v::all")]
 +    All,
 +    Selected(Vec<String>),
 +}
 +
 +#[derive(Deserialize, Debug, Clone)]
 +#[serde(rename_all = "snake_case")]
 +enum InvocationStrategy {
 +    Once,
 +    PerWorkspace,
 +}
 +
 +#[derive(Deserialize, Debug, Clone)]
 +#[serde(rename_all = "snake_case")]
 +enum InvocationLocation {
 +    Root,
 +    Workspace,
 +}
 +
 +#[derive(Deserialize, Debug, Clone)]
 +#[serde(untagged)]
 +enum LifetimeElisionDef {
 +    #[serde(deserialize_with = "true_or_always")]
 +    Always,
 +    #[serde(deserialize_with = "false_or_never")]
 +    Never,
 +    #[serde(deserialize_with = "de_unit_v::skip_trivial")]
 +    SkipTrivial,
 +}
 +
 +#[derive(Deserialize, Debug, Clone)]
 +#[serde(untagged)]
 +enum ClosureReturnTypeHintsDef {
 +    #[serde(deserialize_with = "true_or_always")]
 +    Always,
 +    #[serde(deserialize_with = "false_or_never")]
 +    Never,
 +    #[serde(deserialize_with = "de_unit_v::with_block")]
 +    WithBlock,
 +}
 +
 +#[derive(Deserialize, Debug, Clone)]
 +#[serde(untagged)]
 +enum ReborrowHintsDef {
 +    #[serde(deserialize_with = "true_or_always")]
 +    Always,
 +    #[serde(deserialize_with = "false_or_never")]
 +    Never,
 +    #[serde(deserialize_with = "de_unit_v::mutable")]
 +    Mutable,
 +}
 +
 +#[derive(Deserialize, Debug, Clone)]
 +#[serde(rename_all = "snake_case")]
 +enum FilesWatcherDef {
 +    Client,
 +    Notify,
 +    Server,
 +}
 +
 +#[derive(Deserialize, Debug, Clone)]
 +#[serde(rename_all = "snake_case")]
 +enum ImportPrefixDef {
 +    Plain,
 +    #[serde(alias = "self")]
 +    BySelf,
 +    #[serde(alias = "crate")]
 +    ByCrate,
 +}
 +
 +#[derive(Deserialize, Debug, Clone)]
 +#[serde(rename_all = "snake_case")]
 +enum WorkspaceSymbolSearchScopeDef {
 +    Workspace,
 +    WorkspaceAndDependencies,
 +}
 +
 +#[derive(Deserialize, Debug, Clone)]
 +#[serde(rename_all = "snake_case")]
 +enum SignatureDetail {
 +    Full,
 +    Parameters,
 +}
 +
 +#[derive(Deserialize, Debug, Clone)]
 +#[serde(rename_all = "snake_case")]
 +enum WorkspaceSymbolSearchKindDef {
 +    OnlyTypes,
 +    AllSymbols,
 +}
 +
 +macro_rules! _config_data {
 +    (struct $name:ident {
 +        $(
 +            $(#[doc=$doc:literal])*
 +            $field:ident $(| $alias:ident)*: $ty:ty = $default:expr,
 +        )*
 +    }) => {
 +        #[allow(non_snake_case)]
 +        #[derive(Debug, Clone)]
 +        struct $name { $($field: $ty,)* }
 +        impl $name {
 +            fn from_json(mut json: serde_json::Value, error_sink: &mut Vec<(String, serde_json::Error)>) -> $name {
 +                $name {$(
 +                    $field: get_field(
 +                        &mut json,
 +                        error_sink,
 +                        stringify!($field),
 +                        None$(.or(Some(stringify!($alias))))*,
 +                        $default,
 +                    ),
 +                )*}
 +            }
 +
 +            fn json_schema() -> serde_json::Value {
 +                schema(&[
 +                    $({
 +                        let field = stringify!($field);
 +                        let ty = stringify!($ty);
 +
 +                        (field, ty, &[$($doc),*], $default)
 +                    },)*
 +                ])
 +            }
 +
 +            #[cfg(test)]
 +            fn manual() -> String {
 +                manual(&[
 +                    $({
 +                        let field = stringify!($field);
 +                        let ty = stringify!($ty);
 +
 +                        (field, ty, &[$($doc),*], $default)
 +                    },)*
 +                ])
 +            }
 +        }
 +
 +        #[test]
 +        fn fields_are_sorted() {
 +            [$(stringify!($field)),*].windows(2).for_each(|w| assert!(w[0] <= w[1], "{} <= {} does not hold", w[0], w[1]));
 +        }
 +    };
 +}
 +use _config_data as config_data;
 +
 +fn get_field<T: DeserializeOwned>(
 +    json: &mut serde_json::Value,
 +    error_sink: &mut Vec<(String, serde_json::Error)>,
 +    field: &'static str,
 +    alias: Option<&'static str>,
 +    default: &str,
 +) -> T {
 +    let default = serde_json::from_str(default).unwrap();
 +    // XXX: check alias first, to work-around the VS Code where it pre-fills the
 +    // defaults instead of sending an empty object.
 +    alias
 +        .into_iter()
 +        .chain(iter::once(field))
 +        .find_map(move |field| {
 +            let mut pointer = field.replace('_', "/");
 +            pointer.insert(0, '/');
 +            json.pointer_mut(&pointer).and_then(|it| match serde_json::from_value(it.take()) {
 +                Ok(it) => Some(it),
 +                Err(e) => {
 +                    tracing::warn!("Failed to deserialize config field at {}: {:?}", pointer, e);
 +                    error_sink.push((pointer, e));
 +                    None
 +                }
 +            })
 +        })
 +        .unwrap_or(default)
 +}
 +
 +fn schema(fields: &[(&'static str, &'static str, &[&str], &str)]) -> serde_json::Value {
 +    for ((f1, ..), (f2, ..)) in fields.iter().zip(&fields[1..]) {
 +        fn key(f: &str) -> &str {
 +            f.splitn(2, '_').next().unwrap()
 +        }
 +        assert!(key(f1) <= key(f2), "wrong field order: {:?} {:?}", f1, f2);
 +    }
 +
 +    let map = fields
 +        .iter()
 +        .map(|(field, ty, doc, default)| {
 +            let name = field.replace('_', ".");
 +            let name = format!("rust-analyzer.{}", name);
 +            let props = field_props(field, ty, doc, default);
 +            (name, props)
 +        })
 +        .collect::<serde_json::Map<_, _>>();
 +    map.into()
 +}
 +
 +fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json::Value {
 +    let doc = doc_comment_to_string(doc);
 +    let doc = doc.trim_end_matches('\n');
 +    assert!(
 +        doc.ends_with('.') && doc.starts_with(char::is_uppercase),
 +        "bad docs for {}: {:?}",
 +        field,
 +        doc
 +    );
 +    let default = default.parse::<serde_json::Value>().unwrap();
 +
 +    let mut map = serde_json::Map::default();
 +    macro_rules! set {
 +        ($($key:literal: $value:tt),*$(,)?) => {{$(
 +            map.insert($key.into(), serde_json::json!($value));
 +        )*}};
 +    }
 +    set!("markdownDescription": doc);
 +    set!("default": default);
 +
 +    match ty {
 +        "bool" => set!("type": "boolean"),
 +        "usize" => set!("type": "integer", "minimum": 0),
 +        "String" => set!("type": "string"),
 +        "Vec<String>" => set! {
 +            "type": "array",
 +            "items": { "type": "string" },
 +        },
 +        "Vec<PathBuf>" => set! {
 +            "type": "array",
 +            "items": { "type": "string" },
 +        },
 +        "FxHashSet<String>" => set! {
 +            "type": "array",
 +            "items": { "type": "string" },
 +            "uniqueItems": true,
 +        },
 +        "FxHashMap<Box<str>, Box<[Box<str>]>>" => set! {
 +            "type": "object",
 +        },
 +        "FxHashMap<String, SnippetDef>" => set! {
 +            "type": "object",
 +        },
 +        "FxHashMap<String, String>" => set! {
 +            "type": "object",
 +        },
 +        "Option<usize>" => set! {
 +            "type": ["null", "integer"],
 +            "minimum": 0,
 +        },
 +        "Option<String>" => set! {
 +            "type": ["null", "string"],
 +        },
 +        "Option<PathBuf>" => set! {
 +            "type": ["null", "string"],
 +        },
 +        "Option<bool>" => set! {
 +            "type": ["null", "boolean"],
 +        },
 +        "Option<Vec<String>>" => set! {
 +            "type": ["null", "array"],
 +            "items": { "type": "string" },
 +        },
 +        "MergeBehaviorDef" => set! {
 +            "type": "string",
 +            "enum": ["none", "crate", "module"],
 +            "enumDescriptions": [
 +                "Do not merge imports at all.",
 +                "Merge imports from the same crate into a single `use` statement.",
 +                "Merge imports from the same module into a single `use` statement."
 +            ],
 +        },
 +        "ExprFillDefaultDef" => set! {
 +            "type": "string",
 +            "enum": ["todo", "default"],
 +            "enumDescriptions": [
 +                "Fill missing expressions with the `todo` macro",
 +                "Fill missing expressions with reasonable defaults, `new` or `default` constructors."
 +            ],
 +        },
 +        "ImportGranularityDef" => set! {
 +            "type": "string",
 +            "enum": ["preserve", "crate", "module", "item"],
 +            "enumDescriptions": [
 +                "Do not change the granularity of any imports and preserve the original structure written by the developer.",
 +                "Merge imports from the same crate into a single use statement. Conversely, imports from different crates are split into separate statements.",
 +                "Merge imports from the same module into a single use statement. Conversely, imports from different modules are split into separate statements.",
 +                "Flatten imports so that each has its own use statement."
 +            ],
 +        },
 +        "ImportPrefixDef" => set! {
 +            "type": "string",
 +            "enum": [
 +                "plain",
 +                "self",
 +                "crate"
 +            ],
 +            "enumDescriptions": [
 +                "Insert import paths relative to the current module, using up to one `super` prefix if the parent module contains the requested item.",
 +                "Insert import paths relative to the current module, using up to one `super` prefix if the parent module contains the requested item. Prefixes `self` in front of the path if it starts with a module.",
 +                "Force import paths to be absolute by always starting them with `crate` or the extern crate name they come from."
 +            ],
 +        },
 +        "Vec<ManifestOrProjectJson>" => set! {
 +            "type": "array",
 +            "items": { "type": ["string", "object"] },
 +        },
 +        "WorkspaceSymbolSearchScopeDef" => set! {
 +            "type": "string",
 +            "enum": ["workspace", "workspace_and_dependencies"],
 +            "enumDescriptions": [
 +                "Search in current workspace only.",
 +                "Search in current workspace and dependencies."
 +            ],
 +        },
 +        "WorkspaceSymbolSearchKindDef" => set! {
 +            "type": "string",
 +            "enum": ["only_types", "all_symbols"],
 +            "enumDescriptions": [
 +                "Search for types only.",
 +                "Search for all symbols kinds."
 +            ],
 +        },
 +        "ParallelCachePrimingNumThreads" => set! {
 +            "type": "number",
 +            "minimum": 0,
 +            "maximum": 255
 +        },
 +        "LifetimeElisionDef" => set! {
 +            "type": "string",
 +            "enum": [
 +                "always",
 +                "never",
 +                "skip_trivial"
 +            ],
 +            "enumDescriptions": [
 +                "Always show lifetime elision hints.",
 +                "Never show lifetime elision hints.",
 +                "Only show lifetime elision hints if a return type is involved."
 +            ]
 +        },
 +        "ClosureReturnTypeHintsDef" => set! {
 +            "type": "string",
 +            "enum": [
 +                "always",
 +                "never",
 +                "with_block"
 +            ],
 +            "enumDescriptions": [
 +                "Always show type hints for return types of closures.",
 +                "Never show type hints for return types of closures.",
 +                "Only show type hints for return types of closures with blocks."
 +            ]
 +        },
 +        "ReborrowHintsDef" => set! {
 +            "type": "string",
 +            "enum": [
 +                "always",
 +                "never",
 +                "mutable"
 +            ],
 +            "enumDescriptions": [
 +                "Always show reborrow hints.",
 +                "Never show reborrow hints.",
 +                "Only show mutable reborrow hints."
 +            ]
 +        },
 +        "CargoFeaturesDef" => set! {
 +            "anyOf": [
 +                {
 +                    "type": "string",
 +                    "enum": [
 +                        "all"
 +                    ],
 +                    "enumDescriptions": [
 +                        "Pass `--all-features` to cargo",
 +                    ]
 +                },
 +                {
 +                    "type": "array",
 +                    "items": { "type": "string" }
 +                }
 +            ],
 +        },
 +        "Option<CargoFeaturesDef>" => set! {
 +            "anyOf": [
 +                {
 +                    "type": "string",
 +                    "enum": [
 +                        "all"
 +                    ],
 +                    "enumDescriptions": [
 +                        "Pass `--all-features` to cargo",
 +                    ]
 +                },
 +                {
 +                    "type": "array",
 +                    "items": { "type": "string" }
 +                },
 +                { "type": "null" }
 +            ],
 +        },
 +        "CallableCompletionDef" => set! {
 +            "type": "string",
 +            "enum": [
 +                "fill_arguments",
 +                "add_parentheses",
 +                "none",
 +            ],
 +            "enumDescriptions": [
 +                "Add call parentheses and pre-fill arguments.",
 +                "Add call parentheses.",
 +                "Do no snippet completions for callables."
 +            ]
 +        },
 +        "SignatureDetail" => set! {
 +            "type": "string",
 +            "enum": ["full", "parameters"],
 +            "enumDescriptions": [
 +                "Show the entire signature.",
 +                "Show only the parameters."
 +            ],
 +        },
 +        "FilesWatcherDef" => set! {
 +            "type": "string",
 +            "enum": ["client", "server"],
 +            "enumDescriptions": [
 +                "Use the client (editor) to watch files for changes",
 +                "Use server-side file watching",
 +            ],
 +        },
 +        "AnnotationLocation" => set! {
 +            "type": "string",
 +            "enum": ["above_name", "above_whole_item"],
 +            "enumDescriptions": [
 +                "Render annotations above the name of the item.",
 +                "Render annotations above the whole item, including documentation comments and attributes."
 +            ],
 +        },
 +        "InvocationStrategy" => set! {
 +            "type": "string",
 +            "enum": ["per_workspace", "once"],
 +            "enumDescriptions": [
 +                "The command will be executed for each workspace.",
 +                "The command will be executed once."
 +            ],
 +        },
 +        "InvocationLocation" => set! {
 +            "type": "string",
 +            "enum": ["workspace", "root"],
 +            "enumDescriptions": [
 +                "The command will be executed in the corresponding workspace root.",
 +                "The command will be executed in the project root."
 +            ],
 +        },
 +        _ => panic!("missing entry for {}: {}", ty, default),
 +    }
 +
 +    map.into()
 +}
 +
 +#[cfg(test)]
 +fn manual(fields: &[(&'static str, &'static str, &[&str], &str)]) -> String {
 +    fields
 +        .iter()
 +        .map(|(field, _ty, doc, default)| {
 +            let name = format!("rust-analyzer.{}", field.replace('_', "."));
 +            let doc = doc_comment_to_string(*doc);
 +            if default.contains('\n') {
 +                format!(
 +                    r#"[[{}]]{}::
 ++
 +--
 +Default:
 +----
 +{}
 +----
 +{}
 +--
 +"#,
 +                    name, name, default, doc
 +                )
 +            } else {
 +                format!("[[{}]]{} (default: `{}`)::\n+\n--\n{}--\n", name, name, default, doc)
 +            }
 +        })
 +        .collect::<String>()
 +}
 +
 +fn doc_comment_to_string(doc: &[&str]) -> String {
 +    doc.iter().map(|it| it.strip_prefix(' ').unwrap_or(it)).map(|it| format!("{}\n", it)).collect()
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use std::fs;
 +
 +    use test_utils::{ensure_file_contents, project_root};
 +
 +    use super::*;
 +
 +    #[test]
 +    fn generate_package_json_config() {
 +        let s = Config::json_schema();
 +        let schema = format!("{:#}", s);
 +        let mut schema = schema
 +            .trim_start_matches('{')
 +            .trim_end_matches('}')
 +            .replace("  ", "    ")
 +            .replace('\n', "\n            ")
 +            .trim_start_matches('\n')
 +            .trim_end()
 +            .to_string();
 +        schema.push_str(",\n");
 +
 +        // Transform the asciidoc form link to markdown style.
 +        //
 +        // https://link[text] => [text](https://link)
 +        let url_matches = schema.match_indices("https://");
 +        let mut url_offsets = url_matches.map(|(idx, _)| idx).collect::<Vec<usize>>();
 +        url_offsets.reverse();
 +        for idx in url_offsets {
 +            let link = &schema[idx..];
 +            // matching on whitespace to ignore normal links
 +            if let Some(link_end) = link.find(|c| c == ' ' || c == '[') {
 +                if link.chars().nth(link_end) == Some('[') {
 +                    if let Some(link_text_end) = link.find(']') {
 +                        let link_text = link[link_end..(link_text_end + 1)].to_string();
 +
 +                        schema.replace_range((idx + link_end)..(idx + link_text_end + 1), "");
 +                        schema.insert(idx, '(');
 +                        schema.insert(idx + link_end + 1, ')');
 +                        schema.insert_str(idx, &link_text);
 +                    }
 +                }
 +            }
 +        }
 +
 +        let package_json_path = project_root().join("editors/code/package.json");
 +        let mut package_json = fs::read_to_string(&package_json_path).unwrap();
 +
 +        let start_marker = "                \"$generated-start\": {},\n";
 +        let end_marker = "                \"$generated-end\": {}\n";
 +
 +        let start = package_json.find(start_marker).unwrap() + start_marker.len();
 +        let end = package_json.find(end_marker).unwrap();
 +
 +        let p = remove_ws(&package_json[start..end]);
 +        let s = remove_ws(&schema);
 +        if !p.contains(&s) {
 +            package_json.replace_range(start..end, &schema);
 +            ensure_file_contents(&package_json_path, &package_json)
 +        }
 +    }
 +
 +    #[test]
 +    fn generate_config_documentation() {
 +        let docs_path = project_root().join("docs/user/generated_config.adoc");
 +        let expected = ConfigData::manual();
 +        ensure_file_contents(&docs_path, &expected);
 +    }
 +
 +    fn remove_ws(text: &str) -> String {
 +        text.replace(char::is_whitespace, "")
 +    }
 +}
index 4057a75e7c1e693d053e987ac39bb588ec02b9fa,0000000000000000000000000000000000000000..8c26009add2bb04fbab8203720e32518213270bc
mode 100644,000000..100644
--- /dev/null
@@@ -1,920 -1,0 +1,940 @@@
 +//! This module contains free-standing functions for creating AST fragments out
 +//! of smaller pieces.
 +//!
 +//! Note that all functions here intended to be stupid constructors, which just
 +//! assemble a finish node from immediate children. If you want to do something
 +//! smarter than that, it belongs to the `ext` submodule.
 +//!
 +//! Keep in mind that `from_text` functions should be kept private. The public
 +//! API should require to assemble every node piecewise. The trick of
 +//! `parse(format!())` we use internally is an implementation detail -- long
 +//! term, it will be replaced with direct tree manipulation.
 +use itertools::Itertools;
 +use stdx::{format_to, never};
 +
 +use crate::{ast, AstNode, SourceFile, SyntaxKind, SyntaxToken};
 +
 +/// While the parent module defines basic atomic "constructors", the `ext`
 +/// module defines shortcuts for common things.
 +///
 +/// It's named `ext` rather than `shortcuts` just to keep it short.
 +pub mod ext {
 +    use super::*;
 +
 +    pub fn simple_ident_pat(name: ast::Name) -> ast::IdentPat {
 +        return from_text(&name.text());
 +
 +        fn from_text(text: &str) -> ast::IdentPat {
 +            ast_from_text(&format!("fn f({text}: ())"))
 +        }
 +    }
 +    pub fn ident_path(ident: &str) -> ast::Path {
 +        path_unqualified(path_segment(name_ref(ident)))
 +    }
 +
 +    pub fn path_from_idents<'a>(
 +        parts: impl std::iter::IntoIterator<Item = &'a str>,
 +    ) -> Option<ast::Path> {
 +        let mut iter = parts.into_iter();
 +        let base = ext::ident_path(iter.next()?);
 +        let path = iter.fold(base, |base, s| {
 +            let path = ext::ident_path(s);
 +            path_concat(base, path)
 +        });
 +        Some(path)
 +    }
 +
 +    pub fn field_from_idents<'a>(
 +        parts: impl std::iter::IntoIterator<Item = &'a str>,
 +    ) -> Option<ast::Expr> {
 +        let mut iter = parts.into_iter();
 +        let base = expr_path(ext::ident_path(iter.next()?));
 +        let expr = iter.fold(base, expr_field);
 +        Some(expr)
 +    }
 +
 +    pub fn expr_unreachable() -> ast::Expr {
 +        expr_from_text("unreachable!()")
 +    }
 +    pub fn expr_todo() -> ast::Expr {
 +        expr_from_text("todo!()")
 +    }
 +    pub fn expr_ty_default(ty: &ast::Type) -> ast::Expr {
 +        expr_from_text(&format!("{ty}::default()"))
 +    }
 +    pub fn expr_ty_new(ty: &ast::Type) -> ast::Expr {
 +        expr_from_text(&format!("{ty}::new()"))
 +    }
 +
 +    pub fn zero_number() -> ast::Expr {
 +        expr_from_text("0")
 +    }
 +    pub fn zero_float() -> ast::Expr {
 +        expr_from_text("0.0")
 +    }
 +    pub fn empty_str() -> ast::Expr {
 +        expr_from_text(r#""""#)
 +    }
 +    pub fn empty_char() -> ast::Expr {
 +        expr_from_text("'\x00'")
 +    }
 +    pub fn default_bool() -> ast::Expr {
 +        expr_from_text("false")
 +    }
 +    pub fn option_none() -> ast::Expr {
 +        expr_from_text("None")
 +    }
 +    pub fn empty_block_expr() -> ast::BlockExpr {
 +        block_expr(None, None)
 +    }
 +
 +    pub fn ty_name(name: ast::Name) -> ast::Type {
 +        ty_path(ident_path(&name.to_string()))
 +    }
 +    pub fn ty_bool() -> ast::Type {
 +        ty_path(ident_path("bool"))
 +    }
 +    pub fn ty_option(t: ast::Type) -> ast::Type {
 +        ty_from_text(&format!("Option<{t}>"))
 +    }
 +    pub fn ty_result(t: ast::Type, e: ast::Type) -> ast::Type {
 +        ty_from_text(&format!("Result<{t}, {e}>"))
 +    }
 +}
 +
 +pub fn name(name: &str) -> ast::Name {
 +    let raw_escape = raw_ident_esc(name);
 +    ast_from_text(&format!("mod {raw_escape}{name};"))
 +}
 +pub fn name_ref(name_ref: &str) -> ast::NameRef {
 +    let raw_escape = raw_ident_esc(name_ref);
 +    ast_from_text(&format!("fn f() {{ {raw_escape}{name_ref}; }}"))
 +}
 +fn raw_ident_esc(ident: &str) -> &'static str {
 +    let is_keyword = parser::SyntaxKind::from_keyword(ident).is_some();
 +    if is_keyword && !matches!(ident, "self" | "crate" | "super" | "Self") {
 +        "r#"
 +    } else {
 +        ""
 +    }
 +}
 +
 +pub fn lifetime(text: &str) -> ast::Lifetime {
 +    let mut text = text;
 +    let tmp;
 +    if never!(!text.starts_with('\'')) {
 +        tmp = format!("'{text}");
 +        text = &tmp;
 +    }
 +    ast_from_text(&format!("fn f<{text}>() {{ }}"))
 +}
 +
 +// FIXME: replace stringly-typed constructor with a family of typed ctors, a-la
 +// `expr_xxx`.
 +pub fn ty(text: &str) -> ast::Type {
 +    ty_from_text(text)
 +}
 +pub fn ty_placeholder() -> ast::Type {
 +    ty_from_text("_")
 +}
 +pub fn ty_unit() -> ast::Type {
 +    ty_from_text("()")
 +}
 +pub fn ty_tuple(types: impl IntoIterator<Item = ast::Type>) -> ast::Type {
 +    let mut count: usize = 0;
 +    let mut contents = types.into_iter().inspect(|_| count += 1).join(", ");
 +    if count == 1 {
 +        contents.push(',');
 +    }
 +
 +    ty_from_text(&format!("({contents})"))
 +}
 +pub fn ty_ref(target: ast::Type, exclusive: bool) -> ast::Type {
 +    ty_from_text(&if exclusive { format!("&mut {target}") } else { format!("&{target}") })
 +}
 +pub fn ty_path(path: ast::Path) -> ast::Type {
 +    ty_from_text(&path.to_string())
 +}
 +fn ty_from_text(text: &str) -> ast::Type {
 +    ast_from_text(&format!("type _T = {text};"))
 +}
 +
 +pub fn assoc_item_list() -> ast::AssocItemList {
 +    ast_from_text("impl C for D {}")
 +}
 +
 +// FIXME: `ty_params` should be `ast::GenericArgList`
 +pub fn impl_(
 +    ty: ast::Path,
 +    params: Option<ast::GenericParamList>,
 +    ty_params: Option<ast::GenericParamList>,
 +) -> ast::Impl {
 +    let params = match params {
 +        Some(params) => params.to_string(),
 +        None => String::new(),
 +    };
 +    let ty_params = match ty_params {
 +        Some(params) => params.to_string(),
 +        None => String::new(),
 +    };
 +    ast_from_text(&format!("impl{params} {ty}{ty_params} {{}}"))
 +}
 +
 +pub fn impl_trait(
 +    trait_: ast::Path,
 +    ty: ast::Path,
 +    ty_params: Option<ast::GenericParamList>,
 +) -> ast::Impl {
 +    let ty_params = ty_params.map_or_else(String::new, |params| params.to_string());
 +    ast_from_text(&format!("impl{ty_params} {trait_} for {ty}{ty_params} {{}}"))
 +}
 +
 +pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment {
 +    ast_from_text(&format!("type __ = {name_ref};"))
 +}
 +
 +pub fn path_segment_ty(type_ref: ast::Type, trait_ref: Option<ast::PathType>) -> ast::PathSegment {
 +    let text = match trait_ref {
 +        Some(trait_ref) => format!("fn f(x: <{type_ref} as {trait_ref}>) {{}}"),
 +        None => format!("fn f(x: <{type_ref}>) {{}}"),
 +    };
 +    ast_from_text(&text)
 +}
 +
 +pub fn path_segment_self() -> ast::PathSegment {
 +    ast_from_text("use self;")
 +}
 +
 +pub fn path_segment_super() -> ast::PathSegment {
 +    ast_from_text("use super;")
 +}
 +
 +pub fn path_segment_crate() -> ast::PathSegment {
 +    ast_from_text("use crate;")
 +}
 +
 +pub fn path_unqualified(segment: ast::PathSegment) -> ast::Path {
 +    ast_from_text(&format!("type __ = {segment};"))
 +}
 +
 +pub fn path_qualified(qual: ast::Path, segment: ast::PathSegment) -> ast::Path {
 +    ast_from_text(&format!("{qual}::{segment}"))
 +}
 +// FIXME: path concatenation operation doesn't make sense as AST op.
 +pub fn path_concat(first: ast::Path, second: ast::Path) -> ast::Path {
 +    ast_from_text(&format!("type __ = {first}::{second};"))
 +}
 +
 +pub fn path_from_segments(
 +    segments: impl IntoIterator<Item = ast::PathSegment>,
 +    is_abs: bool,
 +) -> ast::Path {
 +    let segments = segments.into_iter().map(|it| it.syntax().clone()).join("::");
 +    ast_from_text(&if is_abs {
 +        format!("fn f(x: ::{segments}) {{}}")
 +    } else {
 +        format!("fn f(x: {segments}) {{}}")
 +    })
 +}
 +
 +pub fn join_paths(paths: impl IntoIterator<Item = ast::Path>) -> ast::Path {
 +    let paths = paths.into_iter().map(|it| it.syntax().clone()).join("::");
 +    ast_from_text(&format!("type __ = {paths};"))
 +}
 +
 +// FIXME: should not be pub
 +pub fn path_from_text(text: &str) -> ast::Path {
 +    ast_from_text(&format!("fn main() {{ let test = {text}; }}"))
 +}
 +
 +pub fn use_tree_glob() -> ast::UseTree {
 +    ast_from_text("use *;")
 +}
 +pub fn use_tree(
 +    path: ast::Path,
 +    use_tree_list: Option<ast::UseTreeList>,
 +    alias: Option<ast::Rename>,
 +    add_star: bool,
 +) -> ast::UseTree {
 +    let mut buf = "use ".to_string();
 +    buf += &path.syntax().to_string();
 +    if let Some(use_tree_list) = use_tree_list {
 +        format_to!(buf, "::{use_tree_list}");
 +    }
 +    if add_star {
 +        buf += "::*";
 +    }
 +
 +    if let Some(alias) = alias {
 +        format_to!(buf, " {alias}");
 +    }
 +    ast_from_text(&buf)
 +}
 +
 +pub fn use_tree_list(use_trees: impl IntoIterator<Item = ast::UseTree>) -> ast::UseTreeList {
 +    let use_trees = use_trees.into_iter().map(|it| it.syntax().clone()).join(", ");
 +    ast_from_text(&format!("use {{{use_trees}}};"))
 +}
 +
 +pub fn use_(visibility: Option<ast::Visibility>, use_tree: ast::UseTree) -> ast::Use {
 +    let visibility = match visibility {
 +        None => String::new(),
 +        Some(it) => format!("{it} "),
 +    };
 +    ast_from_text(&format!("{visibility}use {use_tree};"))
 +}
 +
 +pub fn record_expr(path: ast::Path, fields: ast::RecordExprFieldList) -> ast::RecordExpr {
 +    ast_from_text(&format!("fn f() {{ {path} {fields} }}"))
 +}
 +
 +pub fn record_expr_field_list(
 +    fields: impl IntoIterator<Item = ast::RecordExprField>,
 +) -> ast::RecordExprFieldList {
 +    let fields = fields.into_iter().join(", ");
 +    ast_from_text(&format!("fn f() {{ S {{ {fields} }} }}"))
 +}
 +
 +pub fn record_expr_field(name: ast::NameRef, expr: Option<ast::Expr>) -> ast::RecordExprField {
 +    return match expr {
 +        Some(expr) => from_text(&format!("{name}: {expr}")),
 +        None => from_text(&name.to_string()),
 +    };
 +
 +    fn from_text(text: &str) -> ast::RecordExprField {
 +        ast_from_text(&format!("fn f() {{ S {{ {text}, }} }}"))
 +    }
 +}
 +
 +pub fn record_field(
 +    visibility: Option<ast::Visibility>,
 +    name: ast::Name,
 +    ty: ast::Type,
 +) -> ast::RecordField {
 +    let visibility = match visibility {
 +        None => String::new(),
 +        Some(it) => format!("{it} "),
 +    };
 +    ast_from_text(&format!("struct S {{ {visibility}{name}: {ty}, }}"))
 +}
 +
 +// TODO
 +pub fn block_expr(
 +    stmts: impl IntoIterator<Item = ast::Stmt>,
 +    tail_expr: Option<ast::Expr>,
 +) -> ast::BlockExpr {
 +    let mut buf = "{\n".to_string();
 +    for stmt in stmts.into_iter() {
 +        format_to!(buf, "    {stmt}\n");
 +    }
 +    if let Some(tail_expr) = tail_expr {
 +        format_to!(buf, "    {tail_expr}\n");
 +    }
 +    buf += "}";
 +    ast_from_text(&format!("fn f() {buf}"))
 +}
 +
++pub fn tail_only_block_expr(tail_expr: ast::Expr) -> ast::BlockExpr {
++    ast_from_text(&format!("fn f() {{ {tail_expr} }}"))
++}
++
 +/// Ideally this function wouldn't exist since it involves manual indenting.
 +/// It differs from `make::block_expr` by also supporting comments.
 +///
 +/// FIXME: replace usages of this with the mutable syntax tree API
 +pub fn hacky_block_expr_with_comments(
 +    elements: impl IntoIterator<Item = crate::SyntaxElement>,
 +    tail_expr: Option<ast::Expr>,
 +) -> ast::BlockExpr {
 +    let mut buf = "{\n".to_string();
 +    for node_or_token in elements.into_iter() {
 +        match node_or_token {
 +            rowan::NodeOrToken::Node(n) => format_to!(buf, "    {n}\n"),
 +            rowan::NodeOrToken::Token(t) if t.kind() == SyntaxKind::COMMENT => {
 +                format_to!(buf, "    {t}\n")
 +            }
 +            _ => (),
 +        }
 +    }
 +    if let Some(tail_expr) = tail_expr {
 +        format_to!(buf, "    {tail_expr}\n");
 +    }
 +    buf += "}";
 +    ast_from_text(&format!("fn f() {buf}"))
 +}
 +
 +pub fn expr_unit() -> ast::Expr {
 +    expr_from_text("()")
 +}
 +pub fn expr_literal(text: &str) -> ast::Literal {
 +    assert_eq!(text.trim(), text);
 +    ast_from_text(&format!("fn f() {{ let _ = {text}; }}"))
 +}
 +
 +pub fn expr_empty_block() -> ast::Expr {
 +    expr_from_text("{}")
 +}
 +pub fn expr_path(path: ast::Path) -> ast::Expr {
 +    expr_from_text(&path.to_string())
 +}
 +pub fn expr_continue(label: Option<ast::Lifetime>) -> ast::Expr {
 +    match label {
 +        Some(label) => expr_from_text(&format!("continue {label}")),
 +        None => expr_from_text("continue"),
 +    }
 +}
 +// Consider `op: SyntaxKind` instead for nicer syntax at the call-site?
 +pub fn expr_bin_op(lhs: ast::Expr, op: ast::BinaryOp, rhs: ast::Expr) -> ast::Expr {
 +    expr_from_text(&format!("{lhs} {op} {rhs}"))
 +}
 +pub fn expr_break(label: Option<ast::Lifetime>, expr: Option<ast::Expr>) -> ast::Expr {
 +    let mut s = String::from("break");
 +
 +    if let Some(label) = label {
 +        format_to!(s, " {label}");
 +    }
 +
 +    if let Some(expr) = expr {
 +        format_to!(s, " {expr}");
 +    }
 +
 +    expr_from_text(&s)
 +}
 +pub fn expr_return(expr: Option<ast::Expr>) -> ast::Expr {
 +    match expr {
 +        Some(expr) => expr_from_text(&format!("return {expr}")),
 +        None => expr_from_text("return"),
 +    }
 +}
 +pub fn expr_try(expr: ast::Expr) -> ast::Expr {
 +    expr_from_text(&format!("{expr}?"))
 +}
 +pub fn expr_await(expr: ast::Expr) -> ast::Expr {
 +    expr_from_text(&format!("{expr}.await"))
 +}
 +pub fn expr_match(expr: ast::Expr, match_arm_list: ast::MatchArmList) -> ast::Expr {
 +    expr_from_text(&format!("match {expr} {match_arm_list}"))
 +}
 +pub fn expr_if(
 +    condition: ast::Expr,
 +    then_branch: ast::BlockExpr,
 +    else_branch: Option<ast::ElseBranch>,
 +) -> ast::Expr {
 +    let else_branch = match else_branch {
 +        Some(ast::ElseBranch::Block(block)) => format!("else {block}"),
 +        Some(ast::ElseBranch::IfExpr(if_expr)) => format!("else {if_expr}"),
 +        None => String::new(),
 +    };
 +    expr_from_text(&format!("if {condition} {then_branch} {else_branch}"))
 +}
 +pub fn expr_for_loop(pat: ast::Pat, expr: ast::Expr, block: ast::BlockExpr) -> ast::Expr {
 +    expr_from_text(&format!("for {pat} in {expr} {block}"))
 +}
 +
 +pub fn expr_loop(block: ast::BlockExpr) -> ast::Expr {
 +    expr_from_text(&format!("loop {block}"))
 +}
 +
 +pub fn expr_prefix(op: SyntaxKind, expr: ast::Expr) -> ast::Expr {
 +    let token = token(op);
 +    expr_from_text(&format!("{token}{expr}"))
 +}
 +pub fn expr_call(f: ast::Expr, arg_list: ast::ArgList) -> ast::Expr {
 +    expr_from_text(&format!("{f}{arg_list}"))
 +}
 +pub fn expr_method_call(
 +    receiver: ast::Expr,
 +    method: ast::NameRef,
 +    arg_list: ast::ArgList,
 +) -> ast::Expr {
 +    expr_from_text(&format!("{receiver}.{method}{arg_list}"))
 +}
 +pub fn expr_macro_call(f: ast::Expr, arg_list: ast::ArgList) -> ast::Expr {
 +    expr_from_text(&format!("{f}!{arg_list}"))
 +}
 +pub fn expr_ref(expr: ast::Expr, exclusive: bool) -> ast::Expr {
 +    expr_from_text(&if exclusive { format!("&mut {expr}") } else { format!("&{expr}") })
 +}
 +pub fn expr_closure(pats: impl IntoIterator<Item = ast::Param>, expr: ast::Expr) -> ast::Expr {
 +    let params = pats.into_iter().join(", ");
 +    expr_from_text(&format!("|{params}| {expr}"))
 +}
 +pub fn expr_field(receiver: ast::Expr, field: &str) -> ast::Expr {
 +    expr_from_text(&format!("{receiver}.{field}"))
 +}
 +pub fn expr_paren(expr: ast::Expr) -> ast::Expr {
 +    expr_from_text(&format!("({expr})"))
 +}
 +pub fn expr_tuple(elements: impl IntoIterator<Item = ast::Expr>) -> ast::Expr {
 +    let expr = elements.into_iter().format(", ");
 +    expr_from_text(&format!("({expr})"))
 +}
 +pub fn expr_assignment(lhs: ast::Expr, rhs: ast::Expr) -> ast::Expr {
 +    expr_from_text(&format!("{lhs} = {rhs}"))
 +}
 +fn expr_from_text(text: &str) -> ast::Expr {
 +    ast_from_text(&format!("const C: () = {text};"))
 +}
 +pub fn expr_let(pattern: ast::Pat, expr: ast::Expr) -> ast::LetExpr {
 +    ast_from_text(&format!("const _: () = while let {pattern} = {expr} {{}};"))
 +}
 +
 +pub fn arg_list(args: impl IntoIterator<Item = ast::Expr>) -> ast::ArgList {
 +    let args = args.into_iter().format(", ");
 +    ast_from_text(&format!("fn main() {{ ()({args}) }}"))
 +}
 +
 +pub fn ident_pat(ref_: bool, mut_: bool, name: ast::Name) -> ast::IdentPat {
 +    let mut s = String::from("fn f(");
 +    if ref_ {
 +        s.push_str("ref ");
 +    }
 +    if mut_ {
 +        s.push_str("mut ");
 +    }
 +    format_to!(s, "{name}");
 +    s.push_str(": ())");
 +    ast_from_text(&s)
 +}
 +
 +pub fn wildcard_pat() -> ast::WildcardPat {
 +    return from_text("_");
 +
 +    fn from_text(text: &str) -> ast::WildcardPat {
 +        ast_from_text(&format!("fn f({text}: ())"))
 +    }
 +}
 +
 +pub fn literal_pat(lit: &str) -> ast::LiteralPat {
 +    return from_text(lit);
 +
 +    fn from_text(text: &str) -> ast::LiteralPat {
 +        ast_from_text(&format!("fn f() {{ match x {{ {text} => {{}} }} }}"))
 +    }
 +}
 +
 +/// Creates a tuple of patterns from an iterator of patterns.
 +///
 +/// Invariant: `pats` must be length > 0
 +pub fn tuple_pat(pats: impl IntoIterator<Item = ast::Pat>) -> ast::TuplePat {
 +    let mut count: usize = 0;
 +    let mut pats_str = pats.into_iter().inspect(|_| count += 1).join(", ");
 +    if count == 1 {
 +        pats_str.push(',');
 +    }
 +    return from_text(&format!("({pats_str})"));
 +
 +    fn from_text(text: &str) -> ast::TuplePat {
 +        ast_from_text(&format!("fn f({text}: ())"))
 +    }
 +}
 +
 +pub fn tuple_struct_pat(
 +    path: ast::Path,
 +    pats: impl IntoIterator<Item = ast::Pat>,
 +) -> ast::TupleStructPat {
 +    let pats_str = pats.into_iter().join(", ");
 +    return from_text(&format!("{path}({pats_str})"));
 +
 +    fn from_text(text: &str) -> ast::TupleStructPat {
 +        ast_from_text(&format!("fn f({text}: ())"))
 +    }
 +}
 +
 +pub fn record_pat(path: ast::Path, pats: impl IntoIterator<Item = ast::Pat>) -> ast::RecordPat {
 +    let pats_str = pats.into_iter().join(", ");
 +    return from_text(&format!("{path} {{ {pats_str} }}"));
 +
 +    fn from_text(text: &str) -> ast::RecordPat {
 +        ast_from_text(&format!("fn f({text}: ())"))
 +    }
 +}
 +
 +pub fn record_pat_with_fields(path: ast::Path, fields: ast::RecordPatFieldList) -> ast::RecordPat {
 +    ast_from_text(&format!("fn f({path} {fields}: ()))"))
 +}
 +
 +pub fn record_pat_field_list(
 +    fields: impl IntoIterator<Item = ast::RecordPatField>,
 +) -> ast::RecordPatFieldList {
 +    let fields = fields.into_iter().join(", ");
 +    ast_from_text(&format!("fn f(S {{ {fields} }}: ()))"))
 +}
 +
 +pub fn record_pat_field(name_ref: ast::NameRef, pat: ast::Pat) -> ast::RecordPatField {
 +    ast_from_text(&format!("fn f(S {{ {name_ref}: {pat} }}: ()))"))
 +}
 +
 +pub fn record_pat_field_shorthand(name_ref: ast::NameRef) -> ast::RecordPatField {
 +    ast_from_text(&format!("fn f(S {{ {name_ref} }}: ()))"))
 +}
 +
 +/// Returns a `BindPat` if the path has just one segment, a `PathPat` otherwise.
 +pub fn path_pat(path: ast::Path) -> ast::Pat {
 +    return from_text(&path.to_string());
 +    fn from_text(text: &str) -> ast::Pat {
 +        ast_from_text(&format!("fn f({text}: ())"))
 +    }
 +}
 +
 +pub fn match_arm(
 +    pats: impl IntoIterator<Item = ast::Pat>,
 +    guard: Option<ast::Expr>,
 +    expr: ast::Expr,
 +) -> ast::MatchArm {
 +    let pats_str = pats.into_iter().join(" | ");
 +    return match guard {
 +        Some(guard) => from_text(&format!("{pats_str} if {guard} => {expr}")),
 +        None => from_text(&format!("{pats_str} => {expr}")),
 +    };
 +
 +    fn from_text(text: &str) -> ast::MatchArm {
 +        ast_from_text(&format!("fn f() {{ match () {{{text}}} }}"))
 +    }
 +}
 +
 +pub fn match_arm_with_guard(
 +    pats: impl IntoIterator<Item = ast::Pat>,
 +    guard: ast::Expr,
 +    expr: ast::Expr,
 +) -> ast::MatchArm {
 +    let pats_str = pats.into_iter().join(" | ");
 +    return from_text(&format!("{pats_str} if {guard} => {expr}"));
 +
 +    fn from_text(text: &str) -> ast::MatchArm {
 +        ast_from_text(&format!("fn f() {{ match () {{{text}}} }}"))
 +    }
 +}
 +
 +pub fn match_arm_list(arms: impl IntoIterator<Item = ast::MatchArm>) -> ast::MatchArmList {
 +    let arms_str = arms
 +        .into_iter()
 +        .map(|arm| {
 +            let needs_comma = arm.expr().map_or(true, |it| !it.is_block_like());
 +            let comma = if needs_comma { "," } else { "" };
 +            let arm = arm.syntax();
 +            format!("    {arm}{comma}\n")
 +        })
 +        .collect::<String>();
 +    return from_text(&arms_str);
 +
 +    fn from_text(text: &str) -> ast::MatchArmList {
 +        ast_from_text(&format!("fn f() {{ match () {{\n{text}}} }}"))
 +    }
 +}
 +
 +pub fn where_pred(
 +    path: ast::Path,
 +    bounds: impl IntoIterator<Item = ast::TypeBound>,
 +) -> ast::WherePred {
 +    let bounds = bounds.into_iter().join(" + ");
 +    return from_text(&format!("{path}: {bounds}"));
 +
 +    fn from_text(text: &str) -> ast::WherePred {
 +        ast_from_text(&format!("fn f() where {text} {{ }}"))
 +    }
 +}
 +
 +pub fn where_clause(preds: impl IntoIterator<Item = ast::WherePred>) -> ast::WhereClause {
 +    let preds = preds.into_iter().join(", ");
 +    return from_text(preds.as_str());
 +
 +    fn from_text(text: &str) -> ast::WhereClause {
 +        ast_from_text(&format!("fn f() where {text} {{ }}"))
 +    }
 +}
 +
 +pub fn let_stmt(
 +    pattern: ast::Pat,
 +    ty: Option<ast::Type>,
 +    initializer: Option<ast::Expr>,
 +) -> ast::LetStmt {
 +    let mut text = String::new();
 +    format_to!(text, "let {pattern}");
 +    if let Some(ty) = ty {
 +        format_to!(text, ": {ty}");
 +    }
 +    match initializer {
 +        Some(it) => format_to!(text, " = {it};"),
 +        None => format_to!(text, ";"),
 +    };
 +    ast_from_text(&format!("fn f() {{ {text} }}"))
 +}
++
++pub fn let_else_stmt(
++    pattern: ast::Pat,
++    ty: Option<ast::Type>,
++    expr: ast::Expr,
++    diverging: ast::BlockExpr,
++) -> ast::LetStmt {
++    let mut text = String::new();
++    format_to!(text, "let {pattern}");
++    if let Some(ty) = ty {
++        format_to!(text, ": {ty}");
++    }
++    format_to!(text, " = {expr} else {diverging};");
++    ast_from_text(&format!("fn f() {{ {text} }}"))
++}
++
 +pub fn expr_stmt(expr: ast::Expr) -> ast::ExprStmt {
 +    let semi = if expr.is_block_like() { "" } else { ";" };
 +    ast_from_text(&format!("fn f() {{ {expr}{semi} (); }}"))
 +}
 +
 +pub fn item_const(
 +    visibility: Option<ast::Visibility>,
 +    name: ast::Name,
 +    ty: ast::Type,
 +    expr: ast::Expr,
 +) -> ast::Const {
 +    let visibility = match visibility {
 +        None => String::new(),
 +        Some(it) => format!("{it} "),
 +    };
 +    ast_from_text(&format!("{visibility} const {name}: {ty} = {expr};"))
 +}
 +
 +pub fn param(pat: ast::Pat, ty: ast::Type) -> ast::Param {
 +    ast_from_text(&format!("fn f({pat}: {ty}) {{ }}"))
 +}
 +
 +pub fn self_param() -> ast::SelfParam {
 +    ast_from_text("fn f(&self) { }")
 +}
 +
 +pub fn ret_type(ty: ast::Type) -> ast::RetType {
 +    ast_from_text(&format!("fn f() -> {ty} {{ }}"))
 +}
 +
 +pub fn param_list(
 +    self_param: Option<ast::SelfParam>,
 +    pats: impl IntoIterator<Item = ast::Param>,
 +) -> ast::ParamList {
 +    let args = pats.into_iter().join(", ");
 +    let list = match self_param {
 +        Some(self_param) if args.is_empty() => format!("fn f({self_param}) {{ }}"),
 +        Some(self_param) => format!("fn f({self_param}, {args}) {{ }}"),
 +        None => format!("fn f({args}) {{ }}"),
 +    };
 +    ast_from_text(&list)
 +}
 +
 +pub fn type_param(name: ast::Name, ty: Option<ast::TypeBoundList>) -> ast::TypeParam {
 +    let bound = match ty {
 +        Some(it) => format!(": {it}"),
 +        None => String::new(),
 +    };
 +    ast_from_text(&format!("fn f<{name}{bound}>() {{ }}"))
 +}
 +
 +pub fn lifetime_param(lifetime: ast::Lifetime) -> ast::LifetimeParam {
 +    ast_from_text(&format!("fn f<{lifetime}>() {{ }}"))
 +}
 +
 +pub fn generic_param_list(
 +    pats: impl IntoIterator<Item = ast::GenericParam>,
 +) -> ast::GenericParamList {
 +    let args = pats.into_iter().join(", ");
 +    ast_from_text(&format!("fn f<{args}>() {{ }}"))
 +}
 +
 +pub fn type_arg(ty: ast::Type) -> ast::TypeArg {
 +    ast_from_text(&format!("const S: T<{ty}> = ();"))
 +}
 +
 +pub fn lifetime_arg(lifetime: ast::Lifetime) -> ast::LifetimeArg {
 +    ast_from_text(&format!("const S: T<{lifetime}> = ();"))
 +}
 +
 +pub(crate) fn generic_arg_list(
 +    args: impl IntoIterator<Item = ast::GenericArg>,
 +) -> ast::GenericArgList {
 +    let args = args.into_iter().join(", ");
 +    ast_from_text(&format!("const S: T<{args}> = ();"))
 +}
 +
 +pub fn visibility_pub_crate() -> ast::Visibility {
 +    ast_from_text("pub(crate) struct S")
 +}
 +
 +pub fn visibility_pub() -> ast::Visibility {
 +    ast_from_text("pub struct S")
 +}
 +
 +pub fn tuple_field_list(fields: impl IntoIterator<Item = ast::TupleField>) -> ast::TupleFieldList {
 +    let fields = fields.into_iter().join(", ");
 +    ast_from_text(&format!("struct f({fields});"))
 +}
 +
 +pub fn record_field_list(
 +    fields: impl IntoIterator<Item = ast::RecordField>,
 +) -> ast::RecordFieldList {
 +    let fields = fields.into_iter().join(", ");
 +    ast_from_text(&format!("struct f {{ {fields} }}"))
 +}
 +
 +pub fn tuple_field(visibility: Option<ast::Visibility>, ty: ast::Type) -> ast::TupleField {
 +    let visibility = match visibility {
 +        None => String::new(),
 +        Some(it) => format!("{it} "),
 +    };
 +    ast_from_text(&format!("struct f({visibility}{ty});"))
 +}
 +
 +pub fn variant(name: ast::Name, field_list: Option<ast::FieldList>) -> ast::Variant {
 +    let field_list = match field_list {
 +        None => String::new(),
 +        Some(it) => match it {
 +            ast::FieldList::RecordFieldList(record) => format!(" {record}"),
 +            ast::FieldList::TupleFieldList(tuple) => format!("{tuple}"),
 +        },
 +    };
 +    ast_from_text(&format!("enum f {{ {name}{field_list} }}"))
 +}
 +
 +pub fn fn_(
 +    visibility: Option<ast::Visibility>,
 +    fn_name: ast::Name,
 +    type_params: Option<ast::GenericParamList>,
 +    params: ast::ParamList,
 +    body: ast::BlockExpr,
 +    ret_type: Option<ast::RetType>,
 +    is_async: bool,
 +) -> ast::Fn {
 +    let type_params = match type_params {
 +        Some(type_params) => format!("{type_params}"),
 +        None => "".into(),
 +    };
 +    let ret_type = match ret_type {
 +        Some(ret_type) => format!("{ret_type} "),
 +        None => "".into(),
 +    };
 +    let visibility = match visibility {
 +        None => String::new(),
 +        Some(it) => format!("{it} "),
 +    };
 +
 +    let async_literal = if is_async { "async " } else { "" };
 +
 +    ast_from_text(&format!(
 +        "{visibility}{async_literal}fn {fn_name}{type_params}{params} {ret_type}{body}",
 +    ))
 +}
 +
 +pub fn struct_(
 +    visibility: Option<ast::Visibility>,
 +    strukt_name: ast::Name,
 +    generic_param_list: Option<ast::GenericParamList>,
 +    field_list: ast::FieldList,
 +) -> ast::Struct {
 +    let semicolon = if matches!(field_list, ast::FieldList::TupleFieldList(_)) { ";" } else { "" };
 +    let type_params = generic_param_list.map_or_else(String::new, |it| it.to_string());
 +    let visibility = match visibility {
 +        None => String::new(),
 +        Some(it) => format!("{it} "),
 +    };
 +
 +    ast_from_text(&format!("{visibility}struct {strukt_name}{type_params}{field_list}{semicolon}",))
 +}
 +
 +#[track_caller]
 +fn ast_from_text<N: AstNode>(text: &str) -> N {
 +    let parse = SourceFile::parse(text);
 +    let node = match parse.tree().syntax().descendants().find_map(N::cast) {
 +        Some(it) => it,
 +        None => {
 +            let node = std::any::type_name::<N>();
 +            panic!("Failed to make ast node `{node}` from text {text}")
 +        }
 +    };
 +    let node = node.clone_subtree();
 +    assert_eq!(node.syntax().text_range().start(), 0.into());
 +    node
 +}
 +
 +pub fn token(kind: SyntaxKind) -> SyntaxToken {
 +    tokens::SOURCE_FILE
 +        .tree()
 +        .syntax()
 +        .clone_for_update()
 +        .descendants_with_tokens()
 +        .filter_map(|it| it.into_token())
 +        .find(|it| it.kind() == kind)
 +        .unwrap_or_else(|| panic!("unhandled token: {kind:?}"))
 +}
 +
 +pub mod tokens {
 +    use once_cell::sync::Lazy;
 +
 +    use crate::{ast, AstNode, Parse, SourceFile, SyntaxKind::*, SyntaxToken};
 +
 +    pub(super) static SOURCE_FILE: Lazy<Parse<SourceFile>> = Lazy::new(|| {
 +        SourceFile::parse(
 +            "const C: <()>::Item = (1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p)\n;\n\n",
 +        )
 +    });
 +
 +    pub fn single_space() -> SyntaxToken {
 +        SOURCE_FILE
 +            .tree()
 +            .syntax()
 +            .clone_for_update()
 +            .descendants_with_tokens()
 +            .filter_map(|it| it.into_token())
 +            .find(|it| it.kind() == WHITESPACE && it.text() == " ")
 +            .unwrap()
 +    }
 +
 +    pub fn whitespace(text: &str) -> SyntaxToken {
 +        assert!(text.trim().is_empty());
 +        let sf = SourceFile::parse(text).ok().unwrap();
 +        sf.syntax().clone_for_update().first_child_or_token().unwrap().into_token().unwrap()
 +    }
 +
 +    pub fn doc_comment(text: &str) -> SyntaxToken {
 +        assert!(!text.trim().is_empty());
 +        let sf = SourceFile::parse(text).ok().unwrap();
 +        sf.syntax().first_child_or_token().unwrap().into_token().unwrap()
 +    }
 +
 +    pub fn literal(text: &str) -> SyntaxToken {
 +        assert_eq!(text.trim(), text);
 +        let lit: ast::Literal = super::ast_from_text(&format!("fn f() {{ let _ = {text}; }}"));
 +        lit.syntax().first_child_or_token().unwrap().into_token().unwrap()
 +    }
 +
 +    pub fn single_newline() -> SyntaxToken {
 +        let res = SOURCE_FILE
 +            .tree()
 +            .syntax()
 +            .clone_for_update()
 +            .descendants_with_tokens()
 +            .filter_map(|it| it.into_token())
 +            .find(|it| it.kind() == WHITESPACE && it.text() == "\n")
 +            .unwrap();
 +        res.detach();
 +        res
 +    }
 +
 +    pub fn blank_line() -> SyntaxToken {
 +        SOURCE_FILE
 +            .tree()
 +            .syntax()
 +            .clone_for_update()
 +            .descendants_with_tokens()
 +            .filter_map(|it| it.into_token())
 +            .find(|it| it.kind() == WHITESPACE && it.text() == "\n\n")
 +            .unwrap()
 +    }
 +
 +    pub struct WsBuilder(SourceFile);
 +
 +    impl WsBuilder {
 +        pub fn new(text: &str) -> WsBuilder {
 +            WsBuilder(SourceFile::parse(text).ok().unwrap())
 +        }
 +        pub fn ws(&self) -> SyntaxToken {
 +            self.0.syntax().first_child_or_token().unwrap().into_token().unwrap()
 +        }
 +    }
 +}
index 52a13da31c5d3ea3d25f963ec94e18b09fdbebd2,0000000000000000000000000000000000000000..56a68ef04379036f0dd95701cc1407d3ba318b58
mode 100644,000000..100644
--- /dev/null
@@@ -1,572 -1,0 +1,572 @@@
- [`module_tree_query`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/module_tree.rs#L116-L123
 +# Guide to rust-analyzer
 +
 +## About the guide
 +
 +This guide describes the current state of rust-analyzer as of 2019-01-20 (git
 +tag [guide-2019-01]). Its purpose is to document various problems and
 +architectural solutions related to the problem of building IDE-first compiler
 +for Rust. There is a video version of this guide as well:
 +https://youtu.be/ANKBNiSWyfc.
 +
 +[guide-2019-01]: https://github.com/rust-lang/rust-analyzer/tree/guide-2019-01
 +
 +## The big picture
 +
 +On the highest possible level, rust-analyzer is a stateful component. A client may
 +apply changes to the analyzer (new contents of `foo.rs` file is "fn main() {}")
 +and it may ask semantic questions about the current state (what is the
 +definition of the identifier with offset 92 in file `bar.rs`?). Two important
 +properties hold:
 +
 +* Analyzer does not do any I/O. It starts in an empty state and all input data is
 +  provided via `apply_change` API.
 +
 +* Only queries about the current state are supported. One can, of course,
 +  simulate undo and redo by keeping a log of changes and inverse changes respectively.
 +
 +## IDE API
 +
 +To see the bigger picture of how the IDE features work, let's take a look at the [`AnalysisHost`] and
 +[`Analysis`] pair of types. `AnalysisHost` has three methods:
 +
 +* `default()` for creating an empty analysis instance
 +* `apply_change(&mut self)` to make changes (this is how you get from an empty
 +  state to something interesting)
 +* `analysis(&self)` to get an instance of `Analysis`
 +
 +`Analysis` has a ton of methods for IDEs, like `goto_definition`, or
 +`completions`. Both inputs and outputs of `Analysis`' methods are formulated in
 +terms of files and offsets, and **not** in terms of Rust concepts like structs,
 +traits, etc. The "typed" API with Rust specific types is slightly lower in the
 +stack, we'll talk about it later.
 +
 +[`AnalysisHost`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/lib.rs#L265-L284
 +[`Analysis`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/lib.rs#L291-L478
 +
 +The reason for this separation of `Analysis` and `AnalysisHost` is that we want to apply
 +changes "uniquely", but we might also want to fork an `Analysis` and send it to
 +another thread for background processing. That is, there is only a single
 +`AnalysisHost`, but there may be several (equivalent) `Analysis`.
 +
 +Note that all of the `Analysis` API return `Cancellable<T>`. This is required to
 +be responsive in an IDE setting. Sometimes a long-running query is being computed
 +and the user types something in the editor and asks for completion. In this
 +case, we cancel the long-running computation (so it returns `Err(Cancelled)`),
 +apply the change and execute request for completion. We never use stale data to
 +answer requests. Under the cover, `AnalysisHost` "remembers" all outstanding
 +`Analysis` instances. The `AnalysisHost::apply_change` method cancels all
 +`Analysis`es, blocks until all of them are `Dropped` and then applies changes
 +in-place. This may be familiar to Rustaceans who use read-write locks for interior
 +mutability.
 +
 +Next, let's talk about what the inputs to the `Analysis` are, precisely.
 +
 +## Inputs
 +
 +rust-analyzer never does any I/O itself, all inputs get passed explicitly via
 +the `AnalysisHost::apply_change` method, which accepts a single argument, a
 +`Change`. [`Change`] is a builder for a single change
 +"transaction", so it suffices to study its methods to understand all of the
 +input data.
 +
 +[`Change`]: https://github.com/rust-lang/rust-analyzer/blob/master/crates/base_db/src/change.rs#L14-L89
 +
 +The `(add|change|remove)_file` methods control the set of the input files, where
 +each file has an integer id (`FileId`, picked by the client), text (`String`)
 +and a filesystem path. Paths are tricky; they'll be explained below, in source roots
 +section, together with the `add_root` method. The `add_library` method allows us to add a
 +group of files which are assumed to rarely change. It's mostly an optimization
 +and does not change the fundamental picture.
 +
 +The `set_crate_graph` method allows us to control how the input files are partitioned
 +into compilation units -- crates. It also controls (in theory, not implemented
 +yet) `cfg` flags. `CrateGraph` is a directed acyclic graph of crates. Each crate
 +has a root `FileId`, a set of active `cfg` flags and a set of dependencies. Each
 +dependency is a pair of a crate and a name. It is possible to have two crates
 +with the same root `FileId` but different `cfg`-flags/dependencies. This model
 +is lower than Cargo's model of packages: each Cargo package consists of several
 +targets, each of which is a separate crate (or several crates, if you try
 +different feature combinations).
 +
 +Procedural macros are inputs as well, roughly modeled as a crate with a bunch of
 +additional black box `dyn Fn(TokenStream) -> TokenStream` functions.
 +
 +Soon we'll talk how we build an LSP server on top of `Analysis`, but first,
 +let's deal with that paths issue.
 +
 +## Source roots (a.k.a. "Filesystems are horrible")
 +
 +This is a non-essential section, feel free to skip.
 +
 +The previous section said that the filesystem path is an attribute of a file,
 +but this is not the whole truth. Making it an absolute `PathBuf` will be bad for
 +several reasons. First, filesystems are full of (platform-dependent) edge cases:
 +
 +* It's hard (requires a syscall) to decide if two paths are equivalent.
 +* Some filesystems are case-sensitive (e.g. macOS).
 +* Paths are not necessarily UTF-8.
 +* Symlinks can form cycles.
 +
 +Second, this might hurt the reproducibility and hermeticity of builds. In theory,
 +moving a project from `/foo/bar/my-project` to `/spam/eggs/my-project` should
 +not change a bit in the output. However, if the absolute path is a part of the
 +input, it is at least in theory observable, and *could* affect the output.
 +
 +Yet another problem is that we really *really* want to avoid doing I/O, but with
 +Rust the set of "input" files is not necessarily known up-front. In theory, you
 +can have `#[path="/dev/random"] mod foo;`.
 +
 +To solve (or explicitly refuse to solve) these problems rust-analyzer uses the
 +concept of a "source root". Roughly speaking, source roots are the contents of a
 +directory on a file systems, like `/home/matklad/projects/rustraytracer/**.rs`.
 +
 +More precisely, all files (`FileId`s) are partitioned into disjoint
 +`SourceRoot`s. Each file has a relative UTF-8 path within the `SourceRoot`.
 +`SourceRoot` has an identity (integer ID). Crucially, the root path of the
 +source root itself is unknown to the analyzer: A client is supposed to maintain a
 +mapping between `SourceRoot` IDs (which are assigned by the client) and actual
 +`PathBuf`s. `SourceRoot`s give a sane tree model of the file system to the
 +analyzer.
 +
 +Note that `mod`, `#[path]` and `include!()` can only reference files from the
 +same source root. It is of course possible to explicitly add extra files to
 +the source root, even `/dev/random`.
 +
 +## Language Server Protocol
 +
 +Now let's see how the `Analysis` API is exposed via the JSON RPC based language server protocol. The
 +hard part here is managing changes (which can come either from the file system
 +or from the editor) and concurrency (we want to spawn background jobs for things
 +like syntax highlighting). We use the event loop pattern to manage the zoo, and
 +the loop is the [`main_loop_inner`] function. The [`main_loop`] does a one-time
 +initialization and tearing down of the resources.
 +
 +[`main_loop`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop.rs#L51-L110
 +[`main_loop_inner`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop.rs#L156-L258
 +
 +
 +Let's walk through a typical analyzer session!
 +
 +First, we need to figure out what to analyze. To do this, we run `cargo
 +metadata` to learn about Cargo packages for current workspace and dependencies,
 +and we run `rustc --print sysroot` and scan the "sysroot" (the directory containing the current Rust toolchain's files) to learn about crates like
 +`std`. Currently we load this configuration once at the start of the server, but
 +it should be possible to dynamically reconfigure it later without restart.
 +
 +[main_loop.rs#L62-L70](https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop.rs#L62-L70)
 +
 +The [`ProjectModel`] we get after this step is very Cargo and sysroot specific,
 +it needs to be lowered to get the input in the form of `Change`. This
 +happens in [`ServerWorldState::new`] method. Specifically
 +
 +* Create a `SourceRoot` for each Cargo package and sysroot.
 +* Schedule a filesystem scan of the roots.
 +* Create an analyzer's `Crate` for each Cargo **target** and sysroot crate.
 +* Setup dependencies between the crates.
 +
 +[`ProjectModel`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/project_model.rs#L16-L20
 +[`ServerWorldState::new`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/server_world.rs#L38-L160
 +
 +The results of the scan (which may take a while) will be processed in the body
 +of the main loop, just like any other change. Here's where we handle:
 +
 +* [File system changes](https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop.rs#L194)
 +* [Changes from the editor](https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop.rs#L377)
 +
 +After a single loop's turn, we group the changes into one `Change` and
 +[apply] it. This always happens on the main thread and blocks the loop.
 +
 +[apply]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/server_world.rs#L216
 +
 +To handle requests, like ["goto definition"], we create an instance of the
 +`Analysis` and [`schedule`] the task (which consumes `Analysis`) on the
 +threadpool. [The task] calls the corresponding `Analysis` method, while
 +massaging the types into the LSP representation. Keep in mind that if we are
 +executing "goto definition" on the threadpool and a new change comes in, the
 +task will be canceled as soon as the main loop calls `apply_change` on the
 +`AnalysisHost`.
 +
 +["goto definition"]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/server_world.rs#L216
 +[`schedule`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop.rs#L426-L455
 +[The task]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop/handlers.rs#L205-L223
 +
 +This concludes the overview of the analyzer's programing *interface*. Next, let's
 +dig into the implementation!
 +
 +## Salsa
 +
 +The most straightforward way to implement an "apply change, get analysis, repeat"
 +API would be to maintain the input state and to compute all possible analysis
 +information from scratch after every change. This works, but scales poorly with
 +the size of the project. To make this fast, we need to take advantage of the
 +fact that most of the changes are small, and that analysis results are unlikely
 +to change significantly between invocations.
 +
 +To do this we use [salsa]: a framework for incremental on-demand computation.
 +You can skip the rest of the section if you are familiar with `rustc`'s red-green
 +algorithm (which is used for incremental compilation).
 +
 +[salsa]: https://github.com/salsa-rs/salsa
 +
 +It's better to refer to salsa's docs to learn about it. Here's a small excerpt:
 +
 +The key idea of salsa is that you define your program as a set of queries. Every
 +query is used like a function `K -> V` that maps from some key of type `K` to a value
 +of type `V`. Queries come in two basic varieties:
 +
 +* **Inputs**: the base inputs to your system. You can change these whenever you
 +  like.
 +
 +* **Functions**: pure functions (no side effects) that transform your inputs
 +  into other values. The results of queries are memoized to avoid recomputing
 +  them a lot. When you make changes to the inputs, we'll figure out (fairly
 +  intelligently) when we can re-use these memoized values and when we have to
 +  recompute them.
 +
 +For further discussion, its important to understand one bit of "fairly
 +intelligently". Suppose we have two functions, `f1` and `f2`, and one input,
 +`z`. We call `f1(X)` which in turn calls `f2(Y)` which inspects `i(Z)`. `i(Z)`
 +returns some value `V1`, `f2` uses that and returns `R1`, `f1` uses that and
 +returns `O`. Now, let's change `i` at `Z` to `V2` from `V1` and try to compute
 +`f1(X)` again. Because `f1(X)` (transitively) depends on `i(Z)`, we can't just
 +reuse its value as is. However, if `f2(Y)` is *still* equal to `R1` (despite
 +`i`'s change), we, in fact, *can* reuse `O` as result of `f1(X)`. And that's how
 +salsa works: it recomputes results in *reverse* order, starting from inputs and
 +progressing towards outputs, stopping as soon as it sees an intermediate value
 +that hasn't changed. If this sounds confusing to you, don't worry: it is
 +confusing. This illustration by @killercup might help:
 +
 +<img alt="step 1" src="https://user-images.githubusercontent.com/1711539/51460907-c5484780-1d6d-11e9-9cd2-d6f62bd746e0.png" width="50%">
 +
 +<img alt="step 2" src="https://user-images.githubusercontent.com/1711539/51460915-c9746500-1d6d-11e9-9a77-27d33a0c51b5.png" width="50%">
 +
 +<img alt="step 3" src="https://user-images.githubusercontent.com/1711539/51460920-cda08280-1d6d-11e9-8d96-a782aa57a4d4.png" width="50%">
 +
 +<img alt="step 4" src="https://user-images.githubusercontent.com/1711539/51460927-d1340980-1d6d-11e9-851e-13c149d5c406.png" width="50%">
 +
 +## Salsa Input Queries
 +
 +All analyzer information is stored in a salsa database. `Analysis` and
 +`AnalysisHost` types are newtype wrappers for [`RootDatabase`] -- a salsa
 +database.
 +
 +[`RootDatabase`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/db.rs#L88-L134
 +
 +Salsa input queries are defined in [`FilesDatabase`] (which is a part of
 +`RootDatabase`). They closely mirror the familiar `Change` structure:
 +indeed, what `apply_change` does is it sets the values of input queries.
 +
 +[`FilesDatabase`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/base_db/src/input.rs#L150-L174
 +
 +## From text to semantic model
 +
 +The bulk of the rust-analyzer is transforming input text into a semantic model of
 +Rust code: a web of entities like modules, structs, functions and traits.
 +
 +An important fact to realize is that (unlike most other languages like C# or
 +Java) there is not a one-to-one mapping between the source code and the semantic model. A
 +single function definition in the source code might result in several semantic
 +functions: for example, the same source file might get included as a module in
 +several crates or a single crate might be present in the compilation DAG
 +several times, with different sets of `cfg`s enabled. The IDE-specific task of
 +mapping source code into a semantic model is inherently imprecise for
 +this reason and gets handled by the [`source_binder`].
 +
 +[`source_binder`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/source_binder.rs
 +
 +The semantic interface is declared in the [`code_model_api`] module. Each entity is
 +identified by an integer ID and has a bunch of methods which take a salsa database
 +as an argument and returns other entities (which are also IDs). Internally, these
 +methods invoke various queries on the database to build the model on demand.
 +Here's [the list of queries].
 +
 +[`code_model_api`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/code_model_api.rs
 +[the list of queries]: https://github.com/rust-lang/rust-analyzer/blob/7e84440e25e19529e4ff8a66e521d1b06349c6ec/crates/hir/src/db.rs#L20-L106
 +
 +The first step of building the model is parsing the source code.
 +
 +## Syntax trees
 +
 +An important property of the Rust language is that each file can be parsed in
 +isolation. Unlike, say, `C++`, an `include` can't change the meaning of the
 +syntax. For this reason, rust-analyzer can build a syntax tree for each "source
 +file", which could then be reused by several semantic models if this file
 +happens to be a part of several crates.
 +
 +The representation of syntax trees that rust-analyzer uses is similar to that of `Roslyn`
 +and Swift's new [libsyntax]. Swift's docs give an excellent overview of the
 +approach, so I skip this part here and instead outline the main characteristics
 +of the syntax trees:
 +
 +* Syntax trees are fully lossless. Converting **any** text to a syntax tree and
 +  back is a total identity function. All whitespace and comments are explicitly
 +  represented in the tree.
 +
 +* Syntax nodes have generic `(next|previous)_sibling`, `parent`,
 +  `(first|last)_child` functions. You can get from any one node to any other
 +  node in the file using only these functions.
 +
 +* Syntax nodes know their range (start offset and length) in the file.
 +
 +* Syntax nodes share the ownership of their syntax tree: if you keep a reference
 +  to a single function, the whole enclosing file is alive.
 +
 +* Syntax trees are immutable and the cost of replacing the subtree is
 +  proportional to the depth of the subtree. Read Swift's docs to learn how
 +  immutable + parent pointers + cheap modification is possible.
 +
 +* Syntax trees are build on best-effort basis. All accessor methods return
 +  `Option`s. The tree for `fn foo` will contain a function declaration with
 +  `None` for parameter list and body.
 +
 +* Syntax trees do not know the file they are built from, they only know about
 +  the text.
 +
 +The implementation is based on the generic [rowan] crate on top of which a
 +[rust-specific] AST is generated.
 +
 +[libsyntax]: https://github.com/apple/swift/tree/5e2c815edfd758f9b1309ce07bfc01c4bc20ec23/lib/Syntax
 +[rowan]: https://github.com/rust-analyzer/rowan/tree/100a36dc820eb393b74abe0d20ddf99077b61f88
 +[rust-specific]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_syntax/src/ast/generated.rs
 +
 +The next step in constructing the semantic model is ...
 +
 +## Building a Module Tree
 +
 +The algorithm for building a tree of modules is to start with a crate root
 +(remember, each `Crate` from a `CrateGraph` has a `FileId`), collect all `mod`
 +declarations and recursively process child modules. This is handled by the
 +[`module_tree_query`], with two slight variations.
 +
- [`submodules_query`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/module_tree.rs#L41
++[`module_tree_query`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/module_tree.rs#L115-L133
 +
 +First, rust-analyzer builds a module tree for all crates in a source root
 +simultaneously. The main reason for this is historical (`module_tree` predates
 +`CrateGraph`), but this approach also enables accounting for files which are not
 +part of any crate. That is, if you create a file but do not include it as a
 +submodule anywhere, you still get semantic completion, and you get a warning
 +about a free-floating module (the actual warning is not implemented yet).
 +
 +The second difference is that `module_tree_query` does not *directly* depend on
 +the "parse" query (which is confusingly called `source_file`). Why would calling
 +the parse directly be bad? Suppose the user changes the file slightly, by adding
 +an insignificant whitespace. Adding whitespace changes the parse tree (because
 +it includes whitespace), and that means recomputing the whole module tree.
 +
 +We deal with this problem by introducing an intermediate [`submodules_query`].
 +This query processes the syntax tree and extracts a set of declared submodule
 +names. Now, changing the whitespace results in `submodules_query` being
 +re-executed for a *single* module, but because the result of this query stays
 +the same, we don't have to re-execute [`module_tree_query`]. In fact, we only
 +need to re-execute it when we add/remove new files or when we change mod
 +declarations.
 +
- [`LocationInterner`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/base_db/src/loc2id.rs#L65-L71
- [interners]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/db.rs#L22-L23
++[`submodules_query`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/module_tree.rs#L41
 +
 +We store the resulting modules in a `Vec`-based indexed arena. The indices in
 +the arena becomes module IDs. And this brings us to the next topic:
 +assigning IDs in the general case.
 +
 +## Location Interner pattern
 +
 +One way to assign IDs is how we've dealt with modules: Collect all items into a
 +single array in some specific order and use the index in the array as an ID. The
 +main drawback of this approach is that these IDs are not stable: Adding a new item can
 +shift the IDs of all other items. This works for modules, because adding a module is
 +a comparatively rare operation, but would be less convenient for, for example,
 +functions.
 +
 +Another solution here is positional IDs: We can identify a function as "the
 +function with name `foo` in a ModuleId(92) module". Such locations are stable:
 +adding a new function to the module (unless it is also named `foo`) does not
 +change the location. However, such "ID" types ceases to be a `Copy`able integer and in
 +general can become pretty large if we account for nesting (for example: "third parameter of
 +the `foo` function of the `bar` `impl` in the `baz` module").
 +
 +[`LocationInterner`] allows us to combine the benefits of positional and numeric
 +IDs. It is a bidirectional append-only map between locations and consecutive
 +integers which can "intern" a location and return an integer ID back. The salsa
 +database we use includes a couple of [interners]. How to "garbage collect"
 +unused locations is an open question.
 +
- [`DefLoc`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/ids.rs#L127-L139
++[`LocationInterner`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_db/src/loc2id.rs#L65-L71
++[interners]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/db.rs#L22-L23
 +
 +For example, we use `LocationInterner` to assign IDs to definitions of functions,
 +structs, enums, etc. The location, [`DefLoc`] contains two bits of information:
 +
 +* the ID of the module which contains the definition,
 +* the ID of the specific item in the modules source code.
 +
 +We "could" use a text offset for the location of a particular item, but that would play
 +badly with salsa: offsets change after edits. So, as a rule of thumb, we avoid
 +using offsets, text ranges or syntax trees as keys and values for queries. What
 +we do instead is we store "index" of the item among all of the items of a file
 +(so, a positional based ID, but localized to a single file).
 +
- [`HirFileId`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/ids.rs#L18-L125
++[`DefLoc`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/ids.rs#L129-L139
 +
 +One thing we've glossed over for the time being is support for macros. We have
 +only proof of concept handling of macros at the moment, but they are extremely
 +interesting from an "assigning IDs" perspective.
 +
 +## Macros and recursive locations
 +
 +The tricky bit about macros is that they effectively create new source files.
 +While we can use `FileId`s to refer to original files, we can't just assign them
 +willy-nilly to the pseudo files of macro expansion. Instead, we use a special
 +ID, [`HirFileId`] to refer to either a usual file or a macro-generated file:
 +
 +```rust
 +enum HirFileId {
 +    FileId(FileId),
 +    Macro(MacroCallId),
 +}
 +```
 +
 +`MacroCallId` is an interned ID that specifies a particular macro invocation.
 +Its `MacroCallLoc` contains:
 +
 +* `ModuleId` of the containing module
 +* `HirFileId` of the containing file or pseudo file
 +* an index of this particular macro invocation in this file (positional id
 +  again).
 +
 +Note how `HirFileId` is defined in terms of `MacroCallLoc` which is defined in
 +terms of `HirFileId`! This does not recur infinitely though: any chain of
 +`HirFileId`s bottoms out in `HirFileId::FileId`, that is, some source file
 +actually written by the user.
 +
- [lower]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/nameres/lower.rs#L113-L117
- [loop]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/nameres.rs#L186-L196
++[`HirFileId`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/ids.rs#L31-L93
 +
 +Now that we understand how to identify a definition, in a source or in a
 +macro-generated file, we can discuss name resolution a bit.
 +
 +## Name resolution
 +
 +Name resolution faces the same problem as the module tree: if we look at the
 +syntax tree directly, we'll have to recompute name resolution after every
 +modification. The solution to the problem is the same: We [lower] the source code of
 +each module into a position-independent representation which does not change if
 +we modify bodies of the items. After that we [loop] resolving all imports until
 +we've reached a fixed point.
 +
- [test]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/nameres/tests.rs#L376
++[lower]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/nameres/lower.rs#L113-L147
++[loop]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/nameres.rs#L186-L196
 +And, given all our preparation with IDs and a position-independent representation,
 +it is satisfying to [test] that typing inside function body does not invalidate
 +name resolution results.
 +
- [imports]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/nameres/lower.rs#L52-L59
- [`SourceMap`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/nameres/lower.rs#L52-L59
- [projection query]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/nameres/lower.rs#L97-L103
- [uses]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/query_definitions.rs#L49
++[test]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/nameres/tests.rs#L376
 +
 +An interesting fact about name resolution is that it "erases" all of the
 +intermediate paths from the imports: in the end, we know which items are defined
 +and which items are imported in each module, but, if the import was `use
 +foo::bar::baz`, we deliberately forget what modules `foo` and `bar` resolve to.
 +
 +To serve "goto definition" requests on intermediate segments we need this info
 +in the IDE, however. Luckily, we need it only for a tiny fraction of imports, so we just ask
 +the module explicitly, "What does the path `foo::bar` resolve to?". This is a
 +general pattern: we try to compute the minimal possible amount of information
 +during analysis while allowing IDE to ask for additional specific bits.
 +
 +Name resolution is also a good place to introduce another salsa pattern used
 +throughout the analyzer:
 +
 +## Source Map pattern
 +
 +Due to an obscure edge case in completion, IDE needs to know the syntax node of
 +a use statement which imported the given completion candidate. We can't just
 +store the syntax node as a part of name resolution: this will break
 +incrementality, due to the fact that syntax changes after every file
 +modification.
 +
 +We solve this problem during the lowering step of name resolution. The lowering
 +query actually produces a *pair* of outputs: `LoweredModule` and [`SourceMap`].
 +The `LoweredModule` module contains [imports], but in a position-independent form.
 +The `SourceMap` contains a mapping from position-independent imports to
 +(position-dependent) syntax nodes.
 +
 +The result of this basic lowering query changes after every modification. But
 +there's an intermediate [projection query] which returns only the first
 +position-independent part of the lowering. The result of this query is stable.
 +Naturally, name resolution [uses] this stable projection query.
 +
- [lower the AST]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/expr.rs
- [positional ID]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/expr.rs#L13-L15
- [a source map]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/expr.rs#L41-L44
- [type inference]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/ty.rs#L1208-L1223
++[imports]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/nameres/lower.rs#L52-L59
++[`SourceMap`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/nameres/lower.rs#L52-L59
++[projection query]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/nameres/lower.rs#L97-L103
++[uses]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/query_definitions.rs#L49
 +
 +## Type inference
 +
 +First of all, implementation of type inference in rust-analyzer was spearheaded
 +by [@flodiebold]. [#327] was an awesome Christmas present, thank you, Florian!
 +
 +Type inference runs on per-function granularity and uses the patterns we've
 +discussed previously.
 +
 +First, we [lower the AST] of a function body into a position-independent
 +representation. In this representation, each expression is assigned a
 +[positional ID]. Alongside the lowered expression, [a source map] is produced,
 +which maps between expression ids and original syntax. This lowering step also
 +deals with "incomplete" source trees by replacing missing expressions by an
 +explicit `Missing` expression.
 +
 +Given the lowered body of the function, we can now run [type inference] and
 +construct a mapping from `ExprId`s to types.
 +
 +[@flodiebold]: https://github.com/flodiebold
 +[#327]: https://github.com/rust-lang/rust-analyzer/pull/327
- [completion implementation]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/completion.rs#L46-L62
- [`CompletionContext`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/completion/completion_context.rs#L14-L37
- ["IntelliJ Trick"]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/completion/completion_context.rs#L72-L75
- [find an ancestor `fn` node]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/completion/completion_context.rs#L116-L120
- [semantic model]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/completion/completion_context.rs#L123
- [series of independent completion routines]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/completion.rs#L52-L59
- [`complete_dot`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/completion/complete_dot.rs#L6-L22
++[lower the AST]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/expr.rs
++[positional ID]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/expr.rs#L13-L15
++[a source map]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/expr.rs#L41-L44
++[type inference]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/ty.rs#L1208-L1223
 +
 +## Tying it all together: completion
 +
 +To conclude the overview of the rust-analyzer, let's trace the request for
 +(type-inference powered!) code completion!
 +
 +We start by [receiving a message] from the language client. We decode the
 +message as a request for completion and [schedule it on the threadpool]. This is
 +the place where we [catch] canceled errors if, immediately after completion, the
 +client sends some modification.
 +
 +In [the handler], we deserialize LSP requests into rust-analyzer specific data
 +types (by converting a file url into a numeric `FileId`), [ask analysis for
 +completion] and serialize results into the LSP.
 +
 +The [completion implementation] is finally the place where we start doing the actual
 +work. The first step is to collect the `CompletionContext` -- a struct which
 +describes the cursor position in terms of Rust syntax and semantics. For
 +example, `function_syntax: Option<&'a ast::FnDef>` stores a reference to
 +the enclosing function *syntax*, while `function: Option<hir::Function>` is the
 +`Def` for this function.
 +
 +To construct the context, we first do an ["IntelliJ Trick"]: we insert a dummy
 +identifier at the cursor's position and parse this modified file, to get a
 +reasonably looking syntax tree. Then we do a bunch of "classification" routines
 +to figure out the context. For example, we [find an ancestor `fn` node] and we get a
 +[semantic model] for it (using the lossy `source_binder` infrastructure).
 +
 +The second step is to run a [series of independent completion routines]. Let's
 +take a closer look at [`complete_dot`], which completes fields and methods in
 +`foo.bar|`. First we extract a semantic function and a syntactic receiver
 +expression out of the `Context`. Then we run type-inference for this single
 +function and map our syntactic expression to `ExprId`. Using the ID, we figure
 +out the type of the receiver expression. Then we add all fields & methods from
 +the type to completion.
 +
 +[receiving a message]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop.rs#L203
 +[schedule it on the threadpool]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop.rs#L428
 +[catch]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop.rs#L436-L442
 +[the handler]: https://salsa.zulipchat.com/#narrow/stream/181542-rfcs.2Fsalsa-query-group/topic/design.20next.20steps
 +[ask analysis for completion]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/lib.rs#L439-L444
++[ask analysis for completion]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/lib.rs#L439-L444
++[completion implementation]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/completion.rs#L46-L62
++[`CompletionContext`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/completion/completion_context.rs#L14-L37
++["IntelliJ Trick"]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/completion/completion_context.rs#L72-L75
++[find an ancestor `fn` node]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/completion/completion_context.rs#L116-L120
++[semantic model]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/completion/completion_context.rs#L123
++[series of independent completion routines]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/completion.rs#L52-L59
++[`complete_dot`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/completion/complete_dot.rs#L6-L22
index 502833de72c161716d30f0456e4f2e3e6563d3ae,0000000000000000000000000000000000000000..36794efe42726126311af4cfcdb3e2500ca1c7ec
mode 100644,000000..100644
--- /dev/null
@@@ -1,746 -1,0 +1,752 @@@
++[[rust-analyzer.assist.emitMustUse]]rust-analyzer.assist.emitMustUse (default: `false`)::
+++
++--
++Whether to insert #[must_use] when generating `as_` methods
++for enum variants.
++--
 +[[rust-analyzer.assist.expressionFillDefault]]rust-analyzer.assist.expressionFillDefault (default: `"todo"`)::
 ++
 +--
 +Placeholder expression to use for missing expressions in assists.
 +--
 +[[rust-analyzer.cachePriming.enable]]rust-analyzer.cachePriming.enable (default: `true`)::
 ++
 +--
 +Warm up caches on project load.
 +--
 +[[rust-analyzer.cachePriming.numThreads]]rust-analyzer.cachePriming.numThreads (default: `0`)::
 ++
 +--
 +How many worker threads to handle priming caches. The default `0` means to pick automatically.
 +--
 +[[rust-analyzer.cargo.autoreload]]rust-analyzer.cargo.autoreload (default: `true`)::
 ++
 +--
 +Automatically refresh project info via `cargo metadata` on
 +`Cargo.toml` or `.cargo/config.toml` changes.
 +--
 +[[rust-analyzer.cargo.buildScripts.enable]]rust-analyzer.cargo.buildScripts.enable (default: `true`)::
 ++
 +--
 +Run build scripts (`build.rs`) for more precise code analysis.
 +--
 +[[rust-analyzer.cargo.buildScripts.invocationLocation]]rust-analyzer.cargo.buildScripts.invocationLocation (default: `"workspace"`)::
 ++
 +--
 +Specifies the working directory for running build scripts.
 +- "workspace": run build scripts for a workspace in the workspace's root directory.
 +  This is incompatible with `#rust-analyzer.cargo.buildScripts.invocationStrategy#` set to `once`.
 +- "root": run build scripts in the project's root directory.
 +This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
 +is set.
 +--
 +[[rust-analyzer.cargo.buildScripts.invocationStrategy]]rust-analyzer.cargo.buildScripts.invocationStrategy (default: `"per_workspace"`)::
 ++
 +--
 +Specifies the invocation strategy to use when running the build scripts command.
 +If `per_workspace` is set, the command will be executed for each workspace.
 +If `once` is set, the command will be executed once.
 +This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
 +is set.
 +--
 +[[rust-analyzer.cargo.buildScripts.overrideCommand]]rust-analyzer.cargo.buildScripts.overrideCommand (default: `null`)::
 ++
 +--
 +Override the command rust-analyzer uses to run build scripts and
 +build procedural macros. The command is required to output json
 +and should therefore include `--message-format=json` or a similar
 +option.
 +
 +By default, a cargo invocation will be constructed for the configured
 +targets and features, with the following base command line:
 +
 +```bash
 +cargo check --quiet --workspace --message-format=json --all-targets
 +```
 +.
 +--
 +[[rust-analyzer.cargo.buildScripts.useRustcWrapper]]rust-analyzer.cargo.buildScripts.useRustcWrapper (default: `true`)::
 ++
 +--
 +Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to
 +avoid checking unnecessary things.
 +--
 +[[rust-analyzer.cargo.extraEnv]]rust-analyzer.cargo.extraEnv (default: `{}`)::
 ++
 +--
 +Extra environment variables that will be set when running cargo, rustc
 +or other commands within the workspace. Useful for setting RUSTFLAGS.
 +--
 +[[rust-analyzer.cargo.features]]rust-analyzer.cargo.features (default: `[]`)::
 ++
 +--
 +List of features to activate.
 +
 +Set this to `"all"` to pass `--all-features` to cargo.
 +--
 +[[rust-analyzer.cargo.noDefaultFeatures]]rust-analyzer.cargo.noDefaultFeatures (default: `false`)::
 ++
 +--
 +Whether to pass `--no-default-features` to cargo.
 +--
 +[[rust-analyzer.cargo.sysroot]]rust-analyzer.cargo.sysroot (default: `"discover"`)::
 ++
 +--
 +Relative path to the sysroot, or "discover" to try to automatically find it via
 +"rustc --print sysroot".
 +
 +Unsetting this disables sysroot loading.
 +
 +This option does not take effect until rust-analyzer is restarted.
 +--
 +[[rust-analyzer.cargo.target]]rust-analyzer.cargo.target (default: `null`)::
 ++
 +--
 +Compilation target override (target triple).
 +--
 +[[rust-analyzer.cargo.unsetTest]]rust-analyzer.cargo.unsetTest (default: `["core"]`)::
 ++
 +--
 +Unsets `#[cfg(test)]` for the specified crates.
 +--
 +[[rust-analyzer.checkOnSave.allTargets]]rust-analyzer.checkOnSave.allTargets (default: `true`)::
 ++
 +--
 +Check all targets and tests (`--all-targets`).
 +--
 +[[rust-analyzer.checkOnSave.command]]rust-analyzer.checkOnSave.command (default: `"check"`)::
 ++
 +--
 +Cargo command to use for `cargo check`.
 +--
 +[[rust-analyzer.checkOnSave.enable]]rust-analyzer.checkOnSave.enable (default: `true`)::
 ++
 +--
 +Run specified `cargo check` command for diagnostics on save.
 +--
 +[[rust-analyzer.checkOnSave.extraArgs]]rust-analyzer.checkOnSave.extraArgs (default: `[]`)::
 ++
 +--
 +Extra arguments for `cargo check`.
 +--
 +[[rust-analyzer.checkOnSave.extraEnv]]rust-analyzer.checkOnSave.extraEnv (default: `{}`)::
 ++
 +--
 +Extra environment variables that will be set when running `cargo check`.
 +Extends `#rust-analyzer.cargo.extraEnv#`.
 +--
 +[[rust-analyzer.checkOnSave.features]]rust-analyzer.checkOnSave.features (default: `null`)::
 ++
 +--
 +List of features to activate. Defaults to
 +`#rust-analyzer.cargo.features#`.
 +
 +Set to `"all"` to pass `--all-features` to Cargo.
 +--
 +[[rust-analyzer.checkOnSave.invocationLocation]]rust-analyzer.checkOnSave.invocationLocation (default: `"workspace"`)::
 ++
 +--
 +Specifies the working directory for running checks.
 +- "workspace": run checks for workspaces in the corresponding workspaces' root directories.
 +  This falls back to "root" if `#rust-analyzer.cargo.checkOnSave.invocationStrategy#` is set to `once`.
 +- "root": run checks in the project's root directory.
 +This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
 +is set.
 +--
 +[[rust-analyzer.checkOnSave.invocationStrategy]]rust-analyzer.checkOnSave.invocationStrategy (default: `"per_workspace"`)::
 ++
 +--
 +Specifies the invocation strategy to use when running the checkOnSave command.
 +If `per_workspace` is set, the command will be executed for each workspace.
 +If `once` is set, the command will be executed once.
 +This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
 +is set.
 +--
 +[[rust-analyzer.checkOnSave.noDefaultFeatures]]rust-analyzer.checkOnSave.noDefaultFeatures (default: `null`)::
 ++
 +--
 +Whether to pass `--no-default-features` to Cargo. Defaults to
 +`#rust-analyzer.cargo.noDefaultFeatures#`.
 +--
 +[[rust-analyzer.checkOnSave.overrideCommand]]rust-analyzer.checkOnSave.overrideCommand (default: `null`)::
 ++
 +--
 +Override the command rust-analyzer uses instead of `cargo check` for
 +diagnostics on save. The command is required to output json and
 +should therefor include `--message-format=json` or a similar option.
 +
 +If you're changing this because you're using some tool wrapping
 +Cargo, you might also want to change
 +`#rust-analyzer.cargo.buildScripts.overrideCommand#`.
 +
 +If there are multiple linked projects, this command is invoked for
 +each of them, with the working directory being the project root
 +(i.e., the folder containing the `Cargo.toml`).
 +
 +An example command would be:
 +
 +```bash
 +cargo check --workspace --message-format=json --all-targets
 +```
 +.
 +--
 +[[rust-analyzer.checkOnSave.target]]rust-analyzer.checkOnSave.target (default: `null`)::
 ++
 +--
 +Check for a specific target. Defaults to
 +`#rust-analyzer.cargo.target#`.
 +--
 +[[rust-analyzer.completion.autoimport.enable]]rust-analyzer.completion.autoimport.enable (default: `true`)::
 ++
 +--
 +Toggles the additional completions that automatically add imports when completed.
 +Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled.
 +--
 +[[rust-analyzer.completion.autoself.enable]]rust-analyzer.completion.autoself.enable (default: `true`)::
 ++
 +--
 +Toggles the additional completions that automatically show method calls and field accesses
 +with `self` prefixed to them when inside a method.
 +--
 +[[rust-analyzer.completion.callable.snippets]]rust-analyzer.completion.callable.snippets (default: `"fill_arguments"`)::
 ++
 +--
 +Whether to add parenthesis and argument snippets when completing function.
 +--
 +[[rust-analyzer.completion.postfix.enable]]rust-analyzer.completion.postfix.enable (default: `true`)::
 ++
 +--
 +Whether to show postfix snippets like `dbg`, `if`, `not`, etc.
 +--
 +[[rust-analyzer.completion.privateEditable.enable]]rust-analyzer.completion.privateEditable.enable (default: `false`)::
 ++
 +--
 +Enables completions of private items and fields that are defined in the current workspace even if they are not visible at the current position.
 +--
 +[[rust-analyzer.completion.snippets.custom]]rust-analyzer.completion.snippets.custom::
 ++
 +--
 +Default:
 +----
 +{
 +            "Arc::new": {
 +                "postfix": "arc",
 +                "body": "Arc::new(${receiver})",
 +                "requires": "std::sync::Arc",
 +                "description": "Put the expression into an `Arc`",
 +                "scope": "expr"
 +            },
 +            "Rc::new": {
 +                "postfix": "rc",
 +                "body": "Rc::new(${receiver})",
 +                "requires": "std::rc::Rc",
 +                "description": "Put the expression into an `Rc`",
 +                "scope": "expr"
 +            },
 +            "Box::pin": {
 +                "postfix": "pinbox",
 +                "body": "Box::pin(${receiver})",
 +                "requires": "std::boxed::Box",
 +                "description": "Put the expression into a pinned `Box`",
 +                "scope": "expr"
 +            },
 +            "Ok": {
 +                "postfix": "ok",
 +                "body": "Ok(${receiver})",
 +                "description": "Wrap the expression in a `Result::Ok`",
 +                "scope": "expr"
 +            },
 +            "Err": {
 +                "postfix": "err",
 +                "body": "Err(${receiver})",
 +                "description": "Wrap the expression in a `Result::Err`",
 +                "scope": "expr"
 +            },
 +            "Some": {
 +                "postfix": "some",
 +                "body": "Some(${receiver})",
 +                "description": "Wrap the expression in an `Option::Some`",
 +                "scope": "expr"
 +            }
 +        }
 +----
 +Custom completion snippets.
 +
 +--
 +[[rust-analyzer.diagnostics.disabled]]rust-analyzer.diagnostics.disabled (default: `[]`)::
 ++
 +--
 +List of rust-analyzer diagnostics to disable.
 +--
 +[[rust-analyzer.diagnostics.enable]]rust-analyzer.diagnostics.enable (default: `true`)::
 ++
 +--
 +Whether to show native rust-analyzer diagnostics.
 +--
 +[[rust-analyzer.diagnostics.experimental.enable]]rust-analyzer.diagnostics.experimental.enable (default: `false`)::
 ++
 +--
 +Whether to show experimental rust-analyzer diagnostics that might
 +have more false positives than usual.
 +--
 +[[rust-analyzer.diagnostics.remapPrefix]]rust-analyzer.diagnostics.remapPrefix (default: `{}`)::
 ++
 +--
 +Map of prefixes to be substituted when parsing diagnostic file paths.
 +This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`.
 +--
 +[[rust-analyzer.diagnostics.warningsAsHint]]rust-analyzer.diagnostics.warningsAsHint (default: `[]`)::
 ++
 +--
 +List of warnings that should be displayed with hint severity.
 +
 +The warnings will be indicated by faded text or three dots in code
 +and will not show up in the `Problems Panel`.
 +--
 +[[rust-analyzer.diagnostics.warningsAsInfo]]rust-analyzer.diagnostics.warningsAsInfo (default: `[]`)::
 ++
 +--
 +List of warnings that should be displayed with info severity.
 +
 +The warnings will be indicated by a blue squiggly underline in code
 +and a blue icon in the `Problems Panel`.
 +--
 +[[rust-analyzer.files.excludeDirs]]rust-analyzer.files.excludeDirs (default: `[]`)::
 ++
 +--
 +These directories will be ignored by rust-analyzer. They are
 +relative to the workspace root, and globs are not supported. You may
 +also need to add the folders to Code's `files.watcherExclude`.
 +--
 +[[rust-analyzer.files.watcher]]rust-analyzer.files.watcher (default: `"client"`)::
 ++
 +--
 +Controls file watching implementation.
 +--
 +[[rust-analyzer.highlightRelated.breakPoints.enable]]rust-analyzer.highlightRelated.breakPoints.enable (default: `true`)::
 ++
 +--
 +Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords.
 +--
 +[[rust-analyzer.highlightRelated.exitPoints.enable]]rust-analyzer.highlightRelated.exitPoints.enable (default: `true`)::
 ++
 +--
 +Enables highlighting of all exit points while the cursor is on any `return`, `?`, `fn`, or return type arrow (`->`).
 +--
 +[[rust-analyzer.highlightRelated.references.enable]]rust-analyzer.highlightRelated.references.enable (default: `true`)::
 ++
 +--
 +Enables highlighting of related references while the cursor is on any identifier.
 +--
 +[[rust-analyzer.highlightRelated.yieldPoints.enable]]rust-analyzer.highlightRelated.yieldPoints.enable (default: `true`)::
 ++
 +--
 +Enables highlighting of all break points for a loop or block context while the cursor is on any `async` or `await` keywords.
 +--
 +[[rust-analyzer.hover.actions.debug.enable]]rust-analyzer.hover.actions.debug.enable (default: `true`)::
 ++
 +--
 +Whether to show `Debug` action. Only applies when
 +`#rust-analyzer.hover.actions.enable#` is set.
 +--
 +[[rust-analyzer.hover.actions.enable]]rust-analyzer.hover.actions.enable (default: `true`)::
 ++
 +--
 +Whether to show HoverActions in Rust files.
 +--
 +[[rust-analyzer.hover.actions.gotoTypeDef.enable]]rust-analyzer.hover.actions.gotoTypeDef.enable (default: `true`)::
 ++
 +--
 +Whether to show `Go to Type Definition` action. Only applies when
 +`#rust-analyzer.hover.actions.enable#` is set.
 +--
 +[[rust-analyzer.hover.actions.implementations.enable]]rust-analyzer.hover.actions.implementations.enable (default: `true`)::
 ++
 +--
 +Whether to show `Implementations` action. Only applies when
 +`#rust-analyzer.hover.actions.enable#` is set.
 +--
 +[[rust-analyzer.hover.actions.references.enable]]rust-analyzer.hover.actions.references.enable (default: `false`)::
 ++
 +--
 +Whether to show `References` action. Only applies when
 +`#rust-analyzer.hover.actions.enable#` is set.
 +--
 +[[rust-analyzer.hover.actions.run.enable]]rust-analyzer.hover.actions.run.enable (default: `true`)::
 ++
 +--
 +Whether to show `Run` action. Only applies when
 +`#rust-analyzer.hover.actions.enable#` is set.
 +--
 +[[rust-analyzer.hover.documentation.enable]]rust-analyzer.hover.documentation.enable (default: `true`)::
 ++
 +--
 +Whether to show documentation on hover.
 +--
 +[[rust-analyzer.hover.documentation.keywords.enable]]rust-analyzer.hover.documentation.keywords.enable (default: `true`)::
 ++
 +--
 +Whether to show keyword hover popups. Only applies when
 +`#rust-analyzer.hover.documentation.enable#` is set.
 +--
 +[[rust-analyzer.hover.links.enable]]rust-analyzer.hover.links.enable (default: `true`)::
 ++
 +--
 +Use markdown syntax for links in hover.
 +--
 +[[rust-analyzer.imports.granularity.enforce]]rust-analyzer.imports.granularity.enforce (default: `false`)::
 ++
 +--
 +Whether to enforce the import granularity setting for all files. If set to false rust-analyzer will try to keep import styles consistent per file.
 +--
 +[[rust-analyzer.imports.granularity.group]]rust-analyzer.imports.granularity.group (default: `"crate"`)::
 ++
 +--
 +How imports should be grouped into use statements.
 +--
 +[[rust-analyzer.imports.group.enable]]rust-analyzer.imports.group.enable (default: `true`)::
 ++
 +--
 +Group inserted imports by the https://rust-analyzer.github.io/manual.html#auto-import[following order]. Groups are separated by newlines.
 +--
 +[[rust-analyzer.imports.merge.glob]]rust-analyzer.imports.merge.glob (default: `true`)::
 ++
 +--
 +Whether to allow import insertion to merge new imports into single path glob imports like `use std::fmt::*;`.
 +--
 +[[rust-analyzer.imports.prefer.no.std]]rust-analyzer.imports.prefer.no.std (default: `false`)::
 ++
 +--
 +Prefer to unconditionally use imports of the core and alloc crate, over the std crate.
 +--
 +[[rust-analyzer.imports.prefix]]rust-analyzer.imports.prefix (default: `"plain"`)::
 ++
 +--
 +The path structure for newly inserted paths to use.
 +--
 +[[rust-analyzer.inlayHints.bindingModeHints.enable]]rust-analyzer.inlayHints.bindingModeHints.enable (default: `false`)::
 ++
 +--
 +Whether to show inlay type hints for binding modes.
 +--
 +[[rust-analyzer.inlayHints.chainingHints.enable]]rust-analyzer.inlayHints.chainingHints.enable (default: `true`)::
 ++
 +--
 +Whether to show inlay type hints for method chains.
 +--
 +[[rust-analyzer.inlayHints.closingBraceHints.enable]]rust-analyzer.inlayHints.closingBraceHints.enable (default: `true`)::
 ++
 +--
 +Whether to show inlay hints after a closing `}` to indicate what item it belongs to.
 +--
 +[[rust-analyzer.inlayHints.closingBraceHints.minLines]]rust-analyzer.inlayHints.closingBraceHints.minLines (default: `25`)::
 ++
 +--
 +Minimum number of lines required before the `}` until the hint is shown (set to 0 or 1
 +to always show them).
 +--
 +[[rust-analyzer.inlayHints.closureReturnTypeHints.enable]]rust-analyzer.inlayHints.closureReturnTypeHints.enable (default: `"never"`)::
 ++
 +--
 +Whether to show inlay type hints for return types of closures.
 +--
 +[[rust-analyzer.inlayHints.lifetimeElisionHints.enable]]rust-analyzer.inlayHints.lifetimeElisionHints.enable (default: `"never"`)::
 ++
 +--
 +Whether to show inlay type hints for elided lifetimes in function signatures.
 +--
 +[[rust-analyzer.inlayHints.lifetimeElisionHints.useParameterNames]]rust-analyzer.inlayHints.lifetimeElisionHints.useParameterNames (default: `false`)::
 ++
 +--
 +Whether to prefer using parameter names as the name for elided lifetime hints if possible.
 +--
 +[[rust-analyzer.inlayHints.maxLength]]rust-analyzer.inlayHints.maxLength (default: `25`)::
 ++
 +--
 +Maximum length for inlay hints. Set to null to have an unlimited length.
 +--
 +[[rust-analyzer.inlayHints.parameterHints.enable]]rust-analyzer.inlayHints.parameterHints.enable (default: `true`)::
 ++
 +--
 +Whether to show function parameter name inlay hints at the call
 +site.
 +--
 +[[rust-analyzer.inlayHints.reborrowHints.enable]]rust-analyzer.inlayHints.reborrowHints.enable (default: `"never"`)::
 ++
 +--
 +Whether to show inlay type hints for compiler inserted reborrows.
 +--
 +[[rust-analyzer.inlayHints.renderColons]]rust-analyzer.inlayHints.renderColons (default: `true`)::
 ++
 +--
 +Whether to render leading colons for type hints, and trailing colons for parameter hints.
 +--
 +[[rust-analyzer.inlayHints.typeHints.enable]]rust-analyzer.inlayHints.typeHints.enable (default: `true`)::
 ++
 +--
 +Whether to show inlay type hints for variables.
 +--
 +[[rust-analyzer.inlayHints.typeHints.hideClosureInitialization]]rust-analyzer.inlayHints.typeHints.hideClosureInitialization (default: `false`)::
 ++
 +--
 +Whether to hide inlay type hints for `let` statements that initialize to a closure.
 +Only applies to closures with blocks, same as `#rust-analyzer.inlayHints.closureReturnTypeHints.enable#`.
 +--
 +[[rust-analyzer.inlayHints.typeHints.hideNamedConstructor]]rust-analyzer.inlayHints.typeHints.hideNamedConstructor (default: `false`)::
 ++
 +--
 +Whether to hide inlay type hints for constructors.
 +--
 +[[rust-analyzer.joinLines.joinAssignments]]rust-analyzer.joinLines.joinAssignments (default: `true`)::
 ++
 +--
 +Join lines merges consecutive declaration and initialization of an assignment.
 +--
 +[[rust-analyzer.joinLines.joinElseIf]]rust-analyzer.joinLines.joinElseIf (default: `true`)::
 ++
 +--
 +Join lines inserts else between consecutive ifs.
 +--
 +[[rust-analyzer.joinLines.removeTrailingComma]]rust-analyzer.joinLines.removeTrailingComma (default: `true`)::
 ++
 +--
 +Join lines removes trailing commas.
 +--
 +[[rust-analyzer.joinLines.unwrapTrivialBlock]]rust-analyzer.joinLines.unwrapTrivialBlock (default: `true`)::
 ++
 +--
 +Join lines unwraps trivial blocks.
 +--
 +[[rust-analyzer.lens.debug.enable]]rust-analyzer.lens.debug.enable (default: `true`)::
 ++
 +--
 +Whether to show `Debug` lens. Only applies when
 +`#rust-analyzer.lens.enable#` is set.
 +--
 +[[rust-analyzer.lens.enable]]rust-analyzer.lens.enable (default: `true`)::
 ++
 +--
 +Whether to show CodeLens in Rust files.
 +--
 +[[rust-analyzer.lens.forceCustomCommands]]rust-analyzer.lens.forceCustomCommands (default: `true`)::
 ++
 +--
 +Internal config: use custom client-side commands even when the
 +client doesn't set the corresponding capability.
 +--
 +[[rust-analyzer.lens.implementations.enable]]rust-analyzer.lens.implementations.enable (default: `true`)::
 ++
 +--
 +Whether to show `Implementations` lens. Only applies when
 +`#rust-analyzer.lens.enable#` is set.
 +--
 +[[rust-analyzer.lens.location]]rust-analyzer.lens.location (default: `"above_name"`)::
 ++
 +--
 +Where to render annotations.
 +--
 +[[rust-analyzer.lens.references.adt.enable]]rust-analyzer.lens.references.adt.enable (default: `false`)::
 ++
 +--
 +Whether to show `References` lens for Struct, Enum, and Union.
 +Only applies when `#rust-analyzer.lens.enable#` is set.
 +--
 +[[rust-analyzer.lens.references.enumVariant.enable]]rust-analyzer.lens.references.enumVariant.enable (default: `false`)::
 ++
 +--
 +Whether to show `References` lens for Enum Variants.
 +Only applies when `#rust-analyzer.lens.enable#` is set.
 +--
 +[[rust-analyzer.lens.references.method.enable]]rust-analyzer.lens.references.method.enable (default: `false`)::
 ++
 +--
 +Whether to show `Method References` lens. Only applies when
 +`#rust-analyzer.lens.enable#` is set.
 +--
 +[[rust-analyzer.lens.references.trait.enable]]rust-analyzer.lens.references.trait.enable (default: `false`)::
 ++
 +--
 +Whether to show `References` lens for Trait.
 +Only applies when `#rust-analyzer.lens.enable#` is set.
 +--
 +[[rust-analyzer.lens.run.enable]]rust-analyzer.lens.run.enable (default: `true`)::
 ++
 +--
 +Whether to show `Run` lens. Only applies when
 +`#rust-analyzer.lens.enable#` is set.
 +--
 +[[rust-analyzer.linkedProjects]]rust-analyzer.linkedProjects (default: `[]`)::
 ++
 +--
 +Disable project auto-discovery in favor of explicitly specified set
 +of projects.
 +
 +Elements must be paths pointing to `Cargo.toml`,
 +`rust-project.json`, or JSON objects in `rust-project.json` format.
 +--
 +[[rust-analyzer.lru.capacity]]rust-analyzer.lru.capacity (default: `null`)::
 ++
 +--
 +Number of syntax trees rust-analyzer keeps in memory. Defaults to 128.
 +--
 +[[rust-analyzer.notifications.cargoTomlNotFound]]rust-analyzer.notifications.cargoTomlNotFound (default: `true`)::
 ++
 +--
 +Whether to show `can't find Cargo.toml` error message.
 +--
 +[[rust-analyzer.procMacro.attributes.enable]]rust-analyzer.procMacro.attributes.enable (default: `true`)::
 ++
 +--
 +Expand attribute macros. Requires `#rust-analyzer.procMacro.enable#` to be set.
 +--
 +[[rust-analyzer.procMacro.enable]]rust-analyzer.procMacro.enable (default: `true`)::
 ++
 +--
 +Enable support for procedural macros, implies `#rust-analyzer.cargo.buildScripts.enable#`.
 +--
 +[[rust-analyzer.procMacro.ignored]]rust-analyzer.procMacro.ignored (default: `{}`)::
 ++
 +--
 +These proc-macros will be ignored when trying to expand them.
 +
 +This config takes a map of crate names with the exported proc-macro names to ignore as values.
 +--
 +[[rust-analyzer.procMacro.server]]rust-analyzer.procMacro.server (default: `null`)::
 ++
 +--
 +Internal config, path to proc-macro server executable (typically,
 +this is rust-analyzer itself, but we override this in tests).
 +--
 +[[rust-analyzer.references.excludeImports]]rust-analyzer.references.excludeImports (default: `false`)::
 ++
 +--
 +Exclude imports from find-all-references.
 +--
 +[[rust-analyzer.runnables.command]]rust-analyzer.runnables.command (default: `null`)::
 ++
 +--
 +Command to be executed instead of 'cargo' for runnables.
 +--
 +[[rust-analyzer.runnables.extraArgs]]rust-analyzer.runnables.extraArgs (default: `[]`)::
 ++
 +--
 +Additional arguments to be passed to cargo for runnables such as
 +tests or binaries. For example, it may be `--release`.
 +--
 +[[rust-analyzer.rustc.source]]rust-analyzer.rustc.source (default: `null`)::
 ++
 +--
 +Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private
 +projects, or "discover" to try to automatically find it if the `rustc-dev` component
 +is installed.
 +
 +Any project which uses rust-analyzer with the rustcPrivate
 +crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it.
 +
 +This option does not take effect until rust-analyzer is restarted.
 +--
 +[[rust-analyzer.rustfmt.extraArgs]]rust-analyzer.rustfmt.extraArgs (default: `[]`)::
 ++
 +--
 +Additional arguments to `rustfmt`.
 +--
 +[[rust-analyzer.rustfmt.overrideCommand]]rust-analyzer.rustfmt.overrideCommand (default: `null`)::
 ++
 +--
 +Advanced option, fully override the command rust-analyzer uses for
 +formatting.
 +--
 +[[rust-analyzer.rustfmt.rangeFormatting.enable]]rust-analyzer.rustfmt.rangeFormatting.enable (default: `false`)::
 ++
 +--
 +Enables the use of rustfmt's unstable range formatting command for the
 +`textDocument/rangeFormatting` request. The rustfmt option is unstable and only
 +available on a nightly build.
 +--
 +[[rust-analyzer.semanticHighlighting.doc.comment.inject.enable]]rust-analyzer.semanticHighlighting.doc.comment.inject.enable (default: `true`)::
 ++
 +--
 +Inject additional highlighting into doc comments.
 +
 +When enabled, rust-analyzer will highlight rust source in doc comments as well as intra
 +doc links.
 +--
 +[[rust-analyzer.semanticHighlighting.operator.enable]]rust-analyzer.semanticHighlighting.operator.enable (default: `true`)::
 ++
 +--
 +Use semantic tokens for operators.
 +
 +When disabled, rust-analyzer will emit semantic tokens only for operator tokens when
 +they are tagged with modifiers.
 +--
 +[[rust-analyzer.semanticHighlighting.operator.specialization.enable]]rust-analyzer.semanticHighlighting.operator.specialization.enable (default: `false`)::
 ++
 +--
 +Use specialized semantic tokens for operators.
 +
 +When enabled, rust-analyzer will emit special token types for operator tokens instead
 +of the generic `operator` token type.
 +--
 +[[rust-analyzer.semanticHighlighting.punctuation.enable]]rust-analyzer.semanticHighlighting.punctuation.enable (default: `false`)::
 ++
 +--
 +Use semantic tokens for punctuations.
 +
 +When disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when
 +they are tagged with modifiers or have a special role.
 +--
 +[[rust-analyzer.semanticHighlighting.punctuation.separate.macro.bang]]rust-analyzer.semanticHighlighting.punctuation.separate.macro.bang (default: `false`)::
 ++
 +--
 +When enabled, rust-analyzer will emit a punctuation semantic token for the `!` of macro
 +calls.
 +--
 +[[rust-analyzer.semanticHighlighting.punctuation.specialization.enable]]rust-analyzer.semanticHighlighting.punctuation.specialization.enable (default: `false`)::
 ++
 +--
 +Use specialized semantic tokens for punctuations.
 +
 +When enabled, rust-analyzer will emit special token types for punctuation tokens instead
 +of the generic `punctuation` token type.
 +--
 +[[rust-analyzer.semanticHighlighting.strings.enable]]rust-analyzer.semanticHighlighting.strings.enable (default: `true`)::
 ++
 +--
 +Use semantic tokens for strings.
 +
 +In some editors (e.g. vscode) semantic tokens override other highlighting grammars.
 +By disabling semantic tokens for strings, other grammars can be used to highlight
 +their contents.
 +--
 +[[rust-analyzer.signatureInfo.detail]]rust-analyzer.signatureInfo.detail (default: `"full"`)::
 ++
 +--
 +Show full signature of the callable. Only shows parameters if disabled.
 +--
 +[[rust-analyzer.signatureInfo.documentation.enable]]rust-analyzer.signatureInfo.documentation.enable (default: `true`)::
 ++
 +--
 +Show documentation.
 +--
 +[[rust-analyzer.typing.autoClosingAngleBrackets.enable]]rust-analyzer.typing.autoClosingAngleBrackets.enable (default: `false`)::
 ++
 +--
 +Whether to insert closing angle brackets when typing an opening angle bracket of a generic argument list.
 +--
 +[[rust-analyzer.workspace.symbol.search.kind]]rust-analyzer.workspace.symbol.search.kind (default: `"only_types"`)::
 ++
 +--
 +Workspace symbol search kind.
 +--
 +[[rust-analyzer.workspace.symbol.search.limit]]rust-analyzer.workspace.symbol.search.limit (default: `128`)::
 ++
 +--
 +Limits the number of items returned from a workspace symbol search (Defaults to 128).
 +Some clients like vs-code issue new searches on result filtering and don't require all results to be returned in the initial search.
 +Other clients requires all results upfront and might require a higher limit.
 +--
 +[[rust-analyzer.workspace.symbol.search.scope]]rust-analyzer.workspace.symbol.search.scope (default: `"workspace"`)::
 ++
 +--
 +Workspace symbol search scope.
 +--
index c30838e5f5e1e09b10ac34ce19c718d195f67a07,0000000000000000000000000000000000000000..49500e390a50246bb90b4da3a53544610abd7908
mode 100644,000000..100644
--- /dev/null
@@@ -1,885 -1,0 +1,891 @@@
 += User Manual
 +:toc: preamble
 +:sectanchors:
 +:page-layout: post
 +:icons: font
 +:source-highlighter: rouge
 +:experimental:
 +
 +////
 +IMPORTANT: the master copy of this document lives in the https://github.com/rust-lang/rust-analyzer repository
 +////
 +
 +At its core, rust-analyzer is a *library* for semantic analysis of Rust code as it changes over time.
 +This manual focuses on a specific usage of the library -- running it as part of a server that implements the
 +https://microsoft.github.io/language-server-protocol/[Language Server Protocol] (LSP).
 +The LSP allows various code editors, like VS Code, Emacs or Vim, to implement semantic features like completion or goto definition by talking to an external language server process.
 +
 +[TIP]
 +====
 +[.lead]
 +To improve this document, send a pull request: +
 +https://github.com/rust-lang/rust-analyzer/blob/master/docs/user/manual.adoc[https://github.com/rust-analyzer/.../manual.adoc]
 +
 +The manual is written in https://asciidoc.org[AsciiDoc] and includes some extra files which are generated from the source code. Run `cargo test` and `cargo test -p xtask` to create these and then `asciidoctor manual.adoc` to create an HTML copy.
 +====
 +
 +If you have questions about using rust-analyzer, please ask them in the https://users.rust-lang.org/c/ide/14["`IDEs and Editors`"] topic of Rust users forum.
 +
 +== Installation
 +
 +In theory, one should be able to just install the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>> and have it automatically work with any editor.
 +We are not there yet, so some editor specific setup is required.
 +
 +Additionally, rust-analyzer needs the sources of the standard library.
 +If the source code is not present, rust-analyzer will attempt to install it automatically.
 +
 +To add the sources manually, run the following command:
 +
 +```bash
 +$ rustup component add rust-src
 +```
 +
 +=== Toolchain
 +
 +Only the latest stable standard library source is officially supported for use with rust-analyzer.
 +If you are using an older toolchain or have an override set, rust-analyzer may fail to understand the Rust source.
 +You will either need to update your toolchain or use an older version of rust-analyzer that is compatible with your toolchain.
 +
 +If you are using an override in your project, you can still force rust-analyzer to use the stable toolchain via the environment variable `RUSTUP_TOOLCHAIN`.
 +For example, with VS Code or coc-rust-analyzer:
 +
 +[source,json]
 +----
 +{ "rust-analyzer.server.extraEnv": { "RUSTUP_TOOLCHAIN": "stable" } }
 +----
 +
 +=== VS Code
 +
 +This is the best supported editor at the moment.
 +The rust-analyzer plugin for VS Code is maintained
 +https://github.com/rust-lang/rust-analyzer/tree/master/editors/code[in tree].
 +
 +You can install the latest release of the plugin from
 +https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer[the marketplace].
 +
 +Note that the plugin may cause conflicts with the
 +https://marketplace.visualstudio.com/items?itemName=rust-lang.rust[official Rust plugin].
 +It is recommended to disable the Rust plugin when using the rust-analyzer extension.
 +
 +By default, the plugin will prompt you to download the matching version of the server as well:
 +
 +image::https://user-images.githubusercontent.com/9021944/75067008-17502500-54ba-11ea-835a-f92aac50e866.png[]
 +
 +[NOTE]
 +====
 +To disable this notification put the following to `settings.json`
 +
 +[source,json]
 +----
 +{ "rust-analyzer.updates.askBeforeDownload": false }
 +----
 +====
 +
 +The server binary is stored in the extension install directory, which starts with `rust-lang.rust-analyzer-` and is located under:
 +
 +* Linux: `~/.vscode/extensions`
 +* Linux (Remote, such as WSL): `~/.vscode-server/extensions`
 +* macOS: `~/.vscode/extensions`
 +* Windows: `%USERPROFILE%\.vscode\extensions`
 +
 +As an exception, on NixOS, the extension makes a copy of the server and stores it under `~/.config/Code/User/globalStorage/rust-lang.rust-analyzer`.
 +
 +Note that we only support the two most recent versions of VS Code.
 +
 +==== Updates
 +
 +The extension will be updated automatically as new versions become available.
 +It will ask your permission to download the matching language server version binary if needed.
 +
 +===== Nightly
 +
 +We ship nightly releases for VS Code.
 +To help us out by testing the newest code, you can enable pre-release versions in the Code extension page.
 +
 +==== Manual installation
 +
 +Alternatively, download a VSIX corresponding to your platform from the
 +https://github.com/rust-lang/rust-analyzer/releases[releases] page.
 +
 +Install the extension with the `Extensions: Install from VSIX` command within VS Code, or from the command line via:
 +[source]
 +----
 +$ code --install-extension /path/to/rust-analyzer.vsix
 +----
 +
 +If you are running an unsupported platform, you can install `rust-analyzer-no-server.vsix` and compile or obtain a server binary.
 +Copy the server anywhere, then add the path to your settings.json, for example:
 +[source,json]
 +----
 +{ "rust-analyzer.server.path": "~/.local/bin/rust-analyzer-linux" }
 +----
 +
 +==== Building From Source
 +
 +Both the server and the Code plugin can be installed from source:
 +
 +[source]
 +----
 +$ git clone https://github.com/rust-lang/rust-analyzer.git && cd rust-analyzer
 +$ cargo xtask install
 +----
 +
 +You'll need Cargo, nodejs (matching a supported version of VS Code) and npm for this.
 +
 +Note that installing via `xtask install` does not work for VS Code Remote, instead you'll need to install the `.vsix` manually.
 +
 +If you're not using Code, you can compile and install only the LSP server:
 +
 +[source]
 +----
 +$ cargo xtask install --server
 +----
 +
 +=== rust-analyzer Language Server Binary
 +
 +Other editors generally require the `rust-analyzer` binary to be in `$PATH`.
 +You can download pre-built binaries from the https://github.com/rust-lang/rust-analyzer/releases[releases] page.
 +You will need to uncompress and rename the binary for your platform, e.g. from `rust-analyzer-aarch64-apple-darwin.gz` on Mac OS to `rust-analyzer`, make it executable, then move it into a directory in your `$PATH`.
 +
 +On Linux to install the `rust-analyzer` binary into `~/.local/bin`, these commands should work:
 +
 +[source,bash]
 +----
 +$ mkdir -p ~/.local/bin
 +$ curl -L https://github.com/rust-lang/rust-analyzer/releases/latest/download/rust-analyzer-x86_64-unknown-linux-gnu.gz | gunzip -c - > ~/.local/bin/rust-analyzer
 +$ chmod +x ~/.local/bin/rust-analyzer
 +----
 +
 +Make sure that `~/.local/bin` is listed in the `$PATH` variable and use the appropriate URL if you're not on a `x86-64` system.
 +
 +You don't have to use `~/.local/bin`, any other path like `~/.cargo/bin` or `/usr/local/bin` will work just as well.
 +
 +Alternatively, you can install it from source using the command below.
 +You'll need the latest stable version of the Rust toolchain.
 +
 +[source,bash]
 +----
 +$ git clone https://github.com/rust-lang/rust-analyzer.git && cd rust-analyzer
 +$ cargo xtask install --server
 +----
 +
 +If your editor can't find the binary even though the binary is on your `$PATH`, the likely explanation is that it doesn't see the same `$PATH` as the shell, see https://github.com/rust-lang/rust-analyzer/issues/1811[this issue].
 +On Unix, running the editor from a shell or changing the `.desktop` file to set the environment should help.
 +
 +==== `rustup`
 +
 +`rust-analyzer` is available in `rustup`:
 +
 +[source,bash]
 +----
 +$ rustup component add rust-analyzer
 +----
 +
 +However, in contrast to `component add clippy` or `component add rustfmt`, this does not actually place a `rust-analyzer` binary in `~/.cargo/bin`, see https://github.com/rust-lang/rustup/issues/2411[this issue]. You can find the path to the binary using:
 +[source,bash]
 +----
 +$ rustup which --toolchain stable rust-analyzer
 +----
 +You can link to there from `~/.cargo/bin` or configure your editor to use the full path.
 +
 +Alternatively you might be able to configure your editor to start `rust-analyzer` using the command:
 +[source,bash]
 +----
 +$ rustup run stable rust-analyzer
 +----
 +
 +==== Arch Linux
 +
 +The `rust-analyzer` binary can be installed from the repos or AUR (Arch User Repository):
 +
 +- https://www.archlinux.org/packages/community/x86_64/rust-analyzer/[`rust-analyzer`] (built from latest tagged source)
 +- https://aur.archlinux.org/packages/rust-analyzer-git[`rust-analyzer-git`] (latest Git version)
 +
 +Install it with pacman, for example:
 +
 +[source,bash]
 +----
 +$ pacman -S rust-analyzer
 +----
 +
 +==== Gentoo Linux
 +
 +`rust-analyzer` is available in the GURU repository:
 +
 +- https://gitweb.gentoo.org/repo/proj/guru.git/tree/dev-util/rust-analyzer?id=9895cea62602cfe599bd48e0fb02127411ca6e81[`dev-util/rust-analyzer`] builds from source
 +- https://gitweb.gentoo.org/repo/proj/guru.git/tree/dev-util/rust-analyzer-bin?id=9895cea62602cfe599bd48e0fb02127411ca6e81[`dev-util/rust-analyzer-bin`] installs an official binary release
 +
 +If not already, GURU must be enabled (e.g. using `app-eselect/eselect-repository`) and sync'd before running `emerge`:
 +
 +[source,bash]
 +----
 +$ eselect repository enable guru && emaint sync -r guru
 +$ emerge rust-analyzer-bin
 +----
 +
 +==== macOS
 +
 +The `rust-analyzer` binary can be installed via https://brew.sh/[Homebrew].
 +
 +[source,bash]
 +----
 +$ brew install rust-analyzer
 +----
 +
 +=== Emacs
 +
 +Note this excellent https://robert.kra.hn/posts/2021-02-07_rust-with-emacs/[guide] from https://github.com/rksm[@rksm].
 +
 +Prerequisites: You have installed the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>.
 +
 +Emacs support is maintained as part of the https://github.com/emacs-lsp/lsp-mode[Emacs-LSP] package in https://github.com/emacs-lsp/lsp-mode/blob/master/lsp-rust.el[lsp-rust.el].
 +
 +1. Install the most recent version of `emacs-lsp` package by following the https://github.com/emacs-lsp/lsp-mode[Emacs-LSP instructions].
 +2. Set `lsp-rust-server` to `'rust-analyzer`.
 +3. Run `lsp` in a Rust buffer.
 +4. (Optionally) bind commands like `lsp-rust-analyzer-join-lines`, `lsp-extend-selection` and `lsp-rust-analyzer-expand-macro` to keys.
 +
 +=== Vim/NeoVim
 +
 +Prerequisites: You have installed the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>.
 +Not needed if the extension can install/update it on its own, coc-rust-analyzer is one example.
 +
 +There are several LSP client implementations for vim or neovim:
 +
 +==== coc-rust-analyzer
 +
 +1. Install coc.nvim by following the instructions at
 +   https://github.com/neoclide/coc.nvim[coc.nvim]
 +   (Node.js required)
 +2. Run `:CocInstall coc-rust-analyzer` to install
 +   https://github.com/fannheyward/coc-rust-analyzer[coc-rust-analyzer],
 +   this extension implements _most_ of the features supported in the VSCode extension:
 +   * automatically install and upgrade stable/nightly releases
 +   * same configurations as VSCode extension, `rust-analyzer.server.path`, `rust-analyzer.cargo.features` etc.
 +   * same commands too, `rust-analyzer.analyzerStatus`, `rust-analyzer.ssr` etc.
 +   * inlay hints for variables and method chaining, _Neovim Only_
 +
 +Note: for code actions, use `coc-codeaction-cursor` and `coc-codeaction-selected`; `coc-codeaction` and `coc-codeaction-line` are unlikely to be useful.
 +
 +==== LanguageClient-neovim
 +
 +1. Install LanguageClient-neovim by following the instructions
 +   https://github.com/autozimu/LanguageClient-neovim[here]
 +   * The GitHub project wiki has extra tips on configuration
 +
 +2. Configure by adding this to your vim/neovim config file (replacing the existing Rust-specific line if it exists):
 ++
 +[source,vim]
 +----
 +let g:LanguageClient_serverCommands = {
 +\ 'rust': ['rust-analyzer'],
 +\ }
 +----
 +
 +==== YouCompleteMe
 +
 +Install YouCompleteMe by following the instructions
 +  https://github.com/ycm-core/YouCompleteMe#installation[here].
 +
 +rust-analyzer is the default in ycm, it should work out of the box.
 +
 +==== ALE
 +
 +To use the LSP server in https://github.com/dense-analysis/ale[ale]:
 +
 +[source,vim]
 +----
 +let g:ale_linters = {'rust': ['analyzer']}
 +----
 +
 +==== nvim-lsp
 +
 +NeoVim 0.5 has built-in language server support.
 +For a quick start configuration of rust-analyzer, use https://github.com/neovim/nvim-lspconfig#rust_analyzer[neovim/nvim-lspconfig].
 +Once `neovim/nvim-lspconfig` is installed, use `+lua require'lspconfig'.rust_analyzer.setup({})+` in your `init.vim`.
 +
 +You can also pass LSP settings to the server:
 +
 +[source,vim]
 +----
 +lua << EOF
 +local nvim_lsp = require'lspconfig'
 +
 +local on_attach = function(client)
 +    require'completion'.on_attach(client)
 +end
 +
 +nvim_lsp.rust_analyzer.setup({
 +    on_attach=on_attach,
 +    settings = {
 +        ["rust-analyzer"] = {
 +            imports = {
 +                granularity = {
 +                    group = "module",
 +                },
 +                prefix = "self",
 +            },
 +            cargo = {
 +                buildScripts = {
 +                    enable = true,
 +                },
 +            },
 +            procMacro = {
 +                enable = true
 +            },
 +        }
 +    }
 +})
 +EOF
 +----
 +
 +See https://sharksforarms.dev/posts/neovim-rust/ for more tips on getting started.
 +
 +Check out https://github.com/simrat39/rust-tools.nvim for a batteries included rust-analyzer setup for neovim.
 +
 +==== vim-lsp
 +
 +vim-lsp is installed by following https://github.com/prabirshrestha/vim-lsp[the plugin instructions].
 +It can be as simple as adding this line to your `.vimrc`:
 +
 +[source,vim]
 +----
 +Plug 'prabirshrestha/vim-lsp'
 +----
 +
 +Next you need to register the `rust-analyzer` binary.
 +If it is available in `$PATH`, you may want to add this to your `.vimrc`:
 +
 +[source,vim]
 +----
 +if executable('rust-analyzer')
 +  au User lsp_setup call lsp#register_server({
 +        \   'name': 'Rust Language Server',
 +        \   'cmd': {server_info->['rust-analyzer']},
 +        \   'whitelist': ['rust'],
 +        \ })
 +endif
 +----
 +
 +There is no dedicated UI for the server configuration, so you would need to send any options as a value of the `initialization_options` field, as described in the <<_configuration,Configuration>> section.
 +Here is an example of how to enable the proc-macro support:
 +
 +[source,vim]
 +----
 +if executable('rust-analyzer')
 +  au User lsp_setup call lsp#register_server({
 +        \   'name': 'Rust Language Server',
 +        \   'cmd': {server_info->['rust-analyzer']},
 +        \   'whitelist': ['rust'],
 +        \   'initialization_options': {
 +        \     'cargo': {
 +        \       'buildScripts': {
 +        \         'enable': v:true,
 +        \       },
 +        \     },
 +        \     'procMacro': {
 +        \       'enable': v:true,
 +        \     },
 +        \   },
 +        \ })
 +endif
 +----
 +
 +=== Sublime Text
 +
 +==== Sublime Text 4:
 +* Follow the instructions in link:https://github.com/sublimelsp/LSP-rust-analyzer[LSP-rust-analyzer].
 +
 +NOTE: Install link:https://packagecontrol.io/packages/LSP-file-watcher-chokidar[LSP-file-watcher-chokidar] to enable file watching (`workspace/didChangeWatchedFiles`).
 +
 +==== Sublime Text 3:
 +* Install the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>.
 +* Install the link:https://packagecontrol.io/packages/LSP[LSP package].
 +* From the command palette, run `LSP: Enable Language Server Globally` and select `rust-analyzer`.
 +
 +If it worked, you should see "rust-analyzer, Line X, Column Y" on the left side of the status bar, and after waiting a bit, functionalities like tooltips on hovering over variables should become available.
 +
 +If you get an error saying `No such file or directory: 'rust-analyzer'`, see the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>> section on installing the language server binary.
 +
 +=== GNOME Builder
 +
 +GNOME Builder 3.37.1 and newer has native `rust-analyzer` support.
 +If the LSP binary is not available, GNOME Builder can install it when opening a Rust file.
 +
 +
 +=== Eclipse IDE
 +
 +Support for Rust development in the Eclipse IDE is provided by link:https://github.com/eclipse/corrosion[Eclipse Corrosion].
 +If available in PATH or in some standard location, `rust-analyzer` is detected and powers editing of Rust files without further configuration.
 +If `rust-analyzer` is not detected, Corrosion will prompt you for configuration of your Rust toolchain and language server with a link to the __Window > Preferences > Rust__ preference page; from here a button allows to download and configure `rust-analyzer`, but you can also reference another installation.
 +You'll need to close and reopen all .rs and Cargo files, or to restart the IDE, for this change to take effect.
 +
 +=== Kate Text Editor
 +
 +Support for the language server protocol is built into Kate through the LSP plugin, which is included by default.
 +It is preconfigured to use rust-analyzer for Rust sources since Kate 21.12.
 +
 +Earlier versions allow you to use rust-analyzer through a simple settings change.
 +In the LSP Client settings of Kate, copy the content of the third tab "default parameters" to the second tab "server configuration".
 +Then in the configuration replace:
 +[source,json]
 +----
 +        "rust": {
 +            "command": ["rls"],
 +            "rootIndicationFileNames": ["Cargo.lock", "Cargo.toml"],
 +            "url": "https://github.com/rust-lang/rls",
 +            "highlightingModeRegex": "^Rust$"
 +        },
 +----
 +With
 +[source,json]
 +----
 +        "rust": {
 +            "command": ["rust-analyzer"],
 +            "rootIndicationFileNames": ["Cargo.lock", "Cargo.toml"],
 +            "url": "https://github.com/rust-lang/rust-analyzer",
 +            "highlightingModeRegex": "^Rust$"
 +        },
 +----
 +Then click on apply, and restart the LSP server for your rust project.
 +
 +=== juCi++
 +
 +https://gitlab.com/cppit/jucipp[juCi++] has built-in support for the language server protocol, and since version 1.7.0 offers installation of both Rust and rust-analyzer when opening a Rust file.
 +
 +=== Kakoune
 +
 +https://kakoune.org/[Kakoune] supports LSP with the help of https://github.com/kak-lsp/kak-lsp[`kak-lsp`].
 +Follow the https://github.com/kak-lsp/kak-lsp#installation[instructions] to install `kak-lsp`.
 +To configure `kak-lsp`, refer to the https://github.com/kak-lsp/kak-lsp#configuring-kak-lsp[configuration section] which is basically about copying the https://github.com/kak-lsp/kak-lsp/blob/master/kak-lsp.toml[configuration file] in the right place (latest versions should use `rust-analyzer` by default).
 +
 +Finally, you need to configure Kakoune to talk to `kak-lsp` (see https://github.com/kak-lsp/kak-lsp#usage[Usage section]).
 +A basic configuration will only get you LSP but you can also activate inlay diagnostics and auto-formatting on save.
 +The following might help you get all of this.
 +
 +[source,txt]
 +----
 +eval %sh{kak-lsp --kakoune -s $kak_session}  # Not needed if you load it with plug.kak.
 +hook global WinSetOption filetype=rust %{
 +    # Enable LSP
 +    lsp-enable-window
 +
 +    # Auto-formatting on save
 +    hook window BufWritePre .* lsp-formatting-sync
 +
 +    # Configure inlay hints (only on save)
 +    hook window -group rust-inlay-hints BufWritePost .* rust-analyzer-inlay-hints
 +    hook -once -always window WinSetOption filetype=.* %{
 +        remove-hooks window rust-inlay-hints
 +    }
 +}
 +----
 +
 +=== Helix
 +
 +https://docs.helix-editor.com/[Helix] supports LSP by default.
 +However, it won't install `rust-analyzer` automatically.
 +You can follow instructions for installing <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>.
 +
++=== Crates
++
++There is a package named `ra_ap_rust_analyzer` available on https://crates.io/crates/ra_ap_rust-analyzer[crates.io], for someone who wants to use it programmatically.
++
++For more details, see https://github.com/rust-lang/rust-analyzer/blob/master/.github/workflows/publish.yml[the publish workflow].
++
 +== Troubleshooting
 +
 +Start with looking at the rust-analyzer version.
 +Try **rust-analyzer: Show RA Version** in VS Code (using **Command Palette** feature typically activated by Ctrl+Shift+P) or `rust-analyzer --version` in the command line.
 +If the date is more than a week ago, it's better to update rust-analyzer version.
 +
 +The next thing to check would be panic messages in rust-analyzer's log.
 +Log messages are printed to stderr, in VS Code you can see then in the `Output > Rust Analyzer Language Server` tab of the panel.
 +To see more logs, set the `RA_LOG=info` environment variable, this can be done either by setting the environment variable manually or by using `rust-analyzer.server.extraEnv`, note that both of these approaches require the server to be restarted.
 +
 +To fully capture LSP messages between the editor and the server, set `"rust-analyzer.trace.server": "verbose"` config and check
 +`Output > Rust Analyzer Language Server Trace`.
 +
 +The root cause for many "`nothing works`" problems is that rust-analyzer fails to understand the project structure.
 +To debug that, first note the `rust-analyzer` section in the status bar.
 +If it has an error icon and red, that's the problem (hover will have somewhat helpful error message).
 +**rust-analyzer: Status** prints dependency information for the current file.
 +Finally, `RA_LOG=project_model=debug` enables verbose logs during project loading.
 +
 +If rust-analyzer outright crashes, try running `rust-analyzer analysis-stats /path/to/project/directory/` on the command line.
 +This command type checks the whole project in batch mode bypassing LSP machinery.
 +
 +When filing issues, it is useful (but not necessary) to try to minimize examples.
 +An ideal bug reproduction looks like this:
 +
 +```bash
 +$ git clone https://github.com/username/repo.git && cd repo && git switch --detach commit-hash
 +$ rust-analyzer --version
 +rust-analyzer dd12184e4 2021-05-08 dev
 +$ rust-analyzer analysis-stats .
 +💀 💀 💀
 +```
 +
 +It is especially useful when the `repo` doesn't use external crates or the standard library.
 +
 +If you want to go as far as to modify the source code to debug the problem, be sure to take a look at the
 +https://github.com/rust-lang/rust-analyzer/tree/master/docs/dev[dev docs]!
 +
 +== Configuration
 +
 +**Source:** https://github.com/rust-lang/rust-analyzer/blob/master/crates/rust-analyzer/src/config.rs[config.rs]
 +
 +The <<_installation,Installation>> section contains details on configuration for some of the editors.
 +In general `rust-analyzer` is configured via LSP messages, which means that it's up to the editor to decide on the exact format and location of configuration files.
 +
 +Some clients, such as <<vs-code,VS Code>> or <<coc-rust-analyzer,COC plugin in Vim>> provide `rust-analyzer` specific configuration UIs. Others may require you to know a bit more about the interaction with `rust-analyzer`.
 +
 +For the later category, it might help to know that the initial configuration is specified as a value of the `initializationOptions` field of the https://microsoft.github.io/language-server-protocol/specifications/specification-current/#initialize[`InitializeParams` message, in the LSP protocol].
 +The spec says that the field type is `any?`, but `rust-analyzer` is looking for a JSON object that is constructed using settings from the list below.
 +Name of the setting, ignoring the `rust-analyzer.` prefix, is used as a path, and value of the setting becomes the JSON property value.
 +
 +For example, a very common configuration is to enable proc-macro support, can be achieved by sending this JSON:
 +
 +[source,json]
 +----
 +{
 +  "cargo": {
 +    "buildScripts": {
 +      "enable": true,
 +    },
 +  },
 +  "procMacro": {
 +    "enable": true,
 +  }
 +}
 +----
 +
 +Please consult your editor's documentation to learn more about how to configure https://microsoft.github.io/language-server-protocol/[LSP servers].
 +
 +To verify which configuration is actually used by `rust-analyzer`, set `RA_LOG` environment variable to `rust_analyzer=info` and look for config-related messages.
 +Logs should show both the JSON that `rust-analyzer` sees as well as the updated config.
 +
 +This is the list of config options `rust-analyzer` supports:
 +
 +include::./generated_config.adoc[]
 +
 +== Non-Cargo Based Projects
 +
 +rust-analyzer does not require Cargo.
 +However, if you use some other build system, you'll have to describe the structure of your project for rust-analyzer in the `rust-project.json` format:
 +
 +[source,TypeScript]
 +----
 +interface JsonProject {
 +    /// Path to the directory with *source code* of
 +    /// sysroot crates.
 +    ///
 +    /// It should point to the directory where std,
 +    /// core, and friends can be found:
 +    ///
 +    /// https://github.com/rust-lang/rust/tree/master/library.
 +    ///
 +    /// If provided, rust-analyzer automatically adds
 +    /// dependencies on sysroot crates. Conversely,
 +    /// if you omit this path, you can specify sysroot
 +    /// dependencies yourself and, for example, have
 +    /// several different "sysroots" in one graph of
 +    /// crates.
 +    sysroot_src?: string;
 +    /// The set of crates comprising the current
 +    /// project. Must include all transitive
 +    /// dependencies as well as sysroot crate (libstd,
 +    /// libcore and such).
 +    crates: Crate[];
 +}
 +
 +interface Crate {
 +    /// Optional crate name used for display purposes,
 +    /// without affecting semantics. See the `deps`
 +    /// key for semantically-significant crate names.
 +    display_name?: string;
 +    /// Path to the root module of the crate.
 +    root_module: string;
 +    /// Edition of the crate.
 +    edition: "2015" | "2018" | "2021";
 +    /// Dependencies
 +    deps: Dep[];
 +    /// Should this crate be treated as a member of
 +    /// current "workspace".
 +    ///
 +    /// By default, inferred from the `root_module`
 +    /// (members are the crates which reside inside
 +    /// the directory opened in the editor).
 +    ///
 +    /// Set this to `false` for things like standard
 +    /// library and 3rd party crates to enable
 +    /// performance optimizations (rust-analyzer
 +    /// assumes that non-member crates don't change).
 +    is_workspace_member?: boolean;
 +    /// Optionally specify the (super)set of `.rs`
 +    /// files comprising this crate.
 +    ///
 +    /// By default, rust-analyzer assumes that only
 +    /// files under `root_module.parent` can belong
 +    /// to a crate. `include_dirs` are included
 +    /// recursively, unless a subdirectory is in
 +    /// `exclude_dirs`.
 +    ///
 +    /// Different crates can share the same `source`.
 +    ///
 +    /// If two crates share an `.rs` file in common,
 +    /// they *must* have the same `source`.
 +    /// rust-analyzer assumes that files from one
 +    /// source can't refer to files in another source.
 +    source?: {
 +        include_dirs: string[],
 +        exclude_dirs: string[],
 +    },
 +    /// The set of cfgs activated for a given crate, like
 +    /// `["unix", "feature=\"foo\"", "feature=\"bar\""]`.
 +    cfg: string[];
 +    /// Target triple for this Crate.
 +    ///
 +    /// Used when running `rustc --print cfg`
 +    /// to get target-specific cfgs.
 +    target?: string;
 +    /// Environment variables, used for
 +    /// the `env!` macro
 +    env: { [key: string]: string; },
 +
 +    /// Whether the crate is a proc-macro crate.
 +    is_proc_macro: boolean;
 +    /// For proc-macro crates, path to compiled
 +    /// proc-macro (.so file).
 +    proc_macro_dylib_path?: string;
 +}
 +
 +interface Dep {
 +    /// Index of a crate in the `crates` array.
 +    crate: number,
 +    /// Name as should appear in the (implicit)
 +    /// `extern crate name` declaration.
 +    name: string,
 +}
 +----
 +
 +This format is provisional and subject to change.
 +Specifically, the `roots` setup will be different eventually.
 +
 +There are three ways to feed `rust-project.json` to rust-analyzer:
 +
 +* Place `rust-project.json` file at the root of the project, and rust-analyzer will discover it.
 +* Specify `"rust-analyzer.linkedProjects": [ "path/to/rust-project.json" ]` in the settings (and make sure that your LSP client sends settings as a part of initialize request).
 +* Specify `"rust-analyzer.linkedProjects": [ { "roots": [...], "crates": [...] }]` inline.
 +
 +Relative paths are interpreted relative to `rust-project.json` file location or (for inline JSON) relative to `rootUri`.
 +
 +See https://github.com/rust-analyzer/rust-project.json-example for a small example.
 +
 +You can set the `RA_LOG` environment variable to `rust_analyzer=info` to inspect how rust-analyzer handles config and project loading.
 +
 +Note that calls to `cargo check` are disabled when using `rust-project.json` by default, so compilation errors and warnings will no longer be sent to your LSP client. To enable these compilation errors you will need to specify explicitly what command rust-analyzer should run to perform the checks using the `checkOnSave.overrideCommand` configuration. As an example, the following configuration explicitly sets `cargo check` as the `checkOnSave` command.
 +
 +[source,json]
 +----
 +{ "rust-analyzer.checkOnSave.overrideCommand": ["cargo", "check", "--message-format=json"] }
 +----
 +
 +The `checkOnSave.overrideCommand` requires the command specified to output json error messages for rust-analyzer to consume. The `--message-format=json` flag does this for `cargo check` so whichever command you use must also output errors in this format. See the <<Configuration>> section for more information.
 +
 +== Security
 +
 +At the moment, rust-analyzer assumes that all code is trusted.
 +Here is a **non-exhaustive** list of ways to make rust-analyzer execute arbitrary code:
 +
 +* proc macros and build scripts are executed by default
 +* `.cargo/config` can override `rustc` with an arbitrary executable
 +* `rust-toolchain.toml` can override `rustc` with an arbitrary executable
 +* VS Code plugin reads configuration from project directory, and that can be used to override paths to various executables, like `rustfmt` or `rust-analyzer` itself.
 +* rust-analyzer's syntax trees library uses a lot of `unsafe` and hasn't been properly audited for memory safety.
 +
 +== Privacy
 +
 +The LSP server performs no network access in itself, but runs `cargo metadata` which will update or download the crate registry and the source code of the project dependencies.
 +If enabled (the default), build scripts and procedural macros can do anything.
 +
 +The Code extension does not access the network.
 +
 +Any other editor plugins are not under the control of the `rust-analyzer` developers. For any privacy concerns, you should check with their respective developers.
 +
 +For `rust-analyzer` developers, `cargo xtask release` uses the GitHub API to put together the release notes.
 +
 +== Features
 +
 +include::./generated_features.adoc[]
 +
 +== Assists (Code Actions)
 +
 +Assists, or code actions, are small local refactorings, available in a particular context.
 +They are usually triggered by a shortcut or by clicking a light bulb icon in the editor.
 +Cursor position or selection is signified by `┃` character.
 +
 +include::./generated_assists.adoc[]
 +
 +== Diagnostics
 +
 +While most errors and warnings provided by rust-analyzer come from the `cargo check` integration, there's a growing number of diagnostics implemented using rust-analyzer's own analysis.
 +Some of these diagnostics don't respect `\#[allow]` or `\#[deny]` attributes yet, but can be turned off using the `rust-analyzer.diagnostics.enable`, `rust-analyzer.diagnostics.experimental.enable` or `rust-analyzer.diagnostics.disabled` settings.
 +
 +include::./generated_diagnostic.adoc[]
 +
 +== Editor Features
 +=== VS Code
 +
 +==== Color configurations
 +
 +It is possible to change the foreground/background color and font family/size of inlay hints.
 +Just add this to your `settings.json`:
 +
 +[source,jsonc]
 +----
 +{
 +  "editor.inlayHints.fontFamily": "Courier New",
 +  "editor.inlayHints.fontSize": 11,
 +
 +  "workbench.colorCustomizations": {
 +    // Name of the theme you are currently using
 +    "[Default Dark+]": {
 +      "editorInlayHint.foreground": "#868686f0",
 +      "editorInlayHint.background": "#3d3d3d48",
 +
 +      // Overrides for specific kinds of inlay hints
 +      "editorInlayHint.typeForeground": "#fdb6fdf0",
 +      "editorInlayHint.parameterForeground": "#fdb6fdf0",
 +    }
 +  }
 +}
 +----
 +
 +==== Semantic style customizations
 +
 +You can customize the look of different semantic elements in the source code.
 +For example, mutable bindings are underlined by default and you can override this behavior by adding the following section to your `settings.json`:
 +
 +[source,jsonc]
 +----
 +{
 +  "editor.semanticTokenColorCustomizations": {
 +    "rules": {
 +      "*.mutable": {
 +        "fontStyle": "", // underline is the default
 +      },
 +    }
 +  },
 +}
 +----
 +
 +Most themes doesn't support styling unsafe operations differently yet. You can fix this by adding overrides for the rules `operator.unsafe`, `function.unsafe`, and `method.unsafe`:
 +
 +[source,jsonc]
 +----
 +{
 +   "editor.semanticTokenColorCustomizations": {
 +         "rules": {
 +             "operator.unsafe": "#ff6600",
 +             "function.unsafe": "#ff6600",
 +             "method.unsafe": "#ff6600"
 +         }
 +    },
 +}
 +----
 +
 +In addition to the top-level rules you can specify overrides for specific themes. For example, if you wanted to use a darker text color on a specific light theme, you might write:
 +
 +[source,jsonc]
 +----
 +{
 +   "editor.semanticTokenColorCustomizations": {
 +         "rules": {
 +             "operator.unsafe": "#ff6600"
 +         },
 +         "[Ayu Light]": {
 +            "rules": {
 +               "operator.unsafe": "#572300"
 +            }
 +         }
 +    },
 +}
 +----
 +
 +Make sure you include the brackets around the theme name. For example, use `"[Ayu Light]"` to customize the theme Ayu Light.
 +
 +==== Special `when` clause context for keybindings.
 +You may use `inRustProject` context to configure keybindings for rust projects only.
 +For example:
 +
 +[source,json]
 +----
 +{
 +  "key": "ctrl+alt+d",
 +  "command": "rust-analyzer.openDocs",
 +  "when": "inRustProject"
 +}
 +----
 +More about `when` clause contexts https://code.visualstudio.com/docs/getstarted/keybindings#_when-clause-contexts[here].
 +
 +==== Setting runnable environment variables
 +You can use "rust-analyzer.runnableEnv" setting to define runnable environment-specific substitution variables.
 +The simplest way for all runnables in a bunch:
 +```jsonc
 +"rust-analyzer.runnableEnv": {
 +    "RUN_SLOW_TESTS": "1"
 +}
 +```
 +
 +Or it is possible to specify vars more granularly:
 +```jsonc
 +"rust-analyzer.runnableEnv": [
 +    {
 +        // "mask": null, // null mask means that this rule will be applied for all runnables
 +        env: {
 +             "APP_ID": "1",
 +             "APP_DATA": "asdf"
 +        }
 +    },
 +    {
 +        "mask": "test_name",
 +        "env": {
 +             "APP_ID": "2", // overwrites only APP_ID
 +        }
 +    }
 +]
 +```
 +
 +You can use any valid regular expression as a mask.
 +Also note that a full runnable name is something like *run bin_or_example_name*, *test some::mod::test_name* or *test-mod some::mod*, so it is possible to distinguish binaries, single tests, and test modules with this masks: `"^run"`, `"^test "` (the trailing space matters!), and `"^test-mod"` respectively.
 +
 +==== Compiler feedback from external commands
 +
 +Instead of relying on the built-in `cargo check`, you can configure Code to run a command in the background and use the `$rustc-watch` problem matcher to generate inline error markers from its output.
 +
 +To do this you need to create a new https://code.visualstudio.com/docs/editor/tasks[VS Code Task] and set `rust-analyzer.checkOnSave.enable: false` in preferences.
 +
 +For example, if you want to run https://crates.io/crates/cargo-watch[`cargo watch`] instead, you might add the following to `.vscode/tasks.json`:
 +
 +```json
 +{
 +    "label": "Watch",
 +    "group": "build",
 +    "type": "shell",
 +    "command": "cargo watch",
 +    "problemMatcher": "$rustc-watch",
 +    "isBackground": true
 +}
 +```
 +
 +==== Live Share
 +
 +VS Code Live Share has partial support for rust-analyzer.
 +
 +Live Share _requires_ the official Microsoft build of VS Code, OSS builds will not work correctly.
 +
 +The host's rust-analyzer instance will be shared with all guests joining the session.
 +The guests do not have to have the rust-analyzer extension installed for this to work.
 +
 +If you are joining a Live Share session and _do_ have rust-analyzer installed locally, commands from the command palette will not work correctly since they will attempt to communicate with the local server.
index a72865d4fe44e1672847cd66548375621bc7db47,0000000000000000000000000000000000000000..0b25564e28d3763240ad5652f5f82aef0e428179
mode 100644,000000..100644
--- /dev/null
@@@ -1,6761 -1,0 +1,6761 @@@
-                 "ovsx": "^0.5.1",
 +{
 +    "name": "rust-analyzer",
 +    "version": "0.5.0-dev",
 +    "lockfileVersion": 2,
 +    "requires": true,
 +    "packages": {
 +        "": {
 +            "name": "rust-analyzer",
 +            "version": "0.5.0-dev",
 +            "license": "MIT OR Apache-2.0",
 +            "dependencies": {
 +                "d3": "^7.6.1",
 +                "d3-graphviz": "^4.1.1",
 +                "vscode-languageclient": "^8.0.2"
 +            },
 +            "devDependencies": {
 +                "@types/node": "~16.11.7",
 +                "@types/vscode": "~1.66.0",
 +                "@typescript-eslint/eslint-plugin": "^5.30.5",
 +                "@typescript-eslint/parser": "^5.30.5",
 +                "@vscode/test-electron": "^2.1.5",
 +                "cross-env": "^7.0.3",
 +                "esbuild": "^0.14.48",
 +                "eslint": "^8.19.0",
 +                "eslint-config-prettier": "^8.5.0",
-             "version": "0.5.1",
-             "resolved": "https://registry.npmjs.org/ovsx/-/ovsx-0.5.1.tgz",
-             "integrity": "sha512-3OWq0l7DuVHi2bd2aQe5+QVQlFIqvrcw3/2vGXL404L6Tr+R4QHtzfnYYghv8CCa85xJHjU0RhcaC7pyXkAUbg==",
++                "ovsx": "^0.5.2",
 +                "prettier": "^2.7.1",
 +                "tslib": "^2.4.0",
 +                "typescript": "^4.7.4",
 +                "vsce": "^2.9.2"
 +            },
 +            "engines": {
 +                "vscode": "^1.66.0"
 +            }
 +        },
 +        "node_modules/@eslint/eslintrc": {
 +            "version": "1.3.0",
 +            "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz",
 +            "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==",
 +            "dev": true,
 +            "dependencies": {
 +                "ajv": "^6.12.4",
 +                "debug": "^4.3.2",
 +                "espree": "^9.3.2",
 +                "globals": "^13.15.0",
 +                "ignore": "^5.2.0",
 +                "import-fresh": "^3.2.1",
 +                "js-yaml": "^4.1.0",
 +                "minimatch": "^3.1.2",
 +                "strip-json-comments": "^3.1.1"
 +            },
 +            "engines": {
 +                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
 +            }
 +        },
 +        "node_modules/@hpcc-js/wasm": {
 +            "version": "1.12.8",
 +            "resolved": "https://registry.npmjs.org/@hpcc-js/wasm/-/wasm-1.12.8.tgz",
 +            "integrity": "sha512-n4q9ARKco2hpCLsuVaW6Az3cDVaua7B3DSONHkc49WtEzgY/btvcDG5Zr1P6PZDv0sQ7oPnAi9Y+W2DI++MgcQ==",
 +            "dependencies": {
 +                "yargs": "^17.3.1"
 +            },
 +            "bin": {
 +                "dot-wasm": "bin/cli.js"
 +            }
 +        },
 +        "node_modules/@humanwhocodes/config-array": {
 +            "version": "0.9.5",
 +            "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz",
 +            "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==",
 +            "dev": true,
 +            "dependencies": {
 +                "@humanwhocodes/object-schema": "^1.2.1",
 +                "debug": "^4.1.1",
 +                "minimatch": "^3.0.4"
 +            },
 +            "engines": {
 +                "node": ">=10.10.0"
 +            }
 +        },
 +        "node_modules/@humanwhocodes/object-schema": {
 +            "version": "1.2.1",
 +            "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
 +            "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
 +            "dev": true
 +        },
 +        "node_modules/@nodelib/fs.scandir": {
 +            "version": "2.1.5",
 +            "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
 +            "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
 +            "dev": true,
 +            "dependencies": {
 +                "@nodelib/fs.stat": "2.0.5",
 +                "run-parallel": "^1.1.9"
 +            },
 +            "engines": {
 +                "node": ">= 8"
 +            }
 +        },
 +        "node_modules/@nodelib/fs.stat": {
 +            "version": "2.0.5",
 +            "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
 +            "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">= 8"
 +            }
 +        },
 +        "node_modules/@nodelib/fs.walk": {
 +            "version": "1.2.8",
 +            "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
 +            "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
 +            "dev": true,
 +            "dependencies": {
 +                "@nodelib/fs.scandir": "2.1.5",
 +                "fastq": "^1.6.0"
 +            },
 +            "engines": {
 +                "node": ">= 8"
 +            }
 +        },
 +        "node_modules/@tootallnate/once": {
 +            "version": "1.1.2",
 +            "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
 +            "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">= 6"
 +            }
 +        },
 +        "node_modules/@types/json-schema": {
 +            "version": "7.0.11",
 +            "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
 +            "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
 +            "dev": true
 +        },
 +        "node_modules/@types/node": {
 +            "version": "16.11.43",
 +            "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.43.tgz",
 +            "integrity": "sha512-GqWykok+3uocgfAJM8imbozrqLnPyTrpFlrryURQlw1EesPUCx5XxTiucWDSFF9/NUEXDuD4bnvHm8xfVGWTpQ==",
 +            "dev": true
 +        },
 +        "node_modules/@types/vscode": {
 +            "version": "1.66.0",
 +            "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.66.0.tgz",
 +            "integrity": "sha512-ZfJck4M7nrGasfs4A4YbUoxis3Vu24cETw3DERsNYtDZmYSYtk6ljKexKFKhImO/ZmY6ZMsmegu2FPkXoUFImA==",
 +            "dev": true
 +        },
 +        "node_modules/@typescript-eslint/eslint-plugin": {
 +            "version": "5.30.5",
 +            "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.30.5.tgz",
 +            "integrity": "sha512-lftkqRoBvc28VFXEoRgyZuztyVUQ04JvUnATSPtIRFAccbXTWL6DEtXGYMcbg998kXw1NLUJm7rTQ9eUt+q6Ig==",
 +            "dev": true,
 +            "dependencies": {
 +                "@typescript-eslint/scope-manager": "5.30.5",
 +                "@typescript-eslint/type-utils": "5.30.5",
 +                "@typescript-eslint/utils": "5.30.5",
 +                "debug": "^4.3.4",
 +                "functional-red-black-tree": "^1.0.1",
 +                "ignore": "^5.2.0",
 +                "regexpp": "^3.2.0",
 +                "semver": "^7.3.7",
 +                "tsutils": "^3.21.0"
 +            },
 +            "engines": {
 +                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
 +            },
 +            "funding": {
 +                "type": "opencollective",
 +                "url": "https://opencollective.com/typescript-eslint"
 +            },
 +            "peerDependencies": {
 +                "@typescript-eslint/parser": "^5.0.0",
 +                "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
 +            },
 +            "peerDependenciesMeta": {
 +                "typescript": {
 +                    "optional": true
 +                }
 +            }
 +        },
 +        "node_modules/@typescript-eslint/parser": {
 +            "version": "5.30.5",
 +            "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.30.5.tgz",
 +            "integrity": "sha512-zj251pcPXI8GO9NDKWWmygP6+UjwWmrdf9qMW/L/uQJBM/0XbU2inxe5io/234y/RCvwpKEYjZ6c1YrXERkK4Q==",
 +            "dev": true,
 +            "dependencies": {
 +                "@typescript-eslint/scope-manager": "5.30.5",
 +                "@typescript-eslint/types": "5.30.5",
 +                "@typescript-eslint/typescript-estree": "5.30.5",
 +                "debug": "^4.3.4"
 +            },
 +            "engines": {
 +                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
 +            },
 +            "funding": {
 +                "type": "opencollective",
 +                "url": "https://opencollective.com/typescript-eslint"
 +            },
 +            "peerDependencies": {
 +                "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
 +            },
 +            "peerDependenciesMeta": {
 +                "typescript": {
 +                    "optional": true
 +                }
 +            }
 +        },
 +        "node_modules/@typescript-eslint/scope-manager": {
 +            "version": "5.30.5",
 +            "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.30.5.tgz",
 +            "integrity": "sha512-NJ6F+YHHFT/30isRe2UTmIGGAiXKckCyMnIV58cE3JkHmaD6e5zyEYm5hBDv0Wbin+IC0T1FWJpD3YqHUG/Ydg==",
 +            "dev": true,
 +            "dependencies": {
 +                "@typescript-eslint/types": "5.30.5",
 +                "@typescript-eslint/visitor-keys": "5.30.5"
 +            },
 +            "engines": {
 +                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
 +            },
 +            "funding": {
 +                "type": "opencollective",
 +                "url": "https://opencollective.com/typescript-eslint"
 +            }
 +        },
 +        "node_modules/@typescript-eslint/type-utils": {
 +            "version": "5.30.5",
 +            "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.30.5.tgz",
 +            "integrity": "sha512-k9+ejlv1GgwN1nN7XjVtyCgE0BTzhzT1YsQF0rv4Vfj2U9xnslBgMYYvcEYAFVdvhuEscELJsB7lDkN7WusErw==",
 +            "dev": true,
 +            "dependencies": {
 +                "@typescript-eslint/utils": "5.30.5",
 +                "debug": "^4.3.4",
 +                "tsutils": "^3.21.0"
 +            },
 +            "engines": {
 +                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
 +            },
 +            "funding": {
 +                "type": "opencollective",
 +                "url": "https://opencollective.com/typescript-eslint"
 +            },
 +            "peerDependencies": {
 +                "eslint": "*"
 +            },
 +            "peerDependenciesMeta": {
 +                "typescript": {
 +                    "optional": true
 +                }
 +            }
 +        },
 +        "node_modules/@typescript-eslint/types": {
 +            "version": "5.30.5",
 +            "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.30.5.tgz",
 +            "integrity": "sha512-kZ80w/M2AvsbRvOr3PjaNh6qEW1LFqs2pLdo2s5R38B2HYXG8Z0PP48/4+j1QHJFL3ssHIbJ4odPRS8PlHrFfw==",
 +            "dev": true,
 +            "engines": {
 +                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
 +            },
 +            "funding": {
 +                "type": "opencollective",
 +                "url": "https://opencollective.com/typescript-eslint"
 +            }
 +        },
 +        "node_modules/@typescript-eslint/typescript-estree": {
 +            "version": "5.30.5",
 +            "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.30.5.tgz",
 +            "integrity": "sha512-qGTc7QZC801kbYjAr4AgdOfnokpwStqyhSbiQvqGBLixniAKyH+ib2qXIVo4P9NgGzwyfD9I0nlJN7D91E1VpQ==",
 +            "dev": true,
 +            "dependencies": {
 +                "@typescript-eslint/types": "5.30.5",
 +                "@typescript-eslint/visitor-keys": "5.30.5",
 +                "debug": "^4.3.4",
 +                "globby": "^11.1.0",
 +                "is-glob": "^4.0.3",
 +                "semver": "^7.3.7",
 +                "tsutils": "^3.21.0"
 +            },
 +            "engines": {
 +                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
 +            },
 +            "funding": {
 +                "type": "opencollective",
 +                "url": "https://opencollective.com/typescript-eslint"
 +            },
 +            "peerDependenciesMeta": {
 +                "typescript": {
 +                    "optional": true
 +                }
 +            }
 +        },
 +        "node_modules/@typescript-eslint/utils": {
 +            "version": "5.30.5",
 +            "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.30.5.tgz",
 +            "integrity": "sha512-o4SSUH9IkuA7AYIfAvatldovurqTAHrfzPApOZvdUq01hHojZojCFXx06D/aFpKCgWbMPRdJBWAC3sWp3itwTA==",
 +            "dev": true,
 +            "dependencies": {
 +                "@types/json-schema": "^7.0.9",
 +                "@typescript-eslint/scope-manager": "5.30.5",
 +                "@typescript-eslint/types": "5.30.5",
 +                "@typescript-eslint/typescript-estree": "5.30.5",
 +                "eslint-scope": "^5.1.1",
 +                "eslint-utils": "^3.0.0"
 +            },
 +            "engines": {
 +                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
 +            },
 +            "funding": {
 +                "type": "opencollective",
 +                "url": "https://opencollective.com/typescript-eslint"
 +            },
 +            "peerDependencies": {
 +                "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
 +            }
 +        },
 +        "node_modules/@typescript-eslint/visitor-keys": {
 +            "version": "5.30.5",
 +            "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.30.5.tgz",
 +            "integrity": "sha512-D+xtGo9HUMELzWIUqcQc0p2PO4NyvTrgIOK/VnSH083+8sq0tiLozNRKuLarwHYGRuA6TVBQSuuLwJUDWd3aaA==",
 +            "dev": true,
 +            "dependencies": {
 +                "@typescript-eslint/types": "5.30.5",
 +                "eslint-visitor-keys": "^3.3.0"
 +            },
 +            "engines": {
 +                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
 +            },
 +            "funding": {
 +                "type": "opencollective",
 +                "url": "https://opencollective.com/typescript-eslint"
 +            }
 +        },
 +        "node_modules/@vscode/test-electron": {
 +            "version": "2.1.5",
 +            "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.1.5.tgz",
 +            "integrity": "sha512-O/ioqFpV+RvKbRykX2ItYPnbcZ4Hk5V0rY4uhQjQTLhGL9WZUvS7exzuYQCCI+ilSqJpctvxq2llTfGXf9UnnA==",
 +            "dev": true,
 +            "dependencies": {
 +                "http-proxy-agent": "^4.0.1",
 +                "https-proxy-agent": "^5.0.0",
 +                "rimraf": "^3.0.2",
 +                "unzipper": "^0.10.11"
 +            },
 +            "engines": {
 +                "node": ">=8.9.3"
 +            }
 +        },
 +        "node_modules/acorn": {
 +            "version": "8.7.1",
 +            "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz",
 +            "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==",
 +            "dev": true,
 +            "bin": {
 +                "acorn": "bin/acorn"
 +            },
 +            "engines": {
 +                "node": ">=0.4.0"
 +            }
 +        },
 +        "node_modules/acorn-jsx": {
 +            "version": "5.3.2",
 +            "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
 +            "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
 +            "dev": true,
 +            "peerDependencies": {
 +                "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
 +            }
 +        },
 +        "node_modules/agent-base": {
 +            "version": "6.0.2",
 +            "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
 +            "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
 +            "dev": true,
 +            "dependencies": {
 +                "debug": "4"
 +            },
 +            "engines": {
 +                "node": ">= 6.0.0"
 +            }
 +        },
 +        "node_modules/ajv": {
 +            "version": "6.12.6",
 +            "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
 +            "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
 +            "dev": true,
 +            "dependencies": {
 +                "fast-deep-equal": "^3.1.1",
 +                "fast-json-stable-stringify": "^2.0.0",
 +                "json-schema-traverse": "^0.4.1",
 +                "uri-js": "^4.2.2"
 +            },
 +            "funding": {
 +                "type": "github",
 +                "url": "https://github.com/sponsors/epoberezkin"
 +            }
 +        },
 +        "node_modules/ansi-regex": {
 +            "version": "5.0.1",
 +            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
 +            "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
 +            "engines": {
 +                "node": ">=8"
 +            }
 +        },
 +        "node_modules/ansi-styles": {
 +            "version": "4.3.0",
 +            "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
 +            "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
 +            "dependencies": {
 +                "color-convert": "^2.0.1"
 +            },
 +            "engines": {
 +                "node": ">=8"
 +            },
 +            "funding": {
 +                "url": "https://github.com/chalk/ansi-styles?sponsor=1"
 +            }
 +        },
 +        "node_modules/argparse": {
 +            "version": "2.0.1",
 +            "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
 +            "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
 +            "dev": true
 +        },
 +        "node_modules/array-union": {
 +            "version": "2.1.0",
 +            "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
 +            "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=8"
 +            }
 +        },
 +        "node_modules/azure-devops-node-api": {
 +            "version": "11.2.0",
 +            "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-11.2.0.tgz",
 +            "integrity": "sha512-XdiGPhrpaT5J8wdERRKs5g8E0Zy1pvOYTli7z9E8nmOn3YGp4FhtjhrOyFmX/8veWCwdI69mCHKJw6l+4J/bHA==",
 +            "dev": true,
 +            "dependencies": {
 +                "tunnel": "0.0.6",
 +                "typed-rest-client": "^1.8.4"
 +            }
 +        },
 +        "node_modules/balanced-match": {
 +            "version": "1.0.2",
 +            "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
 +            "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
 +        },
 +        "node_modules/base64-js": {
 +            "version": "1.5.1",
 +            "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
 +            "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
 +            "dev": true,
 +            "funding": [
 +                {
 +                    "type": "github",
 +                    "url": "https://github.com/sponsors/feross"
 +                },
 +                {
 +                    "type": "patreon",
 +                    "url": "https://www.patreon.com/feross"
 +                },
 +                {
 +                    "type": "consulting",
 +                    "url": "https://feross.org/support"
 +                }
 +            ]
 +        },
 +        "node_modules/big-integer": {
 +            "version": "1.6.51",
 +            "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz",
 +            "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=0.6"
 +            }
 +        },
 +        "node_modules/binary": {
 +            "version": "0.3.0",
 +            "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz",
 +            "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==",
 +            "dev": true,
 +            "dependencies": {
 +                "buffers": "~0.1.1",
 +                "chainsaw": "~0.1.0"
 +            },
 +            "engines": {
 +                "node": "*"
 +            }
 +        },
 +        "node_modules/bl": {
 +            "version": "4.1.0",
 +            "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
 +            "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
 +            "dev": true,
 +            "dependencies": {
 +                "buffer": "^5.5.0",
 +                "inherits": "^2.0.4",
 +                "readable-stream": "^3.4.0"
 +            }
 +        },
 +        "node_modules/bl/node_modules/readable-stream": {
 +            "version": "3.6.0",
 +            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
 +            "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
 +            "dev": true,
 +            "dependencies": {
 +                "inherits": "^2.0.3",
 +                "string_decoder": "^1.1.1",
 +                "util-deprecate": "^1.0.1"
 +            },
 +            "engines": {
 +                "node": ">= 6"
 +            }
 +        },
 +        "node_modules/bluebird": {
 +            "version": "3.4.7",
 +            "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz",
 +            "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==",
 +            "dev": true
 +        },
 +        "node_modules/boolbase": {
 +            "version": "1.0.0",
 +            "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
 +            "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
 +            "dev": true
 +        },
 +        "node_modules/brace-expansion": {
 +            "version": "1.1.11",
 +            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
 +            "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
 +            "dependencies": {
 +                "balanced-match": "^1.0.0",
 +                "concat-map": "0.0.1"
 +            }
 +        },
 +        "node_modules/braces": {
 +            "version": "3.0.2",
 +            "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
 +            "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
 +            "dev": true,
 +            "dependencies": {
 +                "fill-range": "^7.0.1"
 +            },
 +            "engines": {
 +                "node": ">=8"
 +            }
 +        },
 +        "node_modules/buffer": {
 +            "version": "5.7.1",
 +            "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
 +            "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
 +            "dev": true,
 +            "funding": [
 +                {
 +                    "type": "github",
 +                    "url": "https://github.com/sponsors/feross"
 +                },
 +                {
 +                    "type": "patreon",
 +                    "url": "https://www.patreon.com/feross"
 +                },
 +                {
 +                    "type": "consulting",
 +                    "url": "https://feross.org/support"
 +                }
 +            ],
 +            "dependencies": {
 +                "base64-js": "^1.3.1",
 +                "ieee754": "^1.1.13"
 +            }
 +        },
 +        "node_modules/buffer-crc32": {
 +            "version": "0.2.13",
 +            "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
 +            "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
 +            "dev": true,
 +            "engines": {
 +                "node": "*"
 +            }
 +        },
 +        "node_modules/buffer-indexof-polyfill": {
 +            "version": "1.0.2",
 +            "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz",
 +            "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=0.10"
 +            }
 +        },
 +        "node_modules/buffers": {
 +            "version": "0.1.1",
 +            "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz",
 +            "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=0.2.0"
 +            }
 +        },
 +        "node_modules/call-bind": {
 +            "version": "1.0.2",
 +            "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
 +            "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
 +            "dev": true,
 +            "dependencies": {
 +                "function-bind": "^1.1.1",
 +                "get-intrinsic": "^1.0.2"
 +            },
 +            "funding": {
 +                "url": "https://github.com/sponsors/ljharb"
 +            }
 +        },
 +        "node_modules/callsites": {
 +            "version": "3.1.0",
 +            "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
 +            "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=6"
 +            }
 +        },
 +        "node_modules/chainsaw": {
 +            "version": "0.1.0",
 +            "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz",
 +            "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==",
 +            "dev": true,
 +            "dependencies": {
 +                "traverse": ">=0.3.0 <0.4"
 +            },
 +            "engines": {
 +                "node": "*"
 +            }
 +        },
 +        "node_modules/chalk": {
 +            "version": "4.1.2",
 +            "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
 +            "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
 +            "dev": true,
 +            "dependencies": {
 +                "ansi-styles": "^4.1.0",
 +                "supports-color": "^7.1.0"
 +            },
 +            "engines": {
 +                "node": ">=10"
 +            },
 +            "funding": {
 +                "url": "https://github.com/chalk/chalk?sponsor=1"
 +            }
 +        },
 +        "node_modules/cheerio": {
 +            "version": "1.0.0-rc.12",
 +            "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz",
 +            "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==",
 +            "dev": true,
 +            "dependencies": {
 +                "cheerio-select": "^2.1.0",
 +                "dom-serializer": "^2.0.0",
 +                "domhandler": "^5.0.3",
 +                "domutils": "^3.0.1",
 +                "htmlparser2": "^8.0.1",
 +                "parse5": "^7.0.0",
 +                "parse5-htmlparser2-tree-adapter": "^7.0.0"
 +            },
 +            "engines": {
 +                "node": ">= 6"
 +            },
 +            "funding": {
 +                "url": "https://github.com/cheeriojs/cheerio?sponsor=1"
 +            }
 +        },
 +        "node_modules/cheerio-select": {
 +            "version": "2.1.0",
 +            "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz",
 +            "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==",
 +            "dev": true,
 +            "dependencies": {
 +                "boolbase": "^1.0.0",
 +                "css-select": "^5.1.0",
 +                "css-what": "^6.1.0",
 +                "domelementtype": "^2.3.0",
 +                "domhandler": "^5.0.3",
 +                "domutils": "^3.0.1"
 +            },
 +            "funding": {
 +                "url": "https://github.com/sponsors/fb55"
 +            }
 +        },
 +        "node_modules/chownr": {
 +            "version": "1.1.4",
 +            "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
 +            "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
 +            "dev": true
 +        },
 +        "node_modules/ci-info": {
 +            "version": "2.0.0",
 +            "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
 +            "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
 +            "dev": true
 +        },
 +        "node_modules/cliui": {
 +            "version": "7.0.4",
 +            "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
 +            "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
 +            "dependencies": {
 +                "string-width": "^4.2.0",
 +                "strip-ansi": "^6.0.0",
 +                "wrap-ansi": "^7.0.0"
 +            }
 +        },
 +        "node_modules/color-convert": {
 +            "version": "2.0.1",
 +            "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
 +            "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
 +            "dependencies": {
 +                "color-name": "~1.1.4"
 +            },
 +            "engines": {
 +                "node": ">=7.0.0"
 +            }
 +        },
 +        "node_modules/color-name": {
 +            "version": "1.1.4",
 +            "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
 +            "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
 +        },
 +        "node_modules/commander": {
 +            "version": "7.2.0",
 +            "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
 +            "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
 +            "engines": {
 +                "node": ">= 10"
 +            }
 +        },
 +        "node_modules/concat-map": {
 +            "version": "0.0.1",
 +            "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
 +            "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
 +        },
 +        "node_modules/core-util-is": {
 +            "version": "1.0.3",
 +            "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
 +            "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
 +            "dev": true
 +        },
 +        "node_modules/cross-env": {
 +            "version": "7.0.3",
 +            "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
 +            "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==",
 +            "dev": true,
 +            "dependencies": {
 +                "cross-spawn": "^7.0.1"
 +            },
 +            "bin": {
 +                "cross-env": "src/bin/cross-env.js",
 +                "cross-env-shell": "src/bin/cross-env-shell.js"
 +            },
 +            "engines": {
 +                "node": ">=10.14",
 +                "npm": ">=6",
 +                "yarn": ">=1"
 +            }
 +        },
 +        "node_modules/cross-spawn": {
 +            "version": "7.0.3",
 +            "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
 +            "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
 +            "dev": true,
 +            "dependencies": {
 +                "path-key": "^3.1.0",
 +                "shebang-command": "^2.0.0",
 +                "which": "^2.0.1"
 +            },
 +            "engines": {
 +                "node": ">= 8"
 +            }
 +        },
 +        "node_modules/css-select": {
 +            "version": "5.1.0",
 +            "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
 +            "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
 +            "dev": true,
 +            "dependencies": {
 +                "boolbase": "^1.0.0",
 +                "css-what": "^6.1.0",
 +                "domhandler": "^5.0.2",
 +                "domutils": "^3.0.1",
 +                "nth-check": "^2.0.1"
 +            },
 +            "funding": {
 +                "url": "https://github.com/sponsors/fb55"
 +            }
 +        },
 +        "node_modules/css-what": {
 +            "version": "6.1.0",
 +            "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
 +            "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">= 6"
 +            },
 +            "funding": {
 +                "url": "https://github.com/sponsors/fb55"
 +            }
 +        },
 +        "node_modules/d3": {
 +            "version": "7.6.1",
 +            "resolved": "https://registry.npmjs.org/d3/-/d3-7.6.1.tgz",
 +            "integrity": "sha512-txMTdIHFbcpLx+8a0IFhZsbp+PfBBPt8yfbmukZTQFroKuFqIwqswF0qE5JXWefylaAVpSXFoKm3yP+jpNLFLw==",
 +            "dependencies": {
 +                "d3-array": "3",
 +                "d3-axis": "3",
 +                "d3-brush": "3",
 +                "d3-chord": "3",
 +                "d3-color": "3",
 +                "d3-contour": "4",
 +                "d3-delaunay": "6",
 +                "d3-dispatch": "3",
 +                "d3-drag": "3",
 +                "d3-dsv": "3",
 +                "d3-ease": "3",
 +                "d3-fetch": "3",
 +                "d3-force": "3",
 +                "d3-format": "3",
 +                "d3-geo": "3",
 +                "d3-hierarchy": "3",
 +                "d3-interpolate": "3",
 +                "d3-path": "3",
 +                "d3-polygon": "3",
 +                "d3-quadtree": "3",
 +                "d3-random": "3",
 +                "d3-scale": "4",
 +                "d3-scale-chromatic": "3",
 +                "d3-selection": "3",
 +                "d3-shape": "3",
 +                "d3-time": "3",
 +                "d3-time-format": "4",
 +                "d3-timer": "3",
 +                "d3-transition": "3",
 +                "d3-zoom": "3"
 +            },
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/d3-array": {
 +            "version": "3.2.0",
 +            "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.0.tgz",
 +            "integrity": "sha512-3yXFQo0oG3QCxbF06rMPFyGRMGJNS7NvsV1+2joOjbBE+9xvWQ8+GcMJAjRCzw06zQ3/arXeJgbPYcjUCuC+3g==",
 +            "dependencies": {
 +                "internmap": "1 - 2"
 +            },
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/d3-axis": {
 +            "version": "3.0.0",
 +            "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz",
 +            "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==",
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/d3-brush": {
 +            "version": "3.0.0",
 +            "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz",
 +            "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==",
 +            "dependencies": {
 +                "d3-dispatch": "1 - 3",
 +                "d3-drag": "2 - 3",
 +                "d3-interpolate": "1 - 3",
 +                "d3-selection": "3",
 +                "d3-transition": "3"
 +            },
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/d3-brush/node_modules/d3-selection": {
 +            "version": "3.0.0",
 +            "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
 +            "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/d3-chord": {
 +            "version": "3.0.1",
 +            "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz",
 +            "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==",
 +            "dependencies": {
 +                "d3-path": "1 - 3"
 +            },
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/d3-color": {
 +            "version": "3.1.0",
 +            "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
 +            "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/d3-contour": {
 +            "version": "4.0.0",
 +            "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.0.tgz",
 +            "integrity": "sha512-7aQo0QHUTu/Ko3cP9YK9yUTxtoDEiDGwnBHyLxG5M4vqlBkO/uixMRele3nfsfj6UXOcuReVpVXzAboGraYIJw==",
 +            "dependencies": {
 +                "d3-array": "^3.2.0"
 +            },
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/d3-delaunay": {
 +            "version": "6.0.2",
 +            "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.2.tgz",
 +            "integrity": "sha512-IMLNldruDQScrcfT+MWnazhHbDJhcRJyOEBAJfwQnHle1RPh6WDuLvxNArUju2VSMSUuKlY5BGHRJ2cYyoFLQQ==",
 +            "dependencies": {
 +                "delaunator": "5"
 +            },
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/d3-dispatch": {
 +            "version": "3.0.1",
 +            "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
 +            "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/d3-drag": {
 +            "version": "3.0.0",
 +            "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz",
 +            "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
 +            "dependencies": {
 +                "d3-dispatch": "1 - 3",
 +                "d3-selection": "3"
 +            },
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/d3-drag/node_modules/d3-selection": {
 +            "version": "3.0.0",
 +            "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
 +            "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/d3-dsv": {
 +            "version": "3.0.1",
 +            "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz",
 +            "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",
 +            "dependencies": {
 +                "commander": "7",
 +                "iconv-lite": "0.6",
 +                "rw": "1"
 +            },
 +            "bin": {
 +                "csv2json": "bin/dsv2json.js",
 +                "csv2tsv": "bin/dsv2dsv.js",
 +                "dsv2dsv": "bin/dsv2dsv.js",
 +                "dsv2json": "bin/dsv2json.js",
 +                "json2csv": "bin/json2dsv.js",
 +                "json2dsv": "bin/json2dsv.js",
 +                "json2tsv": "bin/json2dsv.js",
 +                "tsv2csv": "bin/dsv2dsv.js",
 +                "tsv2json": "bin/dsv2json.js"
 +            },
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/d3-ease": {
 +            "version": "3.0.1",
 +            "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
 +            "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/d3-fetch": {
 +            "version": "3.0.1",
 +            "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz",
 +            "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==",
 +            "dependencies": {
 +                "d3-dsv": "1 - 3"
 +            },
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/d3-force": {
 +            "version": "3.0.0",
 +            "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",
 +            "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==",
 +            "dependencies": {
 +                "d3-dispatch": "1 - 3",
 +                "d3-quadtree": "1 - 3",
 +                "d3-timer": "1 - 3"
 +            },
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/d3-format": {
 +            "version": "3.1.0",
 +            "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
 +            "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/d3-geo": {
 +            "version": "3.0.1",
 +            "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.0.1.tgz",
 +            "integrity": "sha512-Wt23xBych5tSy9IYAM1FR2rWIBFWa52B/oF/GYe5zbdHrg08FU8+BuI6X4PvTwPDdqdAdq04fuWJpELtsaEjeA==",
 +            "dependencies": {
 +                "d3-array": "2.5.0 - 3"
 +            },
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/d3-graphviz": {
 +            "version": "4.1.1",
 +            "resolved": "https://registry.npmjs.org/d3-graphviz/-/d3-graphviz-4.1.1.tgz",
 +            "integrity": "sha512-s0IVbKf8rs4eJI2xo5Umr7nXDX/LEZw/x2WtKxmlyQxR0qUY49UiLhBNOX7VDHZywMle43NKEXnU6fn22fpJvQ==",
 +            "dependencies": {
 +                "@hpcc-js/wasm": "1.12.8",
 +                "d3-dispatch": "^2.0.0",
 +                "d3-format": "^2.0.0",
 +                "d3-interpolate": "^2.0.1",
 +                "d3-path": "^2.0.0",
 +                "d3-timer": "^2.0.0",
 +                "d3-transition": "^2.0.0",
 +                "d3-zoom": "^2.0.0"
 +            },
 +            "peerDependencies": {
 +                "d3-selection": "^2.0.0"
 +            }
 +        },
 +        "node_modules/d3-graphviz/node_modules/d3-color": {
 +            "version": "2.0.0",
 +            "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-2.0.0.tgz",
 +            "integrity": "sha512-SPXi0TSKPD4g9tw0NMZFnR95XVgUZiBH+uUTqQuDu1OsE2zomHU7ho0FISciaPvosimixwHFl3WHLGabv6dDgQ=="
 +        },
 +        "node_modules/d3-graphviz/node_modules/d3-dispatch": {
 +            "version": "2.0.0",
 +            "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-2.0.0.tgz",
 +            "integrity": "sha512-S/m2VsXI7gAti2pBoLClFFTMOO1HTtT0j99AuXLoGFKO6deHDdnv6ZGTxSTTUTgO1zVcv82fCOtDjYK4EECmWA=="
 +        },
 +        "node_modules/d3-graphviz/node_modules/d3-drag": {
 +            "version": "2.0.0",
 +            "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-2.0.0.tgz",
 +            "integrity": "sha512-g9y9WbMnF5uqB9qKqwIIa/921RYWzlUDv9Jl1/yONQwxbOfszAWTCm8u7HOTgJgRDXiRZN56cHT9pd24dmXs8w==",
 +            "dependencies": {
 +                "d3-dispatch": "1 - 2",
 +                "d3-selection": "2"
 +            }
 +        },
 +        "node_modules/d3-graphviz/node_modules/d3-ease": {
 +            "version": "2.0.0",
 +            "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-2.0.0.tgz",
 +            "integrity": "sha512-68/n9JWarxXkOWMshcT5IcjbB+agblQUaIsbnXmrzejn2O82n3p2A9R2zEB9HIEFWKFwPAEDDN8gR0VdSAyyAQ=="
 +        },
 +        "node_modules/d3-graphviz/node_modules/d3-format": {
 +            "version": "2.0.0",
 +            "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-2.0.0.tgz",
 +            "integrity": "sha512-Ab3S6XuE/Q+flY96HXT0jOXcM4EAClYFnRGY5zsjRGNy6qCYrQsMffs7cV5Q9xejb35zxW5hf/guKw34kvIKsA=="
 +        },
 +        "node_modules/d3-graphviz/node_modules/d3-interpolate": {
 +            "version": "2.0.1",
 +            "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-2.0.1.tgz",
 +            "integrity": "sha512-c5UhwwTs/yybcmTpAVqwSFl6vrQ8JZJoT5F7xNFK9pymv5C0Ymcc9/LIJHtYIggg/yS9YHw8i8O8tgb9pupjeQ==",
 +            "dependencies": {
 +                "d3-color": "1 - 2"
 +            }
 +        },
 +        "node_modules/d3-graphviz/node_modules/d3-path": {
 +            "version": "2.0.0",
 +            "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-2.0.0.tgz",
 +            "integrity": "sha512-ZwZQxKhBnv9yHaiWd6ZU4x5BtCQ7pXszEV9CU6kRgwIQVQGLMv1oiL4M+MK/n79sYzsj+gcgpPQSctJUsLN7fA=="
 +        },
 +        "node_modules/d3-graphviz/node_modules/d3-timer": {
 +            "version": "2.0.0",
 +            "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-2.0.0.tgz",
 +            "integrity": "sha512-TO4VLh0/420Y/9dO3+f9abDEFYeCUr2WZRlxJvbp4HPTQcSylXNiL6yZa9FIUvV1yRiFufl1bszTCLDqv9PWNA=="
 +        },
 +        "node_modules/d3-graphviz/node_modules/d3-transition": {
 +            "version": "2.0.0",
 +            "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-2.0.0.tgz",
 +            "integrity": "sha512-42ltAGgJesfQE3u9LuuBHNbGrI/AJjNL2OAUdclE70UE6Vy239GCBEYD38uBPoLeNsOhFStGpPI0BAOV+HMxog==",
 +            "dependencies": {
 +                "d3-color": "1 - 2",
 +                "d3-dispatch": "1 - 2",
 +                "d3-ease": "1 - 2",
 +                "d3-interpolate": "1 - 2",
 +                "d3-timer": "1 - 2"
 +            },
 +            "peerDependencies": {
 +                "d3-selection": "2"
 +            }
 +        },
 +        "node_modules/d3-graphviz/node_modules/d3-zoom": {
 +            "version": "2.0.0",
 +            "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-2.0.0.tgz",
 +            "integrity": "sha512-fFg7aoaEm9/jf+qfstak0IYpnesZLiMX6GZvXtUSdv8RH2o4E2qeelgdU09eKS6wGuiGMfcnMI0nTIqWzRHGpw==",
 +            "dependencies": {
 +                "d3-dispatch": "1 - 2",
 +                "d3-drag": "2",
 +                "d3-interpolate": "1 - 2",
 +                "d3-selection": "2",
 +                "d3-transition": "2"
 +            }
 +        },
 +        "node_modules/d3-hierarchy": {
 +            "version": "3.1.2",
 +            "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz",
 +            "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==",
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/d3-interpolate": {
 +            "version": "3.0.1",
 +            "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
 +            "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
 +            "dependencies": {
 +                "d3-color": "1 - 3"
 +            },
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/d3-path": {
 +            "version": "3.0.1",
 +            "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.0.1.tgz",
 +            "integrity": "sha512-gq6gZom9AFZby0YLduxT1qmrp4xpBA1YZr19OI717WIdKE2OM5ETq5qrHLb301IgxhLwcuxvGZVLeeWc/k1I6w==",
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/d3-polygon": {
 +            "version": "3.0.1",
 +            "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz",
 +            "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==",
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/d3-quadtree": {
 +            "version": "3.0.1",
 +            "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz",
 +            "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==",
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/d3-random": {
 +            "version": "3.0.1",
 +            "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz",
 +            "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==",
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/d3-scale": {
 +            "version": "4.0.2",
 +            "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
 +            "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
 +            "dependencies": {
 +                "d3-array": "2.10.0 - 3",
 +                "d3-format": "1 - 3",
 +                "d3-interpolate": "1.2.0 - 3",
 +                "d3-time": "2.1.1 - 3",
 +                "d3-time-format": "2 - 4"
 +            },
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/d3-scale-chromatic": {
 +            "version": "3.0.0",
 +            "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz",
 +            "integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==",
 +            "dependencies": {
 +                "d3-color": "1 - 3",
 +                "d3-interpolate": "1 - 3"
 +            },
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/d3-selection": {
 +            "version": "2.0.0",
 +            "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-2.0.0.tgz",
 +            "integrity": "sha512-XoGGqhLUN/W14NmaqcO/bb1nqjDAw5WtSYb2X8wiuQWvSZUsUVYsOSkOybUrNvcBjaywBdYPy03eXHMXjk9nZA=="
 +        },
 +        "node_modules/d3-shape": {
 +            "version": "3.1.0",
 +            "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.1.0.tgz",
 +            "integrity": "sha512-tGDh1Muf8kWjEDT/LswZJ8WF85yDZLvVJpYU9Nq+8+yW1Z5enxrmXOhTArlkaElU+CTn0OTVNli+/i+HP45QEQ==",
 +            "dependencies": {
 +                "d3-path": "1 - 3"
 +            },
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/d3-time": {
 +            "version": "3.0.0",
 +            "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.0.0.tgz",
 +            "integrity": "sha512-zmV3lRnlaLI08y9IMRXSDshQb5Nj77smnfpnd2LrBa/2K281Jijactokeak14QacHs/kKq0AQ121nidNYlarbQ==",
 +            "dependencies": {
 +                "d3-array": "2 - 3"
 +            },
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/d3-time-format": {
 +            "version": "4.1.0",
 +            "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
 +            "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
 +            "dependencies": {
 +                "d3-time": "1 - 3"
 +            },
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/d3-timer": {
 +            "version": "3.0.1",
 +            "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
 +            "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/d3-transition": {
 +            "version": "3.0.1",
 +            "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz",
 +            "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
 +            "dependencies": {
 +                "d3-color": "1 - 3",
 +                "d3-dispatch": "1 - 3",
 +                "d3-ease": "1 - 3",
 +                "d3-interpolate": "1 - 3",
 +                "d3-timer": "1 - 3"
 +            },
 +            "engines": {
 +                "node": ">=12"
 +            },
 +            "peerDependencies": {
 +                "d3-selection": "2 - 3"
 +            }
 +        },
 +        "node_modules/d3-zoom": {
 +            "version": "3.0.0",
 +            "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz",
 +            "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
 +            "dependencies": {
 +                "d3-dispatch": "1 - 3",
 +                "d3-drag": "2 - 3",
 +                "d3-interpolate": "1 - 3",
 +                "d3-selection": "2 - 3",
 +                "d3-transition": "2 - 3"
 +            },
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/d3/node_modules/d3-selection": {
 +            "version": "3.0.0",
 +            "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
 +            "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/debug": {
 +            "version": "4.3.4",
 +            "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
 +            "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
 +            "dev": true,
 +            "dependencies": {
 +                "ms": "2.1.2"
 +            },
 +            "engines": {
 +                "node": ">=6.0"
 +            },
 +            "peerDependenciesMeta": {
 +                "supports-color": {
 +                    "optional": true
 +                }
 +            }
 +        },
 +        "node_modules/decompress-response": {
 +            "version": "6.0.0",
 +            "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
 +            "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
 +            "dev": true,
 +            "dependencies": {
 +                "mimic-response": "^3.1.0"
 +            },
 +            "engines": {
 +                "node": ">=10"
 +            },
 +            "funding": {
 +                "url": "https://github.com/sponsors/sindresorhus"
 +            }
 +        },
 +        "node_modules/deep-extend": {
 +            "version": "0.6.0",
 +            "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
 +            "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=4.0.0"
 +            }
 +        },
 +        "node_modules/deep-is": {
 +            "version": "0.1.4",
 +            "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
 +            "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
 +            "dev": true
 +        },
 +        "node_modules/delaunator": {
 +            "version": "5.0.0",
 +            "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz",
 +            "integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==",
 +            "dependencies": {
 +                "robust-predicates": "^3.0.0"
 +            }
 +        },
 +        "node_modules/detect-libc": {
 +            "version": "2.0.1",
 +            "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz",
 +            "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=8"
 +            }
 +        },
 +        "node_modules/dir-glob": {
 +            "version": "3.0.1",
 +            "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
 +            "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
 +            "dev": true,
 +            "dependencies": {
 +                "path-type": "^4.0.0"
 +            },
 +            "engines": {
 +                "node": ">=8"
 +            }
 +        },
 +        "node_modules/doctrine": {
 +            "version": "3.0.0",
 +            "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
 +            "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
 +            "dev": true,
 +            "dependencies": {
 +                "esutils": "^2.0.2"
 +            },
 +            "engines": {
 +                "node": ">=6.0.0"
 +            }
 +        },
 +        "node_modules/dom-serializer": {
 +            "version": "2.0.0",
 +            "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
 +            "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
 +            "dev": true,
 +            "dependencies": {
 +                "domelementtype": "^2.3.0",
 +                "domhandler": "^5.0.2",
 +                "entities": "^4.2.0"
 +            },
 +            "funding": {
 +                "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
 +            }
 +        },
 +        "node_modules/domelementtype": {
 +            "version": "2.3.0",
 +            "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
 +            "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
 +            "dev": true,
 +            "funding": [
 +                {
 +                    "type": "github",
 +                    "url": "https://github.com/sponsors/fb55"
 +                }
 +            ]
 +        },
 +        "node_modules/domhandler": {
 +            "version": "5.0.3",
 +            "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
 +            "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
 +            "dev": true,
 +            "dependencies": {
 +                "domelementtype": "^2.3.0"
 +            },
 +            "engines": {
 +                "node": ">= 4"
 +            },
 +            "funding": {
 +                "url": "https://github.com/fb55/domhandler?sponsor=1"
 +            }
 +        },
 +        "node_modules/domutils": {
 +            "version": "3.0.1",
 +            "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz",
 +            "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==",
 +            "dev": true,
 +            "dependencies": {
 +                "dom-serializer": "^2.0.0",
 +                "domelementtype": "^2.3.0",
 +                "domhandler": "^5.0.1"
 +            },
 +            "funding": {
 +                "url": "https://github.com/fb55/domutils?sponsor=1"
 +            }
 +        },
 +        "node_modules/duplexer2": {
 +            "version": "0.1.4",
 +            "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
 +            "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==",
 +            "dev": true,
 +            "dependencies": {
 +                "readable-stream": "^2.0.2"
 +            }
 +        },
 +        "node_modules/emoji-regex": {
 +            "version": "8.0.0",
 +            "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
 +            "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
 +        },
 +        "node_modules/end-of-stream": {
 +            "version": "1.4.4",
 +            "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
 +            "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
 +            "dev": true,
 +            "dependencies": {
 +                "once": "^1.4.0"
 +            }
 +        },
 +        "node_modules/entities": {
 +            "version": "4.3.1",
 +            "resolved": "https://registry.npmjs.org/entities/-/entities-4.3.1.tgz",
 +            "integrity": "sha512-o4q/dYJlmyjP2zfnaWDUC6A3BQFmVTX+tZPezK7k0GLSU9QYCauscf5Y+qcEPzKL+EixVouYDgLQK5H9GrLpkg==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=0.12"
 +            },
 +            "funding": {
 +                "url": "https://github.com/fb55/entities?sponsor=1"
 +            }
 +        },
 +        "node_modules/esbuild": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.48.tgz",
 +            "integrity": "sha512-w6N1Yn5MtqK2U1/WZTX9ZqUVb8IOLZkZ5AdHkT6x3cHDMVsYWC7WPdiLmx19w3i4Rwzy5LqsEMtVihG3e4rFzA==",
 +            "dev": true,
 +            "hasInstallScript": true,
 +            "bin": {
 +                "esbuild": "bin/esbuild"
 +            },
 +            "engines": {
 +                "node": ">=12"
 +            },
 +            "optionalDependencies": {
 +                "esbuild-android-64": "0.14.48",
 +                "esbuild-android-arm64": "0.14.48",
 +                "esbuild-darwin-64": "0.14.48",
 +                "esbuild-darwin-arm64": "0.14.48",
 +                "esbuild-freebsd-64": "0.14.48",
 +                "esbuild-freebsd-arm64": "0.14.48",
 +                "esbuild-linux-32": "0.14.48",
 +                "esbuild-linux-64": "0.14.48",
 +                "esbuild-linux-arm": "0.14.48",
 +                "esbuild-linux-arm64": "0.14.48",
 +                "esbuild-linux-mips64le": "0.14.48",
 +                "esbuild-linux-ppc64le": "0.14.48",
 +                "esbuild-linux-riscv64": "0.14.48",
 +                "esbuild-linux-s390x": "0.14.48",
 +                "esbuild-netbsd-64": "0.14.48",
 +                "esbuild-openbsd-64": "0.14.48",
 +                "esbuild-sunos-64": "0.14.48",
 +                "esbuild-windows-32": "0.14.48",
 +                "esbuild-windows-64": "0.14.48",
 +                "esbuild-windows-arm64": "0.14.48"
 +            }
 +        },
 +        "node_modules/esbuild-android-64": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.48.tgz",
 +            "integrity": "sha512-3aMjboap/kqwCUpGWIjsk20TtxVoKck8/4Tu19rubh7t5Ra0Yrpg30Mt1QXXlipOazrEceGeWurXKeFJgkPOUg==",
 +            "cpu": [
 +                "x64"
 +            ],
 +            "dev": true,
 +            "optional": true,
 +            "os": [
 +                "android"
 +            ],
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/esbuild-android-arm64": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.48.tgz",
 +            "integrity": "sha512-vptI3K0wGALiDq+EvRuZotZrJqkYkN5282iAfcffjI5lmGG9G1ta/CIVauhY42MBXwEgDJkweiDcDMRLzBZC4g==",
 +            "cpu": [
 +                "arm64"
 +            ],
 +            "dev": true,
 +            "optional": true,
 +            "os": [
 +                "android"
 +            ],
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/esbuild-darwin-64": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.48.tgz",
 +            "integrity": "sha512-gGQZa4+hab2Va/Zww94YbshLuWteyKGD3+EsVon8EWTWhnHFRm5N9NbALNbwi/7hQ/hM1Zm4FuHg+k6BLsl5UA==",
 +            "cpu": [
 +                "x64"
 +            ],
 +            "dev": true,
 +            "optional": true,
 +            "os": [
 +                "darwin"
 +            ],
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/esbuild-darwin-arm64": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.48.tgz",
 +            "integrity": "sha512-bFjnNEXjhZT+IZ8RvRGNJthLWNHV5JkCtuOFOnjvo5pC0sk2/QVk0Qc06g2PV3J0TcU6kaPC3RN9yy9w2PSLEA==",
 +            "cpu": [
 +                "arm64"
 +            ],
 +            "dev": true,
 +            "optional": true,
 +            "os": [
 +                "darwin"
 +            ],
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/esbuild-freebsd-64": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.48.tgz",
 +            "integrity": "sha512-1NOlwRxmOsnPcWOGTB10JKAkYSb2nue0oM1AfHWunW/mv3wERfJmnYlGzL3UAOIUXZqW8GeA2mv+QGwq7DToqA==",
 +            "cpu": [
 +                "x64"
 +            ],
 +            "dev": true,
 +            "optional": true,
 +            "os": [
 +                "freebsd"
 +            ],
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/esbuild-freebsd-arm64": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.48.tgz",
 +            "integrity": "sha512-gXqKdO8wabVcYtluAbikDH2jhXp+Klq5oCD5qbVyUG6tFiGhrC9oczKq3vIrrtwcxDQqK6+HDYK8Zrd4bCA9Gw==",
 +            "cpu": [
 +                "arm64"
 +            ],
 +            "dev": true,
 +            "optional": true,
 +            "os": [
 +                "freebsd"
 +            ],
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/esbuild-linux-32": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.48.tgz",
 +            "integrity": "sha512-ghGyDfS289z/LReZQUuuKq9KlTiTspxL8SITBFQFAFRA/IkIvDpnZnCAKTCjGXAmUqroMQfKJXMxyjJA69c/nQ==",
 +            "cpu": [
 +                "ia32"
 +            ],
 +            "dev": true,
 +            "optional": true,
 +            "os": [
 +                "linux"
 +            ],
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/esbuild-linux-64": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.48.tgz",
 +            "integrity": "sha512-vni3p/gppLMVZLghI7oMqbOZdGmLbbKR23XFARKnszCIBpEMEDxOMNIKPmMItQrmH/iJrL1z8Jt2nynY0bE1ug==",
 +            "cpu": [
 +                "x64"
 +            ],
 +            "dev": true,
 +            "optional": true,
 +            "os": [
 +                "linux"
 +            ],
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/esbuild-linux-arm": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.48.tgz",
 +            "integrity": "sha512-+VfSV7Akh1XUiDNXgqgY1cUP1i2vjI+BmlyXRfVz5AfV3jbpde8JTs5Q9sYgaoq5cWfuKfoZB/QkGOI+QcL1Tw==",
 +            "cpu": [
 +                "arm"
 +            ],
 +            "dev": true,
 +            "optional": true,
 +            "os": [
 +                "linux"
 +            ],
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/esbuild-linux-arm64": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.48.tgz",
 +            "integrity": "sha512-3CFsOlpoxlKPRevEHq8aAntgYGYkE1N9yRYAcPyng/p4Wyx0tPR5SBYsxLKcgPB9mR8chHEhtWYz6EZ+H199Zw==",
 +            "cpu": [
 +                "arm64"
 +            ],
 +            "dev": true,
 +            "optional": true,
 +            "os": [
 +                "linux"
 +            ],
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/esbuild-linux-mips64le": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.48.tgz",
 +            "integrity": "sha512-cs0uOiRlPp6ymknDnjajCgvDMSsLw5mST2UXh+ZIrXTj2Ifyf2aAP3Iw4DiqgnyYLV2O/v/yWBJx+WfmKEpNLA==",
 +            "cpu": [
 +                "mips64el"
 +            ],
 +            "dev": true,
 +            "optional": true,
 +            "os": [
 +                "linux"
 +            ],
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/esbuild-linux-ppc64le": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.48.tgz",
 +            "integrity": "sha512-+2F0vJMkuI0Wie/wcSPDCqXvSFEELH7Jubxb7mpWrA/4NpT+/byjxDz0gG6R1WJoeDefcrMfpBx4GFNN1JQorQ==",
 +            "cpu": [
 +                "ppc64"
 +            ],
 +            "dev": true,
 +            "optional": true,
 +            "os": [
 +                "linux"
 +            ],
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/esbuild-linux-riscv64": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.48.tgz",
 +            "integrity": "sha512-BmaK/GfEE+5F2/QDrIXteFGKnVHGxlnK9MjdVKMTfvtmudjY3k2t8NtlY4qemKSizc+QwyombGWTBDc76rxePA==",
 +            "cpu": [
 +                "riscv64"
 +            ],
 +            "dev": true,
 +            "optional": true,
 +            "os": [
 +                "linux"
 +            ],
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/esbuild-linux-s390x": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.48.tgz",
 +            "integrity": "sha512-tndw/0B9jiCL+KWKo0TSMaUm5UWBLsfCKVdbfMlb3d5LeV9WbijZ8Ordia8SAYv38VSJWOEt6eDCdOx8LqkC4g==",
 +            "cpu": [
 +                "s390x"
 +            ],
 +            "dev": true,
 +            "optional": true,
 +            "os": [
 +                "linux"
 +            ],
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/esbuild-netbsd-64": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.48.tgz",
 +            "integrity": "sha512-V9hgXfwf/T901Lr1wkOfoevtyNkrxmMcRHyticybBUHookznipMOHoF41Al68QBsqBxnITCEpjjd4yAos7z9Tw==",
 +            "cpu": [
 +                "x64"
 +            ],
 +            "dev": true,
 +            "optional": true,
 +            "os": [
 +                "netbsd"
 +            ],
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/esbuild-openbsd-64": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.48.tgz",
 +            "integrity": "sha512-+IHf4JcbnnBl4T52egorXMatil/za0awqzg2Vy6FBgPcBpisDWT2sVz/tNdrK9kAqj+GZG/jZdrOkj7wsrNTKA==",
 +            "cpu": [
 +                "x64"
 +            ],
 +            "dev": true,
 +            "optional": true,
 +            "os": [
 +                "openbsd"
 +            ],
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/esbuild-sunos-64": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.48.tgz",
 +            "integrity": "sha512-77m8bsr5wOpOWbGi9KSqDphcq6dFeJyun8TA+12JW/GAjyfTwVtOnN8DOt6DSPUfEV+ltVMNqtXUeTeMAxl5KA==",
 +            "cpu": [
 +                "x64"
 +            ],
 +            "dev": true,
 +            "optional": true,
 +            "os": [
 +                "sunos"
 +            ],
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/esbuild-windows-32": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.48.tgz",
 +            "integrity": "sha512-EPgRuTPP8vK9maxpTGDe5lSoIBHGKO/AuxDncg5O3NkrPeLNdvvK8oywB0zGaAZXxYWfNNSHskvvDgmfVTguhg==",
 +            "cpu": [
 +                "ia32"
 +            ],
 +            "dev": true,
 +            "optional": true,
 +            "os": [
 +                "win32"
 +            ],
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/esbuild-windows-64": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.48.tgz",
 +            "integrity": "sha512-YmpXjdT1q0b8ictSdGwH3M8VCoqPpK1/UArze3X199w6u8hUx3V8BhAi1WjbsfDYRBanVVtduAhh2sirImtAvA==",
 +            "cpu": [
 +                "x64"
 +            ],
 +            "dev": true,
 +            "optional": true,
 +            "os": [
 +                "win32"
 +            ],
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/esbuild-windows-arm64": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.48.tgz",
 +            "integrity": "sha512-HHaOMCsCXp0rz5BT2crTka6MPWVno121NKApsGs/OIW5QC0ggC69YMGs1aJct9/9FSUF4A1xNE/cLvgB5svR4g==",
 +            "cpu": [
 +                "arm64"
 +            ],
 +            "dev": true,
 +            "optional": true,
 +            "os": [
 +                "win32"
 +            ],
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/escalade": {
 +            "version": "3.1.1",
 +            "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
 +            "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
 +            "engines": {
 +                "node": ">=6"
 +            }
 +        },
 +        "node_modules/escape-string-regexp": {
 +            "version": "4.0.0",
 +            "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
 +            "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=10"
 +            },
 +            "funding": {
 +                "url": "https://github.com/sponsors/sindresorhus"
 +            }
 +        },
 +        "node_modules/eslint": {
 +            "version": "8.19.0",
 +            "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.19.0.tgz",
 +            "integrity": "sha512-SXOPj3x9VKvPe81TjjUJCYlV4oJjQw68Uek+AM0X4p+33dj2HY5bpTZOgnQHcG2eAm1mtCU9uNMnJi7exU/kYw==",
 +            "dev": true,
 +            "dependencies": {
 +                "@eslint/eslintrc": "^1.3.0",
 +                "@humanwhocodes/config-array": "^0.9.2",
 +                "ajv": "^6.10.0",
 +                "chalk": "^4.0.0",
 +                "cross-spawn": "^7.0.2",
 +                "debug": "^4.3.2",
 +                "doctrine": "^3.0.0",
 +                "escape-string-regexp": "^4.0.0",
 +                "eslint-scope": "^7.1.1",
 +                "eslint-utils": "^3.0.0",
 +                "eslint-visitor-keys": "^3.3.0",
 +                "espree": "^9.3.2",
 +                "esquery": "^1.4.0",
 +                "esutils": "^2.0.2",
 +                "fast-deep-equal": "^3.1.3",
 +                "file-entry-cache": "^6.0.1",
 +                "functional-red-black-tree": "^1.0.1",
 +                "glob-parent": "^6.0.1",
 +                "globals": "^13.15.0",
 +                "ignore": "^5.2.0",
 +                "import-fresh": "^3.0.0",
 +                "imurmurhash": "^0.1.4",
 +                "is-glob": "^4.0.0",
 +                "js-yaml": "^4.1.0",
 +                "json-stable-stringify-without-jsonify": "^1.0.1",
 +                "levn": "^0.4.1",
 +                "lodash.merge": "^4.6.2",
 +                "minimatch": "^3.1.2",
 +                "natural-compare": "^1.4.0",
 +                "optionator": "^0.9.1",
 +                "regexpp": "^3.2.0",
 +                "strip-ansi": "^6.0.1",
 +                "strip-json-comments": "^3.1.0",
 +                "text-table": "^0.2.0",
 +                "v8-compile-cache": "^2.0.3"
 +            },
 +            "bin": {
 +                "eslint": "bin/eslint.js"
 +            },
 +            "engines": {
 +                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
 +            },
 +            "funding": {
 +                "url": "https://opencollective.com/eslint"
 +            }
 +        },
 +        "node_modules/eslint-config-prettier": {
 +            "version": "8.5.0",
 +            "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz",
 +            "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==",
 +            "dev": true,
 +            "bin": {
 +                "eslint-config-prettier": "bin/cli.js"
 +            },
 +            "peerDependencies": {
 +                "eslint": ">=7.0.0"
 +            }
 +        },
 +        "node_modules/eslint-scope": {
 +            "version": "5.1.1",
 +            "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
 +            "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
 +            "dev": true,
 +            "dependencies": {
 +                "esrecurse": "^4.3.0",
 +                "estraverse": "^4.1.1"
 +            },
 +            "engines": {
 +                "node": ">=8.0.0"
 +            }
 +        },
 +        "node_modules/eslint-utils": {
 +            "version": "3.0.0",
 +            "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz",
 +            "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==",
 +            "dev": true,
 +            "dependencies": {
 +                "eslint-visitor-keys": "^2.0.0"
 +            },
 +            "engines": {
 +                "node": "^10.0.0 || ^12.0.0 || >= 14.0.0"
 +            },
 +            "funding": {
 +                "url": "https://github.com/sponsors/mysticatea"
 +            },
 +            "peerDependencies": {
 +                "eslint": ">=5"
 +            }
 +        },
 +        "node_modules/eslint-utils/node_modules/eslint-visitor-keys": {
 +            "version": "2.1.0",
 +            "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
 +            "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=10"
 +            }
 +        },
 +        "node_modules/eslint-visitor-keys": {
 +            "version": "3.3.0",
 +            "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz",
 +            "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==",
 +            "dev": true,
 +            "engines": {
 +                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
 +            }
 +        },
 +        "node_modules/eslint/node_modules/eslint-scope": {
 +            "version": "7.1.1",
 +            "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz",
 +            "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==",
 +            "dev": true,
 +            "dependencies": {
 +                "esrecurse": "^4.3.0",
 +                "estraverse": "^5.2.0"
 +            },
 +            "engines": {
 +                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
 +            }
 +        },
 +        "node_modules/eslint/node_modules/estraverse": {
 +            "version": "5.3.0",
 +            "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
 +            "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=4.0"
 +            }
 +        },
 +        "node_modules/espree": {
 +            "version": "9.3.2",
 +            "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz",
 +            "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==",
 +            "dev": true,
 +            "dependencies": {
 +                "acorn": "^8.7.1",
 +                "acorn-jsx": "^5.3.2",
 +                "eslint-visitor-keys": "^3.3.0"
 +            },
 +            "engines": {
 +                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
 +            }
 +        },
 +        "node_modules/esquery": {
 +            "version": "1.4.0",
 +            "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz",
 +            "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==",
 +            "dev": true,
 +            "dependencies": {
 +                "estraverse": "^5.1.0"
 +            },
 +            "engines": {
 +                "node": ">=0.10"
 +            }
 +        },
 +        "node_modules/esquery/node_modules/estraverse": {
 +            "version": "5.3.0",
 +            "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
 +            "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=4.0"
 +            }
 +        },
 +        "node_modules/esrecurse": {
 +            "version": "4.3.0",
 +            "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
 +            "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
 +            "dev": true,
 +            "dependencies": {
 +                "estraverse": "^5.2.0"
 +            },
 +            "engines": {
 +                "node": ">=4.0"
 +            }
 +        },
 +        "node_modules/esrecurse/node_modules/estraverse": {
 +            "version": "5.3.0",
 +            "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
 +            "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=4.0"
 +            }
 +        },
 +        "node_modules/estraverse": {
 +            "version": "4.3.0",
 +            "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
 +            "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=4.0"
 +            }
 +        },
 +        "node_modules/esutils": {
 +            "version": "2.0.3",
 +            "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
 +            "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=0.10.0"
 +            }
 +        },
 +        "node_modules/expand-template": {
 +            "version": "2.0.3",
 +            "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
 +            "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=6"
 +            }
 +        },
 +        "node_modules/fast-deep-equal": {
 +            "version": "3.1.3",
 +            "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
 +            "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
 +            "dev": true
 +        },
 +        "node_modules/fast-glob": {
 +            "version": "3.2.11",
 +            "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz",
 +            "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==",
 +            "dev": true,
 +            "dependencies": {
 +                "@nodelib/fs.stat": "^2.0.2",
 +                "@nodelib/fs.walk": "^1.2.3",
 +                "glob-parent": "^5.1.2",
 +                "merge2": "^1.3.0",
 +                "micromatch": "^4.0.4"
 +            },
 +            "engines": {
 +                "node": ">=8.6.0"
 +            }
 +        },
 +        "node_modules/fast-glob/node_modules/glob-parent": {
 +            "version": "5.1.2",
 +            "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
 +            "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
 +            "dev": true,
 +            "dependencies": {
 +                "is-glob": "^4.0.1"
 +            },
 +            "engines": {
 +                "node": ">= 6"
 +            }
 +        },
 +        "node_modules/fast-json-stable-stringify": {
 +            "version": "2.1.0",
 +            "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
 +            "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
 +            "dev": true
 +        },
 +        "node_modules/fast-levenshtein": {
 +            "version": "2.0.6",
 +            "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
 +            "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
 +            "dev": true
 +        },
 +        "node_modules/fastq": {
 +            "version": "1.13.0",
 +            "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz",
 +            "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==",
 +            "dev": true,
 +            "dependencies": {
 +                "reusify": "^1.0.4"
 +            }
 +        },
 +        "node_modules/fd-slicer": {
 +            "version": "1.1.0",
 +            "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
 +            "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
 +            "dev": true,
 +            "dependencies": {
 +                "pend": "~1.2.0"
 +            }
 +        },
 +        "node_modules/file-entry-cache": {
 +            "version": "6.0.1",
 +            "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
 +            "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
 +            "dev": true,
 +            "dependencies": {
 +                "flat-cache": "^3.0.4"
 +            },
 +            "engines": {
 +                "node": "^10.12.0 || >=12.0.0"
 +            }
 +        },
 +        "node_modules/fill-range": {
 +            "version": "7.0.1",
 +            "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
 +            "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
 +            "dev": true,
 +            "dependencies": {
 +                "to-regex-range": "^5.0.1"
 +            },
 +            "engines": {
 +                "node": ">=8"
 +            }
 +        },
 +        "node_modules/flat-cache": {
 +            "version": "3.0.4",
 +            "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
 +            "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
 +            "dev": true,
 +            "dependencies": {
 +                "flatted": "^3.1.0",
 +                "rimraf": "^3.0.2"
 +            },
 +            "engines": {
 +                "node": "^10.12.0 || >=12.0.0"
 +            }
 +        },
 +        "node_modules/flatted": {
 +            "version": "3.2.6",
 +            "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.6.tgz",
 +            "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==",
 +            "dev": true
 +        },
 +        "node_modules/follow-redirects": {
 +            "version": "1.15.1",
 +            "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz",
 +            "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==",
 +            "dev": true,
 +            "funding": [
 +                {
 +                    "type": "individual",
 +                    "url": "https://github.com/sponsors/RubenVerborgh"
 +                }
 +            ],
 +            "engines": {
 +                "node": ">=4.0"
 +            },
 +            "peerDependenciesMeta": {
 +                "debug": {
 +                    "optional": true
 +                }
 +            }
 +        },
 +        "node_modules/fs-constants": {
 +            "version": "1.0.0",
 +            "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
 +            "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
 +            "dev": true
 +        },
 +        "node_modules/fs.realpath": {
 +            "version": "1.0.0",
 +            "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
 +            "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
 +            "dev": true
 +        },
 +        "node_modules/fstream": {
 +            "version": "1.0.12",
 +            "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz",
 +            "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==",
 +            "dev": true,
 +            "dependencies": {
 +                "graceful-fs": "^4.1.2",
 +                "inherits": "~2.0.0",
 +                "mkdirp": ">=0.5 0",
 +                "rimraf": "2"
 +            },
 +            "engines": {
 +                "node": ">=0.6"
 +            }
 +        },
 +        "node_modules/fstream/node_modules/rimraf": {
 +            "version": "2.7.1",
 +            "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
 +            "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
 +            "dev": true,
 +            "dependencies": {
 +                "glob": "^7.1.3"
 +            },
 +            "bin": {
 +                "rimraf": "bin.js"
 +            }
 +        },
 +        "node_modules/function-bind": {
 +            "version": "1.1.1",
 +            "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
 +            "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
 +            "dev": true
 +        },
 +        "node_modules/functional-red-black-tree": {
 +            "version": "1.0.1",
 +            "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
 +            "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==",
 +            "dev": true
 +        },
 +        "node_modules/get-caller-file": {
 +            "version": "2.0.5",
 +            "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
 +            "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
 +            "engines": {
 +                "node": "6.* || 8.* || >= 10.*"
 +            }
 +        },
 +        "node_modules/get-intrinsic": {
 +            "version": "1.1.2",
 +            "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz",
 +            "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==",
 +            "dev": true,
 +            "dependencies": {
 +                "function-bind": "^1.1.1",
 +                "has": "^1.0.3",
 +                "has-symbols": "^1.0.3"
 +            },
 +            "funding": {
 +                "url": "https://github.com/sponsors/ljharb"
 +            }
 +        },
 +        "node_modules/github-from-package": {
 +            "version": "0.0.0",
 +            "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
 +            "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==",
 +            "dev": true
 +        },
 +        "node_modules/glob": {
 +            "version": "7.2.3",
 +            "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
 +            "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
 +            "dev": true,
 +            "dependencies": {
 +                "fs.realpath": "^1.0.0",
 +                "inflight": "^1.0.4",
 +                "inherits": "2",
 +                "minimatch": "^3.1.1",
 +                "once": "^1.3.0",
 +                "path-is-absolute": "^1.0.0"
 +            },
 +            "engines": {
 +                "node": "*"
 +            },
 +            "funding": {
 +                "url": "https://github.com/sponsors/isaacs"
 +            }
 +        },
 +        "node_modules/glob-parent": {
 +            "version": "6.0.2",
 +            "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
 +            "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
 +            "dev": true,
 +            "dependencies": {
 +                "is-glob": "^4.0.3"
 +            },
 +            "engines": {
 +                "node": ">=10.13.0"
 +            }
 +        },
 +        "node_modules/globals": {
 +            "version": "13.16.0",
 +            "resolved": "https://registry.npmjs.org/globals/-/globals-13.16.0.tgz",
 +            "integrity": "sha512-A1lrQfpNF+McdPOnnFqY3kSN0AFTy485bTi1bkLk4mVPODIUEcSfhHgRqA+QdXPksrSTTztYXx37NFV+GpGk3Q==",
 +            "dev": true,
 +            "dependencies": {
 +                "type-fest": "^0.20.2"
 +            },
 +            "engines": {
 +                "node": ">=8"
 +            },
 +            "funding": {
 +                "url": "https://github.com/sponsors/sindresorhus"
 +            }
 +        },
 +        "node_modules/globby": {
 +            "version": "11.1.0",
 +            "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
 +            "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
 +            "dev": true,
 +            "dependencies": {
 +                "array-union": "^2.1.0",
 +                "dir-glob": "^3.0.1",
 +                "fast-glob": "^3.2.9",
 +                "ignore": "^5.2.0",
 +                "merge2": "^1.4.1",
 +                "slash": "^3.0.0"
 +            },
 +            "engines": {
 +                "node": ">=10"
 +            },
 +            "funding": {
 +                "url": "https://github.com/sponsors/sindresorhus"
 +            }
 +        },
 +        "node_modules/graceful-fs": {
 +            "version": "4.2.10",
 +            "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
 +            "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
 +            "dev": true
 +        },
 +        "node_modules/has": {
 +            "version": "1.0.3",
 +            "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
 +            "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
 +            "dev": true,
 +            "dependencies": {
 +                "function-bind": "^1.1.1"
 +            },
 +            "engines": {
 +                "node": ">= 0.4.0"
 +            }
 +        },
 +        "node_modules/has-flag": {
 +            "version": "4.0.0",
 +            "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
 +            "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=8"
 +            }
 +        },
 +        "node_modules/has-symbols": {
 +            "version": "1.0.3",
 +            "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
 +            "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">= 0.4"
 +            },
 +            "funding": {
 +                "url": "https://github.com/sponsors/ljharb"
 +            }
 +        },
 +        "node_modules/hosted-git-info": {
 +            "version": "4.1.0",
 +            "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz",
 +            "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==",
 +            "dev": true,
 +            "dependencies": {
 +                "lru-cache": "^6.0.0"
 +            },
 +            "engines": {
 +                "node": ">=10"
 +            }
 +        },
 +        "node_modules/htmlparser2": {
 +            "version": "8.0.1",
 +            "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz",
 +            "integrity": "sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==",
 +            "dev": true,
 +            "funding": [
 +                "https://github.com/fb55/htmlparser2?sponsor=1",
 +                {
 +                    "type": "github",
 +                    "url": "https://github.com/sponsors/fb55"
 +                }
 +            ],
 +            "dependencies": {
 +                "domelementtype": "^2.3.0",
 +                "domhandler": "^5.0.2",
 +                "domutils": "^3.0.1",
 +                "entities": "^4.3.0"
 +            }
 +        },
 +        "node_modules/http-proxy-agent": {
 +            "version": "4.0.1",
 +            "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
 +            "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
 +            "dev": true,
 +            "dependencies": {
 +                "@tootallnate/once": "1",
 +                "agent-base": "6",
 +                "debug": "4"
 +            },
 +            "engines": {
 +                "node": ">= 6"
 +            }
 +        },
 +        "node_modules/https-proxy-agent": {
 +            "version": "5.0.1",
 +            "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
 +            "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
 +            "dev": true,
 +            "dependencies": {
 +                "agent-base": "6",
 +                "debug": "4"
 +            },
 +            "engines": {
 +                "node": ">= 6"
 +            }
 +        },
 +        "node_modules/iconv-lite": {
 +            "version": "0.6.3",
 +            "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
 +            "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
 +            "dependencies": {
 +                "safer-buffer": ">= 2.1.2 < 3.0.0"
 +            },
 +            "engines": {
 +                "node": ">=0.10.0"
 +            }
 +        },
 +        "node_modules/ieee754": {
 +            "version": "1.2.1",
 +            "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
 +            "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
 +            "dev": true,
 +            "funding": [
 +                {
 +                    "type": "github",
 +                    "url": "https://github.com/sponsors/feross"
 +                },
 +                {
 +                    "type": "patreon",
 +                    "url": "https://www.patreon.com/feross"
 +                },
 +                {
 +                    "type": "consulting",
 +                    "url": "https://feross.org/support"
 +                }
 +            ]
 +        },
 +        "node_modules/ignore": {
 +            "version": "5.2.0",
 +            "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
 +            "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">= 4"
 +            }
 +        },
 +        "node_modules/import-fresh": {
 +            "version": "3.3.0",
 +            "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
 +            "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
 +            "dev": true,
 +            "dependencies": {
 +                "parent-module": "^1.0.0",
 +                "resolve-from": "^4.0.0"
 +            },
 +            "engines": {
 +                "node": ">=6"
 +            },
 +            "funding": {
 +                "url": "https://github.com/sponsors/sindresorhus"
 +            }
 +        },
 +        "node_modules/imurmurhash": {
 +            "version": "0.1.4",
 +            "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
 +            "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=0.8.19"
 +            }
 +        },
 +        "node_modules/inflight": {
 +            "version": "1.0.6",
 +            "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
 +            "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
 +            "dev": true,
 +            "dependencies": {
 +                "once": "^1.3.0",
 +                "wrappy": "1"
 +            }
 +        },
 +        "node_modules/inherits": {
 +            "version": "2.0.4",
 +            "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
 +            "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
 +            "dev": true
 +        },
 +        "node_modules/ini": {
 +            "version": "1.3.8",
 +            "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
 +            "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
 +            "dev": true
 +        },
 +        "node_modules/internmap": {
 +            "version": "2.0.3",
 +            "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
 +            "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/is-ci": {
 +            "version": "2.0.0",
 +            "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz",
 +            "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==",
 +            "dev": true,
 +            "dependencies": {
 +                "ci-info": "^2.0.0"
 +            },
 +            "bin": {
 +                "is-ci": "bin.js"
 +            }
 +        },
 +        "node_modules/is-extglob": {
 +            "version": "2.1.1",
 +            "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
 +            "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=0.10.0"
 +            }
 +        },
 +        "node_modules/is-fullwidth-code-point": {
 +            "version": "3.0.0",
 +            "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
 +            "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
 +            "engines": {
 +                "node": ">=8"
 +            }
 +        },
 +        "node_modules/is-glob": {
 +            "version": "4.0.3",
 +            "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
 +            "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
 +            "dev": true,
 +            "dependencies": {
 +                "is-extglob": "^2.1.1"
 +            },
 +            "engines": {
 +                "node": ">=0.10.0"
 +            }
 +        },
 +        "node_modules/is-number": {
 +            "version": "7.0.0",
 +            "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
 +            "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=0.12.0"
 +            }
 +        },
 +        "node_modules/isarray": {
 +            "version": "1.0.0",
 +            "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
 +            "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
 +            "dev": true
 +        },
 +        "node_modules/isexe": {
 +            "version": "2.0.0",
 +            "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
 +            "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
 +            "dev": true
 +        },
 +        "node_modules/js-yaml": {
 +            "version": "4.1.0",
 +            "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
 +            "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
 +            "dev": true,
 +            "dependencies": {
 +                "argparse": "^2.0.1"
 +            },
 +            "bin": {
 +                "js-yaml": "bin/js-yaml.js"
 +            }
 +        },
 +        "node_modules/json-schema-traverse": {
 +            "version": "0.4.1",
 +            "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
 +            "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
 +            "dev": true
 +        },
 +        "node_modules/json-stable-stringify-without-jsonify": {
 +            "version": "1.0.1",
 +            "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
 +            "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
 +            "dev": true
 +        },
 +        "node_modules/keytar": {
 +            "version": "7.9.0",
 +            "resolved": "https://registry.npmjs.org/keytar/-/keytar-7.9.0.tgz",
 +            "integrity": "sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ==",
 +            "dev": true,
 +            "hasInstallScript": true,
 +            "dependencies": {
 +                "node-addon-api": "^4.3.0",
 +                "prebuild-install": "^7.0.1"
 +            }
 +        },
 +        "node_modules/leven": {
 +            "version": "3.1.0",
 +            "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
 +            "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=6"
 +            }
 +        },
 +        "node_modules/levn": {
 +            "version": "0.4.1",
 +            "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
 +            "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
 +            "dev": true,
 +            "dependencies": {
 +                "prelude-ls": "^1.2.1",
 +                "type-check": "~0.4.0"
 +            },
 +            "engines": {
 +                "node": ">= 0.8.0"
 +            }
 +        },
 +        "node_modules/linkify-it": {
 +            "version": "3.0.3",
 +            "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz",
 +            "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==",
 +            "dev": true,
 +            "dependencies": {
 +                "uc.micro": "^1.0.1"
 +            }
 +        },
 +        "node_modules/listenercount": {
 +            "version": "1.0.1",
 +            "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz",
 +            "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==",
 +            "dev": true
 +        },
 +        "node_modules/lodash.merge": {
 +            "version": "4.6.2",
 +            "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
 +            "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
 +            "dev": true
 +        },
 +        "node_modules/lru-cache": {
 +            "version": "6.0.0",
 +            "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
 +            "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
 +            "dependencies": {
 +                "yallist": "^4.0.0"
 +            },
 +            "engines": {
 +                "node": ">=10"
 +            }
 +        },
 +        "node_modules/markdown-it": {
 +            "version": "12.3.2",
 +            "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz",
 +            "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==",
 +            "dev": true,
 +            "dependencies": {
 +                "argparse": "^2.0.1",
 +                "entities": "~2.1.0",
 +                "linkify-it": "^3.0.1",
 +                "mdurl": "^1.0.1",
 +                "uc.micro": "^1.0.5"
 +            },
 +            "bin": {
 +                "markdown-it": "bin/markdown-it.js"
 +            }
 +        },
 +        "node_modules/markdown-it/node_modules/entities": {
 +            "version": "2.1.0",
 +            "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz",
 +            "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==",
 +            "dev": true,
 +            "funding": {
 +                "url": "https://github.com/fb55/entities?sponsor=1"
 +            }
 +        },
 +        "node_modules/mdurl": {
 +            "version": "1.0.1",
 +            "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
 +            "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==",
 +            "dev": true
 +        },
 +        "node_modules/merge2": {
 +            "version": "1.4.1",
 +            "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
 +            "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">= 8"
 +            }
 +        },
 +        "node_modules/micromatch": {
 +            "version": "4.0.5",
 +            "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
 +            "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
 +            "dev": true,
 +            "dependencies": {
 +                "braces": "^3.0.2",
 +                "picomatch": "^2.3.1"
 +            },
 +            "engines": {
 +                "node": ">=8.6"
 +            }
 +        },
 +        "node_modules/mime": {
 +            "version": "1.6.0",
 +            "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
 +            "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
 +            "dev": true,
 +            "bin": {
 +                "mime": "cli.js"
 +            },
 +            "engines": {
 +                "node": ">=4"
 +            }
 +        },
 +        "node_modules/mimic-response": {
 +            "version": "3.1.0",
 +            "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
 +            "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=10"
 +            },
 +            "funding": {
 +                "url": "https://github.com/sponsors/sindresorhus"
 +            }
 +        },
 +        "node_modules/minimatch": {
 +            "version": "3.1.2",
 +            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
 +            "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
 +            "dependencies": {
 +                "brace-expansion": "^1.1.7"
 +            },
 +            "engines": {
 +                "node": "*"
 +            }
 +        },
 +        "node_modules/minimist": {
 +            "version": "1.2.6",
 +            "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
 +            "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
 +            "dev": true
 +        },
 +        "node_modules/mkdirp": {
 +            "version": "0.5.6",
 +            "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
 +            "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
 +            "dev": true,
 +            "dependencies": {
 +                "minimist": "^1.2.6"
 +            },
 +            "bin": {
 +                "mkdirp": "bin/cmd.js"
 +            }
 +        },
 +        "node_modules/mkdirp-classic": {
 +            "version": "0.5.3",
 +            "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
 +            "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
 +            "dev": true
 +        },
 +        "node_modules/ms": {
 +            "version": "2.1.2",
 +            "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
 +            "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
 +            "dev": true
 +        },
 +        "node_modules/mute-stream": {
 +            "version": "0.0.8",
 +            "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
 +            "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
 +            "dev": true
 +        },
 +        "node_modules/napi-build-utils": {
 +            "version": "1.0.2",
 +            "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz",
 +            "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==",
 +            "dev": true
 +        },
 +        "node_modules/natural-compare": {
 +            "version": "1.4.0",
 +            "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
 +            "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
 +            "dev": true
 +        },
 +        "node_modules/node-abi": {
 +            "version": "3.22.0",
 +            "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.22.0.tgz",
 +            "integrity": "sha512-u4uAs/4Zzmp/jjsD9cyFYDXeISfUWaAVWshPmDZOFOv4Xl4SbzTXm53I04C2uRueYJ+0t5PEtLH/owbn2Npf/w==",
 +            "dev": true,
 +            "dependencies": {
 +                "semver": "^7.3.5"
 +            },
 +            "engines": {
 +                "node": ">=10"
 +            }
 +        },
 +        "node_modules/node-addon-api": {
 +            "version": "4.3.0",
 +            "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz",
 +            "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==",
 +            "dev": true
 +        },
 +        "node_modules/nth-check": {
 +            "version": "2.1.1",
 +            "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
 +            "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
 +            "dev": true,
 +            "dependencies": {
 +                "boolbase": "^1.0.0"
 +            },
 +            "funding": {
 +                "url": "https://github.com/fb55/nth-check?sponsor=1"
 +            }
 +        },
 +        "node_modules/object-inspect": {
 +            "version": "1.12.2",
 +            "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
 +            "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==",
 +            "dev": true,
 +            "funding": {
 +                "url": "https://github.com/sponsors/ljharb"
 +            }
 +        },
 +        "node_modules/once": {
 +            "version": "1.4.0",
 +            "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
 +            "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
 +            "dev": true,
 +            "dependencies": {
 +                "wrappy": "1"
 +            }
 +        },
 +        "node_modules/optionator": {
 +            "version": "0.9.1",
 +            "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
 +            "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
 +            "dev": true,
 +            "dependencies": {
 +                "deep-is": "^0.1.3",
 +                "fast-levenshtein": "^2.0.6",
 +                "levn": "^0.4.1",
 +                "prelude-ls": "^1.2.1",
 +                "type-check": "^0.4.0",
 +                "word-wrap": "^1.2.3"
 +            },
 +            "engines": {
 +                "node": ">= 0.8.0"
 +            }
 +        },
 +        "node_modules/ovsx": {
-             "version": "0.5.1",
-             "resolved": "https://registry.npmjs.org/ovsx/-/ovsx-0.5.1.tgz",
-             "integrity": "sha512-3OWq0l7DuVHi2bd2aQe5+QVQlFIqvrcw3/2vGXL404L6Tr+R4QHtzfnYYghv8CCa85xJHjU0RhcaC7pyXkAUbg==",
++            "version": "0.5.2",
++            "resolved": "https://registry.npmjs.org/ovsx/-/ovsx-0.5.2.tgz",
++            "integrity": "sha512-UbLultRCk46WddeA0Cly4hoRhzBJUiLgbIEViXlgOvV54LbsppClDkMLoCevUUBHoiNdMX2NuiSgURAEXgCZdw==",
 +            "dev": true,
 +            "dependencies": {
 +                "commander": "^6.1.0",
 +                "follow-redirects": "^1.14.6",
 +                "is-ci": "^2.0.0",
 +                "leven": "^3.1.0",
 +                "tmp": "^0.2.1",
 +                "vsce": "^2.6.3"
 +            },
 +            "bin": {
 +                "ovsx": "lib/ovsx"
 +            },
 +            "engines": {
 +                "node": ">= 14"
 +            }
 +        },
 +        "node_modules/ovsx/node_modules/commander": {
 +            "version": "6.2.1",
 +            "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz",
 +            "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">= 6"
 +            }
 +        },
 +        "node_modules/parent-module": {
 +            "version": "1.0.1",
 +            "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
 +            "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
 +            "dev": true,
 +            "dependencies": {
 +                "callsites": "^3.0.0"
 +            },
 +            "engines": {
 +                "node": ">=6"
 +            }
 +        },
 +        "node_modules/parse-semver": {
 +            "version": "1.1.1",
 +            "resolved": "https://registry.npmjs.org/parse-semver/-/parse-semver-1.1.1.tgz",
 +            "integrity": "sha512-Eg1OuNntBMH0ojvEKSrvDSnwLmvVuUOSdylH/pSCPNMIspLlweJyIWXCE+k/5hm3cj/EBUYwmWkjhBALNP4LXQ==",
 +            "dev": true,
 +            "dependencies": {
 +                "semver": "^5.1.0"
 +            }
 +        },
 +        "node_modules/parse-semver/node_modules/semver": {
 +            "version": "5.7.1",
 +            "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
 +            "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
 +            "dev": true,
 +            "bin": {
 +                "semver": "bin/semver"
 +            }
 +        },
 +        "node_modules/parse5": {
 +            "version": "7.0.0",
 +            "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.0.0.tgz",
 +            "integrity": "sha512-y/t8IXSPWTuRZqXc0ajH/UwDj4mnqLEbSttNbThcFhGrZuOyoyvNBO85PBp2jQa55wY9d07PBNjsK8ZP3K5U6g==",
 +            "dev": true,
 +            "dependencies": {
 +                "entities": "^4.3.0"
 +            },
 +            "funding": {
 +                "url": "https://github.com/inikulin/parse5?sponsor=1"
 +            }
 +        },
 +        "node_modules/parse5-htmlparser2-tree-adapter": {
 +            "version": "7.0.0",
 +            "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz",
 +            "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==",
 +            "dev": true,
 +            "dependencies": {
 +                "domhandler": "^5.0.2",
 +                "parse5": "^7.0.0"
 +            },
 +            "funding": {
 +                "url": "https://github.com/inikulin/parse5?sponsor=1"
 +            }
 +        },
 +        "node_modules/path-is-absolute": {
 +            "version": "1.0.1",
 +            "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
 +            "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=0.10.0"
 +            }
 +        },
 +        "node_modules/path-key": {
 +            "version": "3.1.1",
 +            "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
 +            "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=8"
 +            }
 +        },
 +        "node_modules/path-type": {
 +            "version": "4.0.0",
 +            "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
 +            "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=8"
 +            }
 +        },
 +        "node_modules/pend": {
 +            "version": "1.2.0",
 +            "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
 +            "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
 +            "dev": true
 +        },
 +        "node_modules/picomatch": {
 +            "version": "2.3.1",
 +            "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
 +            "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=8.6"
 +            },
 +            "funding": {
 +                "url": "https://github.com/sponsors/jonschlinkert"
 +            }
 +        },
 +        "node_modules/prebuild-install": {
 +            "version": "7.1.1",
 +            "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz",
 +            "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==",
 +            "dev": true,
 +            "dependencies": {
 +                "detect-libc": "^2.0.0",
 +                "expand-template": "^2.0.3",
 +                "github-from-package": "0.0.0",
 +                "minimist": "^1.2.3",
 +                "mkdirp-classic": "^0.5.3",
 +                "napi-build-utils": "^1.0.1",
 +                "node-abi": "^3.3.0",
 +                "pump": "^3.0.0",
 +                "rc": "^1.2.7",
 +                "simple-get": "^4.0.0",
 +                "tar-fs": "^2.0.0",
 +                "tunnel-agent": "^0.6.0"
 +            },
 +            "bin": {
 +                "prebuild-install": "bin.js"
 +            },
 +            "engines": {
 +                "node": ">=10"
 +            }
 +        },
 +        "node_modules/prelude-ls": {
 +            "version": "1.2.1",
 +            "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
 +            "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">= 0.8.0"
 +            }
 +        },
 +        "node_modules/prettier": {
 +            "version": "2.7.1",
 +            "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
 +            "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
 +            "dev": true,
 +            "bin": {
 +                "prettier": "bin-prettier.js"
 +            },
 +            "engines": {
 +                "node": ">=10.13.0"
 +            },
 +            "funding": {
 +                "url": "https://github.com/prettier/prettier?sponsor=1"
 +            }
 +        },
 +        "node_modules/process-nextick-args": {
 +            "version": "2.0.1",
 +            "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
 +            "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
 +            "dev": true
 +        },
 +        "node_modules/pump": {
 +            "version": "3.0.0",
 +            "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
 +            "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
 +            "dev": true,
 +            "dependencies": {
 +                "end-of-stream": "^1.1.0",
 +                "once": "^1.3.1"
 +            }
 +        },
 +        "node_modules/punycode": {
 +            "version": "2.1.1",
 +            "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
 +            "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=6"
 +            }
 +        },
 +        "node_modules/qs": {
 +            "version": "6.11.0",
 +            "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
 +            "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
 +            "dev": true,
 +            "dependencies": {
 +                "side-channel": "^1.0.4"
 +            },
 +            "engines": {
 +                "node": ">=0.6"
 +            },
 +            "funding": {
 +                "url": "https://github.com/sponsors/ljharb"
 +            }
 +        },
 +        "node_modules/queue-microtask": {
 +            "version": "1.2.3",
 +            "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
 +            "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
 +            "dev": true,
 +            "funding": [
 +                {
 +                    "type": "github",
 +                    "url": "https://github.com/sponsors/feross"
 +                },
 +                {
 +                    "type": "patreon",
 +                    "url": "https://www.patreon.com/feross"
 +                },
 +                {
 +                    "type": "consulting",
 +                    "url": "https://feross.org/support"
 +                }
 +            ]
 +        },
 +        "node_modules/rc": {
 +            "version": "1.2.8",
 +            "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
 +            "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
 +            "dev": true,
 +            "dependencies": {
 +                "deep-extend": "^0.6.0",
 +                "ini": "~1.3.0",
 +                "minimist": "^1.2.0",
 +                "strip-json-comments": "~2.0.1"
 +            },
 +            "bin": {
 +                "rc": "cli.js"
 +            }
 +        },
 +        "node_modules/rc/node_modules/strip-json-comments": {
 +            "version": "2.0.1",
 +            "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
 +            "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=0.10.0"
 +            }
 +        },
 +        "node_modules/read": {
 +            "version": "1.0.7",
 +            "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz",
 +            "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==",
 +            "dev": true,
 +            "dependencies": {
 +                "mute-stream": "~0.0.4"
 +            },
 +            "engines": {
 +                "node": ">=0.8"
 +            }
 +        },
 +        "node_modules/readable-stream": {
 +            "version": "2.3.7",
 +            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
 +            "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
 +            "dev": true,
 +            "dependencies": {
 +                "core-util-is": "~1.0.0",
 +                "inherits": "~2.0.3",
 +                "isarray": "~1.0.0",
 +                "process-nextick-args": "~2.0.0",
 +                "safe-buffer": "~5.1.1",
 +                "string_decoder": "~1.1.1",
 +                "util-deprecate": "~1.0.1"
 +            }
 +        },
 +        "node_modules/regexpp": {
 +            "version": "3.2.0",
 +            "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
 +            "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=8"
 +            },
 +            "funding": {
 +                "url": "https://github.com/sponsors/mysticatea"
 +            }
 +        },
 +        "node_modules/require-directory": {
 +            "version": "2.1.1",
 +            "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
 +            "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
 +            "engines": {
 +                "node": ">=0.10.0"
 +            }
 +        },
 +        "node_modules/resolve-from": {
 +            "version": "4.0.0",
 +            "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
 +            "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=4"
 +            }
 +        },
 +        "node_modules/reusify": {
 +            "version": "1.0.4",
 +            "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
 +            "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
 +            "dev": true,
 +            "engines": {
 +                "iojs": ">=1.0.0",
 +                "node": ">=0.10.0"
 +            }
 +        },
 +        "node_modules/rimraf": {
 +            "version": "3.0.2",
 +            "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
 +            "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
 +            "dev": true,
 +            "dependencies": {
 +                "glob": "^7.1.3"
 +            },
 +            "bin": {
 +                "rimraf": "bin.js"
 +            },
 +            "funding": {
 +                "url": "https://github.com/sponsors/isaacs"
 +            }
 +        },
 +        "node_modules/robust-predicates": {
 +            "version": "3.0.1",
 +            "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.1.tgz",
 +            "integrity": "sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g=="
 +        },
 +        "node_modules/run-parallel": {
 +            "version": "1.2.0",
 +            "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
 +            "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
 +            "dev": true,
 +            "funding": [
 +                {
 +                    "type": "github",
 +                    "url": "https://github.com/sponsors/feross"
 +                },
 +                {
 +                    "type": "patreon",
 +                    "url": "https://www.patreon.com/feross"
 +                },
 +                {
 +                    "type": "consulting",
 +                    "url": "https://feross.org/support"
 +                }
 +            ],
 +            "dependencies": {
 +                "queue-microtask": "^1.2.2"
 +            }
 +        },
 +        "node_modules/rw": {
 +            "version": "1.3.3",
 +            "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
 +            "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ=="
 +        },
 +        "node_modules/safe-buffer": {
 +            "version": "5.1.2",
 +            "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
 +            "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
 +            "dev": true
 +        },
 +        "node_modules/safer-buffer": {
 +            "version": "2.1.2",
 +            "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
 +            "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
 +        },
 +        "node_modules/sax": {
 +            "version": "1.2.4",
 +            "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
 +            "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
 +            "dev": true
 +        },
 +        "node_modules/semver": {
 +            "version": "7.3.7",
 +            "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
 +            "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
 +            "dependencies": {
 +                "lru-cache": "^6.0.0"
 +            },
 +            "bin": {
 +                "semver": "bin/semver.js"
 +            },
 +            "engines": {
 +                "node": ">=10"
 +            }
 +        },
 +        "node_modules/setimmediate": {
 +            "version": "1.0.5",
 +            "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
 +            "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
 +            "dev": true
 +        },
 +        "node_modules/shebang-command": {
 +            "version": "2.0.0",
 +            "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
 +            "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
 +            "dev": true,
 +            "dependencies": {
 +                "shebang-regex": "^3.0.0"
 +            },
 +            "engines": {
 +                "node": ">=8"
 +            }
 +        },
 +        "node_modules/shebang-regex": {
 +            "version": "3.0.0",
 +            "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
 +            "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=8"
 +            }
 +        },
 +        "node_modules/side-channel": {
 +            "version": "1.0.4",
 +            "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
 +            "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
 +            "dev": true,
 +            "dependencies": {
 +                "call-bind": "^1.0.0",
 +                "get-intrinsic": "^1.0.2",
 +                "object-inspect": "^1.9.0"
 +            },
 +            "funding": {
 +                "url": "https://github.com/sponsors/ljharb"
 +            }
 +        },
 +        "node_modules/simple-concat": {
 +            "version": "1.0.1",
 +            "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
 +            "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
 +            "dev": true,
 +            "funding": [
 +                {
 +                    "type": "github",
 +                    "url": "https://github.com/sponsors/feross"
 +                },
 +                {
 +                    "type": "patreon",
 +                    "url": "https://www.patreon.com/feross"
 +                },
 +                {
 +                    "type": "consulting",
 +                    "url": "https://feross.org/support"
 +                }
 +            ]
 +        },
 +        "node_modules/simple-get": {
 +            "version": "4.0.1",
 +            "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
 +            "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
 +            "dev": true,
 +            "funding": [
 +                {
 +                    "type": "github",
 +                    "url": "https://github.com/sponsors/feross"
 +                },
 +                {
 +                    "type": "patreon",
 +                    "url": "https://www.patreon.com/feross"
 +                },
 +                {
 +                    "type": "consulting",
 +                    "url": "https://feross.org/support"
 +                }
 +            ],
 +            "dependencies": {
 +                "decompress-response": "^6.0.0",
 +                "once": "^1.3.1",
 +                "simple-concat": "^1.0.0"
 +            }
 +        },
 +        "node_modules/slash": {
 +            "version": "3.0.0",
 +            "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
 +            "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=8"
 +            }
 +        },
 +        "node_modules/string_decoder": {
 +            "version": "1.1.1",
 +            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
 +            "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
 +            "dev": true,
 +            "dependencies": {
 +                "safe-buffer": "~5.1.0"
 +            }
 +        },
 +        "node_modules/string-width": {
 +            "version": "4.2.3",
 +            "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
 +            "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
 +            "dependencies": {
 +                "emoji-regex": "^8.0.0",
 +                "is-fullwidth-code-point": "^3.0.0",
 +                "strip-ansi": "^6.0.1"
 +            },
 +            "engines": {
 +                "node": ">=8"
 +            }
 +        },
 +        "node_modules/strip-ansi": {
 +            "version": "6.0.1",
 +            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
 +            "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
 +            "dependencies": {
 +                "ansi-regex": "^5.0.1"
 +            },
 +            "engines": {
 +                "node": ">=8"
 +            }
 +        },
 +        "node_modules/strip-json-comments": {
 +            "version": "3.1.1",
 +            "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
 +            "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=8"
 +            },
 +            "funding": {
 +                "url": "https://github.com/sponsors/sindresorhus"
 +            }
 +        },
 +        "node_modules/supports-color": {
 +            "version": "7.2.0",
 +            "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
 +            "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
 +            "dev": true,
 +            "dependencies": {
 +                "has-flag": "^4.0.0"
 +            },
 +            "engines": {
 +                "node": ">=8"
 +            }
 +        },
 +        "node_modules/tar-fs": {
 +            "version": "2.1.1",
 +            "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz",
 +            "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==",
 +            "dev": true,
 +            "dependencies": {
 +                "chownr": "^1.1.1",
 +                "mkdirp-classic": "^0.5.2",
 +                "pump": "^3.0.0",
 +                "tar-stream": "^2.1.4"
 +            }
 +        },
 +        "node_modules/tar-stream": {
 +            "version": "2.2.0",
 +            "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
 +            "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
 +            "dev": true,
 +            "dependencies": {
 +                "bl": "^4.0.3",
 +                "end-of-stream": "^1.4.1",
 +                "fs-constants": "^1.0.0",
 +                "inherits": "^2.0.3",
 +                "readable-stream": "^3.1.1"
 +            },
 +            "engines": {
 +                "node": ">=6"
 +            }
 +        },
 +        "node_modules/tar-stream/node_modules/readable-stream": {
 +            "version": "3.6.0",
 +            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
 +            "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
 +            "dev": true,
 +            "dependencies": {
 +                "inherits": "^2.0.3",
 +                "string_decoder": "^1.1.1",
 +                "util-deprecate": "^1.0.1"
 +            },
 +            "engines": {
 +                "node": ">= 6"
 +            }
 +        },
 +        "node_modules/text-table": {
 +            "version": "0.2.0",
 +            "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
 +            "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
 +            "dev": true
 +        },
 +        "node_modules/tmp": {
 +            "version": "0.2.1",
 +            "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
 +            "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==",
 +            "dev": true,
 +            "dependencies": {
 +                "rimraf": "^3.0.0"
 +            },
 +            "engines": {
 +                "node": ">=8.17.0"
 +            }
 +        },
 +        "node_modules/to-regex-range": {
 +            "version": "5.0.1",
 +            "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
 +            "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
 +            "dev": true,
 +            "dependencies": {
 +                "is-number": "^7.0.0"
 +            },
 +            "engines": {
 +                "node": ">=8.0"
 +            }
 +        },
 +        "node_modules/traverse": {
 +            "version": "0.3.9",
 +            "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz",
 +            "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==",
 +            "dev": true,
 +            "engines": {
 +                "node": "*"
 +            }
 +        },
 +        "node_modules/tslib": {
 +            "version": "2.4.0",
 +            "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
 +            "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==",
 +            "dev": true
 +        },
 +        "node_modules/tsutils": {
 +            "version": "3.21.0",
 +            "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
 +            "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==",
 +            "dev": true,
 +            "dependencies": {
 +                "tslib": "^1.8.1"
 +            },
 +            "engines": {
 +                "node": ">= 6"
 +            },
 +            "peerDependencies": {
 +                "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta"
 +            }
 +        },
 +        "node_modules/tsutils/node_modules/tslib": {
 +            "version": "1.14.1",
 +            "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
 +            "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
 +            "dev": true
 +        },
 +        "node_modules/tunnel": {
 +            "version": "0.0.6",
 +            "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
 +            "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=0.6.11 <=0.7.0 || >=0.7.3"
 +            }
 +        },
 +        "node_modules/tunnel-agent": {
 +            "version": "0.6.0",
 +            "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
 +            "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
 +            "dev": true,
 +            "dependencies": {
 +                "safe-buffer": "^5.0.1"
 +            },
 +            "engines": {
 +                "node": "*"
 +            }
 +        },
 +        "node_modules/type-check": {
 +            "version": "0.4.0",
 +            "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
 +            "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
 +            "dev": true,
 +            "dependencies": {
 +                "prelude-ls": "^1.2.1"
 +            },
 +            "engines": {
 +                "node": ">= 0.8.0"
 +            }
 +        },
 +        "node_modules/type-fest": {
 +            "version": "0.20.2",
 +            "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
 +            "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=10"
 +            },
 +            "funding": {
 +                "url": "https://github.com/sponsors/sindresorhus"
 +            }
 +        },
 +        "node_modules/typed-rest-client": {
 +            "version": "1.8.9",
 +            "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.9.tgz",
 +            "integrity": "sha512-uSmjE38B80wjL85UFX3sTYEUlvZ1JgCRhsWj/fJ4rZ0FqDUFoIuodtiVeE+cUqiVTOKPdKrp/sdftD15MDek6g==",
 +            "dev": true,
 +            "dependencies": {
 +                "qs": "^6.9.1",
 +                "tunnel": "0.0.6",
 +                "underscore": "^1.12.1"
 +            }
 +        },
 +        "node_modules/typescript": {
 +            "version": "4.7.4",
 +            "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
 +            "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==",
 +            "dev": true,
 +            "bin": {
 +                "tsc": "bin/tsc",
 +                "tsserver": "bin/tsserver"
 +            },
 +            "engines": {
 +                "node": ">=4.2.0"
 +            }
 +        },
 +        "node_modules/uc.micro": {
 +            "version": "1.0.6",
 +            "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
 +            "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==",
 +            "dev": true
 +        },
 +        "node_modules/underscore": {
 +            "version": "1.13.4",
 +            "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.4.tgz",
 +            "integrity": "sha512-BQFnUDuAQ4Yf/cYY5LNrK9NCJFKriaRbD9uR1fTeXnBeoa97W0i41qkZfGO9pSo8I5KzjAcSY2XYtdf0oKd7KQ==",
 +            "dev": true
 +        },
 +        "node_modules/unzipper": {
 +            "version": "0.10.11",
 +            "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.11.tgz",
 +            "integrity": "sha512-+BrAq2oFqWod5IESRjL3S8baohbevGcVA+teAIOYWM3pDVdseogqbzhhvvmiyQrUNKFUnDMtELW3X8ykbyDCJw==",
 +            "dev": true,
 +            "dependencies": {
 +                "big-integer": "^1.6.17",
 +                "binary": "~0.3.0",
 +                "bluebird": "~3.4.1",
 +                "buffer-indexof-polyfill": "~1.0.0",
 +                "duplexer2": "~0.1.4",
 +                "fstream": "^1.0.12",
 +                "graceful-fs": "^4.2.2",
 +                "listenercount": "~1.0.1",
 +                "readable-stream": "~2.3.6",
 +                "setimmediate": "~1.0.4"
 +            }
 +        },
 +        "node_modules/uri-js": {
 +            "version": "4.4.1",
 +            "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
 +            "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
 +            "dev": true,
 +            "dependencies": {
 +                "punycode": "^2.1.0"
 +            }
 +        },
 +        "node_modules/url-join": {
 +            "version": "4.0.1",
 +            "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz",
 +            "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==",
 +            "dev": true
 +        },
 +        "node_modules/util-deprecate": {
 +            "version": "1.0.2",
 +            "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
 +            "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
 +            "dev": true
 +        },
 +        "node_modules/v8-compile-cache": {
 +            "version": "2.3.0",
 +            "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
 +            "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==",
 +            "dev": true
 +        },
 +        "node_modules/vsce": {
 +            "version": "2.9.2",
 +            "resolved": "https://registry.npmjs.org/vsce/-/vsce-2.9.2.tgz",
 +            "integrity": "sha512-xyLqL4U82BilUX1t6Ym2opQEa2tLGWYjbgB7+ETeNVXlIJz5sWBJjQJSYJVFOKJSpiOtQclolu88cj7oY6vvPQ==",
 +            "dev": true,
 +            "dependencies": {
 +                "azure-devops-node-api": "^11.0.1",
 +                "chalk": "^2.4.2",
 +                "cheerio": "^1.0.0-rc.9",
 +                "commander": "^6.1.0",
 +                "glob": "^7.0.6",
 +                "hosted-git-info": "^4.0.2",
 +                "keytar": "^7.7.0",
 +                "leven": "^3.1.0",
 +                "markdown-it": "^12.3.2",
 +                "mime": "^1.3.4",
 +                "minimatch": "^3.0.3",
 +                "parse-semver": "^1.1.1",
 +                "read": "^1.0.7",
 +                "semver": "^5.1.0",
 +                "tmp": "^0.2.1",
 +                "typed-rest-client": "^1.8.4",
 +                "url-join": "^4.0.1",
 +                "xml2js": "^0.4.23",
 +                "yauzl": "^2.3.1",
 +                "yazl": "^2.2.2"
 +            },
 +            "bin": {
 +                "vsce": "vsce"
 +            },
 +            "engines": {
 +                "node": ">= 14"
 +            }
 +        },
 +        "node_modules/vsce/node_modules/ansi-styles": {
 +            "version": "3.2.1",
 +            "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
 +            "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
 +            "dev": true,
 +            "dependencies": {
 +                "color-convert": "^1.9.0"
 +            },
 +            "engines": {
 +                "node": ">=4"
 +            }
 +        },
 +        "node_modules/vsce/node_modules/chalk": {
 +            "version": "2.4.2",
 +            "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
 +            "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
 +            "dev": true,
 +            "dependencies": {
 +                "ansi-styles": "^3.2.1",
 +                "escape-string-regexp": "^1.0.5",
 +                "supports-color": "^5.3.0"
 +            },
 +            "engines": {
 +                "node": ">=4"
 +            }
 +        },
 +        "node_modules/vsce/node_modules/color-convert": {
 +            "version": "1.9.3",
 +            "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
 +            "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
 +            "dev": true,
 +            "dependencies": {
 +                "color-name": "1.1.3"
 +            }
 +        },
 +        "node_modules/vsce/node_modules/color-name": {
 +            "version": "1.1.3",
 +            "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
 +            "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
 +            "dev": true
 +        },
 +        "node_modules/vsce/node_modules/commander": {
 +            "version": "6.2.1",
 +            "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz",
 +            "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">= 6"
 +            }
 +        },
 +        "node_modules/vsce/node_modules/escape-string-regexp": {
 +            "version": "1.0.5",
 +            "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
 +            "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=0.8.0"
 +            }
 +        },
 +        "node_modules/vsce/node_modules/has-flag": {
 +            "version": "3.0.0",
 +            "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
 +            "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=4"
 +            }
 +        },
 +        "node_modules/vsce/node_modules/semver": {
 +            "version": "5.7.1",
 +            "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
 +            "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
 +            "dev": true,
 +            "bin": {
 +                "semver": "bin/semver"
 +            }
 +        },
 +        "node_modules/vsce/node_modules/supports-color": {
 +            "version": "5.5.0",
 +            "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
 +            "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
 +            "dev": true,
 +            "dependencies": {
 +                "has-flag": "^3.0.0"
 +            },
 +            "engines": {
 +                "node": ">=4"
 +            }
 +        },
 +        "node_modules/vscode-jsonrpc": {
 +            "version": "8.0.2",
 +            "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.0.2.tgz",
 +            "integrity": "sha512-RY7HwI/ydoC1Wwg4gJ3y6LpU9FJRZAUnTYMXthqhFXXu77ErDd/xkREpGuk4MyYkk4a+XDWAMqe0S3KkelYQEQ==",
 +            "engines": {
 +                "node": ">=14.0.0"
 +            }
 +        },
 +        "node_modules/vscode-languageclient": {
 +            "version": "8.0.2",
 +            "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-8.0.2.tgz",
 +            "integrity": "sha512-lHlthJtphG9gibGb/y72CKqQUxwPsMXijJVpHEC2bvbFqxmkj9LwQ3aGU9dwjBLqsX1S4KjShYppLvg1UJDF/Q==",
 +            "dependencies": {
 +                "minimatch": "^3.0.4",
 +                "semver": "^7.3.5",
 +                "vscode-languageserver-protocol": "3.17.2"
 +            },
 +            "engines": {
 +                "vscode": "^1.67.0"
 +            }
 +        },
 +        "node_modules/vscode-languageserver-protocol": {
 +            "version": "3.17.2",
 +            "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.2.tgz",
 +            "integrity": "sha512-8kYisQ3z/SQ2kyjlNeQxbkkTNmVFoQCqkmGrzLH6A9ecPlgTbp3wDTnUNqaUxYr4vlAcloxx8zwy7G5WdguYNg==",
 +            "dependencies": {
 +                "vscode-jsonrpc": "8.0.2",
 +                "vscode-languageserver-types": "3.17.2"
 +            }
 +        },
 +        "node_modules/vscode-languageserver-types": {
 +            "version": "3.17.2",
 +            "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.2.tgz",
 +            "integrity": "sha512-zHhCWatviizPIq9B7Vh9uvrH6x3sK8itC84HkamnBWoDFJtzBf7SWlpLCZUit72b3os45h6RWQNC9xHRDF8dRA=="
 +        },
 +        "node_modules/which": {
 +            "version": "2.0.2",
 +            "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
 +            "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
 +            "dev": true,
 +            "dependencies": {
 +                "isexe": "^2.0.0"
 +            },
 +            "bin": {
 +                "node-which": "bin/node-which"
 +            },
 +            "engines": {
 +                "node": ">= 8"
 +            }
 +        },
 +        "node_modules/word-wrap": {
 +            "version": "1.2.3",
 +            "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
 +            "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=0.10.0"
 +            }
 +        },
 +        "node_modules/wrap-ansi": {
 +            "version": "7.0.0",
 +            "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
 +            "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
 +            "dependencies": {
 +                "ansi-styles": "^4.0.0",
 +                "string-width": "^4.1.0",
 +                "strip-ansi": "^6.0.0"
 +            },
 +            "engines": {
 +                "node": ">=10"
 +            },
 +            "funding": {
 +                "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
 +            }
 +        },
 +        "node_modules/wrappy": {
 +            "version": "1.0.2",
 +            "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
 +            "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
 +            "dev": true
 +        },
 +        "node_modules/xml2js": {
 +            "version": "0.4.23",
 +            "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",
 +            "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==",
 +            "dev": true,
 +            "dependencies": {
 +                "sax": ">=0.6.0",
 +                "xmlbuilder": "~11.0.0"
 +            },
 +            "engines": {
 +                "node": ">=4.0.0"
 +            }
 +        },
 +        "node_modules/xmlbuilder": {
 +            "version": "11.0.1",
 +            "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
 +            "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
 +            "dev": true,
 +            "engines": {
 +                "node": ">=4.0"
 +            }
 +        },
 +        "node_modules/y18n": {
 +            "version": "5.0.8",
 +            "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
 +            "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
 +            "engines": {
 +                "node": ">=10"
 +            }
 +        },
 +        "node_modules/yallist": {
 +            "version": "4.0.0",
 +            "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
 +            "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
 +        },
 +        "node_modules/yargs": {
 +            "version": "17.5.1",
 +            "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz",
 +            "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==",
 +            "dependencies": {
 +                "cliui": "^7.0.2",
 +                "escalade": "^3.1.1",
 +                "get-caller-file": "^2.0.5",
 +                "require-directory": "^2.1.1",
 +                "string-width": "^4.2.3",
 +                "y18n": "^5.0.5",
 +                "yargs-parser": "^21.0.0"
 +            },
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/yargs-parser": {
 +            "version": "21.0.1",
 +            "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz",
 +            "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==",
 +            "engines": {
 +                "node": ">=12"
 +            }
 +        },
 +        "node_modules/yauzl": {
 +            "version": "2.10.0",
 +            "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
 +            "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
 +            "dev": true,
 +            "dependencies": {
 +                "buffer-crc32": "~0.2.3",
 +                "fd-slicer": "~1.1.0"
 +            }
 +        },
 +        "node_modules/yazl": {
 +            "version": "2.5.1",
 +            "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz",
 +            "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==",
 +            "dev": true,
 +            "dependencies": {
 +                "buffer-crc32": "~0.2.3"
 +            }
 +        }
 +    },
 +    "dependencies": {
 +        "@eslint/eslintrc": {
 +            "version": "1.3.0",
 +            "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz",
 +            "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==",
 +            "dev": true,
 +            "requires": {
 +                "ajv": "^6.12.4",
 +                "debug": "^4.3.2",
 +                "espree": "^9.3.2",
 +                "globals": "^13.15.0",
 +                "ignore": "^5.2.0",
 +                "import-fresh": "^3.2.1",
 +                "js-yaml": "^4.1.0",
 +                "minimatch": "^3.1.2",
 +                "strip-json-comments": "^3.1.1"
 +            }
 +        },
 +        "@hpcc-js/wasm": {
 +            "version": "1.12.8",
 +            "resolved": "https://registry.npmjs.org/@hpcc-js/wasm/-/wasm-1.12.8.tgz",
 +            "integrity": "sha512-n4q9ARKco2hpCLsuVaW6Az3cDVaua7B3DSONHkc49WtEzgY/btvcDG5Zr1P6PZDv0sQ7oPnAi9Y+W2DI++MgcQ==",
 +            "requires": {
 +                "yargs": "^17.3.1"
 +            }
 +        },
 +        "@humanwhocodes/config-array": {
 +            "version": "0.9.5",
 +            "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz",
 +            "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==",
 +            "dev": true,
 +            "requires": {
 +                "@humanwhocodes/object-schema": "^1.2.1",
 +                "debug": "^4.1.1",
 +                "minimatch": "^3.0.4"
 +            }
 +        },
 +        "@humanwhocodes/object-schema": {
 +            "version": "1.2.1",
 +            "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
 +            "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
 +            "dev": true
 +        },
 +        "@nodelib/fs.scandir": {
 +            "version": "2.1.5",
 +            "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
 +            "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
 +            "dev": true,
 +            "requires": {
 +                "@nodelib/fs.stat": "2.0.5",
 +                "run-parallel": "^1.1.9"
 +            }
 +        },
 +        "@nodelib/fs.stat": {
 +            "version": "2.0.5",
 +            "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
 +            "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
 +            "dev": true
 +        },
 +        "@nodelib/fs.walk": {
 +            "version": "1.2.8",
 +            "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
 +            "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
 +            "dev": true,
 +            "requires": {
 +                "@nodelib/fs.scandir": "2.1.5",
 +                "fastq": "^1.6.0"
 +            }
 +        },
 +        "@tootallnate/once": {
 +            "version": "1.1.2",
 +            "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
 +            "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
 +            "dev": true
 +        },
 +        "@types/json-schema": {
 +            "version": "7.0.11",
 +            "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
 +            "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
 +            "dev": true
 +        },
 +        "@types/node": {
 +            "version": "16.11.43",
 +            "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.43.tgz",
 +            "integrity": "sha512-GqWykok+3uocgfAJM8imbozrqLnPyTrpFlrryURQlw1EesPUCx5XxTiucWDSFF9/NUEXDuD4bnvHm8xfVGWTpQ==",
 +            "dev": true
 +        },
 +        "@types/vscode": {
 +            "version": "1.66.0",
 +            "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.66.0.tgz",
 +            "integrity": "sha512-ZfJck4M7nrGasfs4A4YbUoxis3Vu24cETw3DERsNYtDZmYSYtk6ljKexKFKhImO/ZmY6ZMsmegu2FPkXoUFImA==",
 +            "dev": true
 +        },
 +        "@typescript-eslint/eslint-plugin": {
 +            "version": "5.30.5",
 +            "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.30.5.tgz",
 +            "integrity": "sha512-lftkqRoBvc28VFXEoRgyZuztyVUQ04JvUnATSPtIRFAccbXTWL6DEtXGYMcbg998kXw1NLUJm7rTQ9eUt+q6Ig==",
 +            "dev": true,
 +            "requires": {
 +                "@typescript-eslint/scope-manager": "5.30.5",
 +                "@typescript-eslint/type-utils": "5.30.5",
 +                "@typescript-eslint/utils": "5.30.5",
 +                "debug": "^4.3.4",
 +                "functional-red-black-tree": "^1.0.1",
 +                "ignore": "^5.2.0",
 +                "regexpp": "^3.2.0",
 +                "semver": "^7.3.7",
 +                "tsutils": "^3.21.0"
 +            }
 +        },
 +        "@typescript-eslint/parser": {
 +            "version": "5.30.5",
 +            "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.30.5.tgz",
 +            "integrity": "sha512-zj251pcPXI8GO9NDKWWmygP6+UjwWmrdf9qMW/L/uQJBM/0XbU2inxe5io/234y/RCvwpKEYjZ6c1YrXERkK4Q==",
 +            "dev": true,
 +            "requires": {
 +                "@typescript-eslint/scope-manager": "5.30.5",
 +                "@typescript-eslint/types": "5.30.5",
 +                "@typescript-eslint/typescript-estree": "5.30.5",
 +                "debug": "^4.3.4"
 +            }
 +        },
 +        "@typescript-eslint/scope-manager": {
 +            "version": "5.30.5",
 +            "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.30.5.tgz",
 +            "integrity": "sha512-NJ6F+YHHFT/30isRe2UTmIGGAiXKckCyMnIV58cE3JkHmaD6e5zyEYm5hBDv0Wbin+IC0T1FWJpD3YqHUG/Ydg==",
 +            "dev": true,
 +            "requires": {
 +                "@typescript-eslint/types": "5.30.5",
 +                "@typescript-eslint/visitor-keys": "5.30.5"
 +            }
 +        },
 +        "@typescript-eslint/type-utils": {
 +            "version": "5.30.5",
 +            "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.30.5.tgz",
 +            "integrity": "sha512-k9+ejlv1GgwN1nN7XjVtyCgE0BTzhzT1YsQF0rv4Vfj2U9xnslBgMYYvcEYAFVdvhuEscELJsB7lDkN7WusErw==",
 +            "dev": true,
 +            "requires": {
 +                "@typescript-eslint/utils": "5.30.5",
 +                "debug": "^4.3.4",
 +                "tsutils": "^3.21.0"
 +            }
 +        },
 +        "@typescript-eslint/types": {
 +            "version": "5.30.5",
 +            "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.30.5.tgz",
 +            "integrity": "sha512-kZ80w/M2AvsbRvOr3PjaNh6qEW1LFqs2pLdo2s5R38B2HYXG8Z0PP48/4+j1QHJFL3ssHIbJ4odPRS8PlHrFfw==",
 +            "dev": true
 +        },
 +        "@typescript-eslint/typescript-estree": {
 +            "version": "5.30.5",
 +            "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.30.5.tgz",
 +            "integrity": "sha512-qGTc7QZC801kbYjAr4AgdOfnokpwStqyhSbiQvqGBLixniAKyH+ib2qXIVo4P9NgGzwyfD9I0nlJN7D91E1VpQ==",
 +            "dev": true,
 +            "requires": {
 +                "@typescript-eslint/types": "5.30.5",
 +                "@typescript-eslint/visitor-keys": "5.30.5",
 +                "debug": "^4.3.4",
 +                "globby": "^11.1.0",
 +                "is-glob": "^4.0.3",
 +                "semver": "^7.3.7",
 +                "tsutils": "^3.21.0"
 +            }
 +        },
 +        "@typescript-eslint/utils": {
 +            "version": "5.30.5",
 +            "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.30.5.tgz",
 +            "integrity": "sha512-o4SSUH9IkuA7AYIfAvatldovurqTAHrfzPApOZvdUq01hHojZojCFXx06D/aFpKCgWbMPRdJBWAC3sWp3itwTA==",
 +            "dev": true,
 +            "requires": {
 +                "@types/json-schema": "^7.0.9",
 +                "@typescript-eslint/scope-manager": "5.30.5",
 +                "@typescript-eslint/types": "5.30.5",
 +                "@typescript-eslint/typescript-estree": "5.30.5",
 +                "eslint-scope": "^5.1.1",
 +                "eslint-utils": "^3.0.0"
 +            }
 +        },
 +        "@typescript-eslint/visitor-keys": {
 +            "version": "5.30.5",
 +            "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.30.5.tgz",
 +            "integrity": "sha512-D+xtGo9HUMELzWIUqcQc0p2PO4NyvTrgIOK/VnSH083+8sq0tiLozNRKuLarwHYGRuA6TVBQSuuLwJUDWd3aaA==",
 +            "dev": true,
 +            "requires": {
 +                "@typescript-eslint/types": "5.30.5",
 +                "eslint-visitor-keys": "^3.3.0"
 +            }
 +        },
 +        "@vscode/test-electron": {
 +            "version": "2.1.5",
 +            "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.1.5.tgz",
 +            "integrity": "sha512-O/ioqFpV+RvKbRykX2ItYPnbcZ4Hk5V0rY4uhQjQTLhGL9WZUvS7exzuYQCCI+ilSqJpctvxq2llTfGXf9UnnA==",
 +            "dev": true,
 +            "requires": {
 +                "http-proxy-agent": "^4.0.1",
 +                "https-proxy-agent": "^5.0.0",
 +                "rimraf": "^3.0.2",
 +                "unzipper": "^0.10.11"
 +            }
 +        },
 +        "acorn": {
 +            "version": "8.7.1",
 +            "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz",
 +            "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==",
 +            "dev": true
 +        },
 +        "acorn-jsx": {
 +            "version": "5.3.2",
 +            "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
 +            "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
 +            "dev": true,
 +            "requires": {}
 +        },
 +        "agent-base": {
 +            "version": "6.0.2",
 +            "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
 +            "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
 +            "dev": true,
 +            "requires": {
 +                "debug": "4"
 +            }
 +        },
 +        "ajv": {
 +            "version": "6.12.6",
 +            "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
 +            "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
 +            "dev": true,
 +            "requires": {
 +                "fast-deep-equal": "^3.1.1",
 +                "fast-json-stable-stringify": "^2.0.0",
 +                "json-schema-traverse": "^0.4.1",
 +                "uri-js": "^4.2.2"
 +            }
 +        },
 +        "ansi-regex": {
 +            "version": "5.0.1",
 +            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
 +            "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
 +        },
 +        "ansi-styles": {
 +            "version": "4.3.0",
 +            "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
 +            "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
 +            "requires": {
 +                "color-convert": "^2.0.1"
 +            }
 +        },
 +        "argparse": {
 +            "version": "2.0.1",
 +            "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
 +            "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
 +            "dev": true
 +        },
 +        "array-union": {
 +            "version": "2.1.0",
 +            "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
 +            "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
 +            "dev": true
 +        },
 +        "azure-devops-node-api": {
 +            "version": "11.2.0",
 +            "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-11.2.0.tgz",
 +            "integrity": "sha512-XdiGPhrpaT5J8wdERRKs5g8E0Zy1pvOYTli7z9E8nmOn3YGp4FhtjhrOyFmX/8veWCwdI69mCHKJw6l+4J/bHA==",
 +            "dev": true,
 +            "requires": {
 +                "tunnel": "0.0.6",
 +                "typed-rest-client": "^1.8.4"
 +            }
 +        },
 +        "balanced-match": {
 +            "version": "1.0.2",
 +            "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
 +            "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
 +        },
 +        "base64-js": {
 +            "version": "1.5.1",
 +            "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
 +            "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
 +            "dev": true
 +        },
 +        "big-integer": {
 +            "version": "1.6.51",
 +            "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz",
 +            "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==",
 +            "dev": true
 +        },
 +        "binary": {
 +            "version": "0.3.0",
 +            "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz",
 +            "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==",
 +            "dev": true,
 +            "requires": {
 +                "buffers": "~0.1.1",
 +                "chainsaw": "~0.1.0"
 +            }
 +        },
 +        "bl": {
 +            "version": "4.1.0",
 +            "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
 +            "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
 +            "dev": true,
 +            "requires": {
 +                "buffer": "^5.5.0",
 +                "inherits": "^2.0.4",
 +                "readable-stream": "^3.4.0"
 +            },
 +            "dependencies": {
 +                "readable-stream": {
 +                    "version": "3.6.0",
 +                    "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
 +                    "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
 +                    "dev": true,
 +                    "requires": {
 +                        "inherits": "^2.0.3",
 +                        "string_decoder": "^1.1.1",
 +                        "util-deprecate": "^1.0.1"
 +                    }
 +                }
 +            }
 +        },
 +        "bluebird": {
 +            "version": "3.4.7",
 +            "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz",
 +            "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==",
 +            "dev": true
 +        },
 +        "boolbase": {
 +            "version": "1.0.0",
 +            "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
 +            "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
 +            "dev": true
 +        },
 +        "brace-expansion": {
 +            "version": "1.1.11",
 +            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
 +            "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
 +            "requires": {
 +                "balanced-match": "^1.0.0",
 +                "concat-map": "0.0.1"
 +            }
 +        },
 +        "braces": {
 +            "version": "3.0.2",
 +            "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
 +            "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
 +            "dev": true,
 +            "requires": {
 +                "fill-range": "^7.0.1"
 +            }
 +        },
 +        "buffer": {
 +            "version": "5.7.1",
 +            "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
 +            "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
 +            "dev": true,
 +            "requires": {
 +                "base64-js": "^1.3.1",
 +                "ieee754": "^1.1.13"
 +            }
 +        },
 +        "buffer-crc32": {
 +            "version": "0.2.13",
 +            "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
 +            "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
 +            "dev": true
 +        },
 +        "buffer-indexof-polyfill": {
 +            "version": "1.0.2",
 +            "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz",
 +            "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==",
 +            "dev": true
 +        },
 +        "buffers": {
 +            "version": "0.1.1",
 +            "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz",
 +            "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==",
 +            "dev": true
 +        },
 +        "call-bind": {
 +            "version": "1.0.2",
 +            "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
 +            "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
 +            "dev": true,
 +            "requires": {
 +                "function-bind": "^1.1.1",
 +                "get-intrinsic": "^1.0.2"
 +            }
 +        },
 +        "callsites": {
 +            "version": "3.1.0",
 +            "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
 +            "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
 +            "dev": true
 +        },
 +        "chainsaw": {
 +            "version": "0.1.0",
 +            "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz",
 +            "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==",
 +            "dev": true,
 +            "requires": {
 +                "traverse": ">=0.3.0 <0.4"
 +            }
 +        },
 +        "chalk": {
 +            "version": "4.1.2",
 +            "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
 +            "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
 +            "dev": true,
 +            "requires": {
 +                "ansi-styles": "^4.1.0",
 +                "supports-color": "^7.1.0"
 +            }
 +        },
 +        "cheerio": {
 +            "version": "1.0.0-rc.12",
 +            "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz",
 +            "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==",
 +            "dev": true,
 +            "requires": {
 +                "cheerio-select": "^2.1.0",
 +                "dom-serializer": "^2.0.0",
 +                "domhandler": "^5.0.3",
 +                "domutils": "^3.0.1",
 +                "htmlparser2": "^8.0.1",
 +                "parse5": "^7.0.0",
 +                "parse5-htmlparser2-tree-adapter": "^7.0.0"
 +            }
 +        },
 +        "cheerio-select": {
 +            "version": "2.1.0",
 +            "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz",
 +            "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==",
 +            "dev": true,
 +            "requires": {
 +                "boolbase": "^1.0.0",
 +                "css-select": "^5.1.0",
 +                "css-what": "^6.1.0",
 +                "domelementtype": "^2.3.0",
 +                "domhandler": "^5.0.3",
 +                "domutils": "^3.0.1"
 +            }
 +        },
 +        "chownr": {
 +            "version": "1.1.4",
 +            "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
 +            "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
 +            "dev": true
 +        },
 +        "ci-info": {
 +            "version": "2.0.0",
 +            "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
 +            "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
 +            "dev": true
 +        },
 +        "cliui": {
 +            "version": "7.0.4",
 +            "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
 +            "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
 +            "requires": {
 +                "string-width": "^4.2.0",
 +                "strip-ansi": "^6.0.0",
 +                "wrap-ansi": "^7.0.0"
 +            }
 +        },
 +        "color-convert": {
 +            "version": "2.0.1",
 +            "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
 +            "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
 +            "requires": {
 +                "color-name": "~1.1.4"
 +            }
 +        },
 +        "color-name": {
 +            "version": "1.1.4",
 +            "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
 +            "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
 +        },
 +        "commander": {
 +            "version": "7.2.0",
 +            "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
 +            "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="
 +        },
 +        "concat-map": {
 +            "version": "0.0.1",
 +            "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
 +            "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
 +        },
 +        "core-util-is": {
 +            "version": "1.0.3",
 +            "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
 +            "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
 +            "dev": true
 +        },
 +        "cross-env": {
 +            "version": "7.0.3",
 +            "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
 +            "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==",
 +            "dev": true,
 +            "requires": {
 +                "cross-spawn": "^7.0.1"
 +            }
 +        },
 +        "cross-spawn": {
 +            "version": "7.0.3",
 +            "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
 +            "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
 +            "dev": true,
 +            "requires": {
 +                "path-key": "^3.1.0",
 +                "shebang-command": "^2.0.0",
 +                "which": "^2.0.1"
 +            }
 +        },
 +        "css-select": {
 +            "version": "5.1.0",
 +            "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
 +            "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
 +            "dev": true,
 +            "requires": {
 +                "boolbase": "^1.0.0",
 +                "css-what": "^6.1.0",
 +                "domhandler": "^5.0.2",
 +                "domutils": "^3.0.1",
 +                "nth-check": "^2.0.1"
 +            }
 +        },
 +        "css-what": {
 +            "version": "6.1.0",
 +            "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
 +            "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
 +            "dev": true
 +        },
 +        "d3": {
 +            "version": "7.6.1",
 +            "resolved": "https://registry.npmjs.org/d3/-/d3-7.6.1.tgz",
 +            "integrity": "sha512-txMTdIHFbcpLx+8a0IFhZsbp+PfBBPt8yfbmukZTQFroKuFqIwqswF0qE5JXWefylaAVpSXFoKm3yP+jpNLFLw==",
 +            "requires": {
 +                "d3-array": "3",
 +                "d3-axis": "3",
 +                "d3-brush": "3",
 +                "d3-chord": "3",
 +                "d3-color": "3",
 +                "d3-contour": "4",
 +                "d3-delaunay": "6",
 +                "d3-dispatch": "3",
 +                "d3-drag": "3",
 +                "d3-dsv": "3",
 +                "d3-ease": "3",
 +                "d3-fetch": "3",
 +                "d3-force": "3",
 +                "d3-format": "3",
 +                "d3-geo": "3",
 +                "d3-hierarchy": "3",
 +                "d3-interpolate": "3",
 +                "d3-path": "3",
 +                "d3-polygon": "3",
 +                "d3-quadtree": "3",
 +                "d3-random": "3",
 +                "d3-scale": "4",
 +                "d3-scale-chromatic": "3",
 +                "d3-selection": "3",
 +                "d3-shape": "3",
 +                "d3-time": "3",
 +                "d3-time-format": "4",
 +                "d3-timer": "3",
 +                "d3-transition": "3",
 +                "d3-zoom": "3"
 +            },
 +            "dependencies": {
 +                "d3-selection": {
 +                    "version": "3.0.0",
 +                    "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
 +                    "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ=="
 +                }
 +            }
 +        },
 +        "d3-array": {
 +            "version": "3.2.0",
 +            "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.0.tgz",
 +            "integrity": "sha512-3yXFQo0oG3QCxbF06rMPFyGRMGJNS7NvsV1+2joOjbBE+9xvWQ8+GcMJAjRCzw06zQ3/arXeJgbPYcjUCuC+3g==",
 +            "requires": {
 +                "internmap": "1 - 2"
 +            }
 +        },
 +        "d3-axis": {
 +            "version": "3.0.0",
 +            "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz",
 +            "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw=="
 +        },
 +        "d3-brush": {
 +            "version": "3.0.0",
 +            "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz",
 +            "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==",
 +            "requires": {
 +                "d3-dispatch": "1 - 3",
 +                "d3-drag": "2 - 3",
 +                "d3-interpolate": "1 - 3",
 +                "d3-selection": "3",
 +                "d3-transition": "3"
 +            },
 +            "dependencies": {
 +                "d3-selection": {
 +                    "version": "3.0.0",
 +                    "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
 +                    "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ=="
 +                }
 +            }
 +        },
 +        "d3-chord": {
 +            "version": "3.0.1",
 +            "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz",
 +            "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==",
 +            "requires": {
 +                "d3-path": "1 - 3"
 +            }
 +        },
 +        "d3-color": {
 +            "version": "3.1.0",
 +            "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
 +            "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="
 +        },
 +        "d3-contour": {
 +            "version": "4.0.0",
 +            "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.0.tgz",
 +            "integrity": "sha512-7aQo0QHUTu/Ko3cP9YK9yUTxtoDEiDGwnBHyLxG5M4vqlBkO/uixMRele3nfsfj6UXOcuReVpVXzAboGraYIJw==",
 +            "requires": {
 +                "d3-array": "^3.2.0"
 +            }
 +        },
 +        "d3-delaunay": {
 +            "version": "6.0.2",
 +            "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.2.tgz",
 +            "integrity": "sha512-IMLNldruDQScrcfT+MWnazhHbDJhcRJyOEBAJfwQnHle1RPh6WDuLvxNArUju2VSMSUuKlY5BGHRJ2cYyoFLQQ==",
 +            "requires": {
 +                "delaunator": "5"
 +            }
 +        },
 +        "d3-dispatch": {
 +            "version": "3.0.1",
 +            "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
 +            "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg=="
 +        },
 +        "d3-drag": {
 +            "version": "3.0.0",
 +            "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz",
 +            "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
 +            "requires": {
 +                "d3-dispatch": "1 - 3",
 +                "d3-selection": "3"
 +            },
 +            "dependencies": {
 +                "d3-selection": {
 +                    "version": "3.0.0",
 +                    "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
 +                    "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ=="
 +                }
 +            }
 +        },
 +        "d3-dsv": {
 +            "version": "3.0.1",
 +            "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz",
 +            "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",
 +            "requires": {
 +                "commander": "7",
 +                "iconv-lite": "0.6",
 +                "rw": "1"
 +            }
 +        },
 +        "d3-ease": {
 +            "version": "3.0.1",
 +            "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
 +            "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w=="
 +        },
 +        "d3-fetch": {
 +            "version": "3.0.1",
 +            "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz",
 +            "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==",
 +            "requires": {
 +                "d3-dsv": "1 - 3"
 +            }
 +        },
 +        "d3-force": {
 +            "version": "3.0.0",
 +            "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",
 +            "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==",
 +            "requires": {
 +                "d3-dispatch": "1 - 3",
 +                "d3-quadtree": "1 - 3",
 +                "d3-timer": "1 - 3"
 +            }
 +        },
 +        "d3-format": {
 +            "version": "3.1.0",
 +            "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
 +            "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA=="
 +        },
 +        "d3-geo": {
 +            "version": "3.0.1",
 +            "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.0.1.tgz",
 +            "integrity": "sha512-Wt23xBych5tSy9IYAM1FR2rWIBFWa52B/oF/GYe5zbdHrg08FU8+BuI6X4PvTwPDdqdAdq04fuWJpELtsaEjeA==",
 +            "requires": {
 +                "d3-array": "2.5.0 - 3"
 +            }
 +        },
 +        "d3-graphviz": {
 +            "version": "4.1.1",
 +            "resolved": "https://registry.npmjs.org/d3-graphviz/-/d3-graphviz-4.1.1.tgz",
 +            "integrity": "sha512-s0IVbKf8rs4eJI2xo5Umr7nXDX/LEZw/x2WtKxmlyQxR0qUY49UiLhBNOX7VDHZywMle43NKEXnU6fn22fpJvQ==",
 +            "requires": {
 +                "@hpcc-js/wasm": "1.12.8",
 +                "d3-dispatch": "^2.0.0",
 +                "d3-format": "^2.0.0",
 +                "d3-interpolate": "^2.0.1",
 +                "d3-path": "^2.0.0",
 +                "d3-timer": "^2.0.0",
 +                "d3-transition": "^2.0.0",
 +                "d3-zoom": "^2.0.0"
 +            },
 +            "dependencies": {
 +                "d3-color": {
 +                    "version": "2.0.0",
 +                    "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-2.0.0.tgz",
 +                    "integrity": "sha512-SPXi0TSKPD4g9tw0NMZFnR95XVgUZiBH+uUTqQuDu1OsE2zomHU7ho0FISciaPvosimixwHFl3WHLGabv6dDgQ=="
 +                },
 +                "d3-dispatch": {
 +                    "version": "2.0.0",
 +                    "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-2.0.0.tgz",
 +                    "integrity": "sha512-S/m2VsXI7gAti2pBoLClFFTMOO1HTtT0j99AuXLoGFKO6deHDdnv6ZGTxSTTUTgO1zVcv82fCOtDjYK4EECmWA=="
 +                },
 +                "d3-drag": {
 +                    "version": "2.0.0",
 +                    "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-2.0.0.tgz",
 +                    "integrity": "sha512-g9y9WbMnF5uqB9qKqwIIa/921RYWzlUDv9Jl1/yONQwxbOfszAWTCm8u7HOTgJgRDXiRZN56cHT9pd24dmXs8w==",
 +                    "requires": {
 +                        "d3-dispatch": "1 - 2",
 +                        "d3-selection": "2"
 +                    }
 +                },
 +                "d3-ease": {
 +                    "version": "2.0.0",
 +                    "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-2.0.0.tgz",
 +                    "integrity": "sha512-68/n9JWarxXkOWMshcT5IcjbB+agblQUaIsbnXmrzejn2O82n3p2A9R2zEB9HIEFWKFwPAEDDN8gR0VdSAyyAQ=="
 +                },
 +                "d3-format": {
 +                    "version": "2.0.0",
 +                    "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-2.0.0.tgz",
 +                    "integrity": "sha512-Ab3S6XuE/Q+flY96HXT0jOXcM4EAClYFnRGY5zsjRGNy6qCYrQsMffs7cV5Q9xejb35zxW5hf/guKw34kvIKsA=="
 +                },
 +                "d3-interpolate": {
 +                    "version": "2.0.1",
 +                    "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-2.0.1.tgz",
 +                    "integrity": "sha512-c5UhwwTs/yybcmTpAVqwSFl6vrQ8JZJoT5F7xNFK9pymv5C0Ymcc9/LIJHtYIggg/yS9YHw8i8O8tgb9pupjeQ==",
 +                    "requires": {
 +                        "d3-color": "1 - 2"
 +                    }
 +                },
 +                "d3-path": {
 +                    "version": "2.0.0",
 +                    "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-2.0.0.tgz",
 +                    "integrity": "sha512-ZwZQxKhBnv9yHaiWd6ZU4x5BtCQ7pXszEV9CU6kRgwIQVQGLMv1oiL4M+MK/n79sYzsj+gcgpPQSctJUsLN7fA=="
 +                },
 +                "d3-timer": {
 +                    "version": "2.0.0",
 +                    "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-2.0.0.tgz",
 +                    "integrity": "sha512-TO4VLh0/420Y/9dO3+f9abDEFYeCUr2WZRlxJvbp4HPTQcSylXNiL6yZa9FIUvV1yRiFufl1bszTCLDqv9PWNA=="
 +                },
 +                "d3-transition": {
 +                    "version": "2.0.0",
 +                    "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-2.0.0.tgz",
 +                    "integrity": "sha512-42ltAGgJesfQE3u9LuuBHNbGrI/AJjNL2OAUdclE70UE6Vy239GCBEYD38uBPoLeNsOhFStGpPI0BAOV+HMxog==",
 +                    "requires": {
 +                        "d3-color": "1 - 2",
 +                        "d3-dispatch": "1 - 2",
 +                        "d3-ease": "1 - 2",
 +                        "d3-interpolate": "1 - 2",
 +                        "d3-timer": "1 - 2"
 +                    }
 +                },
 +                "d3-zoom": {
 +                    "version": "2.0.0",
 +                    "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-2.0.0.tgz",
 +                    "integrity": "sha512-fFg7aoaEm9/jf+qfstak0IYpnesZLiMX6GZvXtUSdv8RH2o4E2qeelgdU09eKS6wGuiGMfcnMI0nTIqWzRHGpw==",
 +                    "requires": {
 +                        "d3-dispatch": "1 - 2",
 +                        "d3-drag": "2",
 +                        "d3-interpolate": "1 - 2",
 +                        "d3-selection": "2",
 +                        "d3-transition": "2"
 +                    }
 +                }
 +            }
 +        },
 +        "d3-hierarchy": {
 +            "version": "3.1.2",
 +            "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz",
 +            "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA=="
 +        },
 +        "d3-interpolate": {
 +            "version": "3.0.1",
 +            "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
 +            "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
 +            "requires": {
 +                "d3-color": "1 - 3"
 +            }
 +        },
 +        "d3-path": {
 +            "version": "3.0.1",
 +            "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.0.1.tgz",
 +            "integrity": "sha512-gq6gZom9AFZby0YLduxT1qmrp4xpBA1YZr19OI717WIdKE2OM5ETq5qrHLb301IgxhLwcuxvGZVLeeWc/k1I6w=="
 +        },
 +        "d3-polygon": {
 +            "version": "3.0.1",
 +            "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz",
 +            "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg=="
 +        },
 +        "d3-quadtree": {
 +            "version": "3.0.1",
 +            "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz",
 +            "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw=="
 +        },
 +        "d3-random": {
 +            "version": "3.0.1",
 +            "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz",
 +            "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ=="
 +        },
 +        "d3-scale": {
 +            "version": "4.0.2",
 +            "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
 +            "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
 +            "requires": {
 +                "d3-array": "2.10.0 - 3",
 +                "d3-format": "1 - 3",
 +                "d3-interpolate": "1.2.0 - 3",
 +                "d3-time": "2.1.1 - 3",
 +                "d3-time-format": "2 - 4"
 +            }
 +        },
 +        "d3-scale-chromatic": {
 +            "version": "3.0.0",
 +            "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz",
 +            "integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==",
 +            "requires": {
 +                "d3-color": "1 - 3",
 +                "d3-interpolate": "1 - 3"
 +            }
 +        },
 +        "d3-selection": {
 +            "version": "2.0.0",
 +            "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-2.0.0.tgz",
 +            "integrity": "sha512-XoGGqhLUN/W14NmaqcO/bb1nqjDAw5WtSYb2X8wiuQWvSZUsUVYsOSkOybUrNvcBjaywBdYPy03eXHMXjk9nZA=="
 +        },
 +        "d3-shape": {
 +            "version": "3.1.0",
 +            "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.1.0.tgz",
 +            "integrity": "sha512-tGDh1Muf8kWjEDT/LswZJ8WF85yDZLvVJpYU9Nq+8+yW1Z5enxrmXOhTArlkaElU+CTn0OTVNli+/i+HP45QEQ==",
 +            "requires": {
 +                "d3-path": "1 - 3"
 +            }
 +        },
 +        "d3-time": {
 +            "version": "3.0.0",
 +            "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.0.0.tgz",
 +            "integrity": "sha512-zmV3lRnlaLI08y9IMRXSDshQb5Nj77smnfpnd2LrBa/2K281Jijactokeak14QacHs/kKq0AQ121nidNYlarbQ==",
 +            "requires": {
 +                "d3-array": "2 - 3"
 +            }
 +        },
 +        "d3-time-format": {
 +            "version": "4.1.0",
 +            "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
 +            "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
 +            "requires": {
 +                "d3-time": "1 - 3"
 +            }
 +        },
 +        "d3-timer": {
 +            "version": "3.0.1",
 +            "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
 +            "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="
 +        },
 +        "d3-transition": {
 +            "version": "3.0.1",
 +            "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz",
 +            "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
 +            "requires": {
 +                "d3-color": "1 - 3",
 +                "d3-dispatch": "1 - 3",
 +                "d3-ease": "1 - 3",
 +                "d3-interpolate": "1 - 3",
 +                "d3-timer": "1 - 3"
 +            }
 +        },
 +        "d3-zoom": {
 +            "version": "3.0.0",
 +            "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz",
 +            "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
 +            "requires": {
 +                "d3-dispatch": "1 - 3",
 +                "d3-drag": "2 - 3",
 +                "d3-interpolate": "1 - 3",
 +                "d3-selection": "2 - 3",
 +                "d3-transition": "2 - 3"
 +            }
 +        },
 +        "debug": {
 +            "version": "4.3.4",
 +            "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
 +            "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
 +            "dev": true,
 +            "requires": {
 +                "ms": "2.1.2"
 +            }
 +        },
 +        "decompress-response": {
 +            "version": "6.0.0",
 +            "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
 +            "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
 +            "dev": true,
 +            "requires": {
 +                "mimic-response": "^3.1.0"
 +            }
 +        },
 +        "deep-extend": {
 +            "version": "0.6.0",
 +            "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
 +            "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
 +            "dev": true
 +        },
 +        "deep-is": {
 +            "version": "0.1.4",
 +            "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
 +            "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
 +            "dev": true
 +        },
 +        "delaunator": {
 +            "version": "5.0.0",
 +            "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz",
 +            "integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==",
 +            "requires": {
 +                "robust-predicates": "^3.0.0"
 +            }
 +        },
 +        "detect-libc": {
 +            "version": "2.0.1",
 +            "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz",
 +            "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==",
 +            "dev": true
 +        },
 +        "dir-glob": {
 +            "version": "3.0.1",
 +            "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
 +            "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
 +            "dev": true,
 +            "requires": {
 +                "path-type": "^4.0.0"
 +            }
 +        },
 +        "doctrine": {
 +            "version": "3.0.0",
 +            "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
 +            "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
 +            "dev": true,
 +            "requires": {
 +                "esutils": "^2.0.2"
 +            }
 +        },
 +        "dom-serializer": {
 +            "version": "2.0.0",
 +            "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
 +            "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
 +            "dev": true,
 +            "requires": {
 +                "domelementtype": "^2.3.0",
 +                "domhandler": "^5.0.2",
 +                "entities": "^4.2.0"
 +            }
 +        },
 +        "domelementtype": {
 +            "version": "2.3.0",
 +            "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
 +            "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
 +            "dev": true
 +        },
 +        "domhandler": {
 +            "version": "5.0.3",
 +            "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
 +            "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
 +            "dev": true,
 +            "requires": {
 +                "domelementtype": "^2.3.0"
 +            }
 +        },
 +        "domutils": {
 +            "version": "3.0.1",
 +            "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz",
 +            "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==",
 +            "dev": true,
 +            "requires": {
 +                "dom-serializer": "^2.0.0",
 +                "domelementtype": "^2.3.0",
 +                "domhandler": "^5.0.1"
 +            }
 +        },
 +        "duplexer2": {
 +            "version": "0.1.4",
 +            "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
 +            "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==",
 +            "dev": true,
 +            "requires": {
 +                "readable-stream": "^2.0.2"
 +            }
 +        },
 +        "emoji-regex": {
 +            "version": "8.0.0",
 +            "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
 +            "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
 +        },
 +        "end-of-stream": {
 +            "version": "1.4.4",
 +            "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
 +            "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
 +            "dev": true,
 +            "requires": {
 +                "once": "^1.4.0"
 +            }
 +        },
 +        "entities": {
 +            "version": "4.3.1",
 +            "resolved": "https://registry.npmjs.org/entities/-/entities-4.3.1.tgz",
 +            "integrity": "sha512-o4q/dYJlmyjP2zfnaWDUC6A3BQFmVTX+tZPezK7k0GLSU9QYCauscf5Y+qcEPzKL+EixVouYDgLQK5H9GrLpkg==",
 +            "dev": true
 +        },
 +        "esbuild": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.48.tgz",
 +            "integrity": "sha512-w6N1Yn5MtqK2U1/WZTX9ZqUVb8IOLZkZ5AdHkT6x3cHDMVsYWC7WPdiLmx19w3i4Rwzy5LqsEMtVihG3e4rFzA==",
 +            "dev": true,
 +            "requires": {
 +                "esbuild-android-64": "0.14.48",
 +                "esbuild-android-arm64": "0.14.48",
 +                "esbuild-darwin-64": "0.14.48",
 +                "esbuild-darwin-arm64": "0.14.48",
 +                "esbuild-freebsd-64": "0.14.48",
 +                "esbuild-freebsd-arm64": "0.14.48",
 +                "esbuild-linux-32": "0.14.48",
 +                "esbuild-linux-64": "0.14.48",
 +                "esbuild-linux-arm": "0.14.48",
 +                "esbuild-linux-arm64": "0.14.48",
 +                "esbuild-linux-mips64le": "0.14.48",
 +                "esbuild-linux-ppc64le": "0.14.48",
 +                "esbuild-linux-riscv64": "0.14.48",
 +                "esbuild-linux-s390x": "0.14.48",
 +                "esbuild-netbsd-64": "0.14.48",
 +                "esbuild-openbsd-64": "0.14.48",
 +                "esbuild-sunos-64": "0.14.48",
 +                "esbuild-windows-32": "0.14.48",
 +                "esbuild-windows-64": "0.14.48",
 +                "esbuild-windows-arm64": "0.14.48"
 +            }
 +        },
 +        "esbuild-android-64": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.48.tgz",
 +            "integrity": "sha512-3aMjboap/kqwCUpGWIjsk20TtxVoKck8/4Tu19rubh7t5Ra0Yrpg30Mt1QXXlipOazrEceGeWurXKeFJgkPOUg==",
 +            "dev": true,
 +            "optional": true
 +        },
 +        "esbuild-android-arm64": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.48.tgz",
 +            "integrity": "sha512-vptI3K0wGALiDq+EvRuZotZrJqkYkN5282iAfcffjI5lmGG9G1ta/CIVauhY42MBXwEgDJkweiDcDMRLzBZC4g==",
 +            "dev": true,
 +            "optional": true
 +        },
 +        "esbuild-darwin-64": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.48.tgz",
 +            "integrity": "sha512-gGQZa4+hab2Va/Zww94YbshLuWteyKGD3+EsVon8EWTWhnHFRm5N9NbALNbwi/7hQ/hM1Zm4FuHg+k6BLsl5UA==",
 +            "dev": true,
 +            "optional": true
 +        },
 +        "esbuild-darwin-arm64": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.48.tgz",
 +            "integrity": "sha512-bFjnNEXjhZT+IZ8RvRGNJthLWNHV5JkCtuOFOnjvo5pC0sk2/QVk0Qc06g2PV3J0TcU6kaPC3RN9yy9w2PSLEA==",
 +            "dev": true,
 +            "optional": true
 +        },
 +        "esbuild-freebsd-64": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.48.tgz",
 +            "integrity": "sha512-1NOlwRxmOsnPcWOGTB10JKAkYSb2nue0oM1AfHWunW/mv3wERfJmnYlGzL3UAOIUXZqW8GeA2mv+QGwq7DToqA==",
 +            "dev": true,
 +            "optional": true
 +        },
 +        "esbuild-freebsd-arm64": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.48.tgz",
 +            "integrity": "sha512-gXqKdO8wabVcYtluAbikDH2jhXp+Klq5oCD5qbVyUG6tFiGhrC9oczKq3vIrrtwcxDQqK6+HDYK8Zrd4bCA9Gw==",
 +            "dev": true,
 +            "optional": true
 +        },
 +        "esbuild-linux-32": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.48.tgz",
 +            "integrity": "sha512-ghGyDfS289z/LReZQUuuKq9KlTiTspxL8SITBFQFAFRA/IkIvDpnZnCAKTCjGXAmUqroMQfKJXMxyjJA69c/nQ==",
 +            "dev": true,
 +            "optional": true
 +        },
 +        "esbuild-linux-64": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.48.tgz",
 +            "integrity": "sha512-vni3p/gppLMVZLghI7oMqbOZdGmLbbKR23XFARKnszCIBpEMEDxOMNIKPmMItQrmH/iJrL1z8Jt2nynY0bE1ug==",
 +            "dev": true,
 +            "optional": true
 +        },
 +        "esbuild-linux-arm": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.48.tgz",
 +            "integrity": "sha512-+VfSV7Akh1XUiDNXgqgY1cUP1i2vjI+BmlyXRfVz5AfV3jbpde8JTs5Q9sYgaoq5cWfuKfoZB/QkGOI+QcL1Tw==",
 +            "dev": true,
 +            "optional": true
 +        },
 +        "esbuild-linux-arm64": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.48.tgz",
 +            "integrity": "sha512-3CFsOlpoxlKPRevEHq8aAntgYGYkE1N9yRYAcPyng/p4Wyx0tPR5SBYsxLKcgPB9mR8chHEhtWYz6EZ+H199Zw==",
 +            "dev": true,
 +            "optional": true
 +        },
 +        "esbuild-linux-mips64le": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.48.tgz",
 +            "integrity": "sha512-cs0uOiRlPp6ymknDnjajCgvDMSsLw5mST2UXh+ZIrXTj2Ifyf2aAP3Iw4DiqgnyYLV2O/v/yWBJx+WfmKEpNLA==",
 +            "dev": true,
 +            "optional": true
 +        },
 +        "esbuild-linux-ppc64le": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.48.tgz",
 +            "integrity": "sha512-+2F0vJMkuI0Wie/wcSPDCqXvSFEELH7Jubxb7mpWrA/4NpT+/byjxDz0gG6R1WJoeDefcrMfpBx4GFNN1JQorQ==",
 +            "dev": true,
 +            "optional": true
 +        },
 +        "esbuild-linux-riscv64": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.48.tgz",
 +            "integrity": "sha512-BmaK/GfEE+5F2/QDrIXteFGKnVHGxlnK9MjdVKMTfvtmudjY3k2t8NtlY4qemKSizc+QwyombGWTBDc76rxePA==",
 +            "dev": true,
 +            "optional": true
 +        },
 +        "esbuild-linux-s390x": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.48.tgz",
 +            "integrity": "sha512-tndw/0B9jiCL+KWKo0TSMaUm5UWBLsfCKVdbfMlb3d5LeV9WbijZ8Ordia8SAYv38VSJWOEt6eDCdOx8LqkC4g==",
 +            "dev": true,
 +            "optional": true
 +        },
 +        "esbuild-netbsd-64": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.48.tgz",
 +            "integrity": "sha512-V9hgXfwf/T901Lr1wkOfoevtyNkrxmMcRHyticybBUHookznipMOHoF41Al68QBsqBxnITCEpjjd4yAos7z9Tw==",
 +            "dev": true,
 +            "optional": true
 +        },
 +        "esbuild-openbsd-64": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.48.tgz",
 +            "integrity": "sha512-+IHf4JcbnnBl4T52egorXMatil/za0awqzg2Vy6FBgPcBpisDWT2sVz/tNdrK9kAqj+GZG/jZdrOkj7wsrNTKA==",
 +            "dev": true,
 +            "optional": true
 +        },
 +        "esbuild-sunos-64": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.48.tgz",
 +            "integrity": "sha512-77m8bsr5wOpOWbGi9KSqDphcq6dFeJyun8TA+12JW/GAjyfTwVtOnN8DOt6DSPUfEV+ltVMNqtXUeTeMAxl5KA==",
 +            "dev": true,
 +            "optional": true
 +        },
 +        "esbuild-windows-32": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.48.tgz",
 +            "integrity": "sha512-EPgRuTPP8vK9maxpTGDe5lSoIBHGKO/AuxDncg5O3NkrPeLNdvvK8oywB0zGaAZXxYWfNNSHskvvDgmfVTguhg==",
 +            "dev": true,
 +            "optional": true
 +        },
 +        "esbuild-windows-64": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.48.tgz",
 +            "integrity": "sha512-YmpXjdT1q0b8ictSdGwH3M8VCoqPpK1/UArze3X199w6u8hUx3V8BhAi1WjbsfDYRBanVVtduAhh2sirImtAvA==",
 +            "dev": true,
 +            "optional": true
 +        },
 +        "esbuild-windows-arm64": {
 +            "version": "0.14.48",
 +            "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.48.tgz",
 +            "integrity": "sha512-HHaOMCsCXp0rz5BT2crTka6MPWVno121NKApsGs/OIW5QC0ggC69YMGs1aJct9/9FSUF4A1xNE/cLvgB5svR4g==",
 +            "dev": true,
 +            "optional": true
 +        },
 +        "escalade": {
 +            "version": "3.1.1",
 +            "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
 +            "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw=="
 +        },
 +        "escape-string-regexp": {
 +            "version": "4.0.0",
 +            "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
 +            "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
 +            "dev": true
 +        },
 +        "eslint": {
 +            "version": "8.19.0",
 +            "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.19.0.tgz",
 +            "integrity": "sha512-SXOPj3x9VKvPe81TjjUJCYlV4oJjQw68Uek+AM0X4p+33dj2HY5bpTZOgnQHcG2eAm1mtCU9uNMnJi7exU/kYw==",
 +            "dev": true,
 +            "requires": {
 +                "@eslint/eslintrc": "^1.3.0",
 +                "@humanwhocodes/config-array": "^0.9.2",
 +                "ajv": "^6.10.0",
 +                "chalk": "^4.0.0",
 +                "cross-spawn": "^7.0.2",
 +                "debug": "^4.3.2",
 +                "doctrine": "^3.0.0",
 +                "escape-string-regexp": "^4.0.0",
 +                "eslint-scope": "^7.1.1",
 +                "eslint-utils": "^3.0.0",
 +                "eslint-visitor-keys": "^3.3.0",
 +                "espree": "^9.3.2",
 +                "esquery": "^1.4.0",
 +                "esutils": "^2.0.2",
 +                "fast-deep-equal": "^3.1.3",
 +                "file-entry-cache": "^6.0.1",
 +                "functional-red-black-tree": "^1.0.1",
 +                "glob-parent": "^6.0.1",
 +                "globals": "^13.15.0",
 +                "ignore": "^5.2.0",
 +                "import-fresh": "^3.0.0",
 +                "imurmurhash": "^0.1.4",
 +                "is-glob": "^4.0.0",
 +                "js-yaml": "^4.1.0",
 +                "json-stable-stringify-without-jsonify": "^1.0.1",
 +                "levn": "^0.4.1",
 +                "lodash.merge": "^4.6.2",
 +                "minimatch": "^3.1.2",
 +                "natural-compare": "^1.4.0",
 +                "optionator": "^0.9.1",
 +                "regexpp": "^3.2.0",
 +                "strip-ansi": "^6.0.1",
 +                "strip-json-comments": "^3.1.0",
 +                "text-table": "^0.2.0",
 +                "v8-compile-cache": "^2.0.3"
 +            },
 +            "dependencies": {
 +                "eslint-scope": {
 +                    "version": "7.1.1",
 +                    "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz",
 +                    "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==",
 +                    "dev": true,
 +                    "requires": {
 +                        "esrecurse": "^4.3.0",
 +                        "estraverse": "^5.2.0"
 +                    }
 +                },
 +                "estraverse": {
 +                    "version": "5.3.0",
 +                    "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
 +                    "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
 +                    "dev": true
 +                }
 +            }
 +        },
 +        "eslint-config-prettier": {
 +            "version": "8.5.0",
 +            "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz",
 +            "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==",
 +            "dev": true,
 +            "requires": {}
 +        },
 +        "eslint-scope": {
 +            "version": "5.1.1",
 +            "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
 +            "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
 +            "dev": true,
 +            "requires": {
 +                "esrecurse": "^4.3.0",
 +                "estraverse": "^4.1.1"
 +            }
 +        },
 +        "eslint-utils": {
 +            "version": "3.0.0",
 +            "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz",
 +            "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==",
 +            "dev": true,
 +            "requires": {
 +                "eslint-visitor-keys": "^2.0.0"
 +            },
 +            "dependencies": {
 +                "eslint-visitor-keys": {
 +                    "version": "2.1.0",
 +                    "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
 +                    "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
 +                    "dev": true
 +                }
 +            }
 +        },
 +        "eslint-visitor-keys": {
 +            "version": "3.3.0",
 +            "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz",
 +            "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==",
 +            "dev": true
 +        },
 +        "espree": {
 +            "version": "9.3.2",
 +            "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz",
 +            "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==",
 +            "dev": true,
 +            "requires": {
 +                "acorn": "^8.7.1",
 +                "acorn-jsx": "^5.3.2",
 +                "eslint-visitor-keys": "^3.3.0"
 +            }
 +        },
 +        "esquery": {
 +            "version": "1.4.0",
 +            "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz",
 +            "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==",
 +            "dev": true,
 +            "requires": {
 +                "estraverse": "^5.1.0"
 +            },
 +            "dependencies": {
 +                "estraverse": {
 +                    "version": "5.3.0",
 +                    "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
 +                    "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
 +                    "dev": true
 +                }
 +            }
 +        },
 +        "esrecurse": {
 +            "version": "4.3.0",
 +            "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
 +            "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
 +            "dev": true,
 +            "requires": {
 +                "estraverse": "^5.2.0"
 +            },
 +            "dependencies": {
 +                "estraverse": {
 +                    "version": "5.3.0",
 +                    "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
 +                    "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
 +                    "dev": true
 +                }
 +            }
 +        },
 +        "estraverse": {
 +            "version": "4.3.0",
 +            "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
 +            "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
 +            "dev": true
 +        },
 +        "esutils": {
 +            "version": "2.0.3",
 +            "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
 +            "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
 +            "dev": true
 +        },
 +        "expand-template": {
 +            "version": "2.0.3",
 +            "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
 +            "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",
 +            "dev": true
 +        },
 +        "fast-deep-equal": {
 +            "version": "3.1.3",
 +            "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
 +            "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
 +            "dev": true
 +        },
 +        "fast-glob": {
 +            "version": "3.2.11",
 +            "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz",
 +            "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==",
 +            "dev": true,
 +            "requires": {
 +                "@nodelib/fs.stat": "^2.0.2",
 +                "@nodelib/fs.walk": "^1.2.3",
 +                "glob-parent": "^5.1.2",
 +                "merge2": "^1.3.0",
 +                "micromatch": "^4.0.4"
 +            },
 +            "dependencies": {
 +                "glob-parent": {
 +                    "version": "5.1.2",
 +                    "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
 +                    "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
 +                    "dev": true,
 +                    "requires": {
 +                        "is-glob": "^4.0.1"
 +                    }
 +                }
 +            }
 +        },
 +        "fast-json-stable-stringify": {
 +            "version": "2.1.0",
 +            "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
 +            "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
 +            "dev": true
 +        },
 +        "fast-levenshtein": {
 +            "version": "2.0.6",
 +            "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
 +            "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
 +            "dev": true
 +        },
 +        "fastq": {
 +            "version": "1.13.0",
 +            "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz",
 +            "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==",
 +            "dev": true,
 +            "requires": {
 +                "reusify": "^1.0.4"
 +            }
 +        },
 +        "fd-slicer": {
 +            "version": "1.1.0",
 +            "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
 +            "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
 +            "dev": true,
 +            "requires": {
 +                "pend": "~1.2.0"
 +            }
 +        },
 +        "file-entry-cache": {
 +            "version": "6.0.1",
 +            "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
 +            "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
 +            "dev": true,
 +            "requires": {
 +                "flat-cache": "^3.0.4"
 +            }
 +        },
 +        "fill-range": {
 +            "version": "7.0.1",
 +            "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
 +            "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
 +            "dev": true,
 +            "requires": {
 +                "to-regex-range": "^5.0.1"
 +            }
 +        },
 +        "flat-cache": {
 +            "version": "3.0.4",
 +            "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
 +            "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
 +            "dev": true,
 +            "requires": {
 +                "flatted": "^3.1.0",
 +                "rimraf": "^3.0.2"
 +            }
 +        },
 +        "flatted": {
 +            "version": "3.2.6",
 +            "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.6.tgz",
 +            "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==",
 +            "dev": true
 +        },
 +        "follow-redirects": {
 +            "version": "1.15.1",
 +            "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz",
 +            "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==",
 +            "dev": true
 +        },
 +        "fs-constants": {
 +            "version": "1.0.0",
 +            "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
 +            "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
 +            "dev": true
 +        },
 +        "fs.realpath": {
 +            "version": "1.0.0",
 +            "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
 +            "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
 +            "dev": true
 +        },
 +        "fstream": {
 +            "version": "1.0.12",
 +            "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz",
 +            "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==",
 +            "dev": true,
 +            "requires": {
 +                "graceful-fs": "^4.1.2",
 +                "inherits": "~2.0.0",
 +                "mkdirp": ">=0.5 0",
 +                "rimraf": "2"
 +            },
 +            "dependencies": {
 +                "rimraf": {
 +                    "version": "2.7.1",
 +                    "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
 +                    "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
 +                    "dev": true,
 +                    "requires": {
 +                        "glob": "^7.1.3"
 +                    }
 +                }
 +            }
 +        },
 +        "function-bind": {
 +            "version": "1.1.1",
 +            "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
 +            "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
 +            "dev": true
 +        },
 +        "functional-red-black-tree": {
 +            "version": "1.0.1",
 +            "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
 +            "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==",
 +            "dev": true
 +        },
 +        "get-caller-file": {
 +            "version": "2.0.5",
 +            "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
 +            "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
 +        },
 +        "get-intrinsic": {
 +            "version": "1.1.2",
 +            "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz",
 +            "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==",
 +            "dev": true,
 +            "requires": {
 +                "function-bind": "^1.1.1",
 +                "has": "^1.0.3",
 +                "has-symbols": "^1.0.3"
 +            }
 +        },
 +        "github-from-package": {
 +            "version": "0.0.0",
 +            "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
 +            "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==",
 +            "dev": true
 +        },
 +        "glob": {
 +            "version": "7.2.3",
 +            "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
 +            "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
 +            "dev": true,
 +            "requires": {
 +                "fs.realpath": "^1.0.0",
 +                "inflight": "^1.0.4",
 +                "inherits": "2",
 +                "minimatch": "^3.1.1",
 +                "once": "^1.3.0",
 +                "path-is-absolute": "^1.0.0"
 +            }
 +        },
 +        "glob-parent": {
 +            "version": "6.0.2",
 +            "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
 +            "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
 +            "dev": true,
 +            "requires": {
 +                "is-glob": "^4.0.3"
 +            }
 +        },
 +        "globals": {
 +            "version": "13.16.0",
 +            "resolved": "https://registry.npmjs.org/globals/-/globals-13.16.0.tgz",
 +            "integrity": "sha512-A1lrQfpNF+McdPOnnFqY3kSN0AFTy485bTi1bkLk4mVPODIUEcSfhHgRqA+QdXPksrSTTztYXx37NFV+GpGk3Q==",
 +            "dev": true,
 +            "requires": {
 +                "type-fest": "^0.20.2"
 +            }
 +        },
 +        "globby": {
 +            "version": "11.1.0",
 +            "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
 +            "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
 +            "dev": true,
 +            "requires": {
 +                "array-union": "^2.1.0",
 +                "dir-glob": "^3.0.1",
 +                "fast-glob": "^3.2.9",
 +                "ignore": "^5.2.0",
 +                "merge2": "^1.4.1",
 +                "slash": "^3.0.0"
 +            }
 +        },
 +        "graceful-fs": {
 +            "version": "4.2.10",
 +            "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
 +            "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
 +            "dev": true
 +        },
 +        "has": {
 +            "version": "1.0.3",
 +            "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
 +            "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
 +            "dev": true,
 +            "requires": {
 +                "function-bind": "^1.1.1"
 +            }
 +        },
 +        "has-flag": {
 +            "version": "4.0.0",
 +            "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
 +            "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
 +            "dev": true
 +        },
 +        "has-symbols": {
 +            "version": "1.0.3",
 +            "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
 +            "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
 +            "dev": true
 +        },
 +        "hosted-git-info": {
 +            "version": "4.1.0",
 +            "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz",
 +            "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==",
 +            "dev": true,
 +            "requires": {
 +                "lru-cache": "^6.0.0"
 +            }
 +        },
 +        "htmlparser2": {
 +            "version": "8.0.1",
 +            "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz",
 +            "integrity": "sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==",
 +            "dev": true,
 +            "requires": {
 +                "domelementtype": "^2.3.0",
 +                "domhandler": "^5.0.2",
 +                "domutils": "^3.0.1",
 +                "entities": "^4.3.0"
 +            }
 +        },
 +        "http-proxy-agent": {
 +            "version": "4.0.1",
 +            "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
 +            "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
 +            "dev": true,
 +            "requires": {
 +                "@tootallnate/once": "1",
 +                "agent-base": "6",
 +                "debug": "4"
 +            }
 +        },
 +        "https-proxy-agent": {
 +            "version": "5.0.1",
 +            "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
 +            "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
 +            "dev": true,
 +            "requires": {
 +                "agent-base": "6",
 +                "debug": "4"
 +            }
 +        },
 +        "iconv-lite": {
 +            "version": "0.6.3",
 +            "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
 +            "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
 +            "requires": {
 +                "safer-buffer": ">= 2.1.2 < 3.0.0"
 +            }
 +        },
 +        "ieee754": {
 +            "version": "1.2.1",
 +            "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
 +            "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
 +            "dev": true
 +        },
 +        "ignore": {
 +            "version": "5.2.0",
 +            "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
 +            "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==",
 +            "dev": true
 +        },
 +        "import-fresh": {
 +            "version": "3.3.0",
 +            "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
 +            "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
 +            "dev": true,
 +            "requires": {
 +                "parent-module": "^1.0.0",
 +                "resolve-from": "^4.0.0"
 +            }
 +        },
 +        "imurmurhash": {
 +            "version": "0.1.4",
 +            "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
 +            "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
 +            "dev": true
 +        },
 +        "inflight": {
 +            "version": "1.0.6",
 +            "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
 +            "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
 +            "dev": true,
 +            "requires": {
 +                "once": "^1.3.0",
 +                "wrappy": "1"
 +            }
 +        },
 +        "inherits": {
 +            "version": "2.0.4",
 +            "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
 +            "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
 +            "dev": true
 +        },
 +        "ini": {
 +            "version": "1.3.8",
 +            "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
 +            "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
 +            "dev": true
 +        },
 +        "internmap": {
 +            "version": "2.0.3",
 +            "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
 +            "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="
 +        },
 +        "is-ci": {
 +            "version": "2.0.0",
 +            "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz",
 +            "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==",
 +            "dev": true,
 +            "requires": {
 +                "ci-info": "^2.0.0"
 +            }
 +        },
 +        "is-extglob": {
 +            "version": "2.1.1",
 +            "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
 +            "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
 +            "dev": true
 +        },
 +        "is-fullwidth-code-point": {
 +            "version": "3.0.0",
 +            "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
 +            "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
 +        },
 +        "is-glob": {
 +            "version": "4.0.3",
 +            "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
 +            "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
 +            "dev": true,
 +            "requires": {
 +                "is-extglob": "^2.1.1"
 +            }
 +        },
 +        "is-number": {
 +            "version": "7.0.0",
 +            "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
 +            "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
 +            "dev": true
 +        },
 +        "isarray": {
 +            "version": "1.0.0",
 +            "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
 +            "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
 +            "dev": true
 +        },
 +        "isexe": {
 +            "version": "2.0.0",
 +            "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
 +            "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
 +            "dev": true
 +        },
 +        "js-yaml": {
 +            "version": "4.1.0",
 +            "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
 +            "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
 +            "dev": true,
 +            "requires": {
 +                "argparse": "^2.0.1"
 +            }
 +        },
 +        "json-schema-traverse": {
 +            "version": "0.4.1",
 +            "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
 +            "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
 +            "dev": true
 +        },
 +        "json-stable-stringify-without-jsonify": {
 +            "version": "1.0.1",
 +            "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
 +            "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
 +            "dev": true
 +        },
 +        "keytar": {
 +            "version": "7.9.0",
 +            "resolved": "https://registry.npmjs.org/keytar/-/keytar-7.9.0.tgz",
 +            "integrity": "sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ==",
 +            "dev": true,
 +            "requires": {
 +                "node-addon-api": "^4.3.0",
 +                "prebuild-install": "^7.0.1"
 +            }
 +        },
 +        "leven": {
 +            "version": "3.1.0",
 +            "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
 +            "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==",
 +            "dev": true
 +        },
 +        "levn": {
 +            "version": "0.4.1",
 +            "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
 +            "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
 +            "dev": true,
 +            "requires": {
 +                "prelude-ls": "^1.2.1",
 +                "type-check": "~0.4.0"
 +            }
 +        },
 +        "linkify-it": {
 +            "version": "3.0.3",
 +            "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz",
 +            "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==",
 +            "dev": true,
 +            "requires": {
 +                "uc.micro": "^1.0.1"
 +            }
 +        },
 +        "listenercount": {
 +            "version": "1.0.1",
 +            "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz",
 +            "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==",
 +            "dev": true
 +        },
 +        "lodash.merge": {
 +            "version": "4.6.2",
 +            "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
 +            "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
 +            "dev": true
 +        },
 +        "lru-cache": {
 +            "version": "6.0.0",
 +            "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
 +            "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
 +            "requires": {
 +                "yallist": "^4.0.0"
 +            }
 +        },
 +        "markdown-it": {
 +            "version": "12.3.2",
 +            "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz",
 +            "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==",
 +            "dev": true,
 +            "requires": {
 +                "argparse": "^2.0.1",
 +                "entities": "~2.1.0",
 +                "linkify-it": "^3.0.1",
 +                "mdurl": "^1.0.1",
 +                "uc.micro": "^1.0.5"
 +            },
 +            "dependencies": {
 +                "entities": {
 +                    "version": "2.1.0",
 +                    "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz",
 +                    "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==",
 +                    "dev": true
 +                }
 +            }
 +        },
 +        "mdurl": {
 +            "version": "1.0.1",
 +            "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
 +            "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==",
 +            "dev": true
 +        },
 +        "merge2": {
 +            "version": "1.4.1",
 +            "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
 +            "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
 +            "dev": true
 +        },
 +        "micromatch": {
 +            "version": "4.0.5",
 +            "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
 +            "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
 +            "dev": true,
 +            "requires": {
 +                "braces": "^3.0.2",
 +                "picomatch": "^2.3.1"
 +            }
 +        },
 +        "mime": {
 +            "version": "1.6.0",
 +            "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
 +            "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
 +            "dev": true
 +        },
 +        "mimic-response": {
 +            "version": "3.1.0",
 +            "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
 +            "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
 +            "dev": true
 +        },
 +        "minimatch": {
 +            "version": "3.1.2",
 +            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
 +            "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
 +            "requires": {
 +                "brace-expansion": "^1.1.7"
 +            }
 +        },
 +        "minimist": {
 +            "version": "1.2.6",
 +            "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
 +            "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
 +            "dev": true
 +        },
 +        "mkdirp": {
 +            "version": "0.5.6",
 +            "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
 +            "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
 +            "dev": true,
 +            "requires": {
 +                "minimist": "^1.2.6"
 +            }
 +        },
 +        "mkdirp-classic": {
 +            "version": "0.5.3",
 +            "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
 +            "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
 +            "dev": true
 +        },
 +        "ms": {
 +            "version": "2.1.2",
 +            "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
 +            "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
 +            "dev": true
 +        },
 +        "mute-stream": {
 +            "version": "0.0.8",
 +            "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
 +            "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
 +            "dev": true
 +        },
 +        "napi-build-utils": {
 +            "version": "1.0.2",
 +            "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz",
 +            "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==",
 +            "dev": true
 +        },
 +        "natural-compare": {
 +            "version": "1.4.0",
 +            "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
 +            "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
 +            "dev": true
 +        },
 +        "node-abi": {
 +            "version": "3.22.0",
 +            "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.22.0.tgz",
 +            "integrity": "sha512-u4uAs/4Zzmp/jjsD9cyFYDXeISfUWaAVWshPmDZOFOv4Xl4SbzTXm53I04C2uRueYJ+0t5PEtLH/owbn2Npf/w==",
 +            "dev": true,
 +            "requires": {
 +                "semver": "^7.3.5"
 +            }
 +        },
 +        "node-addon-api": {
 +            "version": "4.3.0",
 +            "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz",
 +            "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==",
 +            "dev": true
 +        },
 +        "nth-check": {
 +            "version": "2.1.1",
 +            "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
 +            "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
 +            "dev": true,
 +            "requires": {
 +                "boolbase": "^1.0.0"
 +            }
 +        },
 +        "object-inspect": {
 +            "version": "1.12.2",
 +            "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
 +            "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==",
 +            "dev": true
 +        },
 +        "once": {
 +            "version": "1.4.0",
 +            "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
 +            "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
 +            "dev": true,
 +            "requires": {
 +                "wrappy": "1"
 +            }
 +        },
 +        "optionator": {
 +            "version": "0.9.1",
 +            "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
 +            "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
 +            "dev": true,
 +            "requires": {
 +                "deep-is": "^0.1.3",
 +                "fast-levenshtein": "^2.0.6",
 +                "levn": "^0.4.1",
 +                "prelude-ls": "^1.2.1",
 +                "type-check": "^0.4.0",
 +                "word-wrap": "^1.2.3"
 +            }
 +        },
 +        "ovsx": {
++            "version": "0.5.2",
++            "resolved": "https://registry.npmjs.org/ovsx/-/ovsx-0.5.2.tgz",
++            "integrity": "sha512-UbLultRCk46WddeA0Cly4hoRhzBJUiLgbIEViXlgOvV54LbsppClDkMLoCevUUBHoiNdMX2NuiSgURAEXgCZdw==",
 +            "dev": true,
 +            "requires": {
 +                "commander": "^6.1.0",
 +                "follow-redirects": "^1.14.6",
 +                "is-ci": "^2.0.0",
 +                "leven": "^3.1.0",
 +                "tmp": "^0.2.1",
 +                "vsce": "^2.6.3"
 +            },
 +            "dependencies": {
 +                "commander": {
 +                    "version": "6.2.1",
 +                    "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz",
 +                    "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==",
 +                    "dev": true
 +                }
 +            }
 +        },
 +        "parent-module": {
 +            "version": "1.0.1",
 +            "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
 +            "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
 +            "dev": true,
 +            "requires": {
 +                "callsites": "^3.0.0"
 +            }
 +        },
 +        "parse-semver": {
 +            "version": "1.1.1",
 +            "resolved": "https://registry.npmjs.org/parse-semver/-/parse-semver-1.1.1.tgz",
 +            "integrity": "sha512-Eg1OuNntBMH0ojvEKSrvDSnwLmvVuUOSdylH/pSCPNMIspLlweJyIWXCE+k/5hm3cj/EBUYwmWkjhBALNP4LXQ==",
 +            "dev": true,
 +            "requires": {
 +                "semver": "^5.1.0"
 +            },
 +            "dependencies": {
 +                "semver": {
 +                    "version": "5.7.1",
 +                    "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
 +                    "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
 +                    "dev": true
 +                }
 +            }
 +        },
 +        "parse5": {
 +            "version": "7.0.0",
 +            "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.0.0.tgz",
 +            "integrity": "sha512-y/t8IXSPWTuRZqXc0ajH/UwDj4mnqLEbSttNbThcFhGrZuOyoyvNBO85PBp2jQa55wY9d07PBNjsK8ZP3K5U6g==",
 +            "dev": true,
 +            "requires": {
 +                "entities": "^4.3.0"
 +            }
 +        },
 +        "parse5-htmlparser2-tree-adapter": {
 +            "version": "7.0.0",
 +            "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz",
 +            "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==",
 +            "dev": true,
 +            "requires": {
 +                "domhandler": "^5.0.2",
 +                "parse5": "^7.0.0"
 +            }
 +        },
 +        "path-is-absolute": {
 +            "version": "1.0.1",
 +            "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
 +            "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
 +            "dev": true
 +        },
 +        "path-key": {
 +            "version": "3.1.1",
 +            "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
 +            "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
 +            "dev": true
 +        },
 +        "path-type": {
 +            "version": "4.0.0",
 +            "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
 +            "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
 +            "dev": true
 +        },
 +        "pend": {
 +            "version": "1.2.0",
 +            "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
 +            "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
 +            "dev": true
 +        },
 +        "picomatch": {
 +            "version": "2.3.1",
 +            "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
 +            "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
 +            "dev": true
 +        },
 +        "prebuild-install": {
 +            "version": "7.1.1",
 +            "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz",
 +            "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==",
 +            "dev": true,
 +            "requires": {
 +                "detect-libc": "^2.0.0",
 +                "expand-template": "^2.0.3",
 +                "github-from-package": "0.0.0",
 +                "minimist": "^1.2.3",
 +                "mkdirp-classic": "^0.5.3",
 +                "napi-build-utils": "^1.0.1",
 +                "node-abi": "^3.3.0",
 +                "pump": "^3.0.0",
 +                "rc": "^1.2.7",
 +                "simple-get": "^4.0.0",
 +                "tar-fs": "^2.0.0",
 +                "tunnel-agent": "^0.6.0"
 +            }
 +        },
 +        "prelude-ls": {
 +            "version": "1.2.1",
 +            "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
 +            "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
 +            "dev": true
 +        },
 +        "prettier": {
 +            "version": "2.7.1",
 +            "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
 +            "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
 +            "dev": true
 +        },
 +        "process-nextick-args": {
 +            "version": "2.0.1",
 +            "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
 +            "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
 +            "dev": true
 +        },
 +        "pump": {
 +            "version": "3.0.0",
 +            "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
 +            "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
 +            "dev": true,
 +            "requires": {
 +                "end-of-stream": "^1.1.0",
 +                "once": "^1.3.1"
 +            }
 +        },
 +        "punycode": {
 +            "version": "2.1.1",
 +            "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
 +            "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
 +            "dev": true
 +        },
 +        "qs": {
 +            "version": "6.11.0",
 +            "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
 +            "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
 +            "dev": true,
 +            "requires": {
 +                "side-channel": "^1.0.4"
 +            }
 +        },
 +        "queue-microtask": {
 +            "version": "1.2.3",
 +            "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
 +            "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
 +            "dev": true
 +        },
 +        "rc": {
 +            "version": "1.2.8",
 +            "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
 +            "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
 +            "dev": true,
 +            "requires": {
 +                "deep-extend": "^0.6.0",
 +                "ini": "~1.3.0",
 +                "minimist": "^1.2.0",
 +                "strip-json-comments": "~2.0.1"
 +            },
 +            "dependencies": {
 +                "strip-json-comments": {
 +                    "version": "2.0.1",
 +                    "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
 +                    "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
 +                    "dev": true
 +                }
 +            }
 +        },
 +        "read": {
 +            "version": "1.0.7",
 +            "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz",
 +            "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==",
 +            "dev": true,
 +            "requires": {
 +                "mute-stream": "~0.0.4"
 +            }
 +        },
 +        "readable-stream": {
 +            "version": "2.3.7",
 +            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
 +            "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
 +            "dev": true,
 +            "requires": {
 +                "core-util-is": "~1.0.0",
 +                "inherits": "~2.0.3",
 +                "isarray": "~1.0.0",
 +                "process-nextick-args": "~2.0.0",
 +                "safe-buffer": "~5.1.1",
 +                "string_decoder": "~1.1.1",
 +                "util-deprecate": "~1.0.1"
 +            }
 +        },
 +        "regexpp": {
 +            "version": "3.2.0",
 +            "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
 +            "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
 +            "dev": true
 +        },
 +        "require-directory": {
 +            "version": "2.1.1",
 +            "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
 +            "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="
 +        },
 +        "resolve-from": {
 +            "version": "4.0.0",
 +            "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
 +            "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
 +            "dev": true
 +        },
 +        "reusify": {
 +            "version": "1.0.4",
 +            "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
 +            "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
 +            "dev": true
 +        },
 +        "rimraf": {
 +            "version": "3.0.2",
 +            "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
 +            "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
 +            "dev": true,
 +            "requires": {
 +                "glob": "^7.1.3"
 +            }
 +        },
 +        "robust-predicates": {
 +            "version": "3.0.1",
 +            "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.1.tgz",
 +            "integrity": "sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g=="
 +        },
 +        "run-parallel": {
 +            "version": "1.2.0",
 +            "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
 +            "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
 +            "dev": true,
 +            "requires": {
 +                "queue-microtask": "^1.2.2"
 +            }
 +        },
 +        "rw": {
 +            "version": "1.3.3",
 +            "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
 +            "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ=="
 +        },
 +        "safe-buffer": {
 +            "version": "5.1.2",
 +            "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
 +            "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
 +            "dev": true
 +        },
 +        "safer-buffer": {
 +            "version": "2.1.2",
 +            "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
 +            "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
 +        },
 +        "sax": {
 +            "version": "1.2.4",
 +            "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
 +            "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
 +            "dev": true
 +        },
 +        "semver": {
 +            "version": "7.3.7",
 +            "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
 +            "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
 +            "requires": {
 +                "lru-cache": "^6.0.0"
 +            }
 +        },
 +        "setimmediate": {
 +            "version": "1.0.5",
 +            "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
 +            "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
 +            "dev": true
 +        },
 +        "shebang-command": {
 +            "version": "2.0.0",
 +            "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
 +            "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
 +            "dev": true,
 +            "requires": {
 +                "shebang-regex": "^3.0.0"
 +            }
 +        },
 +        "shebang-regex": {
 +            "version": "3.0.0",
 +            "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
 +            "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
 +            "dev": true
 +        },
 +        "side-channel": {
 +            "version": "1.0.4",
 +            "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
 +            "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
 +            "dev": true,
 +            "requires": {
 +                "call-bind": "^1.0.0",
 +                "get-intrinsic": "^1.0.2",
 +                "object-inspect": "^1.9.0"
 +            }
 +        },
 +        "simple-concat": {
 +            "version": "1.0.1",
 +            "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
 +            "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
 +            "dev": true
 +        },
 +        "simple-get": {
 +            "version": "4.0.1",
 +            "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
 +            "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
 +            "dev": true,
 +            "requires": {
 +                "decompress-response": "^6.0.0",
 +                "once": "^1.3.1",
 +                "simple-concat": "^1.0.0"
 +            }
 +        },
 +        "slash": {
 +            "version": "3.0.0",
 +            "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
 +            "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
 +            "dev": true
 +        },
 +        "string_decoder": {
 +            "version": "1.1.1",
 +            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
 +            "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
 +            "dev": true,
 +            "requires": {
 +                "safe-buffer": "~5.1.0"
 +            }
 +        },
 +        "string-width": {
 +            "version": "4.2.3",
 +            "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
 +            "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
 +            "requires": {
 +                "emoji-regex": "^8.0.0",
 +                "is-fullwidth-code-point": "^3.0.0",
 +                "strip-ansi": "^6.0.1"
 +            }
 +        },
 +        "strip-ansi": {
 +            "version": "6.0.1",
 +            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
 +            "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
 +            "requires": {
 +                "ansi-regex": "^5.0.1"
 +            }
 +        },
 +        "strip-json-comments": {
 +            "version": "3.1.1",
 +            "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
 +            "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
 +            "dev": true
 +        },
 +        "supports-color": {
 +            "version": "7.2.0",
 +            "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
 +            "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
 +            "dev": true,
 +            "requires": {
 +                "has-flag": "^4.0.0"
 +            }
 +        },
 +        "tar-fs": {
 +            "version": "2.1.1",
 +            "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz",
 +            "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==",
 +            "dev": true,
 +            "requires": {
 +                "chownr": "^1.1.1",
 +                "mkdirp-classic": "^0.5.2",
 +                "pump": "^3.0.0",
 +                "tar-stream": "^2.1.4"
 +            }
 +        },
 +        "tar-stream": {
 +            "version": "2.2.0",
 +            "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
 +            "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
 +            "dev": true,
 +            "requires": {
 +                "bl": "^4.0.3",
 +                "end-of-stream": "^1.4.1",
 +                "fs-constants": "^1.0.0",
 +                "inherits": "^2.0.3",
 +                "readable-stream": "^3.1.1"
 +            },
 +            "dependencies": {
 +                "readable-stream": {
 +                    "version": "3.6.0",
 +                    "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
 +                    "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
 +                    "dev": true,
 +                    "requires": {
 +                        "inherits": "^2.0.3",
 +                        "string_decoder": "^1.1.1",
 +                        "util-deprecate": "^1.0.1"
 +                    }
 +                }
 +            }
 +        },
 +        "text-table": {
 +            "version": "0.2.0",
 +            "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
 +            "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
 +            "dev": true
 +        },
 +        "tmp": {
 +            "version": "0.2.1",
 +            "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
 +            "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==",
 +            "dev": true,
 +            "requires": {
 +                "rimraf": "^3.0.0"
 +            }
 +        },
 +        "to-regex-range": {
 +            "version": "5.0.1",
 +            "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
 +            "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
 +            "dev": true,
 +            "requires": {
 +                "is-number": "^7.0.0"
 +            }
 +        },
 +        "traverse": {
 +            "version": "0.3.9",
 +            "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz",
 +            "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==",
 +            "dev": true
 +        },
 +        "tslib": {
 +            "version": "2.4.0",
 +            "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
 +            "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==",
 +            "dev": true
 +        },
 +        "tsutils": {
 +            "version": "3.21.0",
 +            "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
 +            "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==",
 +            "dev": true,
 +            "requires": {
 +                "tslib": "^1.8.1"
 +            },
 +            "dependencies": {
 +                "tslib": {
 +                    "version": "1.14.1",
 +                    "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
 +                    "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
 +                    "dev": true
 +                }
 +            }
 +        },
 +        "tunnel": {
 +            "version": "0.0.6",
 +            "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
 +            "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
 +            "dev": true
 +        },
 +        "tunnel-agent": {
 +            "version": "0.6.0",
 +            "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
 +            "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
 +            "dev": true,
 +            "requires": {
 +                "safe-buffer": "^5.0.1"
 +            }
 +        },
 +        "type-check": {
 +            "version": "0.4.0",
 +            "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
 +            "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
 +            "dev": true,
 +            "requires": {
 +                "prelude-ls": "^1.2.1"
 +            }
 +        },
 +        "type-fest": {
 +            "version": "0.20.2",
 +            "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
 +            "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
 +            "dev": true
 +        },
 +        "typed-rest-client": {
 +            "version": "1.8.9",
 +            "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.9.tgz",
 +            "integrity": "sha512-uSmjE38B80wjL85UFX3sTYEUlvZ1JgCRhsWj/fJ4rZ0FqDUFoIuodtiVeE+cUqiVTOKPdKrp/sdftD15MDek6g==",
 +            "dev": true,
 +            "requires": {
 +                "qs": "^6.9.1",
 +                "tunnel": "0.0.6",
 +                "underscore": "^1.12.1"
 +            }
 +        },
 +        "typescript": {
 +            "version": "4.7.4",
 +            "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
 +            "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==",
 +            "dev": true
 +        },
 +        "uc.micro": {
 +            "version": "1.0.6",
 +            "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
 +            "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==",
 +            "dev": true
 +        },
 +        "underscore": {
 +            "version": "1.13.4",
 +            "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.4.tgz",
 +            "integrity": "sha512-BQFnUDuAQ4Yf/cYY5LNrK9NCJFKriaRbD9uR1fTeXnBeoa97W0i41qkZfGO9pSo8I5KzjAcSY2XYtdf0oKd7KQ==",
 +            "dev": true
 +        },
 +        "unzipper": {
 +            "version": "0.10.11",
 +            "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.11.tgz",
 +            "integrity": "sha512-+BrAq2oFqWod5IESRjL3S8baohbevGcVA+teAIOYWM3pDVdseogqbzhhvvmiyQrUNKFUnDMtELW3X8ykbyDCJw==",
 +            "dev": true,
 +            "requires": {
 +                "big-integer": "^1.6.17",
 +                "binary": "~0.3.0",
 +                "bluebird": "~3.4.1",
 +                "buffer-indexof-polyfill": "~1.0.0",
 +                "duplexer2": "~0.1.4",
 +                "fstream": "^1.0.12",
 +                "graceful-fs": "^4.2.2",
 +                "listenercount": "~1.0.1",
 +                "readable-stream": "~2.3.6",
 +                "setimmediate": "~1.0.4"
 +            }
 +        },
 +        "uri-js": {
 +            "version": "4.4.1",
 +            "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
 +            "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
 +            "dev": true,
 +            "requires": {
 +                "punycode": "^2.1.0"
 +            }
 +        },
 +        "url-join": {
 +            "version": "4.0.1",
 +            "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz",
 +            "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==",
 +            "dev": true
 +        },
 +        "util-deprecate": {
 +            "version": "1.0.2",
 +            "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
 +            "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
 +            "dev": true
 +        },
 +        "v8-compile-cache": {
 +            "version": "2.3.0",
 +            "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
 +            "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==",
 +            "dev": true
 +        },
 +        "vsce": {
 +            "version": "2.9.2",
 +            "resolved": "https://registry.npmjs.org/vsce/-/vsce-2.9.2.tgz",
 +            "integrity": "sha512-xyLqL4U82BilUX1t6Ym2opQEa2tLGWYjbgB7+ETeNVXlIJz5sWBJjQJSYJVFOKJSpiOtQclolu88cj7oY6vvPQ==",
 +            "dev": true,
 +            "requires": {
 +                "azure-devops-node-api": "^11.0.1",
 +                "chalk": "^2.4.2",
 +                "cheerio": "^1.0.0-rc.9",
 +                "commander": "^6.1.0",
 +                "glob": "^7.0.6",
 +                "hosted-git-info": "^4.0.2",
 +                "keytar": "^7.7.0",
 +                "leven": "^3.1.0",
 +                "markdown-it": "^12.3.2",
 +                "mime": "^1.3.4",
 +                "minimatch": "^3.0.3",
 +                "parse-semver": "^1.1.1",
 +                "read": "^1.0.7",
 +                "semver": "^5.1.0",
 +                "tmp": "^0.2.1",
 +                "typed-rest-client": "^1.8.4",
 +                "url-join": "^4.0.1",
 +                "xml2js": "^0.4.23",
 +                "yauzl": "^2.3.1",
 +                "yazl": "^2.2.2"
 +            },
 +            "dependencies": {
 +                "ansi-styles": {
 +                    "version": "3.2.1",
 +                    "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
 +                    "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
 +                    "dev": true,
 +                    "requires": {
 +                        "color-convert": "^1.9.0"
 +                    }
 +                },
 +                "chalk": {
 +                    "version": "2.4.2",
 +                    "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
 +                    "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
 +                    "dev": true,
 +                    "requires": {
 +                        "ansi-styles": "^3.2.1",
 +                        "escape-string-regexp": "^1.0.5",
 +                        "supports-color": "^5.3.0"
 +                    }
 +                },
 +                "color-convert": {
 +                    "version": "1.9.3",
 +                    "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
 +                    "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
 +                    "dev": true,
 +                    "requires": {
 +                        "color-name": "1.1.3"
 +                    }
 +                },
 +                "color-name": {
 +                    "version": "1.1.3",
 +                    "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
 +                    "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
 +                    "dev": true
 +                },
 +                "commander": {
 +                    "version": "6.2.1",
 +                    "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz",
 +                    "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==",
 +                    "dev": true
 +                },
 +                "escape-string-regexp": {
 +                    "version": "1.0.5",
 +                    "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
 +                    "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
 +                    "dev": true
 +                },
 +                "has-flag": {
 +                    "version": "3.0.0",
 +                    "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
 +                    "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
 +                    "dev": true
 +                },
 +                "semver": {
 +                    "version": "5.7.1",
 +                    "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
 +                    "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
 +                    "dev": true
 +                },
 +                "supports-color": {
 +                    "version": "5.5.0",
 +                    "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
 +                    "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
 +                    "dev": true,
 +                    "requires": {
 +                        "has-flag": "^3.0.0"
 +                    }
 +                }
 +            }
 +        },
 +        "vscode-jsonrpc": {
 +            "version": "8.0.2",
 +            "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.0.2.tgz",
 +            "integrity": "sha512-RY7HwI/ydoC1Wwg4gJ3y6LpU9FJRZAUnTYMXthqhFXXu77ErDd/xkREpGuk4MyYkk4a+XDWAMqe0S3KkelYQEQ=="
 +        },
 +        "vscode-languageclient": {
 +            "version": "8.0.2",
 +            "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-8.0.2.tgz",
 +            "integrity": "sha512-lHlthJtphG9gibGb/y72CKqQUxwPsMXijJVpHEC2bvbFqxmkj9LwQ3aGU9dwjBLqsX1S4KjShYppLvg1UJDF/Q==",
 +            "requires": {
 +                "minimatch": "^3.0.4",
 +                "semver": "^7.3.5",
 +                "vscode-languageserver-protocol": "3.17.2"
 +            }
 +        },
 +        "vscode-languageserver-protocol": {
 +            "version": "3.17.2",
 +            "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.2.tgz",
 +            "integrity": "sha512-8kYisQ3z/SQ2kyjlNeQxbkkTNmVFoQCqkmGrzLH6A9ecPlgTbp3wDTnUNqaUxYr4vlAcloxx8zwy7G5WdguYNg==",
 +            "requires": {
 +                "vscode-jsonrpc": "8.0.2",
 +                "vscode-languageserver-types": "3.17.2"
 +            }
 +        },
 +        "vscode-languageserver-types": {
 +            "version": "3.17.2",
 +            "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.2.tgz",
 +            "integrity": "sha512-zHhCWatviizPIq9B7Vh9uvrH6x3sK8itC84HkamnBWoDFJtzBf7SWlpLCZUit72b3os45h6RWQNC9xHRDF8dRA=="
 +        },
 +        "which": {
 +            "version": "2.0.2",
 +            "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
 +            "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
 +            "dev": true,
 +            "requires": {
 +                "isexe": "^2.0.0"
 +            }
 +        },
 +        "word-wrap": {
 +            "version": "1.2.3",
 +            "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
 +            "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
 +            "dev": true
 +        },
 +        "wrap-ansi": {
 +            "version": "7.0.0",
 +            "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
 +            "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
 +            "requires": {
 +                "ansi-styles": "^4.0.0",
 +                "string-width": "^4.1.0",
 +                "strip-ansi": "^6.0.0"
 +            }
 +        },
 +        "wrappy": {
 +            "version": "1.0.2",
 +            "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
 +            "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
 +            "dev": true
 +        },
 +        "xml2js": {
 +            "version": "0.4.23",
 +            "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",
 +            "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==",
 +            "dev": true,
 +            "requires": {
 +                "sax": ">=0.6.0",
 +                "xmlbuilder": "~11.0.0"
 +            }
 +        },
 +        "xmlbuilder": {
 +            "version": "11.0.1",
 +            "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
 +            "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
 +            "dev": true
 +        },
 +        "y18n": {
 +            "version": "5.0.8",
 +            "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
 +            "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="
 +        },
 +        "yallist": {
 +            "version": "4.0.0",
 +            "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
 +            "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
 +        },
 +        "yargs": {
 +            "version": "17.5.1",
 +            "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz",
 +            "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==",
 +            "requires": {
 +                "cliui": "^7.0.2",
 +                "escalade": "^3.1.1",
 +                "get-caller-file": "^2.0.5",
 +                "require-directory": "^2.1.1",
 +                "string-width": "^4.2.3",
 +                "y18n": "^5.0.5",
 +                "yargs-parser": "^21.0.0"
 +            }
 +        },
 +        "yargs-parser": {
 +            "version": "21.0.1",
 +            "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz",
 +            "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg=="
 +        },
 +        "yauzl": {
 +            "version": "2.10.0",
 +            "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
 +            "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
 +            "dev": true,
 +            "requires": {
 +                "buffer-crc32": "~0.2.3",
 +                "fd-slicer": "~1.1.0"
 +            }
 +        },
 +        "yazl": {
 +            "version": "2.5.1",
 +            "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz",
 +            "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==",
 +            "dev": true,
 +            "requires": {
 +                "buffer-crc32": "~0.2.3"
 +            }
 +        }
 +    }
 +}
index 6771cad28a792650729ef68041ab0c22a001b370,0000000000000000000000000000000000000000..1a97a9c0893752f5ddfe357086ca0c8326ac61db
mode 100644,000000..100644
--- /dev/null
@@@ -1,1748 -1,0 +1,1753 @@@
-         "ovsx": "^0.5.1",
 +{
 +    "name": "rust-analyzer",
 +    "displayName": "rust-analyzer",
 +    "description": "Rust language support for Visual Studio Code",
 +    "private": true,
 +    "icon": "icon.png",
 +    "version": "0.5.0-dev",
 +    "releaseTag": null,
 +    "publisher": "rust-lang",
 +    "repository": {
 +        "url": "https://github.com/rust-lang/rust-analyzer.git",
 +        "type": "git"
 +    },
 +    "homepage": "https://rust-analyzer.github.io/",
 +    "license": "MIT OR Apache-2.0",
 +    "keywords": [
 +        "rust"
 +    ],
 +    "categories": [
 +        "Programming Languages"
 +    ],
 +    "engines": {
 +        "vscode": "^1.66.0"
 +    },
 +    "enabledApiProposals": [],
 +    "scripts": {
 +        "vscode:prepublish": "npm run build-base -- --minify",
 +        "package": "vsce package -o rust-analyzer.vsix",
 +        "build-base": "esbuild ./src/main.ts --bundle --outfile=out/main.js --external:vscode --format=cjs --platform=node --target=node16",
 +        "build": "npm run build-base -- --sourcemap",
 +        "watch": "npm run build-base -- --sourcemap --watch",
 +        "lint": "prettier --check . && eslint -c .eslintrc.js --ext ts ./src ./tests",
 +        "fix": "prettier --write . && eslint -c .eslintrc.js --ext ts ./src ./tests --fix",
 +        "pretest": "tsc && npm run build",
 +        "test": "cross-env TEST_VARIABLE=test node ./out/tests/runTests.js"
 +    },
 +    "dependencies": {
 +        "d3": "^7.6.1",
 +        "d3-graphviz": "^4.1.1",
 +        "vscode-languageclient": "^8.0.2"
 +    },
 +    "devDependencies": {
 +        "@types/node": "~16.11.7",
 +        "@types/vscode": "~1.66.0",
 +        "@typescript-eslint/eslint-plugin": "^5.30.5",
 +        "@typescript-eslint/parser": "^5.30.5",
 +        "@vscode/test-electron": "^2.1.5",
 +        "cross-env": "^7.0.3",
 +        "esbuild": "^0.14.48",
 +        "eslint": "^8.19.0",
 +        "eslint-config-prettier": "^8.5.0",
-                 "category": "rust-analyzer"
++        "ovsx": "^0.5.2",
 +        "prettier": "^2.7.1",
 +        "tslib": "^2.4.0",
 +        "typescript": "^4.7.4",
 +        "vsce": "^2.9.2"
 +    },
 +    "activationEvents": [
 +        "onLanguage:rust",
 +        "onCommand:rust-analyzer.analyzerStatus",
 +        "onCommand:rust-analyzer.memoryUsage",
 +        "onCommand:rust-analyzer.reloadWorkspace",
 +        "onCommand:rust-analyzer.startServer",
 +        "workspaceContains:*/Cargo.toml",
 +        "workspaceContains:*/rust-project.json"
 +    ],
 +    "main": "./out/main",
 +    "contributes": {
 +        "taskDefinitions": [
 +            {
 +                "type": "cargo",
 +                "required": [
 +                    "command"
 +                ],
 +                "properties": {
 +                    "label": {
 +                        "type": "string"
 +                    },
 +                    "command": {
 +                        "type": "string"
 +                    },
 +                    "args": {
 +                        "type": "array",
 +                        "items": {
 +                            "type": "string"
 +                        }
 +                    },
 +                    "env": {
 +                        "type": "object",
 +                        "patternProperties": {
 +                            ".+": {
 +                                "type": "string"
 +                            }
 +                        }
 +                    }
 +                }
 +            }
 +        ],
 +        "commands": [
 +            {
 +                "command": "rust-analyzer.syntaxTree",
 +                "title": "Show Syntax Tree",
-                 "category": "rust-analyzer"
++                "category": "rust-analyzer (debug command)"
 +            },
 +            {
 +                "command": "rust-analyzer.viewHir",
 +                "title": "View Hir",
-                 "category": "rust-analyzer"
++                "category": "rust-analyzer (debug command)"
 +            },
 +            {
 +                "command": "rust-analyzer.viewFileText",
 +                "title": "View File Text (as seen by the server)",
-                 "category": "rust-analyzer"
++                "category": "rust-analyzer (debug command)"
 +            },
 +            {
 +                "command": "rust-analyzer.viewItemTree",
 +                "title": "Debug ItemTree",
-             {
-                 "command": "rust-analyzer.memoryUsage",
-                 "title": "Memory Usage (Clears Database)",
-                 "category": "rust-analyzer"
-             },
-             {
-                 "command": "rust-analyzer.shuffleCrateGraph",
-                 "title": "Shuffle Crate Graph",
-                 "category": "rust-analyzer"
-             },
++                "category": "rust-analyzer (debug command)"
++            },
++            {
++                "command": "rust-analyzer.shuffleCrateGraph",
++                "title": "Shuffle Crate Graph",
++                "category": "rust-analyzer (debug command)"
++            },
++            {
++                "command": "rust-analyzer.memoryUsage",
++                "title": "Memory Usage (Clears Database)",
++                "category": "rust-analyzer (debug command)"
 +            },
 +            {
 +                "command": "rust-analyzer.viewCrateGraph",
 +                "title": "View Crate Graph",
 +                "category": "rust-analyzer"
 +            },
 +            {
 +                "command": "rust-analyzer.viewFullCrateGraph",
 +                "title": "View Crate Graph (Full)",
 +                "category": "rust-analyzer"
 +            },
 +            {
 +                "command": "rust-analyzer.expandMacro",
 +                "title": "Expand macro recursively",
 +                "category": "rust-analyzer"
 +            },
 +            {
 +                "command": "rust-analyzer.matchingBrace",
 +                "title": "Find matching brace",
 +                "category": "rust-analyzer"
 +            },
 +            {
 +                "command": "rust-analyzer.parentModule",
 +                "title": "Locate parent module",
 +                "category": "rust-analyzer"
 +            },
 +            {
 +                "command": "rust-analyzer.joinLines",
 +                "title": "Join lines",
 +                "category": "rust-analyzer"
 +            },
 +            {
 +                "command": "rust-analyzer.run",
 +                "title": "Run",
 +                "category": "rust-analyzer"
 +            },
 +            {
 +                "command": "rust-analyzer.copyRunCommandLine",
 +                "title": "Copy Run Command Line",
 +                "category": "rust-analyzer"
 +            },
 +            {
 +                "command": "rust-analyzer.debug",
 +                "title": "Debug",
 +                "category": "rust-analyzer"
 +            },
 +            {
 +                "command": "rust-analyzer.newDebugConfig",
 +                "title": "Generate launch configuration",
 +                "category": "rust-analyzer"
 +            },
 +            {
 +                "command": "rust-analyzer.analyzerStatus",
 +                "title": "Status",
 +                "category": "rust-analyzer"
 +            },
 +            {
 +                "command": "rust-analyzer.reloadWorkspace",
 +                "title": "Reload workspace",
 +                "category": "rust-analyzer"
 +            },
 +            {
 +                "command": "rust-analyzer.reload",
 +                "title": "Restart server",
 +                "category": "rust-analyzer"
 +            },
 +            {
 +                "command": "rust-analyzer.startServer",
 +                "title": "Start server",
 +                "category": "rust-analyzer"
 +            },
 +            {
 +                "command": "rust-analyzer.stopServer",
 +                "title": "Stop server",
 +                "category": "rust-analyzer"
 +            },
 +            {
 +                "command": "rust-analyzer.onEnter",
 +                "title": "Enhanced enter key",
 +                "category": "rust-analyzer"
 +            },
 +            {
 +                "command": "rust-analyzer.ssr",
 +                "title": "Structural Search Replace",
 +                "category": "rust-analyzer"
 +            },
 +            {
 +                "command": "rust-analyzer.serverVersion",
 +                "title": "Show RA Version",
 +                "category": "rust-analyzer"
 +            },
 +            {
 +                "command": "rust-analyzer.openDocs",
 +                "title": "Open docs under cursor",
 +                "category": "rust-analyzer"
 +            },
 +            {
 +                "command": "rust-analyzer.openCargoToml",
 +                "title": "Open Cargo.toml",
 +                "category": "rust-analyzer"
 +            },
 +            {
 +                "command": "rust-analyzer.peekTests",
 +                "title": "Peek related tests",
 +                "category": "rust-analyzer"
 +            },
 +            {
 +                "command": "rust-analyzer.moveItemUp",
 +                "title": "Move item up",
 +                "category": "rust-analyzer"
 +            },
 +            {
 +                "command": "rust-analyzer.moveItemDown",
 +                "title": "Move item down",
 +                "category": "rust-analyzer"
 +            },
 +            {
 +                "command": "rust-analyzer.cancelFlycheck",
 +                "title": "Cancel running flychecks",
 +                "category": "rust-analyzer"
 +            }
 +        ],
 +        "keybindings": [
 +            {
 +                "command": "rust-analyzer.parentModule",
 +                "key": "ctrl+shift+u",
 +                "when": "editorTextFocus && editorLangId == rust"
 +            },
 +            {
 +                "command": "rust-analyzer.matchingBrace",
 +                "key": "ctrl+shift+m",
 +                "when": "editorTextFocus && editorLangId == rust"
 +            },
 +            {
 +                "command": "rust-analyzer.joinLines",
 +                "key": "ctrl+shift+j",
 +                "when": "editorTextFocus && editorLangId == rust"
 +            }
 +        ],
 +        "configuration": {
 +            "type": "object",
 +            "title": "rust-analyzer",
 +            "properties": {
 +                "rust-analyzer.cargoRunner": {
 +                    "type": [
 +                        "null",
 +                        "string"
 +                    ],
 +                    "default": null,
 +                    "description": "Custom cargo runner extension ID."
 +                },
 +                "rust-analyzer.runnableEnv": {
 +                    "anyOf": [
 +                        {
 +                            "type": "null"
 +                        },
 +                        {
 +                            "type": "array",
 +                            "items": {
 +                                "type": "object",
 +                                "properties": {
 +                                    "mask": {
 +                                        "type": "string",
 +                                        "description": "Runnable name mask"
 +                                    },
 +                                    "env": {
 +                                        "type": "object",
 +                                        "description": "Variables in form of { \"key\": \"value\"}"
 +                                    }
 +                                }
 +                            }
 +                        },
 +                        {
 +                            "type": "object",
 +                            "description": "Variables in form of { \"key\": \"value\"}"
 +                        }
 +                    ],
 +                    "default": null,
 +                    "markdownDescription": "Environment variables passed to the runnable launched using `Test` or `Debug` lens or `rust-analyzer.run` command."
 +                },
 +                "rust-analyzer.server.path": {
 +                    "type": [
 +                        "null",
 +                        "string"
 +                    ],
 +                    "scope": "machine-overridable",
 +                    "default": null,
 +                    "markdownDescription": "Path to rust-analyzer executable (points to bundled binary by default)."
 +                },
 +                "rust-analyzer.server.extraEnv": {
 +                    "type": [
 +                        "null",
 +                        "object"
 +                    ],
 +                    "additionalProperties": {
 +                        "type": [
 +                            "string",
 +                            "number"
 +                        ]
 +                    },
 +                    "default": null,
 +                    "markdownDescription": "Extra environment variables that will be passed to the rust-analyzer executable. Useful for passing e.g. `RA_LOG` for debugging."
 +                },
 +                "rust-analyzer.trace.server": {
 +                    "type": "string",
 +                    "scope": "window",
 +                    "enum": [
 +                        "off",
 +                        "messages",
 +                        "verbose"
 +                    ],
 +                    "enumDescriptions": [
 +                        "No traces",
 +                        "Error only",
 +                        "Full log"
 +                    ],
 +                    "default": "off",
 +                    "description": "Trace requests to the rust-analyzer (this is usually overly verbose and not recommended for regular users)."
 +                },
 +                "rust-analyzer.trace.extension": {
 +                    "description": "Enable logging of VS Code extensions itself.",
 +                    "type": "boolean",
 +                    "default": false
 +                },
 +                "rust-analyzer.debug.engine": {
 +                    "type": "string",
 +                    "enum": [
 +                        "auto",
 +                        "vadimcn.vscode-lldb",
 +                        "ms-vscode.cpptools"
 +                    ],
 +                    "default": "auto",
 +                    "description": "Preferred debug engine.",
 +                    "markdownEnumDescriptions": [
 +                        "First try to use [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb), if it's not installed try to use [MS C++ tools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools).",
 +                        "Use [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb)",
 +                        "Use [MS C++ tools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools)"
 +                    ]
 +                },
 +                "rust-analyzer.debug.sourceFileMap": {
 +                    "type": [
 +                        "object",
 +                        "string"
 +                    ],
 +                    "const": "auto",
 +                    "description": "Optional source file mappings passed to the debug engine.",
 +                    "default": {
 +                        "/rustc/<id>": "${env:USERPROFILE}/.rustup/toolchains/<toolchain-id>/lib/rustlib/src/rust"
 +                    }
 +                },
 +                "rust-analyzer.debug.openDebugPane": {
 +                    "markdownDescription": "Whether to open up the `Debug Panel` on debugging start.",
 +                    "type": "boolean",
 +                    "default": false
 +                },
 +                "rust-analyzer.debug.engineSettings": {
 +                    "type": "object",
 +                    "default": {},
 +                    "markdownDescription": "Optional settings passed to the debug engine. Example: `{ \"lldb\": { \"terminal\":\"external\"} }`"
 +                },
 +                "rust-analyzer.restartServerOnConfigChange": {
 +                    "markdownDescription": "Whether to restart the server automatically when certain settings that require a restart are changed.",
 +                    "default": false,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.typing.continueCommentsOnNewline": {
 +                    "markdownDescription": "Whether to prefix newlines after comments with the corresponding comment prefix.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "$generated-start": {},
++                "rust-analyzer.assist.emitMustUse": {
++                    "markdownDescription": "Whether to insert #[must_use] when generating `as_` methods\nfor enum variants.",
++                    "default": false,
++                    "type": "boolean"
++                },
 +                "rust-analyzer.assist.expressionFillDefault": {
 +                    "markdownDescription": "Placeholder expression to use for missing expressions in assists.",
 +                    "default": "todo",
 +                    "type": "string",
 +                    "enum": [
 +                        "todo",
 +                        "default"
 +                    ],
 +                    "enumDescriptions": [
 +                        "Fill missing expressions with the `todo` macro",
 +                        "Fill missing expressions with reasonable defaults, `new` or `default` constructors."
 +                    ]
 +                },
 +                "rust-analyzer.cachePriming.enable": {
 +                    "markdownDescription": "Warm up caches on project load.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.cachePriming.numThreads": {
 +                    "markdownDescription": "How many worker threads to handle priming caches. The default `0` means to pick automatically.",
 +                    "default": 0,
 +                    "type": "number",
 +                    "minimum": 0,
 +                    "maximum": 255
 +                },
 +                "rust-analyzer.cargo.autoreload": {
 +                    "markdownDescription": "Automatically refresh project info via `cargo metadata` on\n`Cargo.toml` or `.cargo/config.toml` changes.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.cargo.buildScripts.enable": {
 +                    "markdownDescription": "Run build scripts (`build.rs`) for more precise code analysis.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.cargo.buildScripts.invocationLocation": {
 +                    "markdownDescription": "Specifies the working directory for running build scripts.\n- \"workspace\": run build scripts for a workspace in the workspace's root directory.\n    This is incompatible with `#rust-analyzer.cargo.buildScripts.invocationStrategy#` set to `once`.\n- \"root\": run build scripts in the project's root directory.\nThis config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`\nis set.",
 +                    "default": "workspace",
 +                    "type": "string",
 +                    "enum": [
 +                        "workspace",
 +                        "root"
 +                    ],
 +                    "enumDescriptions": [
 +                        "The command will be executed in the corresponding workspace root.",
 +                        "The command will be executed in the project root."
 +                    ]
 +                },
 +                "rust-analyzer.cargo.buildScripts.invocationStrategy": {
 +                    "markdownDescription": "Specifies the invocation strategy to use when running the build scripts command.\nIf `per_workspace` is set, the command will be executed for each workspace.\nIf `once` is set, the command will be executed once.\nThis config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`\nis set.",
 +                    "default": "per_workspace",
 +                    "type": "string",
 +                    "enum": [
 +                        "per_workspace",
 +                        "once"
 +                    ],
 +                    "enumDescriptions": [
 +                        "The command will be executed for each workspace.",
 +                        "The command will be executed once."
 +                    ]
 +                },
 +                "rust-analyzer.cargo.buildScripts.overrideCommand": {
 +                    "markdownDescription": "Override the command rust-analyzer uses to run build scripts and\nbuild procedural macros. The command is required to output json\nand should therefore include `--message-format=json` or a similar\noption.\n\nBy default, a cargo invocation will be constructed for the configured\ntargets and features, with the following base command line:\n\n```bash\ncargo check --quiet --workspace --message-format=json --all-targets\n```\n.",
 +                    "default": null,
 +                    "type": [
 +                        "null",
 +                        "array"
 +                    ],
 +                    "items": {
 +                        "type": "string"
 +                    }
 +                },
 +                "rust-analyzer.cargo.buildScripts.useRustcWrapper": {
 +                    "markdownDescription": "Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to\navoid checking unnecessary things.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.cargo.extraEnv": {
 +                    "markdownDescription": "Extra environment variables that will be set when running cargo, rustc\nor other commands within the workspace. Useful for setting RUSTFLAGS.",
 +                    "default": {},
 +                    "type": "object"
 +                },
 +                "rust-analyzer.cargo.features": {
 +                    "markdownDescription": "List of features to activate.\n\nSet this to `\"all\"` to pass `--all-features` to cargo.",
 +                    "default": [],
 +                    "anyOf": [
 +                        {
 +                            "type": "string",
 +                            "enum": [
 +                                "all"
 +                            ],
 +                            "enumDescriptions": [
 +                                "Pass `--all-features` to cargo"
 +                            ]
 +                        },
 +                        {
 +                            "type": "array",
 +                            "items": {
 +                                "type": "string"
 +                            }
 +                        }
 +                    ]
 +                },
 +                "rust-analyzer.cargo.noDefaultFeatures": {
 +                    "markdownDescription": "Whether to pass `--no-default-features` to cargo.",
 +                    "default": false,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.cargo.sysroot": {
 +                    "markdownDescription": "Relative path to the sysroot, or \"discover\" to try to automatically find it via\n\"rustc --print sysroot\".\n\nUnsetting this disables sysroot loading.\n\nThis option does not take effect until rust-analyzer is restarted.",
 +                    "default": "discover",
 +                    "type": [
 +                        "null",
 +                        "string"
 +                    ]
 +                },
 +                "rust-analyzer.cargo.target": {
 +                    "markdownDescription": "Compilation target override (target triple).",
 +                    "default": null,
 +                    "type": [
 +                        "null",
 +                        "string"
 +                    ]
 +                },
 +                "rust-analyzer.cargo.unsetTest": {
 +                    "markdownDescription": "Unsets `#[cfg(test)]` for the specified crates.",
 +                    "default": [
 +                        "core"
 +                    ],
 +                    "type": "array",
 +                    "items": {
 +                        "type": "string"
 +                    }
 +                },
 +                "rust-analyzer.checkOnSave.allTargets": {
 +                    "markdownDescription": "Check all targets and tests (`--all-targets`).",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.checkOnSave.command": {
 +                    "markdownDescription": "Cargo command to use for `cargo check`.",
 +                    "default": "check",
 +                    "type": "string"
 +                },
 +                "rust-analyzer.checkOnSave.enable": {
 +                    "markdownDescription": "Run specified `cargo check` command for diagnostics on save.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.checkOnSave.extraArgs": {
 +                    "markdownDescription": "Extra arguments for `cargo check`.",
 +                    "default": [],
 +                    "type": "array",
 +                    "items": {
 +                        "type": "string"
 +                    }
 +                },
 +                "rust-analyzer.checkOnSave.extraEnv": {
 +                    "markdownDescription": "Extra environment variables that will be set when running `cargo check`.\nExtends `#rust-analyzer.cargo.extraEnv#`.",
 +                    "default": {},
 +                    "type": "object"
 +                },
 +                "rust-analyzer.checkOnSave.features": {
 +                    "markdownDescription": "List of features to activate. Defaults to\n`#rust-analyzer.cargo.features#`.\n\nSet to `\"all\"` to pass `--all-features` to Cargo.",
 +                    "default": null,
 +                    "anyOf": [
 +                        {
 +                            "type": "string",
 +                            "enum": [
 +                                "all"
 +                            ],
 +                            "enumDescriptions": [
 +                                "Pass `--all-features` to cargo"
 +                            ]
 +                        },
 +                        {
 +                            "type": "array",
 +                            "items": {
 +                                "type": "string"
 +                            }
 +                        },
 +                        {
 +                            "type": "null"
 +                        }
 +                    ]
 +                },
 +                "rust-analyzer.checkOnSave.invocationLocation": {
 +                    "markdownDescription": "Specifies the working directory for running checks.\n- \"workspace\": run checks for workspaces in the corresponding workspaces' root directories.\n    This falls back to \"root\" if `#rust-analyzer.cargo.checkOnSave.invocationStrategy#` is set to `once`.\n- \"root\": run checks in the project's root directory.\nThis config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`\nis set.",
 +                    "default": "workspace",
 +                    "type": "string",
 +                    "enum": [
 +                        "workspace",
 +                        "root"
 +                    ],
 +                    "enumDescriptions": [
 +                        "The command will be executed in the corresponding workspace root.",
 +                        "The command will be executed in the project root."
 +                    ]
 +                },
 +                "rust-analyzer.checkOnSave.invocationStrategy": {
 +                    "markdownDescription": "Specifies the invocation strategy to use when running the checkOnSave command.\nIf `per_workspace` is set, the command will be executed for each workspace.\nIf `once` is set, the command will be executed once.\nThis config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`\nis set.",
 +                    "default": "per_workspace",
 +                    "type": "string",
 +                    "enum": [
 +                        "per_workspace",
 +                        "once"
 +                    ],
 +                    "enumDescriptions": [
 +                        "The command will be executed for each workspace.",
 +                        "The command will be executed once."
 +                    ]
 +                },
 +                "rust-analyzer.checkOnSave.noDefaultFeatures": {
 +                    "markdownDescription": "Whether to pass `--no-default-features` to Cargo. Defaults to\n`#rust-analyzer.cargo.noDefaultFeatures#`.",
 +                    "default": null,
 +                    "type": [
 +                        "null",
 +                        "boolean"
 +                    ]
 +                },
 +                "rust-analyzer.checkOnSave.overrideCommand": {
 +                    "markdownDescription": "Override the command rust-analyzer uses instead of `cargo check` for\ndiagnostics on save. The command is required to output json and\nshould therefor include `--message-format=json` or a similar option.\n\nIf you're changing this because you're using some tool wrapping\nCargo, you might also want to change\n`#rust-analyzer.cargo.buildScripts.overrideCommand#`.\n\nIf there are multiple linked projects, this command is invoked for\neach of them, with the working directory being the project root\n(i.e., the folder containing the `Cargo.toml`).\n\nAn example command would be:\n\n```bash\ncargo check --workspace --message-format=json --all-targets\n```\n.",
 +                    "default": null,
 +                    "type": [
 +                        "null",
 +                        "array"
 +                    ],
 +                    "items": {
 +                        "type": "string"
 +                    }
 +                },
 +                "rust-analyzer.checkOnSave.target": {
 +                    "markdownDescription": "Check for a specific target. Defaults to\n`#rust-analyzer.cargo.target#`.",
 +                    "default": null,
 +                    "type": [
 +                        "null",
 +                        "string"
 +                    ]
 +                },
 +                "rust-analyzer.completion.autoimport.enable": {
 +                    "markdownDescription": "Toggles the additional completions that automatically add imports when completed.\nNote that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.completion.autoself.enable": {
 +                    "markdownDescription": "Toggles the additional completions that automatically show method calls and field accesses\nwith `self` prefixed to them when inside a method.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.completion.callable.snippets": {
 +                    "markdownDescription": "Whether to add parenthesis and argument snippets when completing function.",
 +                    "default": "fill_arguments",
 +                    "type": "string",
 +                    "enum": [
 +                        "fill_arguments",
 +                        "add_parentheses",
 +                        "none"
 +                    ],
 +                    "enumDescriptions": [
 +                        "Add call parentheses and pre-fill arguments.",
 +                        "Add call parentheses.",
 +                        "Do no snippet completions for callables."
 +                    ]
 +                },
 +                "rust-analyzer.completion.postfix.enable": {
 +                    "markdownDescription": "Whether to show postfix snippets like `dbg`, `if`, `not`, etc.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.completion.privateEditable.enable": {
 +                    "markdownDescription": "Enables completions of private items and fields that are defined in the current workspace even if they are not visible at the current position.",
 +                    "default": false,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.completion.snippets.custom": {
 +                    "markdownDescription": "Custom completion snippets.",
 +                    "default": {
 +                        "Arc::new": {
 +                            "postfix": "arc",
 +                            "body": "Arc::new(${receiver})",
 +                            "requires": "std::sync::Arc",
 +                            "description": "Put the expression into an `Arc`",
 +                            "scope": "expr"
 +                        },
 +                        "Rc::new": {
 +                            "postfix": "rc",
 +                            "body": "Rc::new(${receiver})",
 +                            "requires": "std::rc::Rc",
 +                            "description": "Put the expression into an `Rc`",
 +                            "scope": "expr"
 +                        },
 +                        "Box::pin": {
 +                            "postfix": "pinbox",
 +                            "body": "Box::pin(${receiver})",
 +                            "requires": "std::boxed::Box",
 +                            "description": "Put the expression into a pinned `Box`",
 +                            "scope": "expr"
 +                        },
 +                        "Ok": {
 +                            "postfix": "ok",
 +                            "body": "Ok(${receiver})",
 +                            "description": "Wrap the expression in a `Result::Ok`",
 +                            "scope": "expr"
 +                        },
 +                        "Err": {
 +                            "postfix": "err",
 +                            "body": "Err(${receiver})",
 +                            "description": "Wrap the expression in a `Result::Err`",
 +                            "scope": "expr"
 +                        },
 +                        "Some": {
 +                            "postfix": "some",
 +                            "body": "Some(${receiver})",
 +                            "description": "Wrap the expression in an `Option::Some`",
 +                            "scope": "expr"
 +                        }
 +                    },
 +                    "type": "object"
 +                },
 +                "rust-analyzer.diagnostics.disabled": {
 +                    "markdownDescription": "List of rust-analyzer diagnostics to disable.",
 +                    "default": [],
 +                    "type": "array",
 +                    "items": {
 +                        "type": "string"
 +                    },
 +                    "uniqueItems": true
 +                },
 +                "rust-analyzer.diagnostics.enable": {
 +                    "markdownDescription": "Whether to show native rust-analyzer diagnostics.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.diagnostics.experimental.enable": {
 +                    "markdownDescription": "Whether to show experimental rust-analyzer diagnostics that might\nhave more false positives than usual.",
 +                    "default": false,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.diagnostics.remapPrefix": {
 +                    "markdownDescription": "Map of prefixes to be substituted when parsing diagnostic file paths.\nThis should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`.",
 +                    "default": {},
 +                    "type": "object"
 +                },
 +                "rust-analyzer.diagnostics.warningsAsHint": {
 +                    "markdownDescription": "List of warnings that should be displayed with hint severity.\n\nThe warnings will be indicated by faded text or three dots in code\nand will not show up in the `Problems Panel`.",
 +                    "default": [],
 +                    "type": "array",
 +                    "items": {
 +                        "type": "string"
 +                    }
 +                },
 +                "rust-analyzer.diagnostics.warningsAsInfo": {
 +                    "markdownDescription": "List of warnings that should be displayed with info severity.\n\nThe warnings will be indicated by a blue squiggly underline in code\nand a blue icon in the `Problems Panel`.",
 +                    "default": [],
 +                    "type": "array",
 +                    "items": {
 +                        "type": "string"
 +                    }
 +                },
 +                "rust-analyzer.files.excludeDirs": {
 +                    "markdownDescription": "These directories will be ignored by rust-analyzer. They are\nrelative to the workspace root, and globs are not supported. You may\nalso need to add the folders to Code's `files.watcherExclude`.",
 +                    "default": [],
 +                    "type": "array",
 +                    "items": {
 +                        "type": "string"
 +                    }
 +                },
 +                "rust-analyzer.files.watcher": {
 +                    "markdownDescription": "Controls file watching implementation.",
 +                    "default": "client",
 +                    "type": "string",
 +                    "enum": [
 +                        "client",
 +                        "server"
 +                    ],
 +                    "enumDescriptions": [
 +                        "Use the client (editor) to watch files for changes",
 +                        "Use server-side file watching"
 +                    ]
 +                },
 +                "rust-analyzer.highlightRelated.breakPoints.enable": {
 +                    "markdownDescription": "Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.highlightRelated.exitPoints.enable": {
 +                    "markdownDescription": "Enables highlighting of all exit points while the cursor is on any `return`, `?`, `fn`, or return type arrow (`->`).",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.highlightRelated.references.enable": {
 +                    "markdownDescription": "Enables highlighting of related references while the cursor is on any identifier.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.highlightRelated.yieldPoints.enable": {
 +                    "markdownDescription": "Enables highlighting of all break points for a loop or block context while the cursor is on any `async` or `await` keywords.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.hover.actions.debug.enable": {
 +                    "markdownDescription": "Whether to show `Debug` action. Only applies when\n`#rust-analyzer.hover.actions.enable#` is set.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.hover.actions.enable": {
 +                    "markdownDescription": "Whether to show HoverActions in Rust files.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.hover.actions.gotoTypeDef.enable": {
 +                    "markdownDescription": "Whether to show `Go to Type Definition` action. Only applies when\n`#rust-analyzer.hover.actions.enable#` is set.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.hover.actions.implementations.enable": {
 +                    "markdownDescription": "Whether to show `Implementations` action. Only applies when\n`#rust-analyzer.hover.actions.enable#` is set.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.hover.actions.references.enable": {
 +                    "markdownDescription": "Whether to show `References` action. Only applies when\n`#rust-analyzer.hover.actions.enable#` is set.",
 +                    "default": false,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.hover.actions.run.enable": {
 +                    "markdownDescription": "Whether to show `Run` action. Only applies when\n`#rust-analyzer.hover.actions.enable#` is set.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.hover.documentation.enable": {
 +                    "markdownDescription": "Whether to show documentation on hover.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.hover.documentation.keywords.enable": {
 +                    "markdownDescription": "Whether to show keyword hover popups. Only applies when\n`#rust-analyzer.hover.documentation.enable#` is set.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.hover.links.enable": {
 +                    "markdownDescription": "Use markdown syntax for links in hover.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.imports.granularity.enforce": {
 +                    "markdownDescription": "Whether to enforce the import granularity setting for all files. If set to false rust-analyzer will try to keep import styles consistent per file.",
 +                    "default": false,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.imports.granularity.group": {
 +                    "markdownDescription": "How imports should be grouped into use statements.",
 +                    "default": "crate",
 +                    "type": "string",
 +                    "enum": [
 +                        "preserve",
 +                        "crate",
 +                        "module",
 +                        "item"
 +                    ],
 +                    "enumDescriptions": [
 +                        "Do not change the granularity of any imports and preserve the original structure written by the developer.",
 +                        "Merge imports from the same crate into a single use statement. Conversely, imports from different crates are split into separate statements.",
 +                        "Merge imports from the same module into a single use statement. Conversely, imports from different modules are split into separate statements.",
 +                        "Flatten imports so that each has its own use statement."
 +                    ]
 +                },
 +                "rust-analyzer.imports.group.enable": {
 +                    "markdownDescription": "Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.imports.merge.glob": {
 +                    "markdownDescription": "Whether to allow import insertion to merge new imports into single path glob imports like `use std::fmt::*;`.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.imports.prefer.no.std": {
 +                    "markdownDescription": "Prefer to unconditionally use imports of the core and alloc crate, over the std crate.",
 +                    "default": false,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.imports.prefix": {
 +                    "markdownDescription": "The path structure for newly inserted paths to use.",
 +                    "default": "plain",
 +                    "type": "string",
 +                    "enum": [
 +                        "plain",
 +                        "self",
 +                        "crate"
 +                    ],
 +                    "enumDescriptions": [
 +                        "Insert import paths relative to the current module, using up to one `super` prefix if the parent module contains the requested item.",
 +                        "Insert import paths relative to the current module, using up to one `super` prefix if the parent module contains the requested item. Prefixes `self` in front of the path if it starts with a module.",
 +                        "Force import paths to be absolute by always starting them with `crate` or the extern crate name they come from."
 +                    ]
 +                },
 +                "rust-analyzer.inlayHints.bindingModeHints.enable": {
 +                    "markdownDescription": "Whether to show inlay type hints for binding modes.",
 +                    "default": false,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.inlayHints.chainingHints.enable": {
 +                    "markdownDescription": "Whether to show inlay type hints for method chains.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.inlayHints.closingBraceHints.enable": {
 +                    "markdownDescription": "Whether to show inlay hints after a closing `}` to indicate what item it belongs to.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.inlayHints.closingBraceHints.minLines": {
 +                    "markdownDescription": "Minimum number of lines required before the `}` until the hint is shown (set to 0 or 1\nto always show them).",
 +                    "default": 25,
 +                    "type": "integer",
 +                    "minimum": 0
 +                },
 +                "rust-analyzer.inlayHints.closureReturnTypeHints.enable": {
 +                    "markdownDescription": "Whether to show inlay type hints for return types of closures.",
 +                    "default": "never",
 +                    "type": "string",
 +                    "enum": [
 +                        "always",
 +                        "never",
 +                        "with_block"
 +                    ],
 +                    "enumDescriptions": [
 +                        "Always show type hints for return types of closures.",
 +                        "Never show type hints for return types of closures.",
 +                        "Only show type hints for return types of closures with blocks."
 +                    ]
 +                },
 +                "rust-analyzer.inlayHints.lifetimeElisionHints.enable": {
 +                    "markdownDescription": "Whether to show inlay type hints for elided lifetimes in function signatures.",
 +                    "default": "never",
 +                    "type": "string",
 +                    "enum": [
 +                        "always",
 +                        "never",
 +                        "skip_trivial"
 +                    ],
 +                    "enumDescriptions": [
 +                        "Always show lifetime elision hints.",
 +                        "Never show lifetime elision hints.",
 +                        "Only show lifetime elision hints if a return type is involved."
 +                    ]
 +                },
 +                "rust-analyzer.inlayHints.lifetimeElisionHints.useParameterNames": {
 +                    "markdownDescription": "Whether to prefer using parameter names as the name for elided lifetime hints if possible.",
 +                    "default": false,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.inlayHints.maxLength": {
 +                    "markdownDescription": "Maximum length for inlay hints. Set to null to have an unlimited length.",
 +                    "default": 25,
 +                    "type": [
 +                        "null",
 +                        "integer"
 +                    ],
 +                    "minimum": 0
 +                },
 +                "rust-analyzer.inlayHints.parameterHints.enable": {
 +                    "markdownDescription": "Whether to show function parameter name inlay hints at the call\nsite.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.inlayHints.reborrowHints.enable": {
 +                    "markdownDescription": "Whether to show inlay type hints for compiler inserted reborrows.",
 +                    "default": "never",
 +                    "type": "string",
 +                    "enum": [
 +                        "always",
 +                        "never",
 +                        "mutable"
 +                    ],
 +                    "enumDescriptions": [
 +                        "Always show reborrow hints.",
 +                        "Never show reborrow hints.",
 +                        "Only show mutable reborrow hints."
 +                    ]
 +                },
 +                "rust-analyzer.inlayHints.renderColons": {
 +                    "markdownDescription": "Whether to render leading colons for type hints, and trailing colons for parameter hints.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.inlayHints.typeHints.enable": {
 +                    "markdownDescription": "Whether to show inlay type hints for variables.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.inlayHints.typeHints.hideClosureInitialization": {
 +                    "markdownDescription": "Whether to hide inlay type hints for `let` statements that initialize to a closure.\nOnly applies to closures with blocks, same as `#rust-analyzer.inlayHints.closureReturnTypeHints.enable#`.",
 +                    "default": false,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.inlayHints.typeHints.hideNamedConstructor": {
 +                    "markdownDescription": "Whether to hide inlay type hints for constructors.",
 +                    "default": false,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.joinLines.joinAssignments": {
 +                    "markdownDescription": "Join lines merges consecutive declaration and initialization of an assignment.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.joinLines.joinElseIf": {
 +                    "markdownDescription": "Join lines inserts else between consecutive ifs.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.joinLines.removeTrailingComma": {
 +                    "markdownDescription": "Join lines removes trailing commas.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.joinLines.unwrapTrivialBlock": {
 +                    "markdownDescription": "Join lines unwraps trivial blocks.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.lens.debug.enable": {
 +                    "markdownDescription": "Whether to show `Debug` lens. Only applies when\n`#rust-analyzer.lens.enable#` is set.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.lens.enable": {
 +                    "markdownDescription": "Whether to show CodeLens in Rust files.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.lens.forceCustomCommands": {
 +                    "markdownDescription": "Internal config: use custom client-side commands even when the\nclient doesn't set the corresponding capability.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.lens.implementations.enable": {
 +                    "markdownDescription": "Whether to show `Implementations` lens. Only applies when\n`#rust-analyzer.lens.enable#` is set.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.lens.location": {
 +                    "markdownDescription": "Where to render annotations.",
 +                    "default": "above_name",
 +                    "type": "string",
 +                    "enum": [
 +                        "above_name",
 +                        "above_whole_item"
 +                    ],
 +                    "enumDescriptions": [
 +                        "Render annotations above the name of the item.",
 +                        "Render annotations above the whole item, including documentation comments and attributes."
 +                    ]
 +                },
 +                "rust-analyzer.lens.references.adt.enable": {
 +                    "markdownDescription": "Whether to show `References` lens for Struct, Enum, and Union.\nOnly applies when `#rust-analyzer.lens.enable#` is set.",
 +                    "default": false,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.lens.references.enumVariant.enable": {
 +                    "markdownDescription": "Whether to show `References` lens for Enum Variants.\nOnly applies when `#rust-analyzer.lens.enable#` is set.",
 +                    "default": false,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.lens.references.method.enable": {
 +                    "markdownDescription": "Whether to show `Method References` lens. Only applies when\n`#rust-analyzer.lens.enable#` is set.",
 +                    "default": false,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.lens.references.trait.enable": {
 +                    "markdownDescription": "Whether to show `References` lens for Trait.\nOnly applies when `#rust-analyzer.lens.enable#` is set.",
 +                    "default": false,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.lens.run.enable": {
 +                    "markdownDescription": "Whether to show `Run` lens. Only applies when\n`#rust-analyzer.lens.enable#` is set.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.linkedProjects": {
 +                    "markdownDescription": "Disable project auto-discovery in favor of explicitly specified set\nof projects.\n\nElements must be paths pointing to `Cargo.toml`,\n`rust-project.json`, or JSON objects in `rust-project.json` format.",
 +                    "default": [],
 +                    "type": "array",
 +                    "items": {
 +                        "type": [
 +                            "string",
 +                            "object"
 +                        ]
 +                    }
 +                },
 +                "rust-analyzer.lru.capacity": {
 +                    "markdownDescription": "Number of syntax trees rust-analyzer keeps in memory. Defaults to 128.",
 +                    "default": null,
 +                    "type": [
 +                        "null",
 +                        "integer"
 +                    ],
 +                    "minimum": 0
 +                },
 +                "rust-analyzer.notifications.cargoTomlNotFound": {
 +                    "markdownDescription": "Whether to show `can't find Cargo.toml` error message.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.procMacro.attributes.enable": {
 +                    "markdownDescription": "Expand attribute macros. Requires `#rust-analyzer.procMacro.enable#` to be set.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.procMacro.enable": {
 +                    "markdownDescription": "Enable support for procedural macros, implies `#rust-analyzer.cargo.buildScripts.enable#`.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.procMacro.ignored": {
 +                    "markdownDescription": "These proc-macros will be ignored when trying to expand them.\n\nThis config takes a map of crate names with the exported proc-macro names to ignore as values.",
 +                    "default": {},
 +                    "type": "object"
 +                },
 +                "rust-analyzer.procMacro.server": {
 +                    "markdownDescription": "Internal config, path to proc-macro server executable (typically,\nthis is rust-analyzer itself, but we override this in tests).",
 +                    "default": null,
 +                    "type": [
 +                        "null",
 +                        "string"
 +                    ]
 +                },
 +                "rust-analyzer.references.excludeImports": {
 +                    "markdownDescription": "Exclude imports from find-all-references.",
 +                    "default": false,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.runnables.command": {
 +                    "markdownDescription": "Command to be executed instead of 'cargo' for runnables.",
 +                    "default": null,
 +                    "type": [
 +                        "null",
 +                        "string"
 +                    ]
 +                },
 +                "rust-analyzer.runnables.extraArgs": {
 +                    "markdownDescription": "Additional arguments to be passed to cargo for runnables such as\ntests or binaries. For example, it may be `--release`.",
 +                    "default": [],
 +                    "type": "array",
 +                    "items": {
 +                        "type": "string"
 +                    }
 +                },
 +                "rust-analyzer.rustc.source": {
 +                    "markdownDescription": "Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private\nprojects, or \"discover\" to try to automatically find it if the `rustc-dev` component\nis installed.\n\nAny project which uses rust-analyzer with the rustcPrivate\ncrates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it.\n\nThis option does not take effect until rust-analyzer is restarted.",
 +                    "default": null,
 +                    "type": [
 +                        "null",
 +                        "string"
 +                    ]
 +                },
 +                "rust-analyzer.rustfmt.extraArgs": {
 +                    "markdownDescription": "Additional arguments to `rustfmt`.",
 +                    "default": [],
 +                    "type": "array",
 +                    "items": {
 +                        "type": "string"
 +                    }
 +                },
 +                "rust-analyzer.rustfmt.overrideCommand": {
 +                    "markdownDescription": "Advanced option, fully override the command rust-analyzer uses for\nformatting.",
 +                    "default": null,
 +                    "type": [
 +                        "null",
 +                        "array"
 +                    ],
 +                    "items": {
 +                        "type": "string"
 +                    }
 +                },
 +                "rust-analyzer.rustfmt.rangeFormatting.enable": {
 +                    "markdownDescription": "Enables the use of rustfmt's unstable range formatting command for the\n`textDocument/rangeFormatting` request. The rustfmt option is unstable and only\navailable on a nightly build.",
 +                    "default": false,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.semanticHighlighting.doc.comment.inject.enable": {
 +                    "markdownDescription": "Inject additional highlighting into doc comments.\n\nWhen enabled, rust-analyzer will highlight rust source in doc comments as well as intra\ndoc links.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.semanticHighlighting.operator.enable": {
 +                    "markdownDescription": "Use semantic tokens for operators.\n\nWhen disabled, rust-analyzer will emit semantic tokens only for operator tokens when\nthey are tagged with modifiers.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.semanticHighlighting.operator.specialization.enable": {
 +                    "markdownDescription": "Use specialized semantic tokens for operators.\n\nWhen enabled, rust-analyzer will emit special token types for operator tokens instead\nof the generic `operator` token type.",
 +                    "default": false,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.semanticHighlighting.punctuation.enable": {
 +                    "markdownDescription": "Use semantic tokens for punctuations.\n\nWhen disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when\nthey are tagged with modifiers or have a special role.",
 +                    "default": false,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.semanticHighlighting.punctuation.separate.macro.bang": {
 +                    "markdownDescription": "When enabled, rust-analyzer will emit a punctuation semantic token for the `!` of macro\ncalls.",
 +                    "default": false,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.semanticHighlighting.punctuation.specialization.enable": {
 +                    "markdownDescription": "Use specialized semantic tokens for punctuations.\n\nWhen enabled, rust-analyzer will emit special token types for punctuation tokens instead\nof the generic `punctuation` token type.",
 +                    "default": false,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.semanticHighlighting.strings.enable": {
 +                    "markdownDescription": "Use semantic tokens for strings.\n\nIn some editors (e.g. vscode) semantic tokens override other highlighting grammars.\nBy disabling semantic tokens for strings, other grammars can be used to highlight\ntheir contents.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.signatureInfo.detail": {
 +                    "markdownDescription": "Show full signature of the callable. Only shows parameters if disabled.",
 +                    "default": "full",
 +                    "type": "string",
 +                    "enum": [
 +                        "full",
 +                        "parameters"
 +                    ],
 +                    "enumDescriptions": [
 +                        "Show the entire signature.",
 +                        "Show only the parameters."
 +                    ]
 +                },
 +                "rust-analyzer.signatureInfo.documentation.enable": {
 +                    "markdownDescription": "Show documentation.",
 +                    "default": true,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.typing.autoClosingAngleBrackets.enable": {
 +                    "markdownDescription": "Whether to insert closing angle brackets when typing an opening angle bracket of a generic argument list.",
 +                    "default": false,
 +                    "type": "boolean"
 +                },
 +                "rust-analyzer.workspace.symbol.search.kind": {
 +                    "markdownDescription": "Workspace symbol search kind.",
 +                    "default": "only_types",
 +                    "type": "string",
 +                    "enum": [
 +                        "only_types",
 +                        "all_symbols"
 +                    ],
 +                    "enumDescriptions": [
 +                        "Search for types only.",
 +                        "Search for all symbols kinds."
 +                    ]
 +                },
 +                "rust-analyzer.workspace.symbol.search.limit": {
 +                    "markdownDescription": "Limits the number of items returned from a workspace symbol search (Defaults to 128).\nSome clients like vs-code issue new searches on result filtering and don't require all results to be returned in the initial search.\nOther clients requires all results upfront and might require a higher limit.",
 +                    "default": 128,
 +                    "type": "integer",
 +                    "minimum": 0
 +                },
 +                "rust-analyzer.workspace.symbol.search.scope": {
 +                    "markdownDescription": "Workspace symbol search scope.",
 +                    "default": "workspace",
 +                    "type": "string",
 +                    "enum": [
 +                        "workspace",
 +                        "workspace_and_dependencies"
 +                    ],
 +                    "enumDescriptions": [
 +                        "Search in current workspace only.",
 +                        "Search in current workspace and dependencies."
 +                    ]
 +                },
 +                "$generated-end": {}
 +            }
 +        },
 +        "problemPatterns": [
 +            {
 +                "name": "rustc",
 +                "patterns": [
 +                    {
 +                        "regexp": "^(warning|warn|error)(?:\\[(.*?)\\])?: (.*)$",
 +                        "severity": 1,
 +                        "code": 2,
 +                        "message": 3
 +                    },
 +                    {
 +                        "regexp": "^[\\s->=]*(.*?):(\\d*):(\\d*)\\s*$",
 +                        "file": 1,
 +                        "line": 2,
 +                        "column": 3
 +                    }
 +                ]
 +            },
 +            {
 +                "name": "rustc-json",
 +                "patterns": [
 +                    {
 +                        "regexp": "^.*\"message\":{\"message\":\"([^\"]*).*?\"file_name\":\"([^\"]+).*?\"line_start\":(\\d+).*?\"line_end\":(\\d+).*?\"column_start\":(\\d+).*?\"column_end\":(\\d+).*}$",
 +                        "message": 1,
 +                        "file": 2,
 +                        "line": 3,
 +                        "endLine": 4,
 +                        "column": 5,
 +                        "endColumn": 6
 +                    }
 +                ]
 +            }
 +        ],
 +        "languages": [
 +            {
 +                "id": "ra_syntax_tree",
 +                "extensions": [
 +                    ".rast"
 +                ]
 +            },
 +            {
 +                "id": "rust",
 +                "extensions": [
 +                    ".rs"
 +                ],
 +                "aliases": [
 +                    "Rust",
 +                    "rs"
 +                ],
 +                "configuration": "language-configuration.json"
 +            }
 +        ],
 +        "grammars": [
 +            {
 +                "language": "ra_syntax_tree",
 +                "scopeName": "source.ra_syntax_tree",
 +                "path": "ra_syntax_tree.tmGrammar.json"
 +            }
 +        ],
 +        "problemMatchers": [
 +            {
 +                "name": "rustc",
 +                "owner": "rustc",
 +                "source": "rustc",
 +                "fileLocation": [
 +                    "autoDetect",
 +                    "${workspaceRoot}"
 +                ],
 +                "pattern": "$rustc"
 +            },
 +            {
 +                "name": "rustc-json",
 +                "owner": "rustc",
 +                "source": "rustc",
 +                "fileLocation": [
 +                    "autoDetect",
 +                    "${workspaceRoot}"
 +                ],
 +                "pattern": "$rustc-json"
 +            },
 +            {
 +                "name": "rustc-watch",
 +                "owner": "rustc",
 +                "source": "rustc",
 +                "fileLocation": [
 +                    "autoDetect",
 +                    "${workspaceRoot}"
 +                ],
 +                "background": {
 +                    "beginsPattern": "^\\[Running\\b",
 +                    "endsPattern": "^\\[Finished running\\b"
 +                },
 +                "pattern": "$rustc"
 +            }
 +        ],
 +        "colors": [
 +            {
 +                "id": "rust_analyzer.syntaxTreeBorder",
 +                "description": "Color of the border displayed in the Rust source code for the selected syntax node (see \"Show Syntax Tree\" command)",
 +                "defaults": {
 +                    "dark": "#ffffff",
 +                    "light": "#b700ff",
 +                    "highContrast": "#b700ff"
 +                }
 +            }
 +        ],
 +        "semanticTokenTypes": [
 +            {
 +                "id": "angle",
 +                "description": "Style for < or >",
 +                "superType": "punctuation"
 +            },
 +            {
 +                "id": "arithmetic",
 +                "description": "Style for arithmetic operators",
 +                "superType": "operator"
 +            },
 +            {
 +                "id": "attribute",
 +                "description": "Style for attributes"
 +            },
 +            {
 +                "id": "attributeBracket",
 +                "description": "Style for attribute invocation brackets, that is the `#[` and `]` tokens",
 +                "superType": "punctuation"
 +            },
 +            {
 +                "id": "bitwise",
 +                "description": "Style for bitwise operators",
 +                "superType": "operator"
 +            },
 +            {
 +                "id": "boolean",
 +                "description": "Style for boolean literals",
 +                "superType": "keyword"
 +            },
 +            {
 +                "id": "brace",
 +                "description": "Style for { or }",
 +                "superType": "punctuation"
 +            },
 +            {
 +                "id": "bracket",
 +                "description": "Style for [ or ]",
 +                "superType": "punctuation"
 +            },
 +            {
 +                "id": "builtinAttribute",
 +                "description": "Style for builtin attributes",
 +                "superType": "attribute"
 +            },
 +            {
 +                "id": "builtinType",
 +                "description": "Style for builtin types",
 +                "superType": "type"
 +            },
 +            {
 +                "id": "character",
 +                "description": "Style for character literals",
 +                "superType": "string"
 +            },
 +            {
 +                "id": "colon",
 +                "description": "Style for :",
 +                "superType": "punctuation"
 +            },
 +            {
 +                "id": "comma",
 +                "description": "Style for ,",
 +                "superType": "punctuation"
 +            },
 +            {
 +                "id": "comparison",
 +                "description": "Style for comparison operators",
 +                "superType": "operator"
 +            },
 +            {
 +                "id": "constParameter",
 +                "description": "Style for const generics"
 +            },
 +            {
 +                "id": "derive",
 +                "description": "Style for derives",
 +                "superType": "attribute"
 +            },
 +            {
 +                "id": "dot",
 +                "description": "Style for .",
 +                "superType": "punctuation"
 +            },
 +            {
 +                "id": "escapeSequence",
 +                "description": "Style for char escapes in strings"
 +            },
 +            {
 +                "id": "formatSpecifier",
 +                "description": "Style for {} placeholders in format strings"
 +            },
 +            {
 +                "id": "label",
 +                "description": "Style for labels"
 +            },
 +            {
 +                "id": "lifetime",
 +                "description": "Style for lifetimes"
 +            },
 +            {
 +                "id": "logical",
 +                "description": "Style for logic operators",
 +                "superType": "operator"
 +            },
 +            {
 +                "id": "macroBang",
 +                "description": "Style for the ! token of macro calls",
 +                "superType": "punctuation"
 +            },
 +            {
 +                "id": "operator",
 +                "description": "Style for operators",
 +                "superType": "punctuation"
 +            },
 +            {
 +                "id": "parenthesis",
 +                "description": "Style for ( or )",
 +                "superType": "punctuation"
 +            },
 +            {
 +                "id": "punctuation",
 +                "description": "Style for generic punctuation"
 +            },
 +            {
 +                "id": "selfKeyword",
 +                "description": "Style for the self keyword",
 +                "superType": "keyword"
 +            },
 +            {
 +                "id": "selfTypeKeyword",
 +                "description": "Style for the self type keyword",
 +                "superType": "keyword"
 +            },
 +            {
 +                "id": "semicolon",
 +                "description": "Style for ;",
 +                "superType": "punctuation"
 +            },
 +            {
 +                "id": "typeAlias",
 +                "description": "Style for type aliases",
 +                "superType": "type"
 +            },
 +            {
 +                "id": "union",
 +                "description": "Style for C-style untagged unions",
 +                "superType": "type"
 +            },
 +            {
 +                "id": "unresolvedReference",
 +                "description": "Style for names which can not be resolved due to compilation errors"
 +            }
 +        ],
 +        "semanticTokenModifiers": [
 +            {
 +                "id": "async",
 +                "description": "Style for async functions and the `async` and `await` keywords"
 +            },
 +            {
 +                "id": "attribute",
 +                "description": "Style for elements within attributes"
 +            },
 +            {
 +                "id": "callable",
 +                "description": "Style for locals whose types implements one of the `Fn*` traits"
 +            },
 +            {
 +                "id": "constant",
 +                "description": "Style for compile-time constants"
 +            },
 +            {
 +                "id": "consuming",
 +                "description": "Style for locals that are being consumed when use in a function call"
 +            },
 +            {
 +                "id": "controlFlow",
 +                "description": "Style for control-flow related tokens, this includes the `?` operator"
 +            },
 +            {
 +                "id": "crateRoot",
 +                "description": "Style for names resolving to a crate root"
 +            },
 +            {
 +                "id": "injected",
 +                "description": "Style for doc-string injected highlighting like rust source blocks in documentation"
 +            },
 +            {
 +                "id": "intraDocLink",
 +                "description": "Style for intra doc links in doc-strings"
 +            },
 +            {
 +                "id": "library",
 +                "description": "Style for items that are defined outside of the current crate"
 +            },
 +            {
 +                "id": "mutable",
 +                "description": "Style for mutable locals and statics as well as functions taking `&mut self`"
 +            },
 +            {
 +                "id": "public",
 +                "description": "Style for items that are from the current crate and are `pub`"
 +            },
 +            {
 +                "id": "reference",
 +                "description": "Style for locals behind a reference and functions taking `self` by reference"
 +            },
 +            {
 +                "id": "trait",
 +                "description": "Style for associated trait items"
 +            },
 +            {
 +                "id": "unsafe",
 +                "description": "Style for unsafe operations, like unsafe function calls, as well as the `unsafe` token"
 +            }
 +        ],
 +        "semanticTokenScopes": [
 +            {
 +                "language": "rust",
 +                "scopes": {
 +                    "attribute": [
 +                        "meta.attribute.rust"
 +                    ],
 +                    "boolean": [
 +                        "constant.language.boolean.rust"
 +                    ],
 +                    "builtinType": [
 +                        "support.type.primitive.rust"
 +                    ],
 +                    "constParameter": [
 +                        "constant.other.caps.rust"
 +                    ],
 +                    "enum": [
 +                        "entity.name.type.enum.rust"
 +                    ],
 +                    "formatSpecifier": [
 +                        "punctuation.section.embedded.rust"
 +                    ],
 +                    "function": [
 +                        "entity.name.function.rust"
 +                    ],
 +                    "interface": [
 +                        "entity.name.type.trait.rust"
 +                    ],
 +                    "keyword": [
 +                        "keyword.other.rust"
 +                    ],
 +                    "keyword.controlFlow": [
 +                        "keyword.control.rust"
 +                    ],
 +                    "lifetime": [
 +                        "storage.modifier.lifetime.rust"
 +                    ],
 +                    "macroBang": [
 +                        "entity.name.function.macro.rust"
 +                    ],
 +                    "method": [
 +                        "entity.name.function.rust"
 +                    ],
 +                    "struct": [
 +                        "entity.name.type.struct.rust"
 +                    ],
 +                    "typeAlias": [
 +                        "entity.name.type.declaration.rust"
 +                    ],
 +                    "union": [
 +                        "entity.name.type.union.rust"
 +                    ],
 +                    "variable": [
 +                        "variable.other.rust"
 +                    ],
 +                    "variable.constant": [
 +                        "variable.other.constant.rust"
 +                    ],
 +                    "*.mutable": [
 +                        "markup.underline"
 +                    ]
 +                }
 +            }
 +        ],
 +        "menus": {
 +            "commandPalette": [
 +                {
 +                    "command": "rust-analyzer.syntaxTree",
 +                    "when": "inRustProject"
 +                },
 +                {
 +                    "command": "rust-analyzer.viewHir",
 +                    "when": "inRustProject"
 +                },
 +                {
 +                    "command": "rust-analyzer.viewFileText",
 +                    "when": "inRustProject"
 +                },
 +                {
 +                    "command": "rust-analyzer.expandMacro",
 +                    "when": "inRustProject"
 +                },
 +                {
 +                    "command": "rust-analyzer.matchingBrace",
 +                    "when": "inRustProject"
 +                },
 +                {
 +                    "command": "rust-analyzer.parentModule",
 +                    "when": "inRustProject"
 +                },
 +                {
 +                    "command": "rust-analyzer.joinLines",
 +                    "when": "inRustProject"
 +                },
 +                {
 +                    "command": "rust-analyzer.run",
 +                    "when": "inRustProject"
 +                },
 +                {
 +                    "command": "rust-analyzer.debug",
 +                    "when": "inRustProject"
 +                },
 +                {
 +                    "command": "rust-analyzer.newDebugConfig",
 +                    "when": "inRustProject"
 +                },
 +                {
 +                    "command": "rust-analyzer.analyzerStatus",
 +                    "when": "inRustProject"
 +                },
 +                {
 +                    "command": "rust-analyzer.memoryUsage",
 +                    "when": "inRustProject"
 +                },
 +                {
 +                    "command": "rust-analyzer.reloadWorkspace",
 +                    "when": "inRustProject"
 +                },
 +                {
 +                    "command": "rust-analyzer.reload",
 +                    "when": "inRustProject"
 +                },
 +                {
 +                    "command": "rust-analyzer.onEnter",
 +                    "when": "inRustProject"
 +                },
 +                {
 +                    "command": "rust-analyzer.ssr",
 +                    "when": "inRustProject"
 +                },
 +                {
 +                    "command": "rust-analyzer.serverVersion",
 +                    "when": "inRustProject"
 +                },
 +                {
 +                    "command": "rust-analyzer.openDocs",
 +                    "when": "inRustProject"
 +                },
 +                {
 +                    "command": "rust-analyzer.openCargoToml",
 +                    "when": "inRustProject"
 +                }
 +            ],
 +            "editor/context": [
 +                {
 +                    "command": "rust-analyzer.peekTests",
 +                    "when": "inRustProject",
 +                    "group": "navigation@1000"
 +                }
 +            ]
 +        }
 +    }
 +}
index fa0824ac53c0a9c130636512c0271fc3f0e9c0e6,0000000000000000000000000000000000000000..a910e012b73499449e8b9f37077a9030f0fc7a05
mode 100644,000000..100644
--- /dev/null
@@@ -1,1 -1,0 +1,11 @@@
 +[assign]
++
++[shortcut]
++
++[relabel]
++allow-unauthenticated = [
++    "S-*",
++]
++
++[autolabel."S-waiting-on-review"]
++new_pr = true